- Compatible with
Claude Code,
Codex, and other
OpenAI-compatible clients.
- Anonymizes sensitive values before they leave the machine, sends only tokenized payloads upstream, then restores the original values locally in responses.
- Includes a local dashboard and test tool, powered by
Presidio,
Gitleaks, a local NER engine, and
SQLite for stable anonymization and de-anonymization round-trips.
flowchart LR
vscode["VS Code clients<br/>Claude extension / Codex extension"]
proxy["klovys99 local proxy"]
apis["Remote APIs<br/>Anthropic / OpenAI-compatible"]
vscode -->|"1. raw prompt / request"| proxy
proxy -->|"2. anonymized request only"| apis
apis -->|"3. provider response"| proxy
proxy -->|"4. restored response"| vscode
classDef klovys fill:#1371E6,stroke:#1371E6,color:#ffffff;
class proxy klovys;
flowchart LR
client["Local client or web UI"]
input["Prompt / tool output / text payload"]
engine["klovys99 anonymization engine"]
regex["Regex detectors"]
presidio["Presidio rules"]
gitleaks["Gitleaks rules"]
ner["Open-source NER"]
sqlite["SQLite mapping store"]
upstream["LLM provider"]
restore["Response de-anonymization"]
client --> input
input --> engine
engine --> regex
engine --> presidio
engine --> gitleaks
engine --> ner
engine <-->|"stable token map"| sqlite
engine -->|"anonymized text only"| upstream
upstream -->|"tokenized response"| restore
sqlite --> restore
restore -->|"clear response restored locally"| client
classDef klovys fill:#1371E6,stroke:#1371E6,color:#ffffff;
class engine,restore klovys;
flowchart LR
runtime["klovys99 process"]
admin["Admin web UI<br/>dashboard, stats, controls, test tool"]
chat["User web UI<br/>Gemini / Claude / ChatGPT / Mistral AI"]
auth["OAuth login<br/>work in progress"]
keys["Current auth mode<br/>user enters API keys"]
runtime --> admin
runtime --> chat
chat --> keys
chat -. planned .-> auth
classDef klovys fill:#1371E6,stroke:#1371E6,color:#ffffff;
class runtime klovys;
npm install is currently broken. Until it is fixed, run klovys99 from source: start the app manually with Go and start the GLiNER sidecar with Docker, as described below.
The simplest way to start everything (GLiNER sidecar + proxy) in one command:
./scripts/dev-start.shThis builds the GLiNER Docker image, downloads the pinned model on first run,
starts the sidecar container, and then runs go run ./cmd/klovys99.
If you prefer to run the steps yourself from the repository root:
- Build and start the GLiNER sidecar with Docker.
docker build -t klovys99-gliner:local sidecar/gliner
docker run --rm --user "$(id -u):$(id -g)" \
-e GLINER_MODEL=urchade/gliner_multi_pii-v1 \
-e GLINER_MODEL_REVISION=1fcf13e85f4eef5394e1fcd406cf2ca9ea82351d \
-e GLINER_MODEL_DIR=/models/model \
-e HOME=/tmp -e XDG_CACHE_HOME=/tmp/.cache -e HF_HOME=/tmp/.cache/huggingface \
-v "$HOME/.klovys99/gliner:/models" \
klovys99-gliner:local python /app/install_model.py
KLOVIS_GLINER_MODEL=urchade/gliner_multi_pii-v1 \
KLOVIS_GLINER_MODEL_REVISION=1fcf13e85f4eef5394e1fcd406cf2ca9ea82351d \
KLOVIS_GLINER_DATA_DIR="$HOME/.klovys99/gliner" \
docker compose -f sidecar/gliner/compose.yaml up -d --no-build- Start the local proxy with Go, pointing it at the sidecar you just started.
KLOVIS_GLINER_MODE=full \
KLOVIS_GLINER_URL=http://127.0.0.1:8091 \
KLOVIS_GLINER_MODEL=urchade/gliner_multi_pii-v1 \
KLOVIS_GLINER_MODEL_REVISION=1fcf13e85f4eef5394e1fcd406cf2ca9ea82351d \
go run ./cmd/klovys99Klovys99 listens on http://127.0.0.1:8080 by default and exposes:
http://127.0.0.1:8080/anthropicfor Claude Code and other Anthropic clientshttp://127.0.0.1:8080/openai/v1for Codex and other OpenAI-compatible clients
The historical unprefixed route also still exists and forwards to
KLOVIS_TARGET_URL, which defaults to https://api.anthropic.com.
- Optional: launch the AI workspace UI, a chat interface that anonymizes your
prompt before sending it to a provider you configure with your own API key.
If you want the
Compte IA/AI accountspanel to persist provider API keys across reloads and restarts, setKLOVYS99_AI_WORKSPACE_KEYfirst (you can generate one withopenssl rand -hex 32).
npm run ui:ai-workspaceThis starts a local Vite dev server at http://127.0.0.1:3001 that proxies
/api and /dashboard requests to the running proxy on port 8080. The
dashboard header and the AI workspace link both point here. See
AI Workspace for details.
If you installed the published npm package instead of cloning this repository,
use the same flow with npx klovys99:
- Point your client at the proxy manually — see
Client Configuration below, since the
configureCLI helper is part of the currently broken npm flow.
- Local reverse proxy for Anthropic and OpenAI-compatible JSON requests.
- Built-in deterministic detectors for common PII and sensitive identifiers.
- Local GLiNER Docker sidecar with explicit
fullandoffmodes. - Dynamic detector loading from the official Gitleaks and Microsoft Presidio rule sources.
- Stable pseudonym tokens for the lifetime of the proxy process.
- Structured logs with anonymization counters instead of raw prompt values.
- Disk cache for downloaded external rules to avoid repeated network fetches on every startup.
- A dashboard for live anonymization stats and a manual anonymization test tool.
- An AI workspace UI for chatting with Claude, Gemini, OpenAI, or Mistral using an anonymized prompt, without leaving the anonymization boundary.
- Go 1.25 or newer.
- Docker Desktop or Docker Engine, to run the GLiNER sidecar.
- Network access on first startup to download the default Gitleaks and Presidio rule sources, and the pinned GLiNER model.
- An Anthropic API key, Claude subscription, or OpenAI API key depending on the client you route through Klovys99.
Check your local tooling:
go version
docker versionnpm installation is currently broken. Build and run from source instead:
git clone https://github.com/Korbicorp/klovys99.git
cd klovys99
go build -o klovys99 ./cmd/klovys99See Get Started for how to run it alongside the GLiNER sidecar.
The configure CLI helper is part of the currently broken npm flow. Point
your client at the proxy by editing its config file directly.
Edit ~/.codex/config.toml and set:
openai_base_url = "http://127.0.0.1:8080/openai/v1"Edit ~/.claude/settings.json and set:
{
"env": {
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8080/anthropic"
}
}If klovys99 listens on another address, use that instead of
127.0.0.1:8080 in either snippet.
Anthropic-style request through Klovys99:
curl http://127.0.0.1:8080/anthropic/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 128,
"messages": [
{
"role": "user",
"content": "Email Alice at alice@example.com"
}
]
}'OpenAI Responses-style request through Klovys99:
curl http://127.0.0.1:8080/openai/v1/responses \
-H "authorization: Bearer $OPENAI_API_KEY" \
-H "content-type: application/json" \
-d '{
"model": "gpt-5",
"input": "Email Alice at alice@example.com"
}'Upstream providers receive the same request shape, with sensitive values
replaced by pseudonym tokens such as [EMAIL_1].
Klovys99 reads each incoming JSON request body, anonymizes supported prompt content, then forwards the modified request to the configured upstream.
The proxy anonymizes:
- every
<session>...</session>block found anywhere in a JSON request body; - text content in prompts, system messages,
<system-reminder>blocks, text file context, and tool results; - text document sources where
source.typeistext.
Structural metadata such as model names, roles, content block types, tool IDs, tool names, media types, cache-control values, and base64 document data is left unchanged so the upstream request shape remains valid.
For a single proxy process, repeated values are mapped to stable tokens. For
example, the same email address is replaced by the same [EMAIL_N] token across
requests handled by that process.
When matches overlap, the detector with the highest priority wins. If priorities are equal, the longest match wins.
While the proxy is running, a built-in dashboard is available at
http://127.0.0.1:8080/dashboard. It shows live anonymization stats and links
to a test tool at /dashboard/test-tool for previewing how a prompt is
anonymized without sending it upstream.
The AI workspace is a separate chat UI, at frontend/ai-workspace, for
talking to a model directly from the anonymized side of the boundary instead
of through a coding client. Start it with npm run ui:ai-workspace (see
Get Started) while the proxy is running, then open
http://127.0.0.1:3001 or use the AI workspace link in the dashboard header.
How it works:
-
Your prompt is anonymized through the same detectors as the proxy, and the anonymized preview is shown before you send anything.
-
Only the anonymized prompt is sent to the provider you pick, directly with the API key you configure, not through the
/anthropicor/openaiproxy routes. -
Supported providers are Claude, Gemini, OpenAI, and Mistral, configured with a per-provider API key from the
Settingspanel in the UI. -
Conversations are persisted locally by the Go backend, in plain JSON, under the OS user config directory (
klovys99/ai-workspace, override withKLOVYS99_AI_WORKSPACE_DIR). -
Saved API keys are only written to disk (encrypted) when
KLOVYS99_AI_WORKSPACE_KEYis set. Without it,Settings > Saveis a no-op and a key you type only lasts for the current browser tab, resent with each request. -
If you want to use the AI workspace UI with persistent provider accounts from the
Compte IA/AI accountspanel, setKLOVYS99_AI_WORKSPACE_KEYbefore starting the backend. A simple way to generate one is:openssl rand -hex 32
Then export it, for example:
export KLOVYS99_AI_WORKSPACE_KEY="$(openssl rand -hex 32)"
The frontend dev server proxies /api and /dashboard to the proxy on
127.0.0.1:8080, and the backend only accepts cross-origin requests from
http://127.0.0.1:3001 / http://localhost:3001.
Klovys99 runtime is configured with environment variables.
| Variable | Default | Description |
|---|---|---|
KLOVIS_ADDR |
127.0.0.1:8080 |
Listen address for the local proxy. |
KLOVIS_TARGET_URL |
https://api.anthropic.com |
Upstream used by legacy unprefixed routes such as /v1/messages. |
KLOVIS_ANTHROPIC_TARGET_URL |
https://api.anthropic.com |
Upstream used by /anthropic/... routes. |
KLOVIS_OPENAI_TARGET_URL |
https://api.openai.com |
Upstream used by /openai/... routes. |
KLOVIS_PROXY_DEBUG |
false |
Enables additional sanitized diagnostic logging. Raw traffic is never logged. |
KLOVIS_LOG_PII_FINDINGS |
false |
Deprecated and ignored for privacy; raw findings are never logged. |
KLOVIS_LOG_TO_FILE |
false |
Writes logs to proxy.log instead of stdout when set to true. |
KLOVYS99_AI_WORKSPACE_DIR |
OS user config dir + klovys99/ai-workspace |
Storage directory for AI workspace conversations and credentials. |
KLOVYS99_AI_WORKSPACE_KEY |
unset | Encryption secret used to persist AI workspace provider API keys to disk. Required if you want the AI workspace UI to keep configured provider accounts across reloads/restarts. Generate one with openssl rand -hex 32. Without it, saved keys are not written to disk. |
The Go binary keeps GLiNER off unless you set KLOVIS_GLINER_MODE=full
yourself. Two modes are available and explicit:
full: all configured contextual labels.off: regex-only mode without contextual GLiNER analysis.
The default pinned model identity is:
- model:
urchade/gliner_multi_pii-v1 - revision:
1fcf13e85f4eef5394e1fcd406cf2ca9ea82351d
./scripts/dev-start.sh builds the sidecar image, installs this pinned model
under ~/.klovys99/gliner on first run, starts the sidecar on
127.0.0.1:8091, and then launches the Go proxy in full mode — see
Get Started for the equivalent manual docker steps. Skip the
sidecar entirely and run go run ./cmd/klovys99 on its own to stay in
regex-only (off) mode.
The full mode requests:
person nameorganizationlocationemployerschool or educational institutionmedical provider or healthcare institutionstreet address
A sample direct sidecar latency benchmark is available in docs/benchmarks/gliner-benchmark.md.
| Variable | Default | Description |
|---|---|---|
KLOVIS_GLINER_MODE |
off |
Explicit contextual mode for the Go binary: full or off. |
KLOVIS_GLINER_ENABLED |
deprecated | Legacy bool compatibility shim. Prefer KLOVIS_GLINER_MODE. |
KLOVIS_GLINER_URL |
http://127.0.0.1:8091 |
Loopback sidecar URL; non-loopback URLs are rejected. |
KLOVIS_GLINER_MODEL |
urchade/gliner_multi_pii-v1 |
Exact model identifier. |
KLOVIS_GLINER_MODEL_REVISION |
1fcf13e85f4eef5394e1fcd406cf2ca9ea82351d |
Immutable revision/digest. |
KLOVIS_GLINER_TIMEOUT |
5s |
Per-batch deadline. |
KLOVIS_GLINER_THRESHOLD |
0.50 |
Global confidence threshold. |
KLOVIS_GLINER_LABEL_THRESHOLDS |
{} |
JSON object overriding thresholds for fixed labels. |
KLOVIS_GLINER_MAX_CONCURRENCY |
2 |
Maximum concurrent inference calls. |
KLOVIS_GLINER_MAX_BATCH_CHARS |
32768 |
Maximum Unicode characters per request batch. |
KLOVIS_GLINER_FAILURE_POLICY |
fail-closed |
Only supported policy in V1. |
KLOVIS_GLINER_DATA_DIR |
~/.klovys99/gliner |
Sidecar model directory used by scripts/dev-start.sh. |
When full is active, a timeout, unavailable sidecar, saturated queue,
malformed span, or model identity mismatch returns 503 and makes zero
upstream calls. /healthz reports Go liveness, /readyz includes contextual
readiness, and /api/status exposes sanitized metadata including the active
GLiNER mode.
Boolean variables accept only true or false.
Klovys99 writes structured application logs to stdout by default. To write logs to
proxy.log instead, enable file logging:
KLOVIS_LOG_TO_FILE=true go run ./cmd/klovys99Debug and file logging remain available, but request bodies, detected values,
token mappings, credentials, and model inputs are never logged. The historical
KLOVIS_LOG_PII_FINDINGS setting is ignored.
Klovys99 combines built-in detectors with external rules loaded at startup.
External rule payloads are cached for 24 hours in the user cache directory under
klovys99/external-rules.
| Category | Source | Priority | Description |
|---|---|---|---|
EMAIL |
Built-in / Presidio | 1000 / 600 | Email addresses, normalized in lowercase for stable tokens. |
NIR |
Built-in | 1000 | French social security numbers, including spaced formats and Corsica departments 2A and 2B. |
IBAN |
Built-in / Presidio | 1000 / 600 | IBAN-like account identifiers, normalized by removing separators. |
IP |
Built-in / Presidio | 900 / 600 | IPv4 and IPv6 addresses. |
CREDIT_CARD |
Built-in / Presidio | 900 / 600 | Credit card-like digit sequences. |
MAC_ADDRESS |
Built-in / Presidio | 900 / 600 | MAC addresses with : or - separators. |
PHONE |
Built-in | 700 | French and common international phone numbers. |
DATE |
Built-in / Presidio | 600 / external | Conservatively labelled birth dates and supported contextual dates. |
BLOOD_TYPE |
Built-in | 600 | Contextual blood groups such as Groupe sanguin O+. |
SECRET |
Gitleaks | 600 | Secrets loaded dynamically from the official Gitleaks config. |
CRYPTO |
Presidio | 600 | Cryptocurrency wallet identifiers loaded from supported Presidio recognizers. |
ADDRESS |
Built-in | 900 / 700 | French postal addresses and labelled addresses. |
NAME |
Built-in | 900 | Contextual names following strong French or English cues and form labels. |
NUMERIC_ID |
Built-in | 100 | Generic long numeric IDs. |
REFERENCE_ID |
Built-in | 100 | Labelled alphanumeric references requiring letters and digits. |
When Claude Code uses a non-first-party ANTHROPIC_BASE_URL, some Claude
features behave differently upstream. In practice:
- Remote Control is disabled by Claude Code when the base URL does not point to
api.anthropic.com. - Tool search behavior changes when routing through a proxy. If you need
deferred tool references, set
ENABLE_TOOL_SEARCH=truein your Claude environment because Klovys99 forwardstool_referenceblocks unchanged.
Clone the repository and download Go dependencies:
git clone https://github.com/Korbicorp/klovys99.git
cd klovys99
go mod downloadTagged releases build one binary per supported OS and architecture in GitHub
Actions. If NPM_TOKEN_KLOVYS is configured in repository secrets, the same tag
workflow also publishes the npm package after uploading the release assets —
npm installation from that package is currently broken, see
Installation.
Run the Go test suite:
go test ./...The npm packaging code under npm/ has its own unit tests
(node --test npm/test/*.test.js) if you're working on that code specifically,
but they don't exercise a real install and aren't required to run the proxy.
Run the proxy locally:
./scripts/dev-start.shThe AI workspace frontend lives in frontend/ai-workspace as its own Node
project (installed separately from npm install at the repository root).
Install its dependencies once, then use the root scripts to run or build it:
npm --prefix frontend/ai-workspace install
npm run ui:ai-workspace # dev server on http://127.0.0.1:3001
npm run ui:ai-workspace:build # production build into frontend/ai-workspace/distFormat Go code before submitting changes:
gofmt -w ./cmd ./internalKlovys99 reduces the amount of sensitive data sent upstream, but it is not a formal data-loss-prevention guarantee. Review detector coverage for your own threat model before using it with production data.
External Gitleaks and Presidio rules are loaded from their upstream repositories by default. Cached copies are reused for 24 hours and stale cache entries may be used as a fallback if a refresh fails.
Issues and pull requests are welcome. For code changes, please include focused tests that cover the behavior being changed.
Useful checks before opening a pull request:
go test ./...
gofmt -w ./cmd ./internal