Skip to main content

Secure storage vault for secrets in git repositories (SOPS alike)

Project description

xvault

xvault is a CLI for keeping secrets inside your real config and notes files, while staying Git-friendly.

Your files remain readable and reviewable, and only the values you explicitly mark as secret are encrypted.

The core idea is a single source of truth: store secrets, notes, and configuration together in a small set of canonical files (.json, .jsonc, .yaml, .env, .md), and let xvault handle safe editing, encryption, and export.

From these files, you can derive/export/resolve the exact formats your tooling expects (e.g., .env, JSON config, certificate files) without duplicating plaintext secrets across multiple files or repositories.

To minimize cognitive load, xvault keeps the workflow intentionally simple: secure defaults, straightforward commands, and optional OS key caching (no servers to run, no KMS to configure, and no complex policy systems). It’s optimized for solo developers and small teams who want a local-first vault that integrates cleanly with Git.


Intro

Motivation: single source of truth for developer secrets

Most developer workflows end up with friction such as:

  • a config file plus a separate secrets file
  • manual merging before deployment / resolving secrets at runtime
  • ugly diffs (whole files encrypted)
  • accidental leaks when exporting plaintext

xvault is designed to keep your notes and configs as the source of truth, with inline secrets that stay encrypted on disk.

Comparison: SOPS and git-crypt

xvault overlaps with tools like SOPS and git-crypt, but it targets a slightly different “developer ergonomics” space:

  • SOPS shines for GitOps/Kubernetes workflows and KMS-backed key management. It’s great when your infra pipeline is already built around SOPS + KMS/PGP/age.
    xvault is not trying to replace that ecosystem; it focuses on editing arbitrary files (.json, .jsonc, .env, .yml, .md) with minimal ceremony and clean diffs.

  • git-crypt is effective for encrypting entire files transparently in Git.
    xvault takes a more explicit approach: secrets are marked with enc: or ${enc:...} and only those values are encrypted, keeping the rest of the document readable.

If your main need is “encrypt a whole directory of files in git”, git-crypt may be enough.
If your main need is “keep notes/config readable while only encrypting secret values”, xvault is a better fit.

Design philosophy

  • Explicit by default: only values marked with enc: or ${enc:...} are treated as secrets.
  • Git diffs matter: keep secrets stored as single-line ciphertext.
  • Stay close to real formats: work with .json, .jsonc, .yaml, .env, .md, without forcing a rigid schema.
  • Developer UX first: edit is the primary workflow; everything else supports it.
  • Safe defaults: prefer failing loudly over producing ambiguous output.

Usage

xvault is designed for two main workflows. Both rely on the same core idea: keep secrets inside the files you already use, encrypt only what you explicitly mark as secret (enc: or ${enc:...}), and derive/export the exact outputs when needed.

1) Application config files with embedded secrets

Use xvault directly on configuration files that your applications and tooling already understand:

  • .env files
  • JSON / JSONC
  • YAML
  • md files

You keep the config readable, and only encrypt values marked as secrets (enc:... or ${enc:...}).

Example (dev.env):

DB_HOST=localhost
DB_USER=enc:admin
DB_PASS=enc:"my password with spaces"
DATABASE_URL=postgres://${var:DB_USER}:${var:DB_PASS}@localhost/app

Typical workflow:

  • xvault edit ./dev.env to safely edit secrets and non-secret config together
  • xvault export ./dev.env to materialize a decrypted output for scripts/CI/runtime
  • xvault get ./dev.env DB_PASS to extract a decrypted value from file

2) Personal vault notes (Markdown)

Use xvault as a local-first personal vault where you store notes, runbooks, logs, credentials, and key material inside Markdown files.

