The local proxy (cloak-proxy) is a localhost, OpenAI-compatible endpoint that
tokenises PII on your own machine before anything leaves it, then detokenises the
reply locally. You change exactly one line of your existing OpenAI code — the
base_url — and every request now leaves your machine carrying only opaque tokens
(<<CLOAK:CONTACT_EM:22fff1>>), never the raw PII.
The proxy is gateway-locked by design: the already-tokenised request always flows
through the CloakAPI gateway to your provider. The gateway is a blind relay —
it sees only tokens, never your raw data — and that is exactly what lets it meter the
call, sign a receipt, and bill the markup on every request. Your provider, model
and BYOK provider key choice are configured in your CloakAPI account; the proxy
itself accepts no provider URL, so there is no unmetered, receipt-less path.
→ your AI provider (your BYOK key or CloakAPI's pooled key)
You need two secrets from your CloakAPI account: your tenant API key and your
pre-tokenised trust key (the per-tenant HMAC key that proves to the gateway the
request was already tokenised client-side, so it must relay blind).
Path to a file holding your CloakAPI tenant API key (never logged)
CLOAK_TENANT_HMAC_KEY_FILE
—
Path to a file holding your pre-tokenised trust key (never logged)
CLOAK_GATEWAY_BASE_URL
https://api.cloakapi.io/api/v1
The CloakAPI gateway. Gateway-locked: only *.cloakapi.io (or loopback, for dev) is accepted — a direct provider URL is refused at startup
CLOAK_PROXY_LISTEN
127.0.0.1:8799
Bind address (localhost-only by default). Port is configurable — see below.
CLOAK_NER
auto
Free-form NAME protection: auto / on / off
CLOAK_NER_FALLBACK
fail-closed
When auto + model unreachable: fail-closed or l1-only
CLOAK_NER_BASE_URL
http://127.0.0.1:11434/v1
Loopback Ollama endpoint (must be loopback)
CLOAK_NER_MODEL
phi3.5
Local NAME-detection model tag
The default port (8799) is a default, not a requirement. We picked 8799
specifically to avoid clashing with common data-science tools (RStudio Server /
Dask on 8787, Jupyter on 8888). If 8799 is already taken on your machine — or
you just prefer another port — set CLOAK_PROXY_LISTEN=127.0.0.1:<free-port>
and point your SDK’s base_url at the same port. If the port is busy the
proxy exits with a clear message telling you exactly that (it never silently
rebinds, so your base_url always stays predictable). Example:
Terminal window
CLOAK_PROXY_LISTEN=127.0.0.1:8801cloak-proxy
# then: base_url="http://localhost:8801/v1"
NAME coverage needs a local model (honest note)
Structured PII works standalone — email, phone, card PAN, SSN, IBAN, IP, DOB, passport,
UK NINO, medical-record IDs, API keys — all tokenised with no extra dependencies. Run
with CLOAK_NER=off to get exactly this.
Free-form PERSON names carry no structural signal, so they need a small local NER
model. The proxy talks to a loopback-onlyOllama endpoint running
Phi-3.5-Mini by default:
Terminal window
ollamapullphi3.5 && ollamaserve
# then run the proxy with the default CLOAK_NER=auto
The local model only detects name spans; the deterministic engine still mints the
<<CLOAK:PERSON:…>> tokens, so the same name always maps to the same token and is restored
locally. The NER endpoint is enforced loopback — no fragment of your text is ever sent to a
non-local host.
With the default CLOAK_NER=auto, if the local model is unreachable the proxy fails
closed (HTTP 503) rather than let a name egress — unless you opt into
CLOAK_NER_FALLBACK=l1-only (structured PII still tokenised, names not, loudly labelled).
Every response carries an x-cloak-name-protection: active|disabled|fail-closed header.
Status (honest)
Implemented + live-proven: non-streaming /v1/chat/completions, /v1/models passthrough,
/healthz; structured-PII round-trip; the client-side NAME layer; and the gateway
pre-tokenised passthrough (the only forwarding mode — every call is metered and carries a
signed receipt, while the gateway sees only tokens).
Phase 2 (not yet built): SSE streaming ("stream": true returns 501) and Anthropic-native
/v1/messages.