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
| Requirement | Details |
|---|---|
| Docker | Docker 20.10 or later (or Docker Desktop) |
| License key | Issued from piifirewall.com/console → "Self-hosted Proxy" tab |
| GitHub account | Required to pull the image from GitHub Container Registry (GHCR) |
| GitHub PAT | Personal Access Token with read:packages scope |
| LLM API key | Key for the provider you want to use (OpenAI / Anthropic / Google etc.) |
Quick Start (About 5 Minutes)
Step 1 — Get Your License Key
- Log in to piifirewall.com/console
- Open the "Self-hosted Proxy" tab
- Click "Issue license key"
- 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:
- GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
- Click "Generate new token (classic)"
- Check
read:packagesscope - Copy the token
Log in to GHCR:
docker login ghcr.io -u <YOUR_GITHUB_USERNAME> -p <YOUR_GITHUB_PAT>Step 3 — Start the Proxy
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:latestSet only the API key for the LLM provider you are using (not all are required).
Step 4 — Verify It's Running
# 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):
{
"status": "ok",
"license": {
"mode": "jwt",
"plan": "free",
"api_calls_this_month": 0
}
}Step 5 — Update Your App's Endpoint
- const PIIFW_BASE = "https://pii-firewallproxy-production.up.railway.app";
+ const PIIFW_BASE = "http://localhost:3001"; // or your server's internal addressThat'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:
- Set
PIIFW_STORE=redisorPIIFW_STORE=postgresin production to persist tokens - Set up regular backups of Redis / PostgreSQL
- Identify all tables/fields storing
[SECURED:xxx]values and include them in your backup plan
Environment Variable Reference
Required to Start
| Variable | Required | Default | Description |
|---|---|---|---|
PIIFW_LICENSE_KEY | ✅* | — | License key (JWT format). Not required if using PIIFW_LICENSE_FILE |
PORT | 3001 | Listening port | |
NODE_ENV | production | Runtime environment |
LLM API Keys (Set only the provider you use)
| Variable | Description |
|---|---|
OPENAI_API_KEY | OpenAI (GPT-4o, etc.) |
ANTHROPIC_API_KEY | Anthropic (Claude) |
GOOGLE_API_KEY | Google (Gemini) |
CUSTOM_LLM_BASE_URL | Base URL for OpenAI-compatible providers (Groq / Ollama / Azure OpenAI, etc.) |
CUSTOM_LLM_API_KEY | API key for the above provider |
CUSTOM_LLM_MODEL | Model name to use (e.g., llama-3.3-70b-versatile) |
OpenAI-Compatible Providers
Setting CUSTOM_LLM_BASE_URL enables support for the following providers:
| Provider | Base URL |
|---|---|
| Groq | https://api.groq.com/openai/v1 |
| Azure OpenAI | https://{resource}.openai.azure.com/openai/deployments/{deployment}/ |
| Ollama (local) | http://localhost:11434/v1 |
| LM Studio (local) | http://localhost:1234/v1 |
| Mistral | https://api.mistral.ai/v1 |
| Together AI | https://api.together.xyz/v1 |
Using Ollama or LM Studio enables a fully offline (air-gapped) configuration.
Storage Settings
| Variable | Default | Description |
|---|---|---|
PIIFW_STORE | memory | Storage backend (memory / redis / postgres) |
PIIFW_TOKEN_TTL | 86400000 | Token retention time in milliseconds. 0 = no expiry |
PIIFW_STORE_URL | — | Connection URL for Redis or PostgreSQL |
Logging
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL | info | Log 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
| Variable | Description |
|---|---|
PIIFW_LICENSE_FILE | Path to air-gap mode license file (Enterprise) |
UI_ORIGIN | Allowed CORS origins (comma-separated for multiple) |
Storage Configuration
In-Memory (Default)
PIIFW_STORE=memory| ✅ Advantages | ⚠️ Caveats |
|---|---|
| No setup required — starts immediately | Tokens are lost on container restart |
| Ideal for POC and evaluation | Not recommended for production |
Redis (Recommended for Production)
PIIFW_STORE=redis
PIIFW_STORE_URL=redis://localhost:6379
# TLS: rediss://user:password@your-redis-host:6380| ✅ Advantages | Use case |
|---|---|
| Tokens persist across container restarts | Production / staging environments |
| Fast (in-memory database) | High-traffic environments |
PostgreSQL (Enterprise)
PIIFW_STORE=postgres
PIIFW_STORE_URL=postgresql://piifw:password@localhost:5432/piifw| ✅ Advantages | Use case |
|---|---|
| Full persistence with audit log support | Enterprise / compliance requirements |
| Integrates with existing PostgreSQL | Leverage existing infrastructure |
Using Docker Compose
The included docker-compose.selfhosted.yml makes it easy to start with Redis or PostgreSQL.
# 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 -dAir-Gap Mode (Enterprise)
For closed network environments where external communication must be zero.
Uses a .lic file issued annually by M-DEEP.
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:latestAir-Gap Mode Features
PIIFW_LICENSE_KEYis not used (PIIFW_LICENSE_FILEis 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
.licfile 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.
# 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.
{
"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)
}| Data | Sent? |
|---|---|
| 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:
curl -X POST http://localhost:3001/license/refreshTroubleshooting
Container won't start / exits immediately
# Check logs
docker logs piifw-proxyCommon causes:
| Error message | Cause | Resolution |
|---|---|---|
LICENSE ERROR: 有効期限切れ (expired) | License key has expired | Renew your key at piifirewall.com/console |
LICENSE ERROR: 発行元が不正 (invalid issuer) | Invalid license key | Verify the key was copied correctly |
LICENSE ERROR: 署名が不正 (invalid signature) | License key has been tampered | Issue a new key |
Cannot pull image from GHCR
# 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 tokensCannot restore [SECURED:xxx] tokens
# 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 backupsHealth check fails
# Check port binding
docker ps | grep piifw-proxy
# Check from inside the container
docker exec piifw-proxy wget -qO- http://localhost:3001/healthNext Steps
- REST API Reference — Full endpoint specification
- Secure RAG — How to integrate with a RAG pipeline
- Composite Attack Detection — Prompt injection & SQL injection detection