Credentials
Credentials
The shell backend needs no credential, no network access, and no account. If
you only run shell recipes, there is nothing on this page to do.
Provider backends need their own key. The runner never prompts for one and never writes one into a run record.
Resolution order
For a given credential the runner checks, in order:
- the step
envblock, then the recipeenvblock - the process environment
- the macOS Keychain, through
mentu-recipes vault
The first hit wins. In the Keychain the runner tries the backend's vault key
name first, then names derived from the environment variable: OPENAI_API_KEY
also matches openai-api-key, and any FOO_API_KEY matches foo-api-key.
What each backend needs
| Backend | Credential | Environment variable | Vault key |
|---|---|---|---|
shell |
none | ||
openai |
OpenAI key | OPENAI_API_KEY |
openai-api-key |
openai-chat |
OpenAI key | OPENAI_API_KEY |
openai-api-key |
deepseek |
DeepSeek key | DEEPSEEK_API_KEY |
deepseek-api-key |
ollama |
none for a default local server | OLLAMA_API_KEY if your server requires one |
ollama-api-key |
claude |
whatever the Claude CLI is already signed in with | ANTHROPIC_API_KEY if you use one |
only through an env reference |
codex |
whatever the Codex CLI is already signed in with | OPENAI_API_KEY if you use one |
only through an env reference |
The HTTP backends read the Keychain themselves. The two CLI backends do not.
They shell out to a CLI you have already authenticated, so they usually need
nothing from the runner, and they receive an API key only if it is present in
the environment the runner builds: your process environment plus the recipe and
step env blocks. To hand a vault key to a CLI, reference it from env as
shown below. Their environments are allow-listed per backend: the Claude CLI is
passed ANTHROPIC_API_KEY and not OPENAI_API_KEY, and the Codex CLI is
passed OPENAI_API_KEY and not ANTHROPIC_API_KEY.
Store a key in the Keychain
Keys are generic password items in your login Keychain under the service name
mentu-vault, with the key name as the account. You can inspect or remove them
in Keychain Access as well as with the commands below.
Pipe the secret in on stdin so it does not land in your shell history.
printf '%s' "$OPENAI_API_KEY" | mentu-recipes vault set openai-api-key✓ stored openai-api-keyList the key names the vault holds. Values are never printed by list. On a
machine with two keys stored:
mentu-recipes vault listdeepseek-api-key
openai-api-keyRead one back. This prints the secret to stdout.
mentu-recipes vault get openai-api-keyRemove it.
mentu-recipes vault delete openai-api-key✓ deleted openai-api-keyA get after the delete exits non-zero:
mentu-recipes: Key not found: openai-api-keyVault keys are read on demand, not scanned. Availability detection stays out of
the Keychain deliberately, so a backend whose key lives only in the vault shows
as unavailable in mentu-recipes adapters and still runs when a step selects
it by name.
Reference a vault key from a recipe
{
"env": {
"OPENAI_API_KEY": "${openai-api-key}"
}
}A ${name} value is replaced before the step runs: first from a --var of the
same name, then from a process environment variable called name, then from
the Keychain. If none resolves, the literal text is left in place. This is the only way a Keychain value reaches
a CLI agent step.
Do not commit a real key into a recipe file. Recipes are meant to be diffed and reviewed like the rest of your code.
Custom providers
A recipe can define a provider with its own host and its own key name:
{
"providers": {
"acme": {
"api": "chat_completions",
"base_url": "https://api.acme.example/v1",
"api_key_env": "ACME_API_KEY",
"api_key_vault": "acme-api-key",
"model": "acme-large"
}
}
}api_key_vault is optional. Without it the runner still tries the Keychain
under ACME_API_KEY and the derived name acme-api-key. A custom HTTP provider
always needs a key to resolve, even for a server that does not check one.
base_url is a trust boundary. The built-in OpenAI and DeepSeek credential
names are pinned to api.openai.com and api.deepseek.com, so setting a custom
base_url cannot quietly forward one of those keys to a third-party host. Give
a custom provider its own env or vault key.
mentu-recipes doctor raises a generic_provider_credential error when a
custom provider declares OPENAI_API_KEY, DEEPSEEK_API_KEY, openai-api-key,
or deepseek-api-key as its own credential name, whatever its base_url.