A cross-platform daemon that runs in user context, authenticates to HashiCorp Vault, and performs one-way synchronisation of KVv2 secrets into local configuration files. It is intended to run as a long-lived daemon but can also be run interactively for one-off syncs.
If you distribute system-level configuration to a fleet of machines — via NixOS, Ansible, Puppet, or similar — you can manage the structure of dotfiles centrally. But when those files need personal secrets (API tokens, OAuth credentials, private keys), there is a gap.
Template tools own the whole file. vault agent and consul-template render a complete file from a template on every pass. If a user adds a genuinely useful entry to their config.yaml, the next render obliterates it. Baking every possible user preference into the template as an optional field is laborious and doesn't scale when you typically need to place just a handful of KV pairs — often only one — into any given file.
dotvault takes a surgical approach. Instead of owning the file, it merges secret values into the coordinates where they're needed, leaving the rest of the file intact. Sysops define the rules; users remain free to customise their own dotfiles without fear of losing changes.
dotvault is intended to run as a per-user service. Sysops configure desktops and remote Linux machines to launch it in a user context so that each person has their own daemon, their own Vault identity, and their own secrets.
On desktop environments it runs a local web service. If the current session is unauthenticated, dotvault launches a browser at its login page, triggering an OIDC authentication flow against Vault. When this is wired into an SSO provider, users are authenticated more or less transparently — no manual token juggling required.
A planned feature is performing OAuth device-authorisation flows for common services such as GitHub. dotvault would complete the flow, capture the resulting OAuth token, persist it into Vault under the user's path, and then synchronise it out to the appropriate config files (e.g. ~/.config/gh/hosts.yml) on every machine where dotvault is running. Log in once, authenticated everywhere.
dotvault bridges the gap between centralised secret management and the dotfiles that CLI tools expect on disk. Define rules mapping Vault KV paths to local files, and dotvault keeps them in sync — polling for changes, merging safely, and re-syncing if files are modified or deleted externally.
- Multiple auth methods — OIDC (browser-based), LDAP, or token-based authentication to Vault
- Four output formats — Write secrets as YAML, JSON, INI, or netrc, with deep merge support to preserve existing file content
- Go templates — Optionally reshape secret data before writing, with helpers like
env,base64encode,default, andquote - Daemon mode — Runs in the foreground with configurable polling intervals and automatic token refresh
- Web UI — Optional local dashboard to view sync status, inspect secrets, and trigger manual syncs
- Dry-run mode — Preview what would change without writing any files
- Cross-platform — Builds for Linux, macOS, and Windows (amd64/arm64), with platform-native file permission checks (Unix mode bits / Windows ACLs)
Create a config file (see Configuration below) and run:
dotvault run --config path/to/config.yamlOr run a single sync cycle and exit:
dotvault sync --config path/to/config.yamlCheck connection and sync status:
dotvault statusdotvault uses a YAML config file. A minimal example:
vault:
address: "https://vault.example.com:8200"
auth_method: "oidc"
sync:
interval: "15m"
rules:
- name: gh
vault_key: "gh"
target:
path: "~/.config/gh/hosts.yml"
format: yaml
template: |
github.com:
oauth_token: "{{.token}}"| Field | Description | Default |
|---|---|---|
address |
Vault server URL (required) | — |
auth_method |
oidc, ldap, or token |
— |
kv_mount |
KVv2 mount path | kv |
user_prefix |
Prefix for per-user secret paths | users/ |
ca_cert |
Path to CA certificate for TLS | — |
tls_skip_verify |
Skip TLS verification (dev only) | false |
Each rule maps a Vault secret to a local file:
| Field | Description |
|---|---|
name |
Unique rule identifier |
vault_key |
Key in Vault (e.g. gh resolves to kv/data/users/<you>/gh) |
target.path |
Local file path (supports ~) |
target.format |
One of: yaml, json, ini, netrc |
target.template |
Optional Go template for formatting |
web — Enable a local web dashboard:
web:
enabled: true
listen: "127.0.0.1:9000"sync — Control polling behaviour:
sync:
interval: "5m"dotvaultauthenticates to Vault using the configured auth method- On each sync cycle, it reads each rule's secret from Vault
- If the secret version has changed (or the target file was modified/deleted), it renders the data through the optional template, merges with existing file content, and writes the result
- Sync state is persisted locally so unchanged secrets are skipped efficiently
MIT
