Cordon is a local credential-injection proxy. It intercepts HTTP/HTTPS traffic on the loopback interface and injects API credentials from external secret stores. This page describes the security architecture, trust boundaries, and design rationale.
This document describes the Community (local) deployment model. Network-facing deployment models (Team/Enterprise) extend the trust boundary and are documented separately as they ship.
Design principles
These principles govern all security decisions in the local deployment model.
Structural security over configuration-dependent security
Security properties are enforced by code, not by operators setting the right flags. There are no environment variables, CLI flags, or config options that weaken the security posture. If a property matters, it is hardcoded or enforced by the type system. Examples:
- The bind address is hardcoded to
127.0.0.1 — there is no option to bind to another address.
- The SSRF denylist is always on — there is no option to disable it.
- Upstream TLS verification uses the system root store — there is no option to skip certificate validation.
Credential injection, not credential management
Cordon fetches credentials from external vaults and injects them into outbound requests. It never persists, encrypts, or manages a credential store of its own. There is no Cordon-managed database to breach. The proxy’s security does not depend on defending a credential store — it depends on the security of credential sources the developer already trusts and controls.
No infrastructure in the request path
The proxy runs on the developer’s machine. Outbound requests go directly from the proxy to the upstream API — no intermediate cloud services, no shared multi-tenant servers, no SaaS relay. External trust is limited to the credential source (OS keyring or vault provider), which the developer already trusts and controls.
Defense in depth through the type system
The Secret type enforces zeroization, log prevention, and explicit access at compile time. #![forbid(unsafe_code)] prevents bypassing these guarantees. Security properties that depend on developer discipline are replaced by properties that depend on compilation.
Trust boundaries
Credential lifecycle
Credentials pass through five stages. There is no “at rest in Cordon” stage — the proxy holds no credential database, no encrypted store, and no secret material on disk.
| Stage | Location | Protection |
|---|
| At rest | External vault (1Password) or OS keyring | Vault/OS-managed encryption |
| In transit (vault → proxy) | 1Password CLI stdout or D-Bus session bus | Process-scoped; no network exposure |
| In memory | Secret type on the proxy’s task stack | Zeroize on drop, no Debug/Display, token-gated boundary access |
| In transit (proxy → upstream) | TLS-encrypted HTTP request | System root CA validation, no bypass mechanism |
| After use | Zeroed | Guaranteed by Rust Drop implementation |
Explicit trust dependencies
Cordon trusts the following components. Compromise of any of these is outside the proxy’s trust boundary.
- The local operating system — process isolation, memory protection, keyring access control. A compromised OS is outside the trust boundary.
- The credential source — 1Password or OS keyring returns the correct secret. Cordon does not verify secret correctness, only that resolution succeeded. When using the
op CLI backend, the pre-authenticated session may be accessible to other same-user processes depending on the authentication method — see 1Password security considerations.
- The upstream TLS PKI — system root certificates validate upstream API identity. Cordon uses the system trust store and provides no mechanism to override or disable certificate validation.
- The developer’s route configuration — configured routes are an explicit trust decision. Cordon injects credentials for matched routes without further validation. Misconfigured routes can direct credentials to unintended destinations.
Credential storage
Cordon delegates credential storage entirely to external providers. The proxy holds no credential database, no encrypted store, and no secret material on disk. Secrets exist in the proxy’s memory only during active use.
| Source | Config value | Storage | Encryption |
|---|
| OS keyring | keyring | macOS Keychain / Linux Secret Service | OS-managed (encrypted at rest) |
| 1Password | 1password | 1Password vault | 1Password-managed |
macOS Keychain encrypts entries at rest and enforces per-application access control. Only the binary that created an entry can read it without triggering an authorization dialog.
Linux Secret Service (GNOME Keyring, KDE Wallet) encrypts entries on disk and unlocks them with the user’s login session. There are no per-application ACLs — any process running as the user can read entries. Security relies on Unix user isolation.
On Linux, secrets are transmitted to the Secret Service provider over the D-Bus session bus without transport-layer encryption (no DH key exchange). This is a deliberate trade-off: the session bus is a Unix socket restricted to the current user, so any process that could observe bus traffic already has same-user code execution and could query the Secret Service directly. The trust boundary is the session bus access control, not encryption on top of it.
In-memory secret handling
Secrets in memory are protected with multiple layers:
- Zeroized on drop — secret values are overwritten with zeros when no longer needed, reducing the window for memory scraping
- No Debug or Display — the
Secret type has no Debug or Display impl. Attempts to log or format a secret fail at compile time, not at runtime.
- Token-gated boundary access — plaintext is accessible only through callback methods that require a capability token. Each protocol boundary module defines its own token with a private constructor, confining plaintext access to the specific module that needs it. Compile-fail tests enforce this from external crates.
- No unsafe code — the core library forbids unsafe code at compile time
Network security
Loopback-only binding
The proxy always binds to 127.0.0.1. The config only accepts a port number, not a bind address — it is structurally impossible to configure a non-loopback address. This ensures the proxy is only accessible from the local machine.
For matched routes, Cordon unconditionally strips the inbound Authorization header and replaces it with the credential from the configured secret source. This is not conditional — the proxy does not check whether the inbound header matches a placeholder, is empty, or contains a real credential. Strip-and-replace is always applied.
This prevents credential passthrough attacks where a manipulated agent sends a valid credential in the Authorization header hoping the proxy will forward it unchanged to an unintended destination.
SSRF protection
All unmatched outbound connections are subject to SSRF validation. Configured routes are exempt — a route is an explicit trust decision by the developer. For all other traffic, there is no way to bypass the IP denylist.
Cordon acts as a credential oracle — any process that can reach it can cause authenticated requests to configured upstream APIs. For untrusted runtimes (AI agents, LLM-generated code), this creates a potential SSRF exposure if left unmitigated. Cordon addresses this with an always-on IP denylist that blocks connections to internal infrastructure (cloud metadata, private networks, localhost services) before they leave the proxy.
Blocked ranges include:
- Private networks:
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
- Loopback:
127.0.0.0/8
- Link-local / cloud metadata:
169.254.0.0/16 (blocks AWS/GCP/Azure metadata endpoints)
- CGNAT:
100.64.0.0/10
- IPv6 equivalents:
::1, fc00::/7, fe80::/10, and more
- IPv6 transition addresses (IPv4-mapped, NAT64, 6to4, Teredo, ISATAP) have their embedded IPv4 extracted and re-checked
DNS pinning: After DNS resolution, all resolved IPs are validated, then the proxy connects to the validated address directly (not the hostname), closing the TOCTOU window against DNS rebinding.
TLS interception
For HTTPS routes, Cordon performs TLS MITM using a locally-generated CA certificate. See TLS configuration for details.
- The CA private key is stored with
0600 permissions
- The CA cert must be explicitly trusted by the user
- Per-host certificates use
SubjectAltName (not CN) as required by modern clients
- Upstream TLS verification uses the system root certificate store. There is no environment variable, CLI flag, or configuration option to disable certificate validation.