Registering Secrets

Secrets are registered with agentd via ash. The value is read via an echo-disabled prompt; it is never shown on screen or written to shell history.

Initialising the Store

Before registering secrets, the encrypted store must be unlocked. On first run this also initialises the store with your chosen master passphrase:

ash secrets unlock
# Enter master passphrase: (input hidden)
# Secret store initialised and unlocked.

On subsequent daemon restarts, the same command reloads all previously persisted secrets:

ash secrets unlock
# Enter master passphrase: (input hidden)
# Secret store unlocked. 3 secret(s) loaded.

Adding a Secret

ash secrets add my-api-key
# Enter value for 'my-api-key': (input hidden)
# Secret 'my-api-key' registered.

With an optional description:

ash secrets add openrouter-key --description "OpenRouter API key for LLM access"

The name you choose here is the identifier used in:

  • Capability declarations: secret.use:openrouter-key
  • Handle syntax in tool arguments: {{secret:openrouter-key}}
  • Pre-approval policies: --secret "openrouter-key"

If the store is unlocked (i.e. an encryption key is active), the value is also encrypted and persisted to the SQLite store immediately.

Listing Secrets

ash secrets list
# my-api-key
# openrouter-key
# db-password

Only names are shown; values are never displayed.

Removing a Secret

ash secrets remove my-api-key
# Secret 'my-api-key' removed.

Removing a secret wipes the plaintext from heap memory and deletes the encrypted blob from the SQLite store. Any pending tool calls using that handle will fail with secret not found.

Locking and Unlocking

# Lock: immediately zeroize all in-memory plaintext.
ash secrets lock

# Unlock: re-derive key from passphrase and reload from encrypted store.
ash secrets unlock

While the store is locked, all {{secret:<name>}} handle substitutions return StoreLocked. Use ash secrets lock before handing off a session or stepping away from a running daemon.

Rotating the Master Key

ash secrets rekey
# Enter current passphrase: (input hidden)
# Enter new passphrase:     (input hidden)
# Confirm new passphrase:   (input hidden)
# Master key rotated. All secrets re-encrypted.

rekey re-encrypts every secret blob in a single atomic SQLite transaction. The daemon remains fully operational throughout; no secrets need to be re-registered.

Naming Conventions

Choose names that are:

  • Lowercase, kebab-case: openrouter-key, db-password, analytics-api-key
  • Descriptive of the service, not the value: stripe-key not sk_live_abc123
  • Consistent with the glob patterns you will use in policies: db-* matches db-password, db-read-key, etc.

Secret Lifetime and Persistence

Secrets are encrypted and persisted to SQLite whenever the store is unlocked. On daemon restart, run ash secrets unlock with the master passphrase to restore all registered secrets automatically, with no re-registration required.

The SQLite database path is controlled by the AGENTD_ENCRYPTED_SECRETS_DB environment variable (default: /tmp/agentd_secrets.db). For production deployments, point this at a path on an encrypted filesystem.

Note: Per-agent delegation grants (ash secrets grant) are intentionally ephemeral; they live only in the daemon's memory and must be re-granted after a restart.