Sync Rules¶
Rules define the mapping between Vault secrets and local files. Each rule specifies which secret to read, where to write it, and how to format and merge the data.
Rule structure¶
rules:
- name: gh # unique identifier
vault_key: "gh" # key under user's Vault path
target:
path: "~/.config/gh/hosts.yml" # local file (~ is expanded)
format: yaml # output format
template: | # optional Go template
github.com:
oauth_token: "{{ .oauth_token }}"
| Field | Required | Description |
|---|---|---|
name |
yes | Unique rule identifier, used in status output and state tracking |
vault_key |
yes | Secret key under the user's Vault path |
target.path |
yes | Local file path (~ is expanded to the user's home directory) |
target.format |
yes | Output format: yaml, json, ini, toml, text, or netrc |
target.template |
no | Go template to reshape secret data before writing |
How sync works¶
For each rule, on every sync cycle:
- Read the secret from Vault at
{kv_mount}/data/{user_prefix}{username}/{vault_key} - Skip if the Vault secret version is unchanged AND the target file checksum is unchanged
- Render the template (if present) with the Vault data map as the dot context
- Parse the rendered output through the format handler
- Read the existing target file (a missing file is treated as empty, not an error)
- Merge the incoming data into the existing file content
- Write atomically (temp file with correct permissions, then rename)
- Update state (version, timestamp, SHA-256 checksum)
Rules are isolated from each other — one rule failing does not block others.
Merge behaviour by format¶
Each format has a merge strategy appropriate to its structure:
| Format | Merge strategy |
|---|---|
| YAML | Deep merge of mapping nodes; existing keys not in incoming data are preserved |
| JSON | Recursive map merge; arrays are replaced wholesale |
| INI | Section + key merge; supports flat files (default section) |
| TOML | Recursive merge; supports tables, inline tables, and dotted keys |
| Text | Full replacement (no merge) — for private keys, certificates, etc. |
| Netrc | Per-entry merge by machine name; the default entry is skipped |
The key insight is that for structured formats (YAML, JSON, INI, TOML, netrc), dotvault only touches the keys it manages. A user's other settings in the same file are preserved.
File permissions¶
All managed files are written with 0600 permissions (owner read/write only). Parent directories are created with 0755 if they don't exist.
All writes are atomic: dotvault writes to a temporary file with the target permissions and then renames it into place, so the target file is never in a partially-written state.
State tracking¶
Sync state is persisted to {cache_dir}/state.json and tracks per-rule:
- Vault secret version number
- Last synced timestamp
- SHA-256 checksum of the target file
This allows dotvault to efficiently skip unchanged secrets and detect external modifications to target files.