Skip to content

Configuration & File Layout ​

This page documents the optional config.yaml file in the resolved vault directory, the on-disk layout under that directory, the repo-side files TinyVault touches, and the rules that decide which vault, identity, and project a command uses.

TinyVault works with zero configuration. A config file is optional, and a missing file is never an error. Everything here is for when you want to tune defaults or understand exactly where bytes live.

The config file: config.yaml ​

TinyVault reads a single optional YAML file. Its default path is ~/.tvault/config.yaml, but it can also live at an XDG-standard location — see Config file location below for the full resolution order.

A missing file is fine — built-in defaults apply. If the file is malformed or unreadable, tvault doctor reports the problem and exits non-zero.

yaml
# ~/.tvault/config.yaml
agent:
  passphrase_command: ["/opt/homebrew/bin/op", "read", "op://Private/tvault/password"]
  passphrase_file: ~/.config/secrets/env
  log_dir: ""        # empty = $XDG_STATE_HOME/tvault
  log_level: info
backup:
  dir: ~/Library/Application Support/tvault/backups
  keep: 30
  immutable: true

What the typed config parses ​

Only the agent: and backup: blocks are parsed into TinyVault's typed config.

KeyTypeDefaultWhat it does
agent.passphrase_commandlist of strings (or a single whitespace-split string)emptyA program (and its arguments) whose stdout is the vault passphrase — for a password manager or the OS keychain instead of a plaintext file. Run directly, never through a shell; wins over agent.passphrase_file. See Keep the passphrase out of plaintext.
agent.passphrase_filestringemptyPath to an env-style file containing TVAULT_PASSPHRASE for non-interactive unlock (must not be group- or world-readable).
agent.log_dirstringemptyOverride agent log directory. Empty means $XDG_STATE_HOME/tvault.
agent.log_levelstringinfoOne of debug, info, warn, error.
backup.dirstringemptyDirectory for rotated, timestamped tvault backup snapshots. Setting it also makes delete, projects delete, restore, and the MCP delete tools take a safety snapshot first and refuse to proceed if it fails. See Backups & recovery.
backup.keepint30How many rotated snapshots survive pruning.
backup.immutableboolfalseMark rotated snapshots with the macOS/BSD user-immutable flag (chflags uchg); unsupported (warning, not an error) on Linux and Windows.

A leftover browse: block from older releases is ignored. Top-level vault, project, and verbose keys do not configure those command flags.

agent.passphrase_command applies to every non-interactive unlock (CLI, MCP, agent), not only tvault agent — it lives under agent: because the agent running under launchd/systemd was its first consumer. A command sourced from config.yaml only runs when the file is owned by the current user and not writable by group or others; a loosely permissioned config file is refused as an unlock source (it would otherwise be a code-execution primitive for anyone who could write it).

Flags and environment variables win

Explicit command-line flags and TVAULT_* environment variables override the corresponding agent: values.

Config file location ​

config.yaml is resolved in this order (first match wins):

text
--config <file>   >   TVAULT_CONFIG   >   <vault dir>/config.yaml (if it exists)
  >   $XDG_CONFIG_HOME/tvault/config.yaml, else ~/.config/tvault/config.yaml
      (if it exists AND the vault is the default ~/.tvault)
  >   <vault dir>/config.yaml   (default location to create one)
  • --config is a global persistent flag, same precedence rules as --vault.
  • TVAULT_CONFIG is the environment-variable equivalent.
  • The XDG location is ignored for scratch vaults (--vault / TVAULT_DIR): it holds the operator's own unlock settings, and a test fixture or throwaway vault must not accidentally run their passphrase_command.
  • If files exist at both <vault dir>/config.yaml and the XDG location, the vault-dir one wins and the XDG one is ignored — tvault doctor warns when this happens, since it is almost always a half-finished move to the XDG location.
bash
tvault --config /etc/tvault/config.yaml doctor
TVAULT_CONFIG=/etc/tvault/config.yaml tvault doctor

A broken config is caught by doctor

If ~/.tvault/config.yaml fails to parse, tvault doctor reports it and exits non-zero. Run tvault doctor after editing the file:

bash
tvault doctor

