Blind Mode Explained: Secrets Your AI Agent Never Sees
How KeyZero's blind mode uses a local MITM proxy to give AI agents opaque tokens instead of real secrets, swapping credentials at the network edge.
Blind mode is KeyZero's core mechanism for running untrusted workloads — particularly AI agents — with access to authenticated APIs without exposing raw credentials. The agent process receives opaque masked tokens instead of real secret values. A local MITM proxy intercepts outbound HTTP/HTTPS requests and swaps the tokens for real credentials at the network boundary.
Why Blind Mode Exists
Traditional secret injection hands the real credential to the consuming process. This works when the consumer is a trusted server application with controlled I/O. AI agents break this model in three ways:
- Context window exposure: LLM-based agents serialize their full state — including environment variables — into prompts sent to external LLM APIs
- Verbose logging: Agent frameworks log tool calls, parameters, and responses, capturing secrets in observability pipelines
- Autonomous tool use: Agents construct HTTP requests dynamically, placing secrets in parameters visible to the LLM's reasoning chain
For a detailed breakdown of each leak vector, see why AI agents leak secrets.
Blind mode eliminates all three vectors by ensuring the agent process never holds a real secret value.
How the MITM Proxy Works
Step-by-Step Flow
When you run kz run --blind -- node agent.js, KeyZero performs these steps:
- Config loading: Reads
.keyzero.tomland identifies all secrets to resolve - Secret resolution: Fetches real secret values from configured providers (Keychain, 1Password, AWS Secrets Manager, HashiCorp Vault, etc.)
- Token generation: For each secret, generates a masked token in the format
kz_masked_<hash> - Token mapping: Builds an internal mapping table:
kz_masked_7f3a9b... → sk-proj-abc123... - Proxy startup: Launches a local MITM proxy on a random port, configured with the token mapping and an ephemeral CA certificate
- Environment injection: Sets environment variables on the subprocess with masked tokens instead of real values, plus proxy configuration variables
- Subprocess launch: Starts the agent process with the modified environment
Architecture Overview
┌─────────────────────────────────────────────────────┐
│ kz run --blind │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Agent │ HTTP │ MITM Proxy │ │
│ │ Process │────────▶│ │ │
│ │ │ │ 1. Intercept request │ │
│ │ Sees: │ │ 2. Find kz_masked_* │ │
│ │ kz_masked_* │ │ 3. Swap with real │ │
│ │ │ │ secret value │ │
│ └──────────────┘ │ 4. Forward request │ │
│ └──────────┬───────────┘ │
│ │ │
└──────────────────────────────────────┼──────────────┘
│
▼
┌─────────────────┐
│ Upstream API │
│ Receives real │
│ credentials │
└─────────────────┘
What the Agent Sees vs. What Hits the Wire
Agent's environment:
API_KEY=kz_masked_7f3a9b2e4d...
HTTP_PROXY=http://127.0.0.1:54321
HTTPS_PROXY=http://127.0.0.1:54321
SSL_CERT_FILE=/tmp/kz_ca_xyz.pem
NODE_EXTRA_CA_CERTS=/tmp/kz_ca_xyz.pem
Agent's outbound request:
GET /v1/models HTTP/1.1
Host: api.openai.com
Authorization: Bearer kz_masked_7f3a9b2e4d...
What the upstream API receives:
GET /v1/models HTTP/1.1
Host: api.openai.com
Authorization: Bearer sk-proj-abc123def456...
The proxy scans the entire outbound request — headers, query parameters, and body — for any kz_masked_* tokens and replaces them with the corresponding real values. For a full walkthrough of the proxy architecture and how it fits into KeyZero's resolution pipeline, see the architecture deep dive.
TLS Handling
The MITM proxy generates an ephemeral CA certificate at startup. This CA is used to sign certificates for intercepted HTTPS connections. KeyZero automatically sets SSL_CERT_FILE, NODE_EXTRA_CA_CERTS, and REQUESTS_CA_BUNDLE so that common HTTP clients (OpenSSL, Node.js, Python requests) trust the ephemeral CA without manual configuration.
The ephemeral CA exists only for the lifetime of the kz run process and is deleted on exit.
Connection Control
Blind mode includes network-level access control. In .keyzero.toml, you can restrict which hosts the agent process is allowed to contact:
[blind]
[[blind.connections]]
allow = true
host = "api.openai.com"
[[blind.connections]]
allow = true
host = "api.anthropic.com"
[[blind.connections]]
allow = false
host = "*"
This prevents the agent from exfiltrating tokens to arbitrary endpoints. Even though the tokens are masked, connection control adds defense in depth.
When to Use Blind Mode vs. Direct Resolution
| Scenario | Recommended Mode | Reason |
|---|---|---|
| AI agent (LLM-based) | Blind mode | Agent's context window and logs must not contain real secrets |
| Untrusted plugin or extension | Blind mode | Plugin code is not fully audited; limit its access to raw credentials |
| Trusted server application | Direct resolution | The process is controlled; no context window or LLM exposure risk |
| CI/CD pipeline | Direct resolution | Short-lived runner with controlled I/O; blind mode adds unnecessary complexity |
| Local development | Direct resolution | Developer is trusted; blind mode proxy may interfere with debugging |
| Shared development machine | Blind mode | Multiple users or processes; limit blast radius of any single process |
Rule of thumb: If the process communicates with an LLM API or runs code you do not fully control, use blind mode.
Comparison: Blind Mode vs. Traditional Secret Injection
| Category | Traditional Injection | KeyZero Blind Mode |
|---|---|---|
| Secret visibility to process | Full plaintext | Masked token only |
| Log/trace exposure | Real secrets in logs | Only masked tokens in logs |
| Context window safety | Secrets enter LLM context | No real secrets in context |
| Network-level control | None (process calls any host) | Connection allowlist per host |
| Rotation impact | Process must restart | Proxy resolves fresh values; process unchanged |
| Setup complexity | Low (set env vars) | Low (--blind flag + .keyzero.toml) |
| Runtime overhead | None | Minimal (local proxy adds < 1ms per request) |
| TLS interception | Not applicable | Ephemeral CA, auto-configured for Node.js/Python/OpenSSL |
Getting Started with Blind Mode
- Create a
.keyzero.tomlwith your secret mappings:
[secrets]
OPENAI_API_KEY = { provider = "keychain", ref = "openai-key" }
STRIPE_KEY = { provider = "1password", ref = "op://Vault/stripe/api-key" }
- Store the secrets in your provider (if not already present):
kz put --missing
- Run your agent with blind mode:
kz run --blind -- node agent.js
The agent starts with masked tokens. Every outbound HTTP request passes through the local proxy, which swaps tokens for real values transparently. When the process exits, the proxy shuts down and the ephemeral CA is deleted.
Blind mode works seamlessly with AI coding tools like Claude Code and with MCP servers that need authenticated access to external APIs.