Keep API keys, cloud credentials, and private tokens on-device while Claude Code, Codex, and compatible proxy-aware API clients keep working.
Quickstart · How It Works · Why KeyClaw? · Docs · Contributing
KeyClaw sits between your AI tool and the upstream API, detects sensitive values in outbound payloads, swaps them for typed placeholders like {{KEYCLAW_OPAQUE_<id>}}, stores the mapping in a session-scoped local resolver, and reinjects the real values into responses on the way back. The core runtime classes are opaque_token plus typed structured detectors, so the model never sees the raw value but your workflow still behaves as if it did.
- AI coding tools are excellent at reading exactly the files you wish they would ignore.
- Secret scanners catch leaks after the fact; KeyClaw protects the live request before it crosses the network.
- You do not need wrapper SDKs, prompt discipline, or a separate hosted service to keep credentials out of model prompts.
+-------------+ request +-----------------+ sanitized +----------------------+
| Claude Code | ---------->| KeyClaw | request | Supported provider |
| Codex | | local proxy |------------>| APIs / custom hosts |
| Client |<---------- | |<----------- | via --include |
+-------------+ response +---------+-------+ response +----------------------+
(resolved) |
+--------v---------+
| Session-scoped |
| local store |
+------------------+
1. Detect opaque tokens and typed structured data in outbound request body
2. Replace with typed placeholders, store mapping in the session-scoped store
3. Forward sanitized request upstream
4. Resolve placeholders in response (HTTP, SSE, WebSocket)
KeyClaw ships first-class wrappers for Claude Code and Codex. In generic proxy mode, the default interception list also covers Google, Together, Groq, Mistral, Cohere, and DeepSeek API domains.
| Tool | Path | Notes |
|---|---|---|
| Claude Code | keyclaw claude ... wrapper or source ~/.keyclaw/env.sh |
First-class CLI path |
| Codex | keyclaw codex ... wrapper or source ~/.keyclaw/env.sh |
First-class CLI path |
| macOS desktop apps | System proxy + trusted KeyClaw CA | Finder-launched apps do not inherit shell proxy env by default; see docs/macos-gui-apps.md |
| ChatGPT / OpenAI web traffic | Local proxy + trusted CA | In scope at the host layer for chatgpt.com / chat.openai.com when traffic truly traverses the proxy |
| Direct API clients | HTTP_PROXY / HTTPS_PROXY + KeyClaw CA |
Default hosts include OpenAI, Anthropic, Google, Together, Groq, Mistral, Cohere, and DeepSeek |
Cursor, aider, Continue, and similar proxy-aware tools are in scope when they actually route traffic through KeyClaw and trust the local CA, but they remain generic proxy integrations rather than first-class wrappers.
KeyClaw is published on crates.io and packaged for Homebrew via GuthL/tap.
The default runtime uses the built-in sensitive-data engine only: typed structured detectors plus opaque-token entropy detection.
cargo install keyclaw
keyclaw init
keyclaw proxy
source ~/.keyclaw/env.shbrew tap GuthL/tap
brew install keyclaw
keyclaw init
keyclaw proxy
source ~/.keyclaw/env.shIf you want the latest unreleased KeyClaw commit instead of the published crate, use cargo install --git https://github.com/GuthL/KeyClaw keyclaw.
That installs the CLI, generates the local CA, initializes the session-scoped runtime, starts the detached proxy, and wires the current shell to trust and use it.
keyclaw claude --resume latestOr use the proxy globally:
claudesource ~/.keyclaw/env.sh
your-client-hereFor tools outside the built-in claude / codex wrappers:
- Run
keyclaw initonce so~/.keyclaw/ca.crtexists. - Trust
~/.keyclaw/ca.crtin your OS keychain or certificate store. - Route the client through
http://127.0.0.1:8877, either via shell env vars or app/OS proxy settings. - Verify with
keyclaw doctorand a real request that traffic is actually being intercepted.
GUI desktop apps launched outside your shell do not necessarily inherit source ~/.keyclaw/env.sh. On macOS, the reliable path for Claude.app, Codex.app, ChatGPT.app, and similar desktop apps is to trust ~/.keyclaw/ca.crt in the login keychain and use the macOS system proxy instead of assuming shell env injection will reach them. See docs/macos-gui-apps.md for the current supported flow and rollback steps.
Built-in generic-proxy hosts are:
api.openai.com,chat.openai.com,chatgpt.comapi.anthropic.com,claude.aigenerativelanguage.googleapis.comapi.together.xyz,api.groq.com,api.mistral.ai,api.cohere.ai,api.deepseek.com
To intercept an extra host or custom gateway, add repeated --include globs:
keyclaw proxy --include "*my-custom-api.com*"
source ~/.keyclaw/env.shOr persist the same idea with an environment variable:
export KEYCLAW_INCLUDE_HOSTS="*my-custom-api.com*"
keyclaw proxykeyclaw proxy
source ~/.keyclaw/env.sh
claude "scan this repo for AWS credentials"# Terminal 1
keyclaw proxy
# Terminal 2
source ~/.keyclaw/env.sh
claudekeyclaw proxy detaches and keeps running in the background. It prints source ~/.keyclaw/env.sh on stdout, and that script exports HTTP_PROXY, HTTPS_PROXY, SSL_CERT_FILE, and related variables so CLI tools route through KeyClaw and trust the local CA.
Starting the daemon does not reconfigure the current shell automatically. A child process cannot mutate its parent shell environment, so the current shell only routes through KeyClaw after source ~/.keyclaw/env.sh.
Warning:
eval "$(keyclaw proxy)"still works as a shortcut if you trust the localkeyclawbinary and the generated~/.keyclaw/env.sh, but it executes shell code emitted by the command. The documented path is to runkeyclaw proxyfirst and thensource ~/.keyclaw/env.shyourself.
Manage the proxy lifecycle with:
keyclaw proxy status # check if the proxy is running
keyclaw proxy stop # graceful shutdownStarting keyclaw proxy again on the same listen address replaces the previously tracked detached daemon. If you start another proxy on a different address, the existing proxy stays up and the newest detached daemon becomes the instance that keyclaw proxy status and keyclaw proxy stop track.
On Linux with systemd --user, you can also keep the daemon coming back after login or reboot:
keyclaw proxy autostart enable
keyclaw proxy autostart status
keyclaw proxy autostart disableAutostart only keeps the daemon alive. Shells still need source ~/.keyclaw/env.sh to route CLI traffic through it.
If you want the proxy attached to the current terminal instead, use keyclaw proxy start --foreground.
Tip: Add
[ -f ~/.keyclaw/env.sh ] && source ~/.keyclaw/env.shto your~/.bashrcor~/.zshrcto auto-route through KeyClaw in new shells while the proxy is already running. The detached proxy does not auto-start after reboot unless you enablekeyclaw proxy autostart enable, so after a reboot you may need to start it again.
The CLI wrappers (keyclaw claude ..., keyclaw codex ...) are the preferred path on macOS because they inject proxy settings and CA trust into the child process directly.
Finder-launched apps are different: they are started by macOS, not by your shell, so they usually bypass ~/.keyclaw/env.sh unless you configure the OS proxy itself. The current supported desktop-app flow is:
keyclaw init- start a healthy proxy and confirm
keyclaw proxy status - trust
~/.keyclaw/ca.crtin the macOS login keychain for SSL - enable the macOS HTTP and HTTPS system proxy on the active network service
- fully quit and relaunch the desktop app
If the system proxy is enabled while no healthy KeyClaw listener is actually serving the advertised address, desktop apps and browsers can fail with proxy-connection errors. keyclaw doctor now warns on macOS when Finder-launched apps are likely bypassing the proxy or when the KeyClaw CA is not trusted for SSL in the login keychain. Use docs/macos-gui-apps.md for the exact commands, verification steps, and rollback path.
{
"model": "claude-sonnet-4",
"messages": [
{
"role": "user",
"content": "Deploy the staging stack with AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE and AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
]
}{
"model": "claude-sonnet-4",
"messages": [
{
"role": "developer",
"content": "KeyClaw notice: 2 sensitive values were redacted before this request left the machine."
},
{
"role": "user",
"content": "Deploy the staging stack with AWS_ACCESS_KEY_ID={{KEYCLAW_OPAQUE_77dc0005c514277d}} and AWS_SECRET_ACCESS_KEY={{KEYCLAW_OPAQUE_8b4f0f2d14a8b2c1}}"
}
],
"x-keyclaw-contract": "placeholder:v2"
}When the model echoes or manipulates those placeholders in a response, KeyClaw resolves them back to the real values before the local client sees the final payload.
| Approach | Great at | Where KeyClaw is different |
|---|---|---|
llm-interceptor |
Inspecting, analyzing, and logging AI traffic | KeyClaw is designed to actively rewrite live requests, preserve workflows with placeholder reinjection, and fail closed when protection breaks |
gitleaks / trufflehog |
Scanning repos, history, images, or leaked material for secrets | KeyClaw operates inline on live traffic instead of only auditing files or previously leaked data |
| Manual prompt hygiene | Reducing obvious copy/paste mistakes | KeyClaw is automatic and still catches secrets hidden in payloads, configs, or generated request bodies |
If you want to inspect LLM traffic, llm-interceptor is a useful neighbor. If you want to stop raw credentials from reaching the upstream API in the first place, KeyClaw is the tighter fit.
KeyClaw now uses one in-process sensitive-data engine:
- Structured typed detectors in
src/sensitive.rsforpassword,email,phone,national_id,passport,payment_card, andcvv - Opaque-token detection for high-entropy credential-like spans
- Allowlist filtering
- An optional local classifier only for ambiguous low-confidence candidates
The runtime does not require any external detector binary. Placeholder mappings are kept in a session-scoped store, so the same value is stable within one daemon session but not across restarts.
keyclaw doctorkeyclaw doctor is the fastest way to catch broken CA files, proxy bypass, invalid ~/.keyclaw/config.toml, broken allowlist entries, and misconfigured detector knobs before you debug the client itself.
Interpret the output like this:
PASSmeans the check is ready for normal useWARNmeans KeyClaw can still run, but the config is risky or non-standardFAILmeans fix this before relying on the proxy;doctorexits non-zerohint:points at the next operator action for that specific check
KeyClaw reads ~/.keyclaw/config.toml if it exists, then applies environment variable overrides on top. Precedence is:
env vars > ~/.keyclaw/config.toml > built-in defaults
Missing ~/.keyclaw/config.toml is fine; KeyClaw silently falls back to env vars and defaults. Invalid TOML is treated as a blocking configuration error, and keyclaw doctor reports it explicitly.
For the full configuration reference, see docs/configuration.md.
Example ~/.keyclaw/config.toml:
[proxy]
addr = "127.0.0.1:8877"
[logging]
level = "info"
[notice]
mode = "minimal"
[detection]
dry_run = false
entropy_enabled = true
entropy_threshold = 3.5
[audit]
path = "~/.keyclaw/audit.log"
[hosts]
codex = ["api.openai.com", "chat.openai.com", "chatgpt.com"]
claude = ["api.anthropic.com", "claude.ai"]
providers = [
"generativelanguage.googleapis.com",
"api.together.xyz",
"api.groq.com",
"api.mistral.ai",
"api.cohere.ai",
"api.deepseek.com",
]
include = ["*my-custom-api.com*"]
[allowlist]
rule_ids = ["opaque.high_entropy"]
patterns = ["^sk-test-"]
secret_sha256 = ["0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"]
[[hooks]]
event = "secret_detected"
rule_ids = ["opaque.high_entropy", "typed.email"]
action = "exec"
command = "notify-slack --channel security"
[[hooks]]
event = "request_redacted"
rule_ids = ["*"]
action = "log"
path = "~/.keyclaw/hooks.log"
[[hooks]]
event = "secret_detected"
rule_ids = ["opaque.high_entropy"]
action = "block"
message = "production key detected"Supported file sections today are proxy, logging, notice, detection, audit, sensitive, classifier, hosts, allowlist, and hooks. Use the file for steady-state local settings, then reach for env vars when you want a one-off override.
Allowlist entries let you intentionally skip redaction for known-safe matches:
rule_ids: skip every match produced by a specific detector ID such asopaque.high_entropypatterns: skip secrets whose matched value satisfies a regexsecret_sha256: skip one exact secret value by SHA-256 digest, without storing the plaintext in config
To compute a secret_sha256 entry locally:
printf '%s' 'your-known-safe-secret' | sha256sumDisable persistent audit logging with:
[audit]
path = "off"Hook entries let you trigger local side effects from request-side events without exposing the raw sensitive value:
event = "secret_detected"fires when a sensitive value is found during request rewritingevent = "request_redacted"fires after a request has been rewritten, just before it is forwarded upstreamaction = "exec"runs a local command with sanitized metadata in env vars and a JSON payload onstdinaction = "log"appends a JSON line to the configured fileaction = "block"rejects matchingsecret_detectedrequests withhook_blocked
Hook payloads include only event, rule_id, kind, policy, placeholder, and request_host. Raw values are never passed to hook commands or hook log files.
| Variable | Default | Description |
|---|---|---|
KEYCLAW_PROXY_ADDR |
127.0.0.1:8877 |
Proxy listen address |
KEYCLAW_PROXY_URL |
http://127.0.0.1:8877 |
Proxy URL exported to child processes |
KEYCLAW_CA_CERT |
~/.keyclaw/ca.crt |
Override CA cert path passed to clients |
KEYCLAW_CODEX_HOSTS |
api.openai.com,chat.openai.com,chatgpt.com |
Codex/OpenAI hosts to intercept |
KEYCLAW_CLAUDE_HOSTS |
api.anthropic.com,claude.ai |
Claude/Anthropic hosts to intercept |
KEYCLAW_PROVIDER_HOSTS |
generativelanguage.googleapis.com,api.together.xyz,api.groq.com,api.mistral.ai,api.cohere.ai,api.deepseek.com |
Additional provider API hosts intercepted by default |
KEYCLAW_INCLUDE_HOSTS |
unset | Extra exact hosts or glob patterns to intercept |
KEYCLAW_MAX_BODY_BYTES |
2097152 |
Maximum request body size |
KEYCLAW_DETECTOR_TIMEOUT |
20s |
Timeout for request-body collection and rewrite processing; raise it for very large CLI payloads if request_timeout warnings persist |
KEYCLAW_AUDIT_LOG |
~/.keyclaw/audit.log |
Append-only JSONL audit log path, or off to disable |
KEYCLAW_LOG_LEVEL |
info |
Runtime stderr verbosity: error, warn, info, debug |
KEYCLAW_NOTICE_MODE |
verbose |
Redaction notice mode: verbose, minimal, off |
KEYCLAW_DRY_RUN |
false |
Scan and log what would be redacted without modifying traffic |
KEYCLAW_UNSAFE_LOG |
false |
Disable normal log scrubbing for debugging only |
KEYCLAW_FAIL_CLOSED |
true |
Block requests on rewrite or detection failures |
KEYCLAW_REQUIRE_MITM_EFFECTIVE |
true |
Fail if proxy bypass is detected |
KEYCLAW_ENTROPY_ENABLED |
true |
Enable opaque-token detection |
KEYCLAW_ENTROPY_THRESHOLD |
3.5 |
Minimum entropy score for opaque-token matches |
KEYCLAW_ENTROPY_MIN_LEN |
20 |
Minimum token length for opaque-token matches |
KEYCLAW_SENSITIVE_SESSION_TTL |
1h |
Session-store TTL for placeholder mappings |
KEYCLAW_SENSITIVE_EMAILS_ENABLED |
false |
Enable email detection |
KEYCLAW_SENSITIVE_PASSWORDS_ENABLED |
false |
Enable password detection |
KEYCLAW_SENSITIVE_PHONES_ENABLED |
false |
Enable phone detection |
KEYCLAW_SENSITIVE_NATIONAL_IDS_ENABLED |
false |
Enable national-ID detection |
KEYCLAW_SENSITIVE_PASSPORTS_ENABLED |
false |
Enable passport detection |
KEYCLAW_SENSITIVE_PAYMENT_CARDS_ENABLED |
false |
Enable payment-card detection |
KEYCLAW_SENSITIVE_CVV_ENABLED |
false |
Enable CVV detection |
KEYCLAW_LOCAL_CLASSIFIER_ENABLED |
false |
Enable the optional local classifier |
KEYCLAW_LOCAL_CLASSIFIER_ENDPOINT |
unset | OpenAI-compatible local classifier endpoint |
KEYCLAW_LOCAL_CLASSIFIER_MODEL |
unset | Local classifier model name |
KEYCLAW_LOCAL_CLASSIFIER_TIMEOUT |
3s |
Local classifier HTTP timeout |
Large top-level prompt or instructions strings that look like machine-generated hidden context are downgraded to input-only rewriting once they get big enough and do not contain obvious credential hints. Normal messages / input content is still inspected, and large prompt bodies that clearly contain secrets are still rewritten.
Environment variables override the config file when both are set.
Inline for one command:
KEYCLAW_LOG_LEVEL=debug KEYCLAW_NOTICE_MODE=minimal keyclaw claude --resume latest
KEYCLAW_SENSITIVE_EMAILS_ENABLED=true KEYCLAW_SENSITIVE_PASSWORDS_ENABLED=true keyclaw doctorPersistent for the current shell session:
export KEYCLAW_LOG_LEVEL=debug
export KEYCLAW_NOTICE_MODE=minimal
export KEYCLAW_INCLUDE_HOSTS="*my-custom-api.com*"
keyclaw codex exec --model gpt-5Persistent across new shells:
# ~/.bashrc or ~/.zshrc
export KEYCLAW_LOG_LEVEL=debug
export KEYCLAW_NOTICE_MODE=minimal
export KEYCLAW_SENSITIVE_PAYMENT_CARDS_ENABLED=true
export KEYCLAW_INCLUDE_HOSTS="*my-custom-api.com*"If you use keyclaw proxy as a detached daemon, or enable keyclaw proxy autostart, daemon-side settings are read when that proxy process starts. After changing ~/.keyclaw/config.toml or variables such as KEYCLAW_PROXY_ADDR, KEYCLAW_LOG_LEVEL, KEYCLAW_NOTICE_MODE, KEYCLAW_REQUIRE_MITM_EFFECTIVE, KEYCLAW_PROVIDER_HOSTS, KEYCLAW_INCLUDE_HOSTS, or any KEYCLAW_SENSITIVE_* detector toggle, restart the proxy so the running daemon picks them up.
KEYCLAW_NOTICE_MODE=verbose keeps the current full acknowledgment guidance, minimal injects a shorter notice, and off suppresses notice injection entirely while still redacting and reinjecting secrets normally.
Use dry-run when you want to tune detection without changing live traffic: set [detection] dry_run = true, export KEYCLAW_DRY_RUN=true, or pass --dry-run to keyclaw rewrite-json, keyclaw mitm ..., keyclaw codex ..., keyclaw claude ..., or keyclaw proxy start.
Use repeated --include flags on keyclaw proxy, keyclaw proxy start, keyclaw mitm, keyclaw codex, or keyclaw claude when you need to intercept a custom API hostname or gateway without replacing the built-in provider list.
The only intentional exception to scrubbed runtime logging is KEYCLAW_UNSAFE_LOG=true. When enabled, KeyClaw may write raw request fragments to stderr or ~/.keyclaw/mitm.log to help debug interception problems. Leave it unset for normal use.
By default, KeyClaw appends one JSON line per redacted secret to ~/.keyclaw/audit.log. Each entry includes the UTC timestamp, rule_id, placeholder, request host, and action, but never the raw secret value itself.
Set KEYCLAW_AUDIT_LOG=off or [audit] path = "off" to disable the persistent audit log. Set KEYCLAW_AUDIT_LOG=/path/to/audit.log or [audit] path = "/path/to/audit.log" to move it somewhere else.
KeyClaw does not rotate the audit log by itself; it always appends. For size management, point it at a path managed by logrotate, newsyslog, or your platform's equivalent rotation tool.
Operator-facing runtime messages use leveled stderr prefixes:
keyclaw info:startup, shutdown, CA/ruleset initialization, and lifecycle summarieskeyclaw debug:per-request proxy activity such as interception, rewrite, and placeholder resolutionkeyclaw warn:risky but non-fatal conditions such as unsafe logging or bypass riskkeyclaw error:fatal CLI errors before exit
Set KEYCLAW_LOG_LEVEL=error, warn, info, or debug to reduce or expand stderr verbosity. The default is info, which stays lifecycle-focused and avoids emitting a line for every proxied request. Use debug when troubleshooting live traffic. The doctor subcommand is intentionally separate: it writes its doctor: PASS|WARN|FAIL ... report to stdout so it can be piped or parsed cleanly.
KeyClaw uses deterministic error codes for programmatic handling:
| Code | Meaning |
|---|---|
mitm_not_effective |
Proxy bypass detected |
body_too_large |
Request body exceeds KEYCLAW_MAX_BODY_BYTES |
invalid_json |
Failed to parse or rewrite request JSON |
request_timeout |
Request body read or rewrite timed out before inspection completed |
strict_resolve_failed |
Placeholder resolution failed in strict mode |
hook_blocked |
A configured hook rejected the request |
For the full threat model, see docs/threat-model.md.
- Secrets in your codebase being sent to AI APIs
- API keys, tokens, and credentials leaking through CLI or IDE traffic
- Accidental exposure of
.envfiles, config files, and hardcoded credentials
- A compromised local machine
- Traffic that never uses the KeyClaw proxy or targets hosts outside the configured intercept lists
- Perfect secret detection across every provider, credential format, or prompt phrasing
- Side-channel leakage such as exact secret-length preservation
The trust boundary is your machine. KeyClaw only protects traffic that a supported client actually routes through the local proxy. The CA certificate is generated locally and never leaves your machine. Sensitive detection, placeholder generation, session-scoped storage, and reinjection all happen in-process.
src/
├── main.rs # Entry point
├── lib.rs # Module declarations
├── allowlist.rs # Operator-controlled allowlist logic
├── audit.rs # Persistent JSONL audit log writes
├── certgen.rs # Runtime CA certificate generation
├── config.rs # Env + TOML configuration
├── entropy.rs # High-entropy token detection
├── hooks.rs # Configured request-side hook execution
├── sensitive.rs # Typed detection engine + session-scoped store
├── pipeline.rs # Request rewrite pipeline
├── placeholder.rs # Placeholder parsing, generation, and resolution
├── redaction.rs # JSON walker + notice injection
├── proxy.rs # Proxy server entrypoint + handler wiring
├── proxy/
│ ├── common.rs # Shared host checks, response helpers, and logging
│ ├── http.rs # HTTP request/response interception
│ ├── streaming.rs # SSE frame resolution and buffering
│ └── websocket.rs # WebSocket message redaction and resolution
├── launcher.rs # CLI surface and subcommand dispatch
├── launcher/
│ ├── bootstrap.rs # Processor/bootstrap setup and launched-tool wiring
│ └── doctor.rs # Operator health checks
├── logscrub.rs # Log sanitization
└── errors.rs # Error types and codes
| Doc | Purpose |
|---|---|
| docs/README.md | Entry point for deeper documentation |
| docs/architecture.md | Request/response flow, module map, and design decisions |
| docs/configuration.md | Full config file and environment variable reference |
| docs/secret-patterns.md | Supported secret categories and custom-rule guidance |
| docs/threat-model.md | Threat model, trust boundary, and non-goals |
| CLAUDE.md | Agent guide for Claude Code |
| AGENTS.md | Agent guide for Codex |
See CONTRIBUTING.md for local development, validation, release, and documentation expectations.