doctor checks the vault directory and permissions, vault validity, lock state, the config and MCP-policy files, environment, and terminal — all without unlocking the vault.

The MCP access policy: mcp-policy.yaml ​

If you run the MCP server, <vault-dir>/mcp-policy.yaml lets you allow- or deny-list projects and secret keys, control writes, and control command execution. It lives alongside config.yaml and is validated by tvault doctor. If the file is absent, production tvault mcp uses a fail-closed safe policy that denies secret access, writes, and execution.

That file has its own page. See MCP Access Policy for the schema and examples.

Redaction is a safety net, not a control

When policy enables redact_output, the MCP server replaces literal secret values longer than three characters in captured command output. Redaction can be disabled and can be evaded by shortening or transforming a value, so it reduces accidental leaks rather than containing a hostile agent. vault_get_secret deliberately returns a stored value; vault_run_with_secrets can also carry plaintext back through child output. Use tvault projects share and the recipient model for real delegation, and lock down agents with mcp-policy.yaml.

File layout: ~/.tvault/ ​

The vault directory is created with 0700 permissions (owner-only). On every vault open, TinyVault normalizes the directory to 0700 and vault.db to 0600; it rejects a symlink, device, or other non-regular database path. Override its location with --vault or TVAULT_DIR (see precedence below).

text
~/.tvault/                         0700  vault directory (owner-only)
├── vault.db                       0600  bbolt database — encrypted payloads, readable metadata
├── config.yaml                    0600  optional, this page
├── mcp-policy.yaml                0600  optional, MCP access policy
├── identities/                    0700  asymmetric identities
│   └── <name>.key                 0600  one identity keypair per file
├── agent.sock                     0600  unix: agent socket (unix only)
├── agent.pid                            unix: agent PID file
└── agent.lock                           unix: agent single-instance lock

What each file holds ​

PathModeContents
vault.db0600The bbolt database. Holds encrypted secrets and secret_versions, project metadata with wrapped per-project DEKs, the audit log, and the KEK verifier plus Argon2id salt. It is the only file containing stored project secret values; private identity key material lives separately under identities/.
config.yaml0600Optional configuration (above).
mcp-policy.yaml0600Optional MCP access policy.
identities/<name>.key0600One asymmetric identity per file. Stores the private half (tvault-key1...); the public half (tvault1...) is derived and shareable. See Sharing and Key management.
agent.sock0600Unix-only. The agent listens here so get/env/run can skip the passphrase prompt.
agent.pid—Unix-only. PID of the running agent.
agent.lock—Unix-only. flock file enforcing a single agent instance.

Agent files are unix-only

agent.sock, agent.pid, and agent.lock exist only on Linux and macOS. The agent is not available on Windows.

Never commit the vault directory

~/.tvault/ contains encrypted secret material, readable operational metadata, and private identity keys. Treat the entire directory as sensitive: keep it out of version control and out of backups you do not control. tvault identity export prints a private key (tvault-key1...); it is TTY-guarded and requires --force to write off a terminal, precisely because that output must never land in a log or a committed file.

Repo-side files (in your project) ​

These files live in your project repository, not under ~/.tvault/. They make encrypted secrets safe to commit and let git filters encrypt and decrypt on the fly. See Committable secrets and Git filter.

FileCommit it?Contents
.tvault-recipientsYesThe read-set: a list of public recipients (tvault1...) who can decrypt. Used by the git filter and by sealing. Committing it is safe — public keys only.
.env.encryptedYesCommittable ciphertext. v1 is passphrase-based; v2 wraps to the recipients in .tvault-recipients and is KEK-independent.
.gitattributesYesRegisters the clean/smudge filter (written by tvault git-filter track).
.env (and other plaintext dotenvs)NoPlaintext import/export targets. Keep these in .gitignore.
bash
# Typical project setup
tvault projects share tvault1exampleRecipient -p myapp       # grant read access (writes no repo file)
tvault git-filter install --recipient tvault1exampleRecipient # seeds .tvault-recipients + filter config
tvault git-filter track .env 'secrets/*.env'                 # writes .gitattributes
git add .tvault-recipients .gitattributes .env.encrypted
echo ".env" >> .gitignore

