The previous version of Digitorn asked you to drop API keys into env vars and reference them from your YAML with {{env.ANTHROPIC_API_KEY}} templates. It worked, it was simple, and it was the wrong shape for anything beyond a single-user dev box. The new credential system replaces it. This piece walks through what changed, why, and how to migrate.
The shape of the change
Before, your agent brain looked like this.
1brain:2 provider: anthropic3 model: claude-sonnet-54 config:5 api_key: "{{env.ANTHROPIC_API_KEY}}"The runtime resolved {{env.X}} at deploy time, the API key landed in the bundle the daemon stored, and every user of that app shared the same key. Fine for solo use, an awkward fit for multi-user setups.
The new shape uses a credential: block.
1brain:2 provider: anthropic3 model: claude-sonnet-54 credential:5 ref: anthropic_main6 scope: per_user7 provider: anthropic8 config:9 api_key: "{{env.ANTHROPIC_API_KEY}}" # dev fallback, optionalThe ref is a name you registered earlier in Settings. The scope says how the lookup happens (more on that below). The provider is asserted at compile time so a typo in a vendor field gets caught before deploy. The plaintext API key never appears in this YAML, never enters git, never gets bundled into the deployed app - it's resolved server-side and injected onto the live provider client at session start.
{{env.X}} template still resolves, so your existing YAML keeps working. New apps should use the credential: block. Mixing both (like the production builtins do) gives you a vault-backed prod path with a fast dev fallback.
The four scopes
The scope answers two questions at once: who can see this credential, and when does the runtime resolve it. Two are baked into the deployed bundle, two are looked up per session.
system_wideresolved at compileper_app_sharedresolved at compileper_userresolved at sessionper_app_per_userresolved at sessionper_user is the right default for almost everything. Each user brings their own Anthropic key, your YAML stays the same, and removing a user from your team revokes their access to every agent at once. system_wide is for daemon-wide configs (a shared internal service credential) and requires admin privileges to set. per_app_shared and per_app_per_user cover the niche cases between those two, the edges where one app needs a key the others should not see.
If you are setting up your first agent and are unsure, pick per_user and move on. The other scopes exist for when you actually need them.
Where the plaintext lives
Your YAML has a name, not a key. The credential's value is stored server-side, resolved only when the runtime is opening a session for the user who owns it, and injected directly onto the live provider client for that session - it never gets written into an installed app's bundle, a log line, or your shell history.
Setting one up
Two steps: register it once in Settings, reference it by name from your YAML.
1. Register the credential
Open Settings > Credentials, pick the provider, paste the key, and give it a name - that name is the ref your YAML will point at. If you skip naming it, it defaults to the provider name (anthropic, deepseek, ...). One credential per provider works fine for most setups; use distinct names when you want multiple keys for the same provider (one for prod, one for dev).
User-owned credentials default to per_user scope. The other scopes (system_wide, per_app_shared, per_app_per_user) are for team/admin setups.
2. Reference it from your YAML
1agents:2 - id: main3 brain:4 provider: anthropic5 model: claude-sonnet-56 credential:7 ref: anthropic_main8 scope: per_user9 provider: anthropicCompact form works too if you're fine with the per-user default and skipping the provider assertion.
1agents:2 - id: helper3 brain:4 provider: anthropic5 model: claude-sonnet-56 credential: anthropic_mainIf your scope is per_user, the compact form is fine - spell out the full block when you need a scope other than the default.
3. Install and chat
1digitorn install ./my-agent2digitorn chat my-agentThe first message you send opens a session, the runtime looks up anthropic_main for your user, and the LLM call goes out with your key. No daemon restart, no key visible in any log, no secret material in any deployed file.
Sharing credentials across agents
Two agents on the same machine can reference the same credential by name. No copy-paste, no second registration. This is the day-to-day pattern for power users: register one Anthropic key, reference it from your coding agent, your research agent, your knowledge-base agent. Rotate once, propagates everywhere.
1agents:2 - id: writer3 brain:4 provider: anthropic5 model: claude-sonnet-56 credential: anthropic_main1agents:2 - id: main3 brain:4 provider: anthropic5 model: claude-sonnet-56 credential: anthropic_mainSame ref, same stored credential.
Rotation, the part that matters
Update the value in Settings > Credentials - the CLI doesn't have a separate credentials command for this today, it's a Settings-driven flow. Per-user and per-app-per-user scopes pick up the new value on the next session automatically. Deploy-time scopes (system_wide, per_app_shared) need a redeploy of the apps that bake them in, which is by design: a system_wide credential is meant to be stable across sessions, so rotating it is a deliberate, visible event rather than a silent swap.
When you genuinely don't want the vault
The escape hatch is still there. If you have a non-standard auth scheme, or you're iterating fast on a dev box, drop the credential: block and use the inline config.api_key: "{{env.X}}" template. The runtime resolves it the same way it always did. You lose per-user scoping, but you keep the simplicity of "edit a .env file and go".
We use this ourselves on dev machines for new providers we are integrating, and we always switch to the credential: block before shipping. The inline path is for fast iteration, the credential-ref path is for production.
Further reading
- The full architecture of a coding agent that uses this pattern: Build a Claude Code clone in YAML
- Cost-routing patterns that pair naturally with per-agent credentials: How we cut our coding agent's bill by 60%
- Ten more patterns, ready to fork: /templates
One post a fortnight, in your inbox.
Engineering notes from the Digitorn team. No marketing, no launch announcements, no "10 prompts that will change your life". Just the things we write that we'd want to read.
We build the open-source AI agent runtime that runs on your own machine. YAML over Python, multi-agent by default, marketplace for sharing.
Keep reading
Ship your first AI agent in 5 minutes.
Open-source. Self-hosted. YAML-first. Bring your own LLM keys, agents run on your machine.
