Skip to content

Secrets and keys

appctl never writes API keys or tokens to the schema or history. They live in one of two places.

Service name: appctl. Each secret is keyed by a short name (secret_ref in config.toml).

Store:

Terminal window
appctl config set-secret ANTHROPIC_API_KEY --value "$ANTHROPIC_API_KEY"
appctl config set-secret supabase_anon --value "$SUPABASE_ANON_KEY"

Platforms:

  • macOS: the native Keychain.
  • Linux: secret-service (GNOME Keyring or KWallet must be running and unlocked).
  • Windows: Credential Manager.

If a secret is not in the keychain, appctl falls back to an environment variable of the same name:

Terminal window
export ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY"
appctl chat

Env vars always override the keychain at runtime. Useful inside containers, CI, and systemd.

In config.toml:

[[provider]]
name = "claude"
kind = "anthropic"
auth = { kind = "api_key", secret_ref = "ANTHROPIC_API_KEY" } # <- this name
model = "claude-sonnet-4"

In a schema auth block:

"auth": { "kind": "api_key", "header": "apikey", "env_ref": "supabase_anon" }

The env_ref field names the keychain/env key, not the raw value.

Terminal window
# Rotate
appctl config set-secret ANTHROPIC_API_KEY --value "$NEW_KEY"

No process restart is required. The next tool call reads the updated secret.

appctl auth target login <provider> stores target-app OAuth tokens under service appctl with key appctl_oauth::<provider>. Provider OAuth / device code flows store their token blob under appctl_llm_oauth::<profile>. Refresh happens automatically when a refresh token is present; otherwise rerun the login flow.

.appctl/config.toml can be committed (no secrets). .appctl/history.db should not. Recommended .gitignore:

.appctl/history.db