> ## 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.

# Secret Sources

> Configure where cordon fetches credentials — 1Password or the OS keyring.

For HTTP routes, Cordon fetches credentials from external secret stores per request. PostgreSQL listeners currently resolve credentials at startup. Two sources are supported.

## Rotation and restarts

| Consumer             | When the secret is resolved                    | Rotation behavior                                                                        |
| -------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------- |
| HTTP routes          | Startup validation, then every matched request | New values are picked up on the next request. No restart required after secret rotation. |
| PostgreSQL listeners | Listener startup                               | Restart Cordon after rotating the listener password.                                     |

Route definitions are still loaded at startup. Restart Cordon after adding, removing, or editing routes or listeners.

## 1Password

Fetches secrets from 1Password vaults using the [1Password CLI](https://1password.com/downloads/command-line/) (`op`).

```toml theme={null}
[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
```

| Field   | Description                                                       |
| ------- | ----------------------------------------------------------------- |
| `vault` | 1Password vault name (within the currently authenticated account) |
| `item`  | Item name within the vault                                        |
| `field` | Field label within the item (case-sensitive)                      |

Cordon uses whichever 1Password account the `op` CLI is currently authenticated against. It does not support specifying which account to use — if you have multiple 1Password accounts, ensure the correct one is signed in before starting cordon. Use `op whoami` to check which account is active and `op vault list` to see available vaults.

### Prerequisites

The `op` CLI must be installed and authenticated before starting Cordon. For installation, sign-in options, service accounts, and troubleshooting, see the [1Password guide](/guides/onepassword).

### Provider configuration (optional)

If `op` is not on `PATH` (common for background services), specify the binary path explicitly in the provider config:

```toml theme={null}
[secrets]
[[secrets.providers]]
type = "1password"
path = "/opt/homebrew/bin/op"
```

## OS Keyring

Reads credentials from the operating system's built-in credential store.

```toml theme={null}
[routes.auth.secret]
source = "keyring"
account = "stripe-api-key"
```

| Field     | Required | Description                                                              |
| --------- | -------- | ------------------------------------------------------------------------ |
| `account` | yes      | Account name (identifier for the credential)                             |
| `service` | no       | Keyring service name. Defaults to `"cordon"` when omitted.               |
| `decode`  | no       | Decoding strategy for encoded values. Currently supports `"go-keyring"`. |

By default, cordon 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`.

### Reading third-party keychain entries

You can use `service` to read credentials stored by other tools under their own keychain entries, avoiding token duplication. For example, the GitHub CLI (`gh`) stores its token under service `gh:github.com`:

```toml theme={null}
[routes.auth.secret]
source = "keyring"
account = "lightcap"
service = "gh:github.com"
decode = "go-keyring"
```

This reads `gh`'s token directly — no need to copy it into cordon's keyring. When `gh auth refresh` updates the token, cordon picks up the new value automatically.

### The `decode` field

Some tools encode values before storing them in the keychain. The `decode` field tells cordon how to decode the raw keychain value before using it.

| Value          | Description                                                                                                                                                                                                                                                                     |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"go-keyring"` | Handles encoding from Go's [zalando/go-keyring](https://github.com/zalando/go-keyring) library. Strips `go-keyring-base64:` and base64-decodes the remainder, or strips `go-keyring-encoded:` and hex-decodes the remainder. Values without a recognized prefix are used as-is. |

`decode` is only needed when the tool that stored the credential uses an encoding wrapper. Most tools (Python, Rust, native CLI tools) store raw values and don't need it. The `go-keyring` encoding is used by Go CLI tools including `gh`.

### Storing keyring credentials

Use `cordon secret set` to store credentials under the default `cordon` service. The account name defaults to the route name during `cordon route add`, so in most cases they match. Use `cordon route show <name>` to confirm the account name for a route:

```bash theme={null}
cordon secret set stripe
# Prompts for the secret value
```

On macOS, you can also use the native `security` CLI:

```bash theme={null}
security add-generic-password -s cordon -a stripe-api-key -w 'sk_live_...'
```

<Note>
  `cordon secret set` always stores under the `cordon` service. When using a custom `service` to read from a third-party keychain entry, the credential is managed by that tool — you don't need to store it via cordon.
</Note>

### Platform differences

<AccordionGroup>
  <Accordion title="macOS Keychain">
    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, doing a fresh `cargo build` during development, or changing the install path changes the binary identity. macOS will prompt for keychain access the first time the new binary reads the secret. When this prompt appears, click **Always Allow** (not just Allow) to permanently grant the new binary access. Because cordon resolves keyring secrets just-in-time on each request, clicking "Allow" (one-time) means the prompt reappears on the very next request — which can be milliseconds later.

    In SSH, headless, or detached screen/tmux sessions, the keychain is locked and cannot display a prompt. Run `security unlock-keychain ~/Library/Keychains/login.keychain-db` first, or run the command from a terminal in a desktop session (directly or via screen sharing) where the login keychain is already unlocked. See [Troubleshooting (macOS)](#troubleshooting-macos) for details.
  </Accordion>

  <Accordion title="Linux Secret Service">
    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:

    ```bash theme={null}
    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.
  </Accordion>
</AccordionGroup>

### Troubleshooting (macOS)

<AccordionGroup>
  <Accordion title="Platform secure storage failure: User interaction is not allowed.">
    The macOS login keychain is locked. This happens in SSH sessions, headless CI,
    and detached screen/tmux sessions where Keychain cannot display its
    authorization dialog.

    Unlock the login keychain before running `cordon secret set`:

    ```bash theme={null}
    security unlock-keychain ~/Library/Keychains/login.keychain-db
    ```

    This prompts for your macOS login password. Once unlocked, keychain operations
    work for the remainder of the session. If you have access to the Mac directly
    or via screen sharing, run `cordon secret set` from a terminal in the desktop
    session instead — the login keychain is already unlocked there.
  </Accordion>

  <Accordion title="Authorization prompt on every request after upgrade">
    macOS Keychain enforces per-application access control based on the binary
    identity. Any change to the cordon binary — upgrades, fresh builds during
    development, different install paths — triggers a new authorization prompt.

    **Preferred fix:** Re-store credentials with `cordon secret set <account>`
    so the current binary owns the entry.

    **Quick unblock:** Click **Always Allow** on the macOS Keychain dialog. This
    permanently grants the new binary access. Do not click just "Allow" — HTTP
    routes resolve keyring secrets per-request, so a one-time allow triggers a
    new prompt on every subsequent request.
  </Accordion>
</AccordionGroup>

### Troubleshooting (Linux)

<AccordionGroup>
  <Accordion title="DBus error: Object does not exist at path &#x22;/org/freedesktop/secrets/collection/login&#x22;">
    A Secret Service provider is not running or has not initialized its default collection. Fix:

    ```bash theme={null}
    sudo apt install gnome-keyring
    ```

    Then **log out and log back in** so the keyring daemon starts and creates the "login" collection.
  </Accordion>

  <Accordion title="Platform secure storage failure">
    The D-Bus session bus is not available. Verify it's reachable:

    ```bash theme={null}
    # 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.
  </Accordion>

  <Accordion title="Keyring access timed out">
    The Secret Service provider is not responding. Check that gnome-keyring-daemon (or your provider) is running:

    ```bash theme={null}
    ps aux | grep gnome-keyring
    ```

    If not running, log out and back in, or start it manually:

    ```bash theme={null}
    gnome-keyring-daemon --start --components=secrets
    ```
  </Accordion>
</AccordionGroup>

## Mixing sources

You can use different secret sources for different routes:

```toml theme={null}
[[routes]]
name = "stripe"

[routes.match]
host = "api.stripe.com"

[routes.auth]
type = "header"
header_name = "Authorization"
scheme = "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 = "header"
header_name = "X-Api-Key"

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