A common pattern is:

  • one Markdown file per topic (e.g., servers.md, accounts.md, infra.md)
  • secrets stored inside standard fenced blocks like ```env

Example (servers.md):

## prod-api-01

Notes and runbook steps in plain text...

```env
SSH_HOST=prod-api-01.example.com
SSH_USER=enc:admin
SSH_PASS=${enc:my password with spaces}
SSH_KEY_PEM_B64=enc:LS0tLS1CRUdJTiBPU...
```

Guidelines:

  • Use enc:... or ${enc:...} for secret values.
  • Use *_B64 variables for multi-line or binary material (PEM/PFX/PDF/PNG) encoded as base64.
  • Keep everything else (notes, procedures, logs) in plain text for excellent Git diffs and searchability.

Features

  • Formats: .json, .jsonc, .yaml, .env, .md
  • Inline secret marking: enc:... and ${enc:...} indicate secret values
  • Single-line ciphertext for clean Git diffs
  • Optional variable substitution: resolve ${var:VARNAME} placeholders (xvault get file.json secret_name --resolve)
  • Key caching (optional):
    • unlock stores a derived key in the OS key store
    • lock removes it
    • --no-cache-key disables cache read/write per command
  • Crypto (v1):
    • Password-based key derivation: Argon2id
    • Encryption: AES-256-GCM (authenticated encryption)
  • Rekey support: rotate secrets to a new password (xvault rekey)
  • Validation: sanity checks for file structure and encrypted markers (xvault validate)

Security Model

What xvault protects against

  • Accidental commits of plaintext secrets by keeping secrets encrypted on disk
  • “Diff leakage”: avoids storing whole plaintext configs; only enc:.../${enc:...} values become ciphertext
  • Backup leakage: encrypted vault files are safe to back up as ciphertext
  • Tampering detection: AES-GCM provides integrity/authentication for secret values (wrong key or modified ciphertext fails to decrypt)
  • By default, xvault edit uses an in-terminal editor and does not write plaintext temp files.

Threats it does NOT fully solve (limitations)

  • Weak passwords: your vault is only as strong as the password you choose. Use a strong passphrase (e.g., 12–16+ characters, preferably more).
  • Compromised machine: if an attacker owns your box while you edit, they can read memory, or use a keylogger, or inspect terminal buffers. Or if an attacker gains access to your machine, they may be able to extract the cached key from the OS key store and decrypt your vault without ever needing to wait for you to edit.
  • Plaintext exports: export can produce decrypted content. Handle it carefully (gitignore, temporary locations).
  • OS key cache availability: keyring/credential storage may fail in some contexts (e.g., Windows over SSH). Use --no-cache-key or alternative cache strategies.

Threat mitigations (practical)

  • Use a strong, unique password (prefer long passphrases).
  • Keep repositories private; avoid cloning vault repos on untrusted machines.
  • Prefer --no-cache-key in sensitive environments (remote sessions, servers, CI).
  • Run xvault validate before committing.

Example file (conceptual)

xvault stores a small metadata header (e.g. _xvault) and secrets as enc:/${enc:...} values:

{
  "_xvault": "xvault:<opaque-metadata-blob>",
  "db": {
    "host": "...",
    "user": "enc:...",
    "password": "enc:...",
    "url": "https://login:${enc:...}@server/path/to/resource"
  }
}
_xvault="xvault:<opaque-metadata-blob>"
DB_HOST=...
DB_USER=enc:...
DB_PASSWORD=enc:...

Installation

Install from PyPI:

pip install xvault
xvault version

Install from source (development)

Clone the repository:

# clone the repository:
git clone https://github.com/marcdp/xvault.git
# run the CLI:
python -m xvault
# or install as a command:
pip install .

Roadmap

Short-term (quality and UX):

  • Improve TUI editor UX (colors, navigation, safer save guards, undo-redo)
  • Better Markdown conventions for multi-line secrets and binary references
  • Safer export modes (explicit confirmations)

Mid-term (developer workflows):

  • Export filtering by section/scope in Markdown (e.g., server.var)
  • SSH-agent helpers (load selected keys with TTL)
  • A "blob store" for large binary secrets (PFX/PDF/PNG) with manifest + encrypted blobs

Long-term:

  • VS Code virtual filesystem provider (xvault:/...) backed by xvault (no plaintext temp files)
  • Optional policy validation hooks (pre-commit integration)
  • Optional alternative key caching backends (more reliable across SSH / WSL)

License

MIT License

See the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xvault-0.0.38.tar.gz (24.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xvault-0.0.38-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file xvault-0.0.38.tar.gz.

File metadata

  • Download URL: xvault-0.0.38.tar.gz
  • Upload date:
  • Size: 24.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for xvault-0.0.38.tar.gz
Algorithm Hash digest
SHA256 dbc667fc8751e647f4e8514af7f39b06e17d9cf84a309677c76b2071d2695f51
MD5 809dbcb573917f7f31aa757a1582664f
BLAKE2b-256 2c8f4283b5781e41df2c963ef228306deac87afae32e12cec8884089a5c4c635

See more details on using hashes here.

File details

Details for the file xvault-0.0.38-py3-none-any.whl.

File metadata

  • Download URL: xvault-0.0.38-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for xvault-0.0.38-py3-none-any.whl
Algorithm Hash digest
SHA256 d4209870ec7daaf3bd8beec5e722f65d97e91b2ed079ad5d9a9b2cb9bb2dabda
MD5 21d550f38be94f2f373ab6c24fa1c662
BLAKE2b-256 5b18a9df5a2239c0efc2e708375bed4efe3751e70782f1a6d6330d411331bfda

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page