projects share changes only the vault: it wraps the project key to that recipient. The committed read-set in .tvault-recipients is seeded by git-filter install --recipient (repeatable), which is what makes the tracked files decryptable by anyone holding a matching identity.

.env is plaintext — never commit it

The plaintext .env is an import/export convenience only. Commit .env.encrypted instead, and keep .env in .gitignore. Likewise, tvault k8s render writes plaintext YAML to stdout — pipe it straight to kubectl and never commit the result:

bash
tvault k8s render --in sealed.yaml --identity cluster | kubectl apply -f -

Recipient removal re-keys the live vault

When you remove a recipient with tvault projects unshare, TinyVault rotates the project DEK and re-encrypts every current value and archived version in the updated vault. The removed recipient cannot decrypt that updated state or future ciphertext under the new DEK. Pre-removal snapshots and previously sealed or exported artifacts remain readable; rotate underlying credentials and re-seal distributed artifacts when needed.

Path precedence ​

When a setting can come from several places, TinyVault resolves it left-to-right (first match wins).

Vault directory ​

text
--vault <dir>   >   TVAULT_DIR   >   ~/.tvault

The flag beats the environment variable, which beats the default home location.

Identity: file vs. env key ​

The private key material is resolved before its name:

text
~/.tvault/identities/<name>.key   >   TVAULT_IDENTITY_KEY

A local identity file always takes precedence over a TVAULT_IDENTITY_KEY set in the environment. When a file overrides a set env key, tvault prints a warning to stderr so the override is visible. The env key's value is never echoed in an error message.

TVAULT_IDENTITY_KEY is for CI and agents

TVAULT_IDENTITY_KEY carries a private identity string (tvault-key1...) for passphrase-free decryption in CI, SSH sessions, or agents that have no key file on disk. Treat it like any other secret — inject it from your CI secret store, never hard-code it. See CI/CD.

Identity name ​

Identity-name resolution depends on the command:

  • tvault open: --identity <name> > TVAULT_IDENTITY > default.
  • Git clean/smudge filters: TVAULT_IDENTITY > git config tvault.identity > default.
  • For recipient-encrypted decrypt-env and k8s render, an explicit --identity selects the file name; without it, the resolver checks the default file before falling back to TVAULT_IDENTITY_KEY.
  • tvault env and tvault run enter identity mode only when --identity or TVAULT_IDENTITY_KEY is present. In that mode, the selected file (the explicit name, or default) takes precedence over the environment key.

TVAULT_IDENTITY is therefore not a generic environment override for every --identity flag.

Project ​

text
--project <name>   >   stored current project   >   "default"

The -p/--project flag beats the project you last selected (stored in the vault), which beats the project named default.

Relevant command-line flags ​

These command-line flags affect the paths and workflows described on this page:

FlagEffect
--vault <dir>Use an alternate vault directory.
--config <file>Use an alternate config.yaml, overriding the location precedence (same as TVAULT_CONFIG).
-p, --project <name>Operate on a specific project.
--jsonEmit machine-readable JSON on commands that support it.
--no-agentMake get, env, or run bypass a running agent and unlock directly (same as TVAULT_NO_AGENT).

-h/--help is available everywhere. --version is root-only — print it with tvault --version (there is no version subcommand).

For the full environment-variable reference, see Environment variables.

Exit codes ​

Scripts can branch on tvault's exit status:

CodeMeaning
0Success.
1Generic error.
3Vault is locked at rest.
4Secret or project not found.
5Vault not initialized.
6Wrong passphrase.
7Vault database is in use by another process.
bash
tvault get API_KEY --project myapp >/dev/null 2>&1
status=$?
case $status in
  0) echo "secret is available" ;;
  3) echo "vault is locked — provide TVAULT_PASSPHRASE, use a TTY, or start the agent" ;;
  4) echo "no such secret" ;;
  5) echo "run: tvault init" ;;
  6) echo "wrong passphrase" ;;
  *) echo "tvault failed" ;;
esac

tvault unlock and tvault lock are per-process operations. unlock validates the passphrase and then exits; it does not cache an unlock for the next command. lock does not persist state or stop a running agent. Use tvault agent start / tvault agent stop when you want a persistent in-memory unlock.

See also ​

Released under the MIT License.