Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.codezero.io/llms.txt

Use this file to discover all available pages before exploring further.

Cordon integrates with Claude Code so your AI agent can make authenticated API calls without holding real credentials.
Cordon can inject an ANTHROPIC_API_KEY for Claude’s API access, but this requires manually adding a route for api.anthropic.com and setting a dummy ANTHROPIC_API_KEY env var — see API key setup. If you use Claude Code with a Claude Pro/Team/Enterprise subscription (OAuth login), Anthropic API credential injection won’t apply — Claude Code authenticates directly via OAuth, bypassing the proxy. This limitation applies only to Claude’s own authentication; cordon supports bearer, basic, and api_key auth types for routes to other APIs. Support for OAuth-based subscriptions is coming soon.

Scope

Claude Code setup defaults to project scope: cordon.toml lives in $CWD, and settings are written to $CWD/.claude/settings.local.json. This keeps credentials and proxy configuration isolated per repository and per checkout — settings.local.json is Claude Code’s gitignored-by-convention override file, so git worktrees do not inherit stale proxy env vars from a tracked settings.json. To share a single cordon configuration across all projects, use user scope:
cordon setup claude-code --scope user
User scope writes settings to $HOME/.claude/settings.json and stores config at $XDG_CONFIG_HOME/cordon/cordon.toml. See Scopes for path details and trade-offs.

Automated setup

The fastest way to get started:
cordon setup claude-code
This:
  1. Generates CA certificates (if not already present)
  2. Creates a scaffold cordon.toml
  3. Generates a combined CA bundle (system CAs + Cordon CA)
  4. Configures Claude Code’s settings file (.claude/settings.local.json for project scope, ~/.claude/settings.json for user scope) with proxy env vars (HTTPS_PROXY, HTTP_PROXY, https_proxy, http_proxy, NODE_EXTRA_CA_CERTS, SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE)
  5. Installs a health-check hook that blocks Claude Code when cordon is not running
  6. Installs a cordon agent skill (.claude/skills/cordon/SKILL.md for project scope, ~/.claude/skills/cordon/SKILL.md for user scope)
  7. Offers to add routes and store secrets interactively (post-setup wizard)
Any existing settings file is backed up to <name>.cordon.bak before any changes are made. If setup detects cordon env vars in a project’s tracked settings.json (from a pre-fix install), they are migrated into settings.local.json automatically during the next cordon setup claude-code run. To run cordon as a background service, run cordon service install after setup (add --scope user if you set up Claude Code with --scope user).

Remove the setup

cordon integration disable claude-code

Sandbox configuration (macOS)

Claude Code runs tools in a macOS sandbox that manages proxy env var propagation to subprocesses. The sandbox must be enabled for HTTP_PROXY/HTTPS_PROXY env vars (set in your settings file) to reach tools like curl, wget, and gh. When the sandbox is disabled, these env vars are not propagated — requests bypass cordon entirely and no credential injection occurs. cordon setup claude-code configures these settings automatically. The resulting sandbox configuration in your Claude Code settings file (.claude/settings.local.json for project scope, ~/.claude/settings.json for user scope) looks like:
{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "network": {
      "allowLocalBinding": true
    }
  }
}
  • enabled: true activates the sandbox and its proxy env var propagation pipeline. Without this, subprocesses will not receive proxy env vars and cordon credential injection will silently fail.
  • autoAllowBashIfSandboxed: true allows Bash commands to run without individual permission prompts when the sandbox is active.
  • allowLocalBinding: true permits sandboxed processes to connect to loopback addresses, where cordon listens.

Go-based tools (gh, terraform, kubectl, gcloud)

If you use Go-based CLI tools through Claude Code’s sandbox, you also need enableWeakerNetworkIsolation. Go binaries use Apple’s Security.framework for TLS certificate verification, which delegates to the trustd daemon via Mach IPC. The sandbox blocks this by default, causing OSStatus -26276 errors. This must be added manually — cordon setup claude-code does not set it by default because it reduces sandbox isolation.
{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "enableWeakerNetworkIsolation": true,
    "network": {
      "allowLocalBinding": true
    }
  }
}
enableWeakerNetworkIsolation allows sandboxed processes to communicate with com.apple.trustd.agent. It is strictly more secure than disabling the entire sandbox with dangerouslyDisableSandbox: true, but it does reduce isolation by opening access to the system TLS trust service. Only add it if you need Go-based tools to work through the proxy. Users who only use curl, npm, or Node.js tools through the sandbox do not need enableWeakerNetworkIsolation.
Sandbox settings are applied when Claude Code launches. After changing these settings, you must restart Claude Code for them to take effect.

