gitvaulty
v4.2.0
Published
Git-backed secrets for humans.
Maintainers
Readme
GitVaulty encrypts complete files with SOPS and age so they can live safely in Git. Filenames,
contents, comments, formatting, and file types are preserved when decrypted; the committed
*.gitvaulty file reveals none of the plaintext structure.
Access is assigned to groups per file, with direct user grants available for exceptions. Every person keeps one private GitVaulty master identity; there is no shared team decryption key.
GitVaulty is available through its official Homebrew tap and on npm. Homebrew installs the required Node.js runtime; direct npm installation requires Node.js 20 or newer.
Explore the guides, editor integrations, and complete command reference at divb0.github.io/gitvaulty.

A real CLI walkthrough from encrypted-file basics to group-based access control.
Contents
Getting started
Install GitVaulty
On macOS, install GitVaulty from the official Homebrew tap:
brew install divB0/tap/gitvaultyHomebrew is the recommended macOS installation and also works with Homebrew on Linux. Upgrade with
brew upgrade gitvaulty.
For Node.js projects, Windows, and CI, install from npm or run a pinned version with npx:
npm install --global gitvaulty
npx gitvaulty@latest --versionQuick start
Run GitVaulty inside an existing Git repository. Repository commands prepare GitVaulty automatically, so the first command can create a secret directly:
npx gitvaulty create .envIf your global key is missing, GitVaulty offers to restore an existing backup with masked input or
create a new key. It then creates a team group with you as its manager and first member, the public
identity registry, repository preferences, SOPS configuration, and managed repository agent skill
before continuing the requested command. New and imported files use team by default, so the normal
workflow needs no access flags.
You can run the same preparation explicitly and idempotently before other work:
npx gitvaulty initGitVaulty opens a private temporary .env in your editor and stores its contents as
.env.gitvaulty; it never creates a repository plaintext file. Save the file, close the editor,
and commit the encrypted project state:
git add .gitvaulty .sops.yaml .agents .env.gitvaulty
git commit -m "chore: initialize GitVaulty"Never commit the plaintext .env. If the plaintext already exists, use
npx gitvaulty import .env instead of create.
While agentSkill.mode is managed, GitVaulty automatically installs or updates
.agents/skills/gitvaulty/SKILL.md from the currently installed CLI. Compatible coding agents can
discover this repository-scoped skill and learn to use gitvaulty run with only the files required
for a task, without placing secret values in prompts or command arguments. Set the repository mode
to disabled before maintaining custom skill instructions.
Add a new developer
On their own branch, the new developer registers their public identity without receiving access:
npx gitvaulty user register
git add .gitvaulty/recipients.json
git commit -m "chore: register alice's GitVaulty key"The command prompts for the repository username, defaults to the current system $USER, and lets
the developer accept or replace it. For automation, pass --username alice. It creates one private
GitVaulty master identity when needed, but commits only its public age recipient and Ed25519
verification key. Alice opens a pull request with that commit. Her private
GITVAULTY-IDENTITY-... backup must never be shared or committed.
After reviewing Alice's registration, a manager of the default group checks out the commit and adds her to it:
npx gitvaulty group add team alice
git add .gitvaulty/recipients.json .sops.yaml
git add -u -- '*.gitvaulty'
git commit -m "chore: grant alice team access"group add appends a manager-signed policy revision and re-encrypts every affected file for the
updated group. Alice can decrypt those files after the access commit is merged and pulled. The
public-key commit and access-grant commit are separate so a current manager explicitly approves
access.
Install in a project
Install GitVaulty as a development dependency so everyone working on the project uses the same version:
npm install --save-dev gitvaultyRun the project's installed version with npx:
npx gitvaulty <command>For example:
npx gitvaulty init
npx gitvaulty run --all -- npm startWorking with encrypted files
Common workflows
Import an existing plaintext file
npx gitvaulty import .env
git add .env.gitvaulty .gitvaulty/recipients.json .sops.yaml
git commit -m "chore: encrypt development environment"The plaintext remains available only in the current clone and is added to Git's clone-local exclude file. If it was already tracked, rotate its secrets even if you later remove it from Git history.
Create another encrypted file
npx gitvaulty create config/secrets.yaml
git add config/secrets.yaml.gitvaulty .gitvaulty/recipients.json .sops.yaml
git commit -m "chore: add encrypted service configuration"Use create when the plaintext path does not exist and import when it does.
Edit an encrypted file
npx gitvaulty edit config/secrets.yaml
git add config/secrets.yaml.gitvaulty
git commit -m "chore: update service configuration"Always pass the logical plaintext path, without the .gitvaulty suffix.
edit also initializes an unregistered zero-byte config/secrets.yaml.gitvaulty placeholder with
the repository's default group access. It does not adopt non-empty or already registered files.
Materialize files for local development
npx gitvaulty materialize .env config/secrets.yaml
npx gitvaulty status
npx gitvaulty cleanmaterialize creates private local plaintext copies. clean removes only unchanged copies that
still match their ciphertext.
Review local plaintext changes
npx gitvaulty diff
npx gitvaulty diff .env config/secrets.yamldiff compares decrypted encrypted sources with local plaintext files and prints unified Git-style
output. The output intentionally contains plaintext secret values. Like git diff, differences
exit successfully by default; use --exit-code when a difference should exit with status 1.
Pipe a file without materializing it
npx gitvaulty cat config/secrets.json | jq .cat writes the exact decrypted bytes to stdout and creates no plaintext file. It refuses to print
directly to an interactive terminal unless --force is supplied.
Expose files only while a command runs
npx gitvaulty run -f .env -- npm startUse --all instead of repeatable --file options when the command needs every file you can access.
GitVaulty removes unchanged plaintext files created by that invocation when the command exits.
Migrate an existing file
If .env already exists, import it:
npx gitvaulty import .envGitVaulty creates .env.gitvaulty, decrypts it again to verify an exact byte-for-byte match, and
keeps the original .env available locally. It also adds .env to the clone-local Git exclude
file. Commit .env.gitvaulty, not .env.
If the plaintext file is tracked, GitVaulty warns that its secrets may already exist in Git history and asks before continuing:
.env is tracked by Git and may already exist in Git history.
Rotate any exposed credentials even if you continue.
? Stop tracking .env and continue importing? (y/N)Accepting preserves the local plaintext file, adds it to the clone-local Git exclude file, and
removes it from Git's index with git rm --cached. If the file was already committed, its deletion
is staged alongside the new encrypted file. Declining makes no import or index changes.
This does not erase the plaintext from existing commits or other clones. Rewriting shared Git history is a separate, disruptive repository operation; rotate every credential that may have been exposed regardless of whether you later rewrite that history.
Create a new file
If the plaintext file does not exist:
npx gitvaulty create config/secrets.yamlGitVaulty creates config/secrets.yaml.gitvaulty and opens a temporary plaintext copy in
$VISUAL, $EDITOR, or the platform's default editor. Save an ordinary file—there are no special
markers and no values to label as secrets. The entire file is encrypted when the editor closes.
create never imports an existing file. Use import explicitly for migration.
Choose a narrower group while creating or importing when needed:
npx gitvaulty create .env.production --group production
npx gitvaulty import service-account.json --group platform --user alice--group and --user may be repeated. Direct users are intended for exceptions; groups are the
primary access model.
Edit encrypted files
Always use the logical plaintext path:
npx gitvaulty edit .env
npx gitvaulty edit config/secrets.yamlGitVaulty decrypts the file into a private temporary directory, opens the normal filename for
editor syntax highlighting, encrypts changed bytes atomically, and removes the temporary directory.
The directory is created below the operating system's standard temporary location (for example,
/tmp on many Linux systems or %TEMP% on Windows) with a name such as
gitvaulty-edit-Ab12Cd.
While the editor is open, GitVaulty holds a process-owned localhost lock for that directory. On a
normal exit, the plaintext directory is removed immediately. A crash, power loss, or SIGKILL can
prevent that immediate removal, so every later GitVaulty command also checks for abandoned edit
directories. An unlocked directory must be at least five minutes old before it is removed; a
responding lock always wins and never expires merely because the edit has been open for a long
time.
Startup cleanup is deliberately conservative. It considers only exact gitvaulty-edit-* direct
children owned by the current user, with private directory and lock-file permissions and valid lock
metadata. Symlinks, malformed locks, unusual permissions, and unrelated temporary files are left
untouched. Cleanup failures never prevent the requested GitVaulty command from running.
If a matching plaintext file is already materialized, GitVaulty updates it too. If that file has independent local changes, GitVaulty asks what to do:
Materialized plaintext .env differs from encrypted .env.gitvaulty
› Use .env changes: encrypt them, then edit
Discard .env changes: restore it from .env.gitvaulty, then edit
CancelFor scripts, make local changes authoritative explicitly:
npx gitvaulty import --update .envThe updated encrypted file is decrypted and verified before replacing the previous version.
Streaming decrypted bytes
Use cat when the receiving tool accepts standard input and does not need a native file path:
npx gitvaulty cat config/credentials.json | jq .
npx gitvaulty cat manifests/secret.yaml | kubectl apply -f -GitVaulty writes only the exact decrypted bytes to stdout. Errors stay on stderr, and no plaintext
file is created. Direct output to an interactive terminal is refused unless --force is supplied.
See the cat command reference for the output and safety contract.
Local development
Materialize every file you can access:
npx gitvaulty materializeOr select files by their plaintext paths:
npx gitvaulty materialize .env config/secrets.yamlMaterialized files receive mode 0600. Existing files are accepted only when their bytes match the
encrypted source. To intentionally discard local changes and restore encrypted bytes, pass
-f or --force:
npx gitvaulty materialize --force .envForce still refuses symlinked, unsafe, or Git-tracked destinations.
Inspect their state:
npx gitvaulty statuscurrent .env
missing config/secrets.yaml
modified terraform/secrets.auto.tfvars.jsonRemove materialized files when you no longer need them:
npx gitvaulty cleanclean removes only regular, untracked files whose bytes still match GitVaulty. Modified or unsafe
files are reported and kept.
Ephemeral files while running a command
run materializes missing files, starts the command, and removes only the unchanged files that
this invocation created:
npx gitvaulty run --all -- npm startrun requires an explicit scope. Use --all for every file the current identity may access, or
repeat --file to expose only the files the command needs:
npx gitvaulty run -f .env.production -- npm start
npx gitvaulty run \
-f terraform/secrets.auto.tfvars.json \
-- terraform -chdir=terraform planGitVaulty materializes files; it does not interpret them or inject their contents as environment variables. Applications and tools continue loading their native files normally. For plain Node.js:
{
"scripts": {
"start": "node --env-file=.env src/server.js"
}
}If the child modifies a file created by run, GitVaulty keeps it and prints a warning. Existing
matching files are never owned or removed by run. Cleanup also runs after nonzero exits and common
termination signals; an uncatchable crash, power loss, or SIGKILL can still leave plaintext behind.
Private-key variables are available to SOPS but removed from the child process environment.
The skill offered by gitvaulty init teaches coding agents this workflow and warns them not to
print, log, or inspect secret values unnecessarily. Agent instructions reduce accidental exposure;
they are not a security sandbox. Use the agent harness or operating-system isolation when an agent
must be technically prevented from reading plaintext available to its process.
Access control
Keys, users, and groups
npx gitvaulty key create
npx gitvaulty key public
npx gitvaulty key backup
npx gitvaulty key restore
npx gitvaulty user register
npx gitvaulty user add
npx gitvaulty user list
npx gitvaulty user remove
npx gitvaulty group create production
npx gitvaulty group add production alice
npx gitvaulty group remove production alice
npx gitvaulty group manager add production alice
npx gitvaulty group manager remove production alice
npx gitvaulty group list
npx gitvaulty group delete productionThe global GitVaulty master identity normally lives at ~/.config/gitvaulty/identity. Back it
up once with gitvaulty key backup; the same identity works across GitVaulty repositories. Native
age/X25519 and Ed25519 keys are derived in memory for each command and are never cached on disk.
GitVaulty 2.0 uses the extensionless identity path exclusively. Before upgrading from 1.x, rename
~/.config/gitvaulty/identity.txt to ~/.config/gitvaulty/identity if the extensionless file does
not already exist. Moving the same master identity does not change its public keys or require
re-encrypting existing files. See gitvaulty key for Windows
instructions and conflict guidance.
The interactive backup command can save the identity to a detected 1Password or Bitwarden CLI, copy it to the desktop clipboard, or print it after an additional warning. The password-manager picker keeps supported but unavailable CLIs selectable so it can show installation instructions and check again without restarting. For scripts, choose the destination explicitly:
npx gitvaulty key backup --clipboard
npx gitvaulty key backup --print--clipboard and --print are mutually exclusive. Clipboard history and synchronization tools may
retain copied keys; direct password-manager storage is preferred.
A new developer runs gitvaulty user register, confirms the suggested system username, and commits
both public keys with no access. Scripts can use gitvaulty user register --username <username>.
An existing group manager reviews that commit and runs
gitvaulty group add <group> <username> to approve access. user add remains available as an
interactive shortcut when a manager already has someone else's public identity.
Private keys are never shared.
Change the policy of an existing file with one interactive command:
npx gitvaulty access .env.productionGitVaulty shows group grants first and direct-user exceptions second. For automation, set the exact policy with repeatable flags:
npx gitvaulty access .env.production --group production --group platform --user aliceOnly a current group manager can add or remove members or promote or demote managers. Every manager
is also a member and can read the group's secrets. Each change appends a signed, revision-linked
policy and automatically re-encrypts every affected file for its new exact recipient set. A group
cannot be deleted while a file uses it, and the default team group cannot be deleted.
The first policy revision is trusted through Git history. Protect the default branch and review
changes to .gitvaulty/recipients.json; cryptography detects edits within the accepted policy chain,
while Git review prevents an attacker from replacing that chain with a different genesis policy.
Removing a user rotates every affected file's data key and removes that recipient. It cannot erase Git history or plaintext the user previously copied, so rotate external credentials after offboarding.
CI and service accounts can inject a separate private identity:
GITVAULTY_KEY='GITVAULTY-IDENTITY-...' npx gitvaulty run --all -- npm startMounted master identities use GITVAULTY_AGE_KEY_FILE=/secure/identity.txt. GitVaulty 3.0 no
longer accepts SOPS_AGE_KEY_FILE as a master-identity source; rename that environment variable to
GITVAULTY_AGE_KEY_FILE before upgrading. The referenced identity file and encrypted files do not
change. GitVaulty derives and passes the native age identity to SOPS internally.
Create and use a narrower access group
npx gitvaulty group create production
npx gitvaulty group add production alice
npx gitvaulty create .env.production --group productionCommit .gitvaulty/recipients.json, .sops.yaml, and every ciphertext changed by the membership or
file-policy update.
Change who can access an existing file
npx gitvaulty access .env.productionThe interactive command selects groups first and direct-user exceptions second. For automation, replace the complete policy explicitly:
npx gitvaulty access .env.production --group production --user aliceInspect users and groups
npx gitvaulty user list
npx gitvaulty group listRun npx gitvaulty access <path> without flags to inspect and interactively update one file's
policy.
Offboard a developer
npx gitvaulty user remove
git add .gitvaulty/recipients.json .sops.yaml
git add -u -- '*.gitvaulty'
git commit -m "chore: revoke GitVaulty access"Removing a user re-encrypts affected files without their recipient. It cannot revoke plaintext or historical ciphertext they already copied, so rotate every external credential they knew.
Registry compatibility and upgrades
GitVaulty continues to read and write every previously released recipient registry format. A repository stays on its current format during ordinary commands and editor saves; upgrading the client never silently rewrites the registry or its access model. Features that require a newer format fail only for that feature, while existing file and access workflows continue to work.
Registry v3 repositories use their registered native age identity from the historical environment
or identity.txt locations. GitVaulty shows one informational v4 upgrade notice per clone. Inspect
the migration without changing tracked files:
npx gitvaulty registry upgrade --check --actor aliceThe v4 migration is always explicit. It requires replacement age and signing public keys for every
legacy user, an actor who already belongs to every group and can decrypt every file, and confirmation
before GitVaulty creates signed group policies and re-encrypts affected ciphertext. See the
registry upgrade reference for the complete workflow. A client still
rejects unknown future formats because it cannot safely interpret an access policy it does not
understand.
Editors and integrations
VS Code
Install GitVaulty from the Visual Studio Marketplace, search for GitVaulty in VS Code's Extensions view, or run:
code --install-extension divb0.gitvaultyThe Marketplace provides native packages for macOS (Apple Silicon and Intel), Linux (ARM64 and x64), and Windows x64.
To edit an encrypted file:
- Open the local GitVaulty repository as a trusted VS Code workspace.
- Select a
*.gitvaultyfile in the Explorer. - Edit the decrypted virtual document normally.
- Save or use Auto Save to re-encrypt, verify, and atomically replace the ciphertext.
The virtual document keeps the plaintext filename for syntax highlighting and compatible language
features. GitVaulty does not materialize a plaintext repository file. Opening an unregistered
zero-byte *.gitvaulty file initializes it with the repository's default group access; non-empty or
already registered files are never reinitialized.
The extension detects ciphertext changes and refuses to overwrite a newer encrypted version. Native
editing means decrypted text is visible to VS Code, compatible extensions and language servers, and
possibly VS Code's private Hot Exit recovery storage. Use gitvaulty edit when that security boundary
is not appropriate.
See the full VS Code guide for setup, commands, conflict handling, security details, and troubleshooting.
JetBrains IDEs
Install GitVaulty from JetBrains Marketplace, or open Settings | Plugins | Marketplace in a compatible IDE, search for GitVaulty, and select Install.
GitVaulty's JetBrains plugin opens *.gitvaulty files as decrypted native editor documents in
IntelliJ IDEA and other desktop JetBrains IDEs. Saving re-encrypts, verifies, and atomically replaces
the ciphertext without creating a plaintext sibling in the repository.
The plugin supports macOS (Apple Silicon and Intel), Linux (ARM64 and x64), and Windows x64. It downloads a native GitVaulty runtime for the current platform from an exact GitHub Release asset, then verifies both its byte length and SHA-256 digest before installation.
The editor detects ciphertext changes and refuses to overwrite a newer encrypted version. Decrypted
text is visible to the IDE document model, compatible plugins and language services, and potentially
IDE recovery storage. Use gitvaulty edit when that security boundary is not appropriate.
See the JetBrains plugin guide for installation, editor actions, conflict handling, security details, development, and release packaging.
Agent skill updates
Before every repository command, GitVaulty compares
.agents/skills/gitvaulty/SKILL.md with the skill bundled in the installed GitVaulty package. It
uses a SHA-256 digest of normalized text, so normal LF and CRLF line-ending differences do not look
like updates. The check runs after any required implicit initialization. Global key commands,
help, and version output do not inspect repository skill state.
In the default managed mode, a missing skill is installed and differing content is replaced
automatically, including during non-interactive and CI commands. Installation and update notices go
to stderr so command output remains composable.
The repository-wide policy is stored in .gitvaulty/config.yaml:
version: 1
agentSkill:
mode: managedSet mode: disabled before adding local customizations to leave the skill untouched for every
contributor. Commit the configuration change.
Reference
Command reference
Files and workflows
| Command | Purpose |
| --- | --- |
| gitvaulty init | Explicitly prepare or repair GitVaulty repository state. |
| gitvaulty create | Create and edit a new encrypted file. |
| gitvaulty import | Encrypt an existing plaintext file or update existing ciphertext. |
| gitvaulty access | Replace a file's group and direct-user access policy. |
| gitvaulty edit | Edit a file through a private temporary plaintext copy. |
| gitvaulty cat | Stream one decrypted file to standard output without materializing it. |
| gitvaulty diff | Show Git-style plaintext changes relative to encrypted sources. |
| gitvaulty materialize | Create persistent local plaintext copies. |
| gitvaulty clean | Remove unchanged materialized plaintext files. |
| gitvaulty status | Compare local plaintext with encrypted sources. |
| gitvaulty run | Materialize files while a child command runs. |
Identity and access commands
gitvaulty key— Manage your global master identity:create,public,backup, andrestore.gitvaulty registry upgrade— Check or explicitly migrate an older recipient registry to the current signed-policy format.gitvaulty user— Manage registered repository users:register,add,list, andremove.gitvaulty group— Manage access groups:create,add,remove,manager,list, anddelete.
Supported files
Whole-file encryption works with any regular file:
.env.gitvaulty -> .env
config/secrets.yaml.gitvaulty -> config/secrets.yaml
terraform/prod.tfvars.json.gitvaulty -> terraform/prod.tfvars.json
certs/client.pem.gitvaulty -> certs/client.pemThe .gitvaulty suffix is the only storage convention. All commands accept the path on the right,
without the suffix.
Because the complete byte stream is opaque, Git does not reveal keys or document structure. The tradeoff is that Git cannot merge concurrent edits to the same encrypted file meaningfully; keep files small and split unrelated secrets into separate files when different people edit them.
Repository layout
.gitvaulty/config.yaml # repository-wide GitVaulty preferences
.gitvaulty/recipients.json # public users, groups, and per-file access
.sops.yaml # generated public SOPS rules
.agents/skills/gitvaulty/SKILL.md # safe GitVaulty workflow for coding agents
.env.gitvaulty # opaque encrypted .env bytes
terraform/prod.tfvars.json.gitvaulty # opaque encrypted Terraform bytesTroubleshooting
npx reports uv_cwd
npx asks npm to read the current working directory before GitVaulty starts. If that directory was
removed, or the operating system no longer permits access to it, npm can exit with
process.cwd failed and uv_cwd. Move to an accessible directory and rerun the command:
cd ~
npx gitvaulty key backupGlobal key commands do not need to run inside a Git repository. If the error persists from an accessible directory, check that the terminal has permission to access that directory.
About
Comparisons
Evaluating repository and environment-secret tools?
License
MIT
