Skip to main content
Cordon can fetch credentials from 1Password vaults using the 1Password CLI (op). This guide covers setup, configuration, the security model for agent workflows, and troubleshooting. For the OS keyring alternative, see Secret Sources.

Prerequisites

Before configuring 1Password as a secret source, you need:
  1. A 1Password account with a vault containing the secrets you want to inject.
  2. The 1Password CLI (op) installed:
# macOS
brew install --cask 1password-cli

# Linux / other platforms
# See https://developer.1password.com/docs/cli/get-started/#install
  1. An authenticated session — either interactive or via a service account:
# Interactive sign-in (eval exports the session token to your shell)
eval $(op signin)

# Or set a service account token
export OP_SERVICE_ACCOUNT_TOKEN="..."
  1. Verify the session works:
op vault list
Cordon does not handle 1Password authentication. The op CLI must have a valid session before cordon start. If the session expires, Cordon must be restarted after re-authenticating.

Configuration

Per-route secret reference

Each route that uses 1Password specifies the vault, item, and field:
routes:
  - name: anthropic
    match:
      host: api.anthropic.com
    auth:
      type: bearer
      secret:
        source: 1password
        vault: Engineering
        item: Anthropic API Key
        field: credential
FieldDescription
vault1Password vault name
itemItem name within the vault
fieldField label within the item (case-sensitive)

Provider configuration (optional)

By default, Cordon finds the op binary by searching your PATH. If op is not on PATH — common when running as a background service via launchd or systemd — specify the path explicitly:
secrets:
  providers:
    - type: 1password
      path: /opt/homebrew/bin/op
Find the full path to your op binary with which op. Background services often have a minimal PATH that doesn’t include Homebrew or user-installed binaries.

Complete example

listen: 6790

tls:
  enabled: true
  ca_cert_path: "$HOME/.config/cordon/projects/my-app/certs/ca-cert.pem"
  ca_key_path: "$HOME/.config/cordon/projects/my-app/certs/ca-key.pem"

secrets:
  providers:
    - type: 1password
      path: /opt/homebrew/bin/op

routes:
  - name: stripe
    match:
      host: api.stripe.com
    auth:
      type: bearer
      secret:
        source: 1password
        vault: Engineering
        item: Stripe API Key
        field: secret_key

  - name: anthropic
    match:
      host: api.anthropic.com
    auth:
      type: bearer
      secret:
        source: 1password
        vault: Engineering
        item: Anthropic API Key
        field: credential
You can mix 1Password and keyring sources in the same config. See Secret Sources for an example.

How it works

Cordon resolves all 1Password secrets at startup, not per-request:
  1. Cordon locates the op binary (from the configured path or by searching PATH).
  2. For each route with source: 1password, Cordon runs:
    op item get "<item>" --vault "<vault>" --fields "label=<field>" --format json
    
  3. The JSON response’s value field is extracted and wrapped in Cordon’s Secret type, which zeroizes memory on drop and redacts on all log output.
  4. The op CLI is not called again after startup. Secrets are held in memory for the proxy’s lifetime.
No automatic refresh. If you rotate a credential in 1Password, restart Cordon to pick up the new value.

Authentication methods

Interactive sign-in

eval $(op signin)
Suitable for development sessions. The session may expire after a period of inactivity (controlled by 1Password’s settings). If the session expires before Cordon starts, cordon start will fail with an op error — re-authenticate and try again.

Service accounts

export OP_SERVICE_ACCOUNT_TOKEN="..."
cordon start
Suitable for background services, CI, and headless environments. Service accounts don’t expire and don’t require interactive authentication.
Use service accounts with cordon service install. The launchd or systemd service runs without a terminal, so interactive sign-in is not possible. Set OP_SERVICE_ACCOUNT_TOKEN in the service’s environment (launchd plist or systemctl --user edit cordon-<name>.service).
For service account setup, see 1Password Service Accounts.

Security considerations

What Cordon protects

When using Cordon with 1Password, credentials are injected at the HTTP layer — the agent process never handles secret values. Specifically, Cordon keeps the following out of the agent’s environment variables, logs, and request headers:
  • The credential value (injected after the request leaves the agent’s process)
  • Any Authorization header value (stripped and replaced unconditionally for matched routes)
Vault names, item names, and field names are configuration metadata, not credentials. Cordon does not inject them into the agent’s environment or expose them through the proxy. However, these values are present in cordon.yaml on disk — a same-user agent with file or shell access could read the config file and discover them. This is a limitation of the local same-user trust model, not credential exposure.

The op CLI attack surface