Adding routes

The cordon route, cordon start, and cordon service commands below default to project scope. If you set up Claude Code with --scope user, append --scope user to each of these commands so they target ~/.config/cordon/cordon.toml instead of ./cordon.toml.
After setup, add a route for your API provider. Example for Anthropic:
cordon route add
Cordon supports api_key, bearer, and basic auth types. For example, to add a bearer auth route for the GitHub API:
cordon route add --host api.github.com --auth-type bearer --source keyring --account github
If you chose keyring as the secret source, store the secret using the account name from the route:
cordon secret set <account>
To modify an existing route, use cordon route edit <name> — see Routes for the full format.

API key setup

Claude Code needs an API key env var set so it selects the API key auth path. Add a placeholder to your settings file — .claude/settings.local.json for project scope or ~/.claude/settings.json for user scope:
{
  "env": {
    "ANTHROPIC_API_KEY": "dummy-replaced-by-cordon"
  }
}
Cordon strips this dummy key and injects the real one from your secret store at the network layer.

Manual setup

If you prefer manual configuration, set these env vars in Claude Code’s environment. Replace <PORT> with the port written into your cordon.toml (check listen = ...):
export HTTPS_PROXY=http://127.0.0.1:<PORT>
export HTTP_PROXY=http://127.0.0.1:<PORT>
export https_proxy=http://127.0.0.1:<PORT>
export http_proxy=http://127.0.0.1:<PORT>
export NODE_EXTRA_CA_CERTS=./ca-cert.pem
export SSL_CERT_FILE=/path/to/combined-ca.pem
export REQUESTS_CA_BUNDLE=/path/to/combined-ca.pem
export CURL_CA_BUNDLE=/path/to/combined-ca.pem
You must create a combined CA bundle manually if not using cordon setup claude-code. Concatenate your system CA bundle with the Cordon CA cert — see Combined CA bundle below.
You can add these to Claude Code’s settings file (replace <PORT> with the port in your cordon.toml). For project scope, write to .claude/settings.local.json — Claude Code’s per-checkout, gitignored-by-convention override file — so worktrees don’t inherit stale proxy env vars. For user scope, write to ~/.claude/settings.json:
{
  "env": {
    "HTTPS_PROXY": "http://127.0.0.1:<PORT>",
    "HTTP_PROXY": "http://127.0.0.1:<PORT>",
    "https_proxy": "http://127.0.0.1:<PORT>",
    "http_proxy": "http://127.0.0.1:<PORT>",
    "NODE_EXTRA_CA_CERTS": "./ca-cert.pem",
    "SSL_CERT_FILE": "/path/to/combined-ca.pem",
    "REQUESTS_CA_BUNDLE": "/path/to/combined-ca.pem",
    "CURL_CA_BUNDLE": "/path/to/combined-ca.pem"
  }
}

Combined CA bundle

Claude Code can launch MCP servers and tools that use Python, curl, wget, or other non-Node runtimes. These tools use SSL_CERT_FILE, REQUESTS_CA_BUNDLE, or CURL_CA_BUNDLE to locate trusted certificates. Python’s SSL_CERT_FILE replaces the default certificate store rather than appending to it. Setting it to just the Cordon CA cert would break TLS for all non-proxied connections (the system CAs would be missing). To solve this, cordon setup claude-code generates a combined CA bundle that concatenates:
  1. Your system CA certificates (e.g., from /etc/ssl/cert.pem)
  2. The Cordon proxy CA certificate
