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 is configured via cordon.toml. cordon setup creates this file for you — you do not need to write it by hand.
See setup for details.
Scopes
Cordon is project-first — most integrations default to project scope, giving each repository its own config, credentials, and proxy instance. A user scope is also available for tools that span projects. The two scopes are independent: each runs its own daemon with its own config file, TLS certificates, and optional OS service. They are not merged or layered.
| Resource | Project scope | User scope |
|---|
| Config file | $CWD/cordon.toml | $XDG_CONFIG_HOME/cordon/cordon.toml |
| TLS certificates | $XDG_CONFIG_HOME/cordon/projects/<dirname>-<hash8>/certs/ | $XDG_CONFIG_HOME/cordon/user/certs/ |
| Service name | <dirname>-<hash8> | user |
<dirname>-<hash8> is the project directory basename plus the first 8 hex characters of the SHA-256 of the absolute CWD path, so two projects with the same directory name never collide.
On macOS (launchd) and Linux (systemd user units) these names are automatically namespaced. The launchd label becomes io.codezero.cordon.<name>.
Directory layout
Cordon follows the XDG Base Directory spec for its config location. $XDG_CONFIG_HOME defaults to ~/.config on both Linux and macOS — cordon intentionally uses ~/.config/cordon/ on macOS for consistency with cross-platform dev tools like git and VS Code. Set XDG_CONFIG_HOME=/custom/path to override; cordon will then use /custom/path/cordon/.
~/.config/cordon/ # base directory
├── cordon.toml # user-scope config
├── user/
│ └── certs/ # user-scope TLS material
│ ├── ca-cert.pem
│ └── ca-key.pem
└── projects/
└── myapp-a1b2c3d4/ # <dirname>-<hash8>
└── certs/ # project-scope TLS material
├── ca-cert.pem
└── ca-key.pem
<project>/
└── cordon.toml # project-scope config (one per project)
Project-scope configs live beside your code. Everything else — user-scope config and all TLS certificates — lives under the user’s cordon config directory. The <dirname>-<hash8> naming uses SHA-256 of the absolute project path so two projects sharing the same directory name never collide.
Port allocation
Each cordon setup invocation — project or user scope — asks the OS for a free port and writes it into cordon.toml at setup time. In practice this avoids collisions between concurrent projects and rapid successive cordon setup invocations. There is no base port to configure.
The allocated port is not reserved: cordon setup binds to port 0, reads the port the OS assigned, then releases the socket before writing the number into cordon.toml. Between setup and cordon start, another process could theoretically claim the port. If that happens, cordon start will fail to bind and you can rerun cordon setup to pick a new port. Persistent port reservation and re-allocation at start time is tracked in #370.
Per-integration defaults
| Integration | Default scope | Rationale |
|---|
claude-code | project | Editor configs are typically per-repo |
codex | project | Project-specific Codex env isolates credentials |
hermes | user | Hermes operates across projects; user scope avoids per-repo repetition |
Overriding the scope
Pass --scope user or --scope project to override the default for claude-code or codex:
# Configure Claude Code globally (one config for all projects)
cordon setup claude-code --scope user
Hermes only supports user scope — cordon setup hermes --scope project is rejected by the CLI.
When to choose which scope
- Project scope — the right default for most tools. Each repo gets its own credentials, port, and service. No cross-project interference.
- User scope — best for tools like Hermes that naturally span multiple projects, or when you want a single cordon instance shared across repos. One cordon process handles all requests.
cordon start and scope
cordon start with no flags uses ./cordon.toml (project scope). To start the user-scope instance explicitly:
cordon start --scope user
cordon service and scope
cordon service install/start/stop --scope <project|user> derives the service name and config path from the scope automatically:
cordon service install --scope project # installs <dirname>-<hash8>
cordon service install --scope user # installs user
After setup, use commands like cordon route add and cordon route edit to add and modify routes. For automation, run cordon setup non-interactively (e.g. --yes) instead of maintaining a hand-written config.
cordon service install does not write cordon.toml — it points an OS service at an existing file, typically the project-local file setup produced.
Paths in cordon.toml are literal strings: Cordon does not expand $HOME, ~, or other environment variables. Use the absolute paths written by cordon setup, or substitute placeholders like elsewhere in these docs (/path/to/...). If ca_cert_path or ca_key_path are relative, they are resolved against the config file’s directory (not the working directory), so hand-edited configs work correctly under service managers where the working directory is /.
Minimal example
The listen port is assigned automatically by cordon setup — the 6790 shown in the example is illustrative; your actual port is OS-assigned and typically in the ephemeral range. Check the listen = ... line in your generated cordon.toml to find the port in use.
listen = 6790 # illustrative — your setup may allocate a different port
[tls]
enabled = true
# Substitute paths from your real `cordon.toml` (typically under ~/.config/cordon/projects/<namespace>/certs/).
ca_cert_path = "/path/to/ca-cert.pem"
ca_key_path = "/path/to/ca-key.pem"
[[routes]]
name = "stripe"
[routes.match]
host = "api.stripe.com"
[routes.auth]
type = "bearer"
[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
Top-level fields
| Field | Type | Required | Description |
|---|
listen | integer | Yes | Port number to listen on. The proxy always binds to 127.0.0.1 (loopback) — this is not configurable because binding to a non-loopback address would expose injected credentials to the network. |
tls | object | No | TLS interception settings. Required for HTTPS routes. |
secrets | object | No | Secret provider configuration. Use to specify an explicit path to the op binary when it’s not on PATH (common for background services). See Secret Sources — Provider configuration. |
routes | array | No | List of HTTP route definitions. |
listeners | array | No | List of PostgreSQL listener definitions. See Routes — PostgreSQL listeners. |
TLS settings
| Field | Type | Required | Description |
|---|
tls.enabled | boolean | Yes | Enable HTTPS interception via TLS MITM. |
tls.ca_cert_path | string | Yes | Path to the CA certificate file. Created by cordon setup. |
tls.ca_key_path | string | Yes | Path to the CA private key file. Created by cordon setup. |
When TLS is enabled, cordon performs MITM on HTTPS connections for matched routes. It generates per-host certificates signed by the local CA. See TLS for details.
Full example
listen = 6790 # illustrative — your setup may allocate a different port
[tls]
enabled = true
# Substitute paths from your real `cordon.toml` (typically under ~/.config/cordon/projects/<namespace>/certs/).
ca_cert_path = "/path/to/ca-cert.pem"
ca_key_path = "/path/to/ca-key.pem"
[[routes]]
name = "stripe"
[routes.match]
host = "api.stripe.com"
[routes.auth]
type = "bearer"
[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
[[routes]]
name = "openai"
[routes.match]
host = "api.openai.com"
[routes.auth]
type = "bearer"
[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "OpenAI API Key"
field = "credential"
[[routes]]
name = "custom-service"
[routes.match]
host = "api.example.com"
[routes.auth]
type = "api_key"
header_name = "X-Api-Key"
[routes.auth.secret]
source = "keyring"
account = "example-api-key"
Config file location
cordon start resolves the config file based on scope:
- Project scope (default) —
./cordon.toml
- User scope (
--scope user) — $XDG_CONFIG_HOME/cordon/cordon.toml (or ~/.config/cordon/cordon.toml if XDG_CONFIG_HOME is not set)
cordon setup writes the config to the scope-appropriate path automatically. See Scopes above for the full scope path table.
You can bypass scope resolution entirely with the --config flag:
cordon start --config /path/to/cordon.toml
cordon.toml typically contains project-specific secret references and should be gitignored.