Cordon invokes the op CLI as a subprocess. Whether other processes can reuse that authenticated session depends on how op is configured:
  • Desktop app integration (biometric): The op CLI communicates with the 1Password desktop app over a local socket. While the app is unlocked, any same-user process can invoke op and authenticate through the same socket.
  • Shell-based sign-in (eval $(op signin)): The session token is stored in a shell environment variable (OP_SESSION_*). Only processes that inherit that variable can reuse it — other terminals and independently launched processes cannot.
  • Service accounts (OP_SERVICE_ACCOUNT_TOKEN): Only processes with this environment variable can authenticate.
In cases where the session is reachable, an AI coding agent with shell access can run commands like:
op read "op://Engineering/Stripe API Key/secret_key"
This bypasses Cordon entirely. The proxy cannot intercept or restrict local process execution.
This side channel is not introduced by Cordon — it is inherent to any setup where the op CLI is installed and an authenticated session is reachable. Cordon reduces the attack surface by keeping credentials out of environment variables, logs, and the agent’s context, but it cannot fully mediate local CLI access to the vault.

Mitigations

Scope service account access. If using a service account, grant it access only to the specific vault items Cordon needs. A compromised agent can only reach items the service account can access. Consider the OS keyring on macOS. macOS Keychain enforces per-application access control lists (ACLs) on keychain entries. By default, only the binary that created an entry can read it silently — other binaries trigger a system authorization dialog requiring user approval. This means an agent process calling security find-generic-password to read an entry created by the cordon binary would prompt the user visibly, providing an opportunity to deny access. See Secret Sources — OS Keyring. Restrict agent shell access where possible. Some agents support permission modes that limit arbitrary command execution:
  • Claude Code’s permission system can restrict tool use
  • Codex offers a sandboxed execution mode
  • IDE-based agents (Cursor) can limit terminal access through configuration
Monitor 1Password audit logs. 1Password Teams and Business plans provide audit logs that record op CLI access. Unexpected access patterns may indicate an agent attempting to read vault items directly.

Future direction

A direct SDK integration — compiling 1Password access into Cordon’s binary rather than shelling out to op — is being explored. This would eliminate the external CLI as an attack vector by keeping the authenticated session inside Cordon’s process memory, inaccessible to other processes. This integration does not exist today; the op CLI is the current integration path.

Workflow

  1. Store the secret in 1Password (create a vault, item, and field if needed).
  2. Install and authenticate the op CLI (see Prerequisites).
  3. Configure the route in cordon.yaml with source: 1password and the vault/item/field names.
  4. Start Cordon: cordon start — secrets are fetched from 1Password at startup.
  5. Route traffic through the proxy — credentials are injected transparently.

Diagnostics

# Verify Cordon can find and use the op binary
cordon doctor

# Verify op session is active
op vault list

# Verify which account is signed in
op whoami

# Inspect a specific item's fields (to verify field labels)
op item get "Stripe API Key" --vault "Engineering" --format json
cordon doctor checks for the op binary on PATH and validates configured provider paths. Run it first when troubleshooting 1Password issues.

Troubleshooting

The op CLI returned an error. Common causes:
  • Session expired: Re-authenticate with eval $(op signin) and restart Cordon.
  • Vault not found: Verify the vault name matches exactly (case-sensitive). Check with op vault list.
  • Item not found: Verify the item name matches exactly. Check with op item list --vault "VaultName".
  • Permission denied: The signed-in account or service account lacks access to the vault. Check vault permissions in the 1Password app.
Cordon passes op error output directly to the terminal — read the error message for specifics.
The op binary is not on PATH. Solutions:
  • Install it: brew install --cask 1password-cli (macOS) or see the 1Password CLI install guide.
  • Specify the path explicitly in your config under secrets.providers (see Provider configuration).
  • Background services: launchd and systemd services have a minimal PATH. Use an explicit path in the provider config, or add the op directory to the service’s PATH.
Run which op in your shell to find the binary location.
Cordon fetches secrets once at startup. After rotating a credential in 1Password:
  1. Stop Cordon: cordon stop
  2. Restart: cordon start
The new value will be fetched from 1Password on the next startup.
OP_SERVICE_ACCOUNT_TOKEN must be set in the environment where Cordon runs.
  • Shell: export OP_SERVICE_ACCOUNT_TOKEN="..." && cordon start
  • launchd: Add an EnvironmentVariables entry to the plist.
  • systemd: Use systemctl --user edit cordon-default.service to add an Environment= line in the override, then restart the service with systemctl --user restart cordon-default.service. Replace default with your instance name if you used --name during setup.
Verify with: OP_SERVICE_ACCOUNT_TOKEN="..." op vault list
The field value in your config must match the field label in 1Password exactly (case-sensitive).Inspect the item’s fields to find the correct label:
op item get "Item Name" --vault "Vault Name" --format json
Look for the label property on each field in the JSON output. Common mistakes:
  • Using password instead of credential (or vice versa)
  • Using a section name instead of a field label
  • Case mismatch (e.g., Secret_Key vs secret_key)