xenvault
v1.1.14
Published
CLI to pull SOPS-encrypted secrets from a private GitHub repo
Readme
xenvault
CLI to pull SOPS-encrypted secrets from a private GitHub secrets repo into your projects.
How it works
Secrets live in a dedicated GitHub repo structured like this:
secrets-repo/
.sops.yaml
myapp/
.env.development
.env.production
anotherapp/
.env.stagingEach .env.<environment> file is encrypted with SOPS + age. xenvault fetches and decrypts them into your project directory.
Prerequisites
brew install sops ageYou also need an age private key with access to the secrets. Set one of:
export SOPS_AGE_KEY_FILE=~/.age/key.txt # path to key file
export SOPS_AGE_KEY=AGE-SECRET-KEY-1... # inline key (good for CI)If you don't have a key yet, generate one and share the public key with the repo owner:
age-keygen -o ~/.age/key.txt
# Share the "Public key: age1..." line with the repo ownerInstallation
npm install -g xenvaultCommands
xenvault init
Initialize either a new project inside the secrets repo, or a secrets.config.json in a consumer project.
? What would you like to initialize?
> New project in secrets repo
secrets.config.json (to pull secrets into this directory)New project in secrets repo — run from inside the secrets repo. Creates the project directory.
secrets.config.json — run from a consumer project. Saves the secrets repo location so xenvault pull knows where to fetch from.
xenvault list
List all projects and environments available in the secrets repo.
xenvault list myapp development, production
anotherapp stagingWorks from inside the secrets repo (no PAT needed) or remotely via GitHub API.
xenvault pull
Fetch and decrypt secrets into your project directory.
# Interactive
xenvault pull
# Direct (skips prompts)
xenvault pull --project myapp --env development --output .Writes .env.<environment> into the output directory (defaults to current directory).
Flags:
| Flag | Description |
|------|-------------|
| --project | Project name (skips interactive prompt) |
| --env | Environment name, e.g. development |
| --output | Output directory (default: .) |
xenvault view <file>
Decrypt and print an encrypted file to stdout. Must have a local clone of the secrets repo.
xenvault view path/to/.env.developmentxenvault edit <file>
Open an encrypted file in your editor via sops. Must have a local clone of the secrets repo.
xenvault edit path/to/.env.developmentUses the $EDITOR environment variable (SOPS default).
Configuration
Remote access (consumer projects)
Run xenvault init → choose secrets.config.json to create this file in your project:
{
"github": {
"url": "https://github.com/your-org/secrets-repo"
}
}You also need a GitHub PAT with repo read scope. Set it via:
export SECRETS_GITHUB_PAT=ghp_...Local access (inside the secrets repo)
No PAT needed. xenvault detects the secrets repo by looking for .sops.yaml in the directory tree.
Secrets repo structure
Files must follow the convention <project>/.env.<environment>:
myapp/.env.development
myapp/.env.production
anotherapp/.env.stagingA minimal .sops.yaml for age encryption:
creation_rules:
- path_regex: .*\.env\..*
age: >-
age1abc...(team member 1),
age1xyz...(team member 2)Encrypt a new file:
sops --encrypt .env.development > myapp/.env.developmentAdding a new team member
The new member generates their own key — the private key never leaves their machine.
1. New member generates an age key pair:
age-keygen -o ~/.age/key.txt
export SOPS_AGE_KEY_FILE=~/.age/key.txt # add to ~/.zshrcThe command prints a public key like:
Public key: age1abc123...They send you just that age1abc123... line.
2. Repo owner adds the public key to .sops.yaml:
creation_rules:
- path_regex: .*\.env\.development.*
age: >-
age1existing...,
age1abc123...3. Repo owner re-encrypts affected files:
sops updatekeys myapp/.env.development
# repeat for each file they need access toThis adds a copy of the data key encrypted for their public key — no secret is ever shared.
4. New member sets up their consumer project:
brew install sops age
export SOPS_AGE_KEY_FILE=~/.age/key.txt
# in their project directory
xenvault init # choose "secrets.config.json"
export SECRETS_GITHUB_PAT=ghp_...
xenvault pull