Secrets manager purpose-built for AI agents. Bury credentials securely, fetch them safely. Minimal, secure, hierarchical password and secrets manager with PID-bound sessions for AI agent security.
Key Features:
- 🔐 Strong cryptography: Argon2id + XSalsa20-Poly1305 (via libsodium/PyNaCl)
- 🌳 Hierarchical organization: Store secrets at paths like
work/aws/prodorpersonal/email/gmail - 🤖 PID-bound sessions: AI agents get scoped access tied to their process—no credential leakage
- 👁️ Real-time monitoring: All access logged to
~/.vault/access.logfor instant visibility - 🔑 Credential proxy: Execute commands with
[CRED:path]pattern substitution - 📋 Clipboard integration: Secrets copied securely, never touch shell history
- 🏠 Self-contained: Single SQLite file, no external services
- Why Bury?
- Installation
- Quick Start
- PID-Bound Sessions (v2.0)
- Usage Guide
- Vault-Proxy Mode
- Security
- Comparison
- Contributing
- License
AI agents (Claude Code, Cursor, Copilot) create a new credential security problem:
- Agents read
.envfiles by default, exposing secrets in chat history - Traditional password managers require interactive auth per access—unsuitable for agents
- No existing tool provides real-time visibility into what credentials AI agents access
Bury solves this with PID-bound sessions:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ vault agent │─────▶│ Vault Daemon │─────▶│ ~/.vault/ │
│ (launcher) │ │ (key holder) │ │ (SQLite DB) │
└────────┬────────┘ └────────┬────────┘ └─────────────────┘
│ │
│ launches │
▼ │
┌─────────────────┐ │ ┌─────────────────┐
│ Claude/Code │───────────────┘ │ ~/.vault/ │
│ (AI agent) │ Unix socket │ access.log │
└─────────────────┘ └─────────────────┘
| Feature | Bury | Pass | 1Password CLI | HashiCorp Vault |
|---|---|---|---|---|
| No server required | ✅ | ✅ | ❌ | ❌ |
| PID-bound sessions | ✅ | ❌ | ❌ | ❌ |
| Real-time audit log | ✅ | ❌ | ❌ | ✅ |
| Scoped agent access | ✅ | ❌ | ❌ | ✅ |
| Credential proxy | ✅ | ❌ | ❌ | ❌ |
| Open source | ✅ | ✅ | ❌ | ✅ |
Install directly from the repository using uv:
uv tool install git+https://github.com/hammerhoundai/bury.gitOr using pip:
pip install git+https://github.com/hammerhoundai/bury.gitgit clone https://github.com/hammerhoundai/bury.git
cd bury
uv syncThen use uv run for all commands:
# Run vault
uv run vault --version
# Run tests
uv run pytest
# Run linting
uv run ruff check .Once published:
uv tool install bury
# or
pip install bury# 1. Create a new vault (one-time setup)
vault init
Enter master password: ********
Confirm master password: ********
Vault created at ~/.vault
# 2. Start the daemon
vault daemon
Vault daemon started (PID: 2345)
# 3. Launch Claude with scoped access
vault agent --allow "work/*" --ttl 3600 -- claude
Enter master password: ********
[Claude launches with access to work/* only]
# 4. Inside Claude, retrieve secrets
vault get work/api/key
(copied to clipboard)
# 5. Claude CANNOT access personal secrets
vault get personal/bank
Error: Access denied: path 'personal/bank' not in session scope
# 6. Monitor in real-time (another terminal)
tail -f ~/.vault/access.log
2025-02-15T10:23:01Z [12345/claude] ALLOWED GET work/api/key
2025-02-15T10:24:15Z [12345/claude] DENIED GET personal/bank out-of-scope
# 7. Emergency lock (revokes all sessions)
vault lock
Locked: 1 session revoked
# Lock a specific vault
vault lock --vault /path/to/custom.vaultAI agents need credential access, but traditional approaches are insecure:
| Approach | Problem |
|---|---|
.env files |
Agents leak secrets in chat history |
| Unlocked password manager | No visibility, no scope control |
| Interactive unlock per access | Doesn't work for autonomous agents |
Sessions are bound to a process tree, not just a time window:
- Process Authentication: Only the launched process and its children can access vault
- Scope Control: Each session has explicit path patterns (e.g.,
work/*) - Real-time Logging: Every access attempt logged immediately
- Auto-Revoke: Session dies when process dies or TTL expires
Start or stop the vault daemon:
# Start daemon in background
vault daemon
Vault daemon started (PID: 2345)
# Run in foreground (for debugging)
vault daemon --foreground
# Stop daemon
vault daemon --stop
Vault daemon stoppedLaunch a command with a vault session:
# Launch Claude with work scope (flags before command)
vault agent --allow "work/*" --ttl 3600 claude
# Or use -- separator for clarity (recommended)
vault agent --allow "work/*" --ttl 3600 -- claude
# Multiple scopes
vault agent --allow "work/*" --allow "personal/email/*" --ttl 7200 -- claude
# Launch any command
vault agent --allow "production/*" --ttl 600 -- python deploy.py
# Create session without launching (sets VAULT_SESSION_ID)
vault agent --allow "work/*" --ttl 3600
Session created: vs-a3f9k2m8
Expires in 3600 seconds
Set VAULT_SESSION_ID environment variable to use:
export VAULT_SESSION_ID=vs-a3f9k2m8Options:
--allow <pattern>: Allowed path pattern (can specify multiple)--ttl <seconds>: Session TTL (default: 1800)--vault <path>: Use custom vault path-- <command>: Use--to separate vault options from the command being launched
Show active sessions:
vault status
Vault: UNLOCKED (2 active sessions)
Session: vs-a3f9k2m8
PID: 12345 (claude)
Scope: work/*
Expires: 32 mins
Accesses: 12 allowed, 1 denied
Session: vs-b7d4m1p9
PID: 12389 (python deploy.py)
Scope: work/*, personal/email/*
Expires: 58 mins
Accesses: 3 allowed, 0 denied
# JSON output for scripting
vault status --jsonRevoke a specific session:
vault revoke vs-a3f9k2m8
Session vs-a3f9k2m8 revokedRevoke all sessions (emergency lock):
vault lock
Locked: 2 sessions revokedAll access is logged to ~/.vault/access.log:
2025-02-15T10:23:01.123456Z [12345/claude] ALLOWED GET work/api/key
2025-02-15T10:24:15.789012Z [12345/claude] DENIED GET personal/bank out-of-scope
2025-02-15T10:30:00.000000Z [12345/claude] EXPIRED SESSION vs-a3f9k2m8 ttl-expired
2025-02-15T10:31:45.456789Z [12389/python] ALLOWED PROXY work/db psql
Log format:
timestamp: ISO8601 with microseconds (UTC)[PID/command]: Process ID and command nameresult: ALLOWED | DENIED | EXPIRED | REVOKED | ERRORaction: GET | ADD | DELETE | LIST | PROXY | SESSIONpath: Vault path or session IDreason: Optional (for DENIED/ERROR)
Rotation:
- Daily rotation at midnight UTC
- Current:
~/.vault/access.log - Rotated:
~/.vault/access.log.YYYYMMDD - Retention: 30 days
Migrate existing .env files to vault:
# Create session for migration
vault agent bash --allow "work/project/*" --ttl 600
# Migrate
vault migrate-env .env.local --prefix "work/project"
Migrated 5 entries:
work/project/database_url
work/project/api_key
work/project/secret_key
work/project/aws_access_key
work/project/aws_secret_key
# Dry run to preview
vault migrate-env .env.local --prefix "work/project" --dry-run
Would migrate 5 entries:
work/project/database_url (from DATABASE_URL)
...| Command | Description |
|---|---|
vault init [--vault PATH] |
Create a new vault (default: ~/.vault) |
vault daemon [--foreground] [--stop] |
Start/stop the vault daemon |
vault unlock [--ttl SECONDS] |
Unlock vault (use VAULT_PASSWORD env var for automation) |
vault lock [--vault PATH] |
Revoke all sessions |
vault --version |
Show version information |
vault status [--json] |
Show active sessions |
vault revoke <session_id> |
Revoke specific session |
| Command | Description |
|---|---|
vault add <path> [--note TEXT] |
Add or overwrite an entry |
vault get <path> [--show] |
Retrieve entry (clipboard or stdout) |
vault list [path] |
List entries (optionally under prefix) |
vault tree |
Display hierarchical tree view |
vault search <term> |
Case-insensitive substring search |
vault mv <old> <new> |
Move or rename an entry |
vault delete <path> |
Delete an entry |
vault migrate-env <file> [--prefix P] [--dry-run] |
Migrate .env to vault |
# Organize hierarchically
vault add personal/banking/chase --note "Chase Sapphire"
vault add work/github/token --note "GitHub PAT"
vault add work/aws/prod/access_key
# Search across all entries
vault search aws
# work/aws/dev/access_key
# work/aws/prod/access_key
# List only work entries
vault list workExecute commands with credential substitution:
# Start daemon and create session
vault daemon
vault agent bash --allow "db/*" --ttl 3600
# Use credentials in commands
vault-proxy psql postgresql://[CRED:db_prod_user]:[CRED:db_prod_pass]@host/db
vault-proxy curl -H "Authorization: Bearer [CRED:api_token]" https://api.example.com
vault-proxy ansible-playbook -e "db_pass=[CRED:db_password]" deploy.yml
# Use with custom vault path
vault-proxy --vault /path/to/work.vault psql postgresql://[CRED:user]:[CRED:pass]@host/dbHow it works:
[CRED:path]patterns matched against vault entries- Credentials substituted at execution time
- Scripts contain only references, never actual secrets
- Uses same vault as
vaultcommand (respects--vaultoption)
| Component | Implementation |
|---|---|
| Key Derivation | Argon2id (OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE) |
| Encryption | XSalsa20-Poly1305 (NaCl SecretBox) |
| Salt | 16 bytes random per vault |
| Nonce | 24 bytes random per entry |
| Key | 32 bytes derived from master password |
| Threat | Protection |
|---|---|
| Session ID leaked | Useless without matching PID tree |
| Process impersonation | PID validated against session tree |
| PID reuse | Start time checked to detect reuse |
| Session hijacking | Session dies with originating process |
| Scope bypass | Path matched against explicit patterns |
| Threat | Protection |
|---|---|
| Vault file stolen | Encrypted, needs master password |
| Password in shell history | getpass prevents echo |
| Process snooping | Credentials in memory only during use |
| Tampering | Authenticated encryption (Poly1305) |
| Daemon crash recovery | Auto-detects and cleans stale socket/PID files |
For automation and CI/CD pipelines, you can provide the master password via environment variable:
# Use VAULT_PASSWORD for non-interactive unlock
export VAULT_PASSWORD="your-master-password"
vault unlock
vault get work/api/key
# Or one-shot
VAULT_PASSWORD="$(cat /run/secrets/vault_pass)" vault agent ci-script.sh --allow "prod/*"Security Warning: Environment variables may be visible in process lists (ps e). For production, use file-based secrets or secret management systems that inject via /run/secrets/.
# Check installed version
vault --version
vault 2.0.0| File | Location | Permissions |
|---|---|---|
| Vault database | ~/.vault |
0o600 |
| Daemon socket | ~/.vault/daemon.sock |
0o600 |
| Daemon PID | ~/.vault/daemon.pid |
0o600 |
| Audit log | ~/.vault/access.log |
0o600 |
| Legacy session | ~/.vault-session |
0o600 |
| Bury | pass | |
|---|---|---|
| Storage | SQLite | GPG-encrypted files |
| PID-bound sessions | ✅ | ❌ |
| Real-time audit | ✅ | ❌ |
| Credential proxy | ✅ | ❌ |
| Git integration | Manual | Built-in |
| Bury | 1Password CLI | |
|---|---|---|
| Cost | Free | Subscription |
| Offline | ✅ | Partial |
| PID scoping | ✅ | ❌ |
| Biometric unlock | ❌ | ✅ |
| Bury | HashiCorp Vault | |
|---|---|---|
| Server | None | Required |
| PID sessions | ✅ | ❌ |
| Complexity | Minimal | Significant |
| Dynamic secrets | ❌ | ✅ |
Contributions welcome! Please submit a Pull Request.
git clone https://github.com/hammerhoundai/bury.git
cd bury
uv syncuv run pytestuv run ruff check .
uv run ruff format .
uv run mypy src/bury/
├── src/vault_cli/
│ ├── __init__.py
│ ├── main.py # Main CLI implementation
│ ├── daemon.py # Background daemon
│ ├── session.py # Session management
│ ├── protocol.py # Socket protocol
│ ├── pid_tree.py # Process tree discovery
│ ├── audit.py # Audit logging
│ └── vault_proxy.py # Proxy entry point
├── tests/
├── pyproject.toml
└── README.md
MIT License - see LICENSE file for details.
- PyNaCl for Python bindings to libsodium
- Argon2 for the memory-hard KDF
- psutil for cross-platform process utilities
- Inspired by
passand HashiCorp Vault