API tokens and credentials the agent can use · ← concepts
A secret is a named value the agent can reach without seeing the plaintext in its prompt. arizuko stores secrets in the secrets table in messages.db and hands them to the right caller at the right moment — either as container env at spawn, or as a tool-call argument on the host.
Every secret row carries a scope_kind:
atlas/eng. Owned by the operator; shared by every user that interacts with that folder.auth_users.sub. Owned by the user (typed into /dash/me/secrets) or seeded by the operator as a fallback.The primary key is (scope_kind, scope_id, key). Keys are uppercase env-style ids: ^[A-Z][A-Z0-9_]*$.
At spawn time, container/runner.go resolves the folder's secrets, merges them with the base env, and writes the map into the container as env vars. The agent process inside reads them like any other env var.
Resolution walks from the folder up to a synthetic root row, deepest wins. So atlas/eng/sre sees its own keys overlaid on atlas/eng, on atlas, and on root. A key set at atlas reaches every child folder under atlas/ unless a child overrides it.
User-scoped secrets never enter the container. They are resolved at tool-call time inside the host MCP dispatch chain. When a tool declares requires_secrets: ["GITHUB_TOKEN"], the gateway looks up the calling user’s value and passes it as an argument to the handler running in the host process. The handler makes the outbound HTTP call. The agent sees only the handler’s response.
This is why a leaked agent transcript can’t leak a user’s GitHub token — the token was never in the prompt and never in the container env.
# folder-scoped
arizuko secret <instance> set <folder> KEY --value V
arizuko secret <instance> list <folder>
arizuko secret <instance> delete <folder> KEY
# user-scoped (fallback for users who haven't logged in yet)
arizuko user-secret <instance> set <user_sub> KEY --value V
arizuko user-secret <instance> list <user_sub>
arizuko user-secret <instance> delete <user_sub> KEY
Logged-in users manage their own user secrets through the dashboard at /dash/me/secrets — the CLI is the operator fallback for seeded values.
Secret values are stored AES-256-GCM encrypted in the secrets table. The key comes from the SECRETS_KEY env var (falls back to AUTH_SECRET when SECRETS_KEY is unset, so existing deployments are unaffected). On startup, any plaintext rows found in the table are purged — you must re-enter secrets after the first encrypted startup — and if that purge fails, the process exits instead of continuing with retained plaintext. There is no migration path from plaintext; re-entering a secret is less risky than leaving plaintext in the DB.
A second set with the same key upserts the value and bumps created_at. The next container spawn picks up the new folder value; the next broker call picks up the new user value. There is no restart-the-world step — the old value lives in the env of running containers until the agent process restarts.
secret_use_log table records key, scope, status, and latency — never the value.Full broker design, threat model, and the tool-descriptor field: specs/7/Y. Where secrets sit in the trust map: SECURITY.md. The folder hierarchy that drives resolution: scopes.