This combined bundle is written next to the Cordon CA cert (e.g., combined-ca.pem) and SSL_CERT_FILE, REQUESTS_CA_BUNDLE, and CURL_CA_BUNDLE all point to it.
  • SSL_CERT_FILE — used by Python’s ssl module and OpenSSL-based tools
  • REQUESTS_CA_BUNDLE — used by Python’s requests library (does not read SSL_CERT_FILE)
  • CURL_CA_BUNDLE — used by curl and libcurl-based tools
  • NODE_EXTRA_CA_CERTS — used by Node.js (appends to built-in CAs, so it points to just the Cordon CA cert, not the combined bundle)

Trust the CA

If tools used by Claude Code fail with certificate errors:
tls: failed to verify certificate: x509: certificate signed by unknown authority
Add the CA to the system trust store:
cordon trust
Node.js ignores the system trust store, so you still need NODE_EXTRA_CA_CERTS for Node-based tools (this is handled automatically by cordon setup claude-code).

Workflow

Once configured, the workflow is:
  1. Start cordon: cordon start (or use the background service)
  2. Start Claude Code as usual
  3. When Claude Code makes API calls to configured hosts, cordon transparently injects credentials
  4. Claude Code never sees or logs real API keys
Use cordon doctor to diagnose any setup issues. It checks config validity, cert paths, trust store status, and port availability.

Health-check hook

When cordon setup claude-code runs, it installs a UserPromptSubmit hook that checks whether cordon is responding before every message you send. If cordon is configured but not running, the hook blocks the message and shows an error — preventing you from sitting through repeated ConnectionRefused retries with no explanation. The hook runs cordon status -q, a fast health check (50ms timeout with one retry) that exits 0 if cordon is healthy and 1 if not.

What you see when cordon is down

Cordon proxy is not running. Please start it, or remove the integration to continue without it.
The hook exits with code 2, which blocks the message. The ! prefix runs shell commands directly from Claude Code’s prompt without going through the AI — use it to start cordon or remove the integration even while the hook is blocking.

Recovery options

OptionCommandNotes
Start as service! cordon service startRequires prior cordon service install. Runs in background.
Start manuallycordon start (separate terminal)Foreground process — keeps running until you Ctrl+C.
Remove integration! cordon integration disable claude-code --yesRemoves proxy env vars, hook, and skill. Restart session with claude --continue.
Hooks are loaded when Claude Code starts. If you install or remove the hook mid-session, restart with claude --continue for the change to take effect.

Hook details

  • Project scope: script is installed at .claude/cordon-status-hook.sh in the project directory
  • User scope: script is installed at ~/.config/cordon/hooks/claude-code-status.sh
  • The hook entry is added to settings.local.json (project) or ~/.claude/settings.json (user) under hooks.UserPromptSubmit
  • The hook appends to existing hooks — it does not overwrite any UserPromptSubmit hooks you already have
  • Running setup again is idempotent — if the hook is already installed, it prints “Hook already installed” and moves on
  • cordon integration disable claude-code removes both the hook entry from settings and the script file
  • The hook script is scope-aware: if setup was given an explicit --config <path>, the hook passes --config <path> to cordon status -q. For user scope (without explicit config), it passes --scope user. This ensures the hook checks the correct cordon instance.

Troubleshooting

This message comes from the health-check hook. It means cordon’s proxy env vars are set in your Claude Code settings, but cordon status -q could not reach the health endpoint.
  1. Start cordon: type ! cordon service start in the Claude Code prompt, or run cordon start in another terminal
  2. Check status: ! cordon status shows whether the proxy is running, stopped, or blocked by another process
  3. Remove the integration: if you no longer want cordon for this project, type ! cordon integration disable claude-code --yes, then restart with claude --continue
If tools used by Claude Code fail with certificate errors:
tls: failed to verify certificate: x509: certificate signed by unknown authority
  1. Verify NODE_EXTRA_CA_CERTS is set in your settings file (.claude/settings.local.json for project scope, ~/.claude/settings.json for user scope) and points to an existing file
  2. For Python MCP servers, verify SSL_CERT_FILE and REQUESTS_CA_BUNDLE are set and point to the combined CA bundle (not just the Cordon CA cert)
  3. For curl/wget-based tools, verify CURL_CA_BUNDLE is set
  4. For other non-Node tools, add the CA to the system trust store: cordon trust
