Skip to content

BYOK Secrets

dcluomax edited this page Jun 1, 2026 · 1 revision

BYOK Secrets

Coop is bring-your-own-key (BYOK): you supply the model API key, Coop never ships one. A Hen's brain.provider_id chooses where that key is read from.

Scheme Backend Key on disk?
vault:<secret> Local sealed file vault Yes β€” sealed at rest
azure-kv://<vault>/<secret>[/<version>] Azure Key Vault No β€” fetched at run time

Both can coexist; different hens on the same farm can use different backends.


Local sealed vault (default)

A single xchacha20poly1305 file, sealed with a key derived from your passphrase via Argon2id. File mode is 0600; the passphrase never touches disk.

export COOP_PASSPHRASE='change-me'
coop vault init ~/.coop/vault.json
COOP_SECRET_VALUE='sk-ant-...' coop vault put ~/.coop/vault.json byok-anthropic
coop vault list ~/.coop/vault.json

Auto-unlock the vault at daemon startup:

COOP_VAULT=~/.coop/vault.json COOP_PASSPHRASE='change-me' coopd serve

Or unlock a running daemon over the API:

curl -X POST http://127.0.0.1:9700/api/v1/vault/unlock \
  -H 'content-type: application/json' \
  -d '{"path":"~/.coop/vault.json","passphrase":"change-me"}'

Reference it from a manifest:

brain:
  provider_id: vault:byok-anthropic
  model: claude-sonnet-4-5-20250929

Azure Key Vault

For teams who already centralize secrets in Azure. The key is fetched over HTTPS each time a brain is built and held only in zeroized memory β€” it is never written to the local vault file or disk.

Reference syntax

azure-kv://<vault-name>/<secret-name>
azure-kv://<vault-name>/<secret-name>/<version>
brain:
  provider_id: azure-kv://my-coop-kv/byok-anthropic
  model: claude-sonnet-4-5-20250929

The optional /<version> pins a specific secret version; omit it for the latest enabled one.

Authentication

Credentials come from the environment (the standard Azure EnvironmentCredential model), resolved in this order:

1. Static bearer token β€” bring your own already-acquired AAD token:

export AZURE_KEYVAULT_TOKEN="$(az account get-access-token \
  --resource https://vault.azure.net --query accessToken -o tsv)"

Coop does not refresh it β€” supply a fresh one if it expires (useful with a managed identity that mints tokens out-of-band).

2. Service principal (client credentials) β€” Coop performs the OAuth2 client-credentials flow against AAD and caches the token until just before expiry:

export AZURE_TENANT_ID=<tenant-guid>
export AZURE_CLIENT_ID=<app-registration-client-id>
export AZURE_CLIENT_SECRET=<client-secret>

Sovereign / national clouds

Variable Default Example override
AZURE_KEYVAULT_DNS_SUFFIX vault.azure.net vault.azure.cn, vault.usgovcloudapi.net
AZURE_AUTHORITY_HOST https://login.microsoftonline.com https://login.chinacloudapi.cn

Required permission

The token / service principal needs the Get secret permission on the target vault β€” the Key Vault Secrets User role under RBAC, or a get secrets access policy.

Setup example (Azure CLI)

# create a vault + secret
az keyvault create --name my-coop-kv --resource-group my-rg --location eastus
az keyvault secret set --vault-name my-coop-kv \
  --name byok-anthropic --value 'sk-ant-...'

# create a service principal and grant Get on secrets
az ad sp create-for-rbac --name coop-byok
az role assignment create --assignee <appId> \
  --role "Key Vault Secrets User" \
  --scope $(az keyvault show --name my-coop-kv --query id -o tsv)

Then export AZURE_TENANT_ID / AZURE_CLIENT_ID / AZURE_CLIENT_SECRET and point a manifest at azure-kv://my-coop-kv/byok-anthropic.


Security properties

  • Secret values are wrapped in Zeroizing and wiped from memory on drop.
  • AAD tokens and client secrets are redacted in Debug output and never logged.
  • Azure Key Vault error bodies are truncated to 512 bytes and contain no secret values.
  • All Azure traffic is HTTPS-only (https_only).
  • See the Security Model hardening table (M1, M2).

Clone this wiki locally