Skip to content

Self-hosted Proxy Setup Guide

Audience: Developers and infrastructure engineers who want to run PII Firewall Proxy on their own servers

The Self-hosted Proxy is a Docker image that performs PII masking entirely within your own network.
No PII leaves your organization's trust boundary.

Privacy by Design

Your App → [PIIFW Proxy (your server)] → masked text only

                                          LLM API (external)

PII stays inside your network ✅

Prerequisites

RequirementDetails
DockerDocker 20.10 or later (or Docker Desktop)
License keyIssued from piifirewall.com/console → "Self-hosted Proxy" tab
GitHub accountRequired to pull the image from GitHub Container Registry (GHCR)
GitHub PATPersonal Access Token with read:packages scope
LLM API keyKey for the provider you want to use (OpenAI / Anthropic / Google etc.)

Quick Start (About 5 Minutes)

Step 1 — Get Your License Key

  1. Log in to piifirewall.com/console
  2. Open the "Self-hosted Proxy" tab
  3. Click "Issue license key"
  4. Copy the JWT-format key (eyJ...)

Store your license key safely

The license key cannot be displayed again. Store it immediately in a secure location (password manager, etc.).

Step 2 — Log in to GHCR

The PII Firewall container image is distributed via a private GitHub Container Registry (GHCR) repository.
A GitHub PAT with read:packages scope is required to pull.

Create a GitHub PAT:

  1. GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
  2. Click "Generate new token (classic)"
  3. Check read:packages scope
  4. Copy the token

Log in to GHCR:

bash
docker login ghcr.io -u <YOUR_GITHUB_USERNAME> -p <YOUR_GITHUB_PAT>

Step 3 — Start the Proxy

bash
docker run -d \
  --name piifw-proxy \
  -p 3001:3001 \
  -e PIIFW_LICENSE_KEY="eyJhbGci..." \
  -e OPENAI_API_KEY="sk-..." \
  ghcr.io/kmishimaslgithub/pii-firewall/proxy:latest

Set only the API key for the LLM provider you are using (not all are required).

Step 4 — Verify It's Running

bash
# Health check
curl http://localhost:3001/health

# PII detection test
curl -X POST http://localhost:3001/detect \
  -H "Content-Type: application/json" \
  -d '{"text": "Please email John Smith at john@example.com"}'

Expected response (/health):

json
{
  "status": "ok",
  "license": {
    "mode": "jwt",
    "plan": "free",
    "api_calls_this_month": 0
  }
}

Step 5 — Update Your App's Endpoint

diff
- const PIIFW_BASE = "https://pii-firewallproxy-production.up.railway.app";
+ const PIIFW_BASE = "http://localhost:3001";  // or your server's internal address

That's it. PII protection now runs entirely inside your network.


⚠️ Important: Token Loss Risk

Backup required

The [SECURED:xxx] tokens returned by mask_pii() exist only in this Proxy container's storage.
If the container is deleted or restarted (default memory mode), stored tokens are permanently lost and cannot be restored.

Required actions:

  1. Set PIIFW_STORE=redis or PIIFW_STORE=postgres in production to persist tokens
  2. Set up regular backups of Redis / PostgreSQL
  3. Identify all tables/fields storing [SECURED:xxx] values and include them in your backup plan

Environment Variable Reference

Required to Start

VariableRequiredDefaultDescription
PIIFW_LICENSE_KEY✅*License key (JWT format). Not required if using PIIFW_LICENSE_FILE
PORT3001Listening port
NODE_ENVproductionRuntime environment

LLM API Keys (Set only the provider you use)

VariableDescription
OPENAI_API_KEYOpenAI (GPT-4o, etc.)
ANTHROPIC_API_KEYAnthropic (Claude)
GOOGLE_API_KEYGoogle (Gemini)
CUSTOM_LLM_BASE_URLBase URL for OpenAI-compatible providers (Groq / Ollama / Azure OpenAI, etc.)
CUSTOM_LLM_API_KEYAPI key for the above provider
CUSTOM_LLM_MODELModel name to use (e.g., llama-3.3-70b-versatile)

OpenAI-Compatible Providers

Setting CUSTOM_LLM_BASE_URL enables support for the following providers:

ProviderBase URL
Groqhttps://api.groq.com/openai/v1
Azure OpenAIhttps://{resource}.openai.azure.com/openai/deployments/{deployment}/
Ollama (local)http://localhost:11434/v1
LM Studio (local)http://localhost:1234/v1
Mistralhttps://api.mistral.ai/v1
Together AIhttps://api.together.xyz/v1

Using Ollama or LM Studio enables a fully offline (air-gapped) configuration.

Storage Settings

VariableDefaultDescription
PIIFW_STOREmemoryStorage backend (memory / redis / postgres)
PIIFW_TOKEN_TTL86400000Token retention time in milliseconds. 0 = no expiry
PIIFW_STORE_URLConnection URL for Redis or PostgreSQL

Logging

VariableDefaultDescription
LOG_LEVELinfoLog level (debug / info / warn / error)

Privacy by Design — Logging

Regardless of the log level, request bodies (which may contain PII) are never logged.
Only metadata is recorded (timestamps, HTTP status codes, processing time). This behavior is enforced at the code level and cannot be changed by configuration.

