Skip to main content
For HTTP routes, Cordon fetches credentials from external secret stores per-request. PostgreSQL services currently resolve credentials at startup. Two sources are supported.

1Password

Fetches secrets from 1Password vaults using the 1Password CLI (op).
[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
FieldDescription
vault1Password vault name
itemItem name within the vault
fieldField label within the item (case-sensitive)

Prerequisites

The op CLI must be installed and authenticated before starting cordon:
# Install (macOS)
brew install --cask 1password-cli

# Sign in (eval exports the session token to your shell)
eval $(op signin)
For non-interactive use (CI, services), configure 1Password Service Accounts instead of interactive sign-in.
If op is not on PATH (common for background services), specify the binary path explicitly in the provider config:
[secrets]
[[secrets.providers]]
type = "1password"
path = "/opt/homebrew/bin/op"
For detailed setup, security considerations, and troubleshooting, see the 1Password guide.

OS Keyring

Reads credentials from the operating system’s built-in credential store.
[routes.auth.secret]
source = "keyring"
account = "stripe-api-key"
FieldDescription
accountAccount name (identifier for the credential)
Cordon always uses cordon as the keyring service name. If you inspect the OS keychain directly (e.g., macOS Keychain Access, security find-generic-password -s cordon), entries appear under the service cordon.

Storing keyring credentials

Use the cordon secret set command to store credentials. Pass the route or service name from your config:
cordon secret set stripe
# Prompts for the secret value
On macOS, you can also use the native security CLI:
security add-generic-password -s cordon -a stripe-api-key -w 'sk_live_...'

Platform differences

macOS Keychain enforces per-application access control. The binary that creates a keychain entry owns it and can read it without prompting. Any other binary triggers a system authorization dialog.Use cordon secret set to create entries owned by cordon. Entries created by other tools (e.g., security CLI) will prompt on every request that needs the secret.Upgrading cordon changes the binary signature. macOS may prompt for keychain access after an upgrade.
On Linux, cordon uses the D-Bus Secret Service API, typically provided by GNOME Keyring or KDE Wallet. Secrets are encrypted on disk and unlocked with the user’s login session. There are no per-application ACLs — any process running as the user can read entries. Security relies on standard Unix user isolation.A Secret Service provider must be installed and running:
sudo apt install gnome-keyring   # Debian/Ubuntu
After installing, log out and log back in to initialize the default “login” collection. Without this step, cordon secret set will fail with:
DBus error: Object does not exist at path "/org/freedesktop/secrets/collection/login"
A running D-Bus session bus is required — the keyring source does not work in containers, CI, or headless environments. Use 1Password as the secret source in those cases.

Troubleshooting (Linux)

A Secret Service provider is not running or has not initialized its default collection. Fix:
sudo apt install gnome-keyring
Then log out and log back in so the keyring daemon starts and creates the “login” collection.
The D-Bus session bus is not available. Verify it’s reachable:
# Check for session bus address
echo $DBUS_SESSION_BUS_ADDRESS

# Or check for systemd user bus socket
ls $XDG_RUNTIME_DIR/bus
If neither is available, you’re not in a desktop session (e.g., SSH, container, or headless server). The keyring source requires a desktop session — use 1Password instead.
The Secret Service provider is not responding. Check that gnome-keyring-daemon (or your provider) is running:
ps aux | grep gnome-keyring
If not running, log out and back in, or start it manually:
gnome-keyring-daemon --start --components=secrets

Mixing sources

You can use different secret sources for different routes:
[[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 = "internal-api"

[routes.match]
host = "api.internal.example.com"

[routes.auth]
type = "api_key"
header_name = "X-Api-Key"

[routes.auth.secret]
source = "keyring"
account = "internal-api-key"