Skip to content

Configuration

This is the complete reference for how kovra is configured: the environment variables it reads, and the files it uses.

Variable Default What it does
KOVRA_VAULT_DIR ~/.vaults Override the vault registry root. A vault in another folder keeps its master key in a Keychain item of its own (below).
KOVRA_PASSPHRASE (unset) Switch to passphrase mode: derive the master key with Argon2id instead of using the OS keyring. Refused on macOS.
KOVRA_CONFIRMER development builds: biometric (falls back to file) Confirmation channel: biometric (Touch ID) or file (the kovra approve broker). Development builds only: a released kovra on a Mac always asks with Touch ID or your login password, and every build ignores it inside a Claude Code session.
KOVRA_UI_NO_CONFIRM (unset) Skip the Dashboard launch confirmation (same as kovra ui --no-confirm).
KOVRA_MCP_ENVIRONMENTS * MCP session scope — addressable environments (comma list, or * for any).
KOVRA_MCP_PROJECTS * MCP session scope — addressable projects (comma list, or * for any).

The registry root (default ~/.vaults, or KOVRA_VAULT_DIR) holds:

~/.vaults/
global/ # the global vault — sealed per-secret records + a sealed index
projects/
<name>/ # one directory per project vault, same layout
keycheck.sec # the key check: kovra refuses a master key that does not open it
allowlist.sec # the executor allowlist, sealed under the master key
kdf.salt # passphrase-mode only: the non-secret Argon2 salt

Every record is sealed at rest; coordinates aren’t exposed as plaintext filenames. See Cryptography for the at-rest format.

Each vault folder keeps its master key in an item of its own in the Keychain (the Credential Manager on Windows), under the service kovra:

  • ~/.vaults uses the item master-key, as every version of kovra has.
  • A vault in another folder (KOVRA_VAULT_DIR) uses master-key.<…>, 16 characters derived from the folder’s path. A vault you create to try something out never touches the key of your real vault: its kovra init --force or kovra key import --force replaces only its own item.
  • After updating, a vault in another folder that used master-key until now adopts that key into its own item, once, and only if the key opens every one of its records. kovra says so once; master-key stays as it was. A vault that holds nothing yet does not adopt it: its kovra init creates a key of its own.
  • Moving a vault’s folder: the vault no longer finds its item at the new path. Run kovra key export before you move it and kovra key import after.

When you delete a vault’s folder, its master-key.<…> item stays in the Keychain. It opens no vault that still exists.

kovra setup registers the MCP server here so your agent can launch it. The env block carries the MCP session scope:

{
"mcpServers": {
"kovra": {
"command": "kovra",
"args": ["mcp"],
"env": {
"KOVRA_MCP_ENVIRONMENTS": "dev,test",
"KOVRA_MCP_PROJECTS": "my-app"
}
}
}
}

This is what bounds what an agent over MCP can address (* = any). It’s distinct from agent.toml, which scopes the ssh-agent.

The governed ssh-agent reads its scope from <vault-root>/agent.toml. The format is intentionally tiny — two array keys, with # comments:

# <vault-root>/agent.toml — kovra ssh-agent scope
environments = ["dev", "test"] # omit (or []) → any environment
projects = ["api"] # omit (or []) → global + any project

Two things are not configurable here, by design: the operation set is fixed to metadata + inject (an ssh-agent never reveals a private key), and when the file is absent the agent serves any environment/project — still never revealing, and still requiring a bioProve on every high/prod signature.

.env.refs maps local environment-variable names to sources. It holds addresses, never values, so it’s safe to commit. One mapping per line:

Form Meaning
project = <name> Bind the file to a project vault (resolution targets it).
NAME=secret:<env>/<comp>/<key> A vault coordinate. May use ${ENV}; an optional | fallback applies if it doesn’t resolve.
NAME=secret://global/<env>/<comp>/<key> Force resolution against the global vault, bypassing the project.
NAME=${env:VAR} A passthrough from the execution environment. Supports ${env:VAR | fallback}.
NAME=literal A literal value (not a secret), e.g. PORT=8080.

Rules that keep it safe:

  • No values, ever — addresses only, so a leaked .env.refs exposes nothing.
  • ${ENV} is substituted by kovra run --env <e> inside a coordinate’s environment segment; ${env:VAR} reads from the surrounding environment.
  • Cross-variable interpolation is rejected — you can’t compose one secret inside another variable’s string (that composed string would get logged).
  • Resolution is a single ordered pass over the file.

See The .env.refs contract for the narrative version.