Advanced Settings

VariableDescription
PIIFW_LICENSE_FILEPath to air-gap mode license file (Enterprise)
UI_ORIGINAllowed CORS origins (comma-separated for multiple)

Storage Configuration

In-Memory (Default)

bash
PIIFW_STORE=memory
✅ Advantages⚠️ Caveats
No setup required — starts immediatelyTokens are lost on container restart
Ideal for POC and evaluationNot recommended for production
bash
PIIFW_STORE=redis
PIIFW_STORE_URL=redis://localhost:6379
# TLS: rediss://user:password@your-redis-host:6380
✅ AdvantagesUse case
Tokens persist across container restartsProduction / staging environments
Fast (in-memory database)High-traffic environments

PostgreSQL (Enterprise)

bash
PIIFW_STORE=postgres
PIIFW_STORE_URL=postgresql://piifw:password@localhost:5432/piifw
✅ AdvantagesUse case
Full persistence with audit log supportEnterprise / compliance requirements
Integrates with existing PostgreSQLLeverage existing infrastructure

Using Docker Compose

The included docker-compose.selfhosted.yml makes it easy to start with Redis or PostgreSQL.

bash
# Download
curl -O https://raw.githubusercontent.com/kmishimaslgithub/pii-firewall/main/proxy/docker-compose.selfhosted.yml

# Create environment file
cp .env.selfhosted.example .env
# Edit .env: set PIIFW_LICENSE_KEY and your LLM API key(s)

# Start (in-memory mode)
docker compose -f docker-compose.selfhosted.yml up -d

# Start (with Redis — recommended for production)
docker compose -f docker-compose.selfhosted.yml --profile redis up -d

Air-Gap Mode (Enterprise)

For closed network environments where external communication must be zero.
Uses a .lic file issued annually by M-DEEP.

bash
docker run -d \
  --name piifw-proxy \
  -p 3001:3001 \
  -e PIIFW_LICENSE_FILE=/etc/piifw/license.lic \
  -v /path/to/your/license.lic:/etc/piifw/license.lic:ro \
  -e CUSTOM_LLM_BASE_URL=http://your-ollama-server:11434/v1 \
  -e CUSTOM_LLM_MODEL=llama3.2 \
  ghcr.io/kmishimaslgithub/pii-firewall/proxy:latest

Air-Gap Mode Features

  • PIIFW_LICENSE_KEY is not used (PIIFW_LICENSE_FILE is used instead)
  • No monthly reports are sent (zero external communication)
  • LLM can be an on-premises inference server like Ollama
  • License validity is 1 year. To renew, replace the .lic file and restart the container

Verifying Image Authenticity (SHA256)

The SHA256 digest of each release is published on the GitHub Releases page.
Use the following steps to verify that the pulled image is an official build.

bash
# 1. Find the SHA256 digest on the GitHub Release page
#    e.g., sha256:abc123...

# 2. Pull by digest (recommended — tamper-proof)
docker pull ghcr.io/kmishimaslgithub/pii-firewall/proxy@sha256:abc123...

# 3. Verify the digest of your pulled image
docker inspect ghcr.io/kmishimaslgithub/pii-firewall/proxy:latest \
  --format='{{index .RepoDigests 0}}'

Privacy by Design — What M-DEEP Receives

In standard mode (PIIFW_LICENSE_KEY), the Proxy sends a report to M-DEEP once a month.
Only the following 3 fields are sent. No PII, text, or IP addresses are included.

json
{
  "license_key_hash": "sha256:xxxx",  // Hash of the license key (never the key itself)
  "api_calls_this_month": 8432,        // API call count (integer only)
  "proxy_version": "1.2.0"             // Proxy version (for compatibility checks)
}
DataSent?
Text / prompt content❌ Never
Pre-mask or post-mask data❌ Never
IP addresses / host information❌ Never
User information / access logs❌ Never
API call count (integer)✅ Monthly only

To manually trigger or inspect the monthly report:

bash
curl -X POST http://localhost:3001/license/refresh

Troubleshooting

Container won't start / exits immediately

bash
# Check logs
docker logs piifw-proxy

Common causes:

Error messageCauseResolution
LICENSE ERROR: 有効期限切れ (expired)License key has expiredRenew your key at piifirewall.com/console
LICENSE ERROR: 発行元が不正 (invalid issuer)Invalid license keyVerify the key was copied correctly
LICENSE ERROR: 署名が不正 (invalid signature)License key has been tamperedIssue a new key

Cannot pull image from GHCR

bash
# Re-authenticate
docker login ghcr.io -u <YOUR_GITHUB_USERNAME> -p <YOUR_GITHUB_PAT>

# Verify PAT scope (read:packages required)
# GitHub → Settings → Developer settings → Personal access tokens

Cannot restore [SECURED:xxx] tokens

bash
# Check storage status
curl http://localhost:3001/health | jq .

# If PIIFW_STORE=memory → tokens are lost on container restart
# → Switch to PIIFW_STORE=redis or postgres and set up backups

Health check fails

bash
# Check port binding
docker ps | grep piifw-proxy

# Check from inside the container
docker exec piifw-proxy wget -qO- http://localhost:3001/health

Next Steps

Privacy by Design.