Node.js ignores the system trust store — it only reads NODE_EXTRA_CA_CERTS. Python’s SSL_CERT_FILE replaces the system store, so it must point to the combined bundle. Both are handled automatically by cordon setup claude-code.
If Go-based tools (gh, terraform, kubectl, gcloud) fail with:
tls: failed to verify certificate: OSStatus -26276
This means Claude Code’s macOS sandbox is blocking com.apple.trustd.agent Mach IPC. Go uses Apple’s Security.framework for TLS certificate verification, which communicates with the trustd daemon. The sandbox blocks this by default.Add enableWeakerNetworkIsolation to your sandbox settings:
{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "enableWeakerNetworkIsolation": true,
    "network": {
      "allowLocalBinding": true
    }
  }
}
See Sandbox configuration for details and trade-offs. Restart Claude Code after changing sandbox settings.Python tools may also encounter TLS errors in the sandbox, but these are more commonly resolved by ensuring SSL_CERT_FILE and REQUESTS_CA_BUNDLE point to the combined CA bundle — see the Certificate errors accordion above.
Verify the env vars are in Claude Code’s settings file. Project scope writes .claude/settings.local.json; user scope writes ~/.claude/settings.json.
# Project scope
cat .claude/settings.local.json

# User scope
cat ~/.claude/settings.json
If the vars are set but Claude Code isn’t routing through the proxy, ensure cordon is running:
cordon status
# Replace <PORT> with the listen port from your cordon.toml
curl http://127.0.0.1:<PORT>/health
If HTTP_PROXY/HTTPS_PROXY are set in your settings file but subprocesses (curl, wget, gh) don’t see them — requests go directly to the internet and cordon credential injection silently fails.Cause: The sandbox is disabled ("enabled": false or absent). Claude Code’s sandbox manages proxy env var propagation to subprocesses. When the sandbox is off, the proxy env vars from your settings file are not passed through, even though other env vars (like CA cert paths) may be.Diagnosis: Run env | grep HTTP_PROXY in a Claude Code Bash tool call. If the proxy vars are missing but CA cert vars are present, the sandbox is likely disabled.Fix: Ensure sandbox.enabled is true in your settings file:
{
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "network": {
      "allowLocalBinding": true
    }
  }
}
Restart Claude Code after changing sandbox settings.
Node.js MCP servers need the bootstrap loader to enable proxy support. Add to your MCP server config:
{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["-y", "@some/mcp-server"],
      "env": {
        "NODE_OPTIONS": "--import @codezero-io/cordon/register"
      }
    }
  }
}
Python MCP servers using requests or httpx will respect HTTPS_PROXY automatically if it’s set in the environment.
A route is configured and cordon is running, but requests from Node.js never appear in cordon start output.Cause: Node.js built-in fetch (and SDKs that use it, such as the Anthropic, OpenAI, and Resend clients) ignores HTTPS_PROXY / HTTP_PROXY environment variables. Requests bypass the proxy entirely and go direct to the API, so cordon never sees them.Fix: Install the cordon bootstrap loader and set NODE_OPTIONS so Node.js routes all HTTP traffic through the proxy:
npm install @codezero-io/cordon
Then add to your Claude Code settings file (.claude/settings.local.json for project scope, ~/.claude/settings.json for user scope):
{
  "env": {
    "NODE_OPTIONS": "--import @codezero-io/cordon/register"
  }
}
cordon setup claude-code sets this automatically when the package is installed.Native alternative (Node 22.21+ / 24+): Node.js is adding built-in proxy support via NODE_USE_ENV_PROXY=1. Once you are on a supported version, this environment variable tells Node’s fetch to respect HTTPS_PROXY without requiring the register import.
Cordon loads routes at startup. If you add or change routes in cordon.toml, restart the proxy (secrets are fetched per-request and don’t require a restart):
# If running manually
# Ctrl+C, then:
cordon start

# If running as a service
cordon service stop && cordon service start
  1. Verify the secret is stored: cordon secret set <account> (find the account name with cordon route show <name>)
  2. Check the auth type: Anthropic uses type: api_key with header_name: x-api-key, not type: bearer
  3. Check the secret source: Secrets are fetched per-request — if you changed a secret, the next request picks it up automatically