Skip to main content
Glama

πŸ” cvault

A local, encrypted, multi-tenant password manager for Claude Code. Claude can use your credentials (run commands, write .env files, call APIs) without the values ever entering the chat, and you get a fast terminal UI to browse, copy and edit them.

tenant (client / workspace)
 └─ environment (optional)      develop Β· staging Β· prod …
     └─ project                 ← linked to a directory on disk
         └─ service             (postgres, stripe, admin-panel, …)
             └─ item            secret Β· credential (multi-field) Β· file
                                 β”œβ”€ role + default   ("log in as admin")
                                 └─ versions v1, v2, … (never deleted)

path: tenant/env/project/service/key     e.g. rezilens/staging/digrc-api/admin-panel/systemadmin

For Claude (MCP server)

  • Uses secrets without seeing them: output is scrubbed to ***

  • Sealed mode: you type or receive values in a native macOS dialog or on the clipboard

  • Knows the current project's credentials at session start (hook)

  • Picks the right credential by role, or falls back to the default

For you (CLI + interactive UI)

  • cvault: arrow-key explorer with tables, search, copy, edit

  • Every change is a new version; roll back any time

  • Archive instead of delete, restorable at any level

  • Full audit log of every access


Contents


Related MCP server: SecureCode

Quick start

git clone <this repo> ~/mcp/vault-mcp && cd ~/mcp/vault-mcp
npm install && npm run build
npm link                       # puts `cvault` on your PATH

# 1. store a random master password in the macOS Keychain and create the vault
security add-generic-password -a "$USER" -s vault-mcp -w "$(openssl rand -base64 33)"
export VAULT_MASTER_PASSWORD_CMD='security find-generic-password -s vault-mcp -w'   # add to ~/.zshrc
cvault init

# 2. register the MCP server with Claude Code (user scope = every project)
claude mcp add cvault -s user \
  -e VAULT_MASTER_PASSWORD_CMD='security find-generic-password -s vault-mcp -w' \
  -- "$(which node)" ~/mcp/vault-mcp/dist/index.js

# 3. (recommended) SessionStart hook, so Claude knows each project's credentials
#    add to ~/.claude/settings.json β†’ hooks.SessionStart:
#    { "hooks": [{ "type": "command", "timeout": 10,
#                  "command": "node ~/mcp/vault-mcp/dist/cli.js hook session-start" }] }

# 4. add your first project, linked to its directory
cvault project add acme/api --bind ~/work/acme-api
cvault set-cred acme/staging/api/admin-panel/superadmin -u root --role admin --default   # password prompted, hidden

The vault lives in ~/.vault-mcp/ (override with VAULT_HOME). The master password never leaves the Keychain.


How Claude uses it

Start Claude inside a linked directory and the session hook tells it which items exist (names only, never values):

CVAULT: this working directory is bound to vault project "acme/api"
Available items (names only, no values):
- admin-panel/superadmin (credential; role: admin; DEFAULT; fields: username, password, url)
- admin-panel/auditor    (credential; role: viewer; fields: username, password)
- stripe/api_key         (secret; "Stripe live key")

Then just ask:

You say

Claude does

Claude sees

"run the migrations with the DB creds"

run_with_secrets with PGPASSWORD ← postgres/app

command output with ***

"create the .env for this project"

write_env_file (0600, refuses if not gitignored)

the list of keys written

"list Stripe customers"

http_request with Bearer {{secret:stripe/api_key}}

the response, scrubbed

"log into the admin panel as viewer"

picks admin-panel/auditor by role

nothing secret

"log into the staging panel" (no credential stored yet)

automatically calls request_credential: dialogs ask you for the save path (pre-filled), then username and password

stored … as v1 (fields: username, password)

"save the OpenAI key sealed"

sealed_save: path dialog, then masked value

stored … as v1

"give me the DB password sealed"

sealed_fetch copies it to your clipboard (auto-clears in 30s)

copied


Interactive explorer

Run cvault with no arguments (or cvault ui). cvault --help lists the scriptable commands.

 cvault  5 items in 1 project Β· ~/.vault-mcp
 home β€Ί acme β€Ί api
──────────────────────────────────────────────── Esc back/cancel Β· Ctrl+C quit ──
? Pick an item
  SERVICE     β”‚ KEY        β”‚ TYPE       β”‚ ROLE   β”‚ DEFAULT β”‚ FIELDS / FILE           β”‚ VER β”‚ DESCRIPTION
  ────────────┼────────────┼────────────┼────────┼─────────┼─────────────────────────┼─────┼────────────────
❯ admin-panel β”‚ auditor    β”‚ credential β”‚ viewer β”‚ -       β”‚ username, password      β”‚ v1  β”‚ Read-only
  admin-panel β”‚ superadmin β”‚ credential β”‚ admin  β”‚ yes     β”‚ username, password, url β”‚ v2  β”‚ Full access
  ssh         β”‚ deploy-key β”‚ file       β”‚ -      β”‚ -       β”‚ deploy.pem (412 B)      β”‚ v1  β”‚ CI deploy key
  stripe      β”‚ api_key    β”‚ secret     β”‚ -      β”‚ -       β”‚ -                       β”‚ v3  β”‚ Stripe live key
 ── Actions ──────────────────────
  + New item…
  Services…
  • Navigate: opens at the project linked to your current directory; search across all items by ref, role or description.

  • Copy any secret or credential field. The clipboard auto-clears after 30s, even if you've already quit.

  • Export: an encrypted bundle, .env or JSON for a project or the whole vault.

  • View all fields, version history and the audit log as tables.

  • Edit values and fields, add or remove fields, replace files. Each change saves a new version.

  • Labels: role, default and description, without creating a new version.

  • Manage: create tenants, projects, services and items; link directories; toggle Claude reveal; archive and restore; roll back; export a project or the whole vault.

  • Keys: ↑↓ move Β· ⏎ select Β· Esc back / cancel the current action Β· Ctrl+C quit.

  • Responsive: tables drop low-priority columns on narrow terminals.


CLI reference

Command

What it does

cvault / cvault ui

interactive explorer

cvault init

create the vault

cvault ls [scope] [--archived] [--json]

tables of projects, services and all items (scope: t, t/env, t/p, t/env/p, …)

cvault project add <t/p> [--bind dir]

create a project, optionally linked to a directory

cvault project bind <t/p> [dir] [--env e] [--remove]

link or unlink a directory (optionally with a default environment)

cvault project reveal <t/p> on|off

allow Claude to read plaintext (reveal_secret)

cvault service <t/[env/]p/s> [--url] [--allow-host h…] [--add-host h…] [--clear-hosts]

create or update a service, and restrict where its secrets may be sent

cvault service-move <from> <to>

rename a service or move it into or out of an environment

cvault unlock <ref>

unlock a credential locked after a 401 or a blocked use

cvault project chat-values <t/p> on|off

allow Claude to store values it received in chat (off by default)

cvault set <ref> [--stdin] [-r role] [--default]

store a secret (hidden prompt)

cvault set-cred <ref> -u user [-f k=v] [-r role] [--default]

store a credential (password prompted, hidden)

cvault put-file <ref> <file>

encrypt a file into the vault

cvault get <ref>[@N][#field] [--out file]

print a value (credentials as a table)

cvault tag <ref> [-r role] [--default|--no-default]

set labels, no new version

cvault versions <ref> Β· cvault rollback <ref> <N>

history and roll back

cvault archive <target> Β· cvault restore <target>

archive or restore a tenant, project, service or item

cvault import .env --into <t/p/s>

bulk-import a .env file

cvault export [scope] [-f bundle|env|json] [-o file]

export a tenant, project, service or everything (details)

cvault import-bundle <file> [--into t or t/p]

restore an encrypted bundle (existing items get a new version)

cvault context [dir]

preview what the session hook injects

cvault audit [-n N] [-r prefix]

access log

cvault backup <dir> Β· cvault change-password

maintenance


MCP tools

Group

Tools

Browse (no values)

vault_status Β· resolve_context Β· list_tenants Β· list_projects Β· list_services Β· list_items Β· audit_log

Use (values hidden)

run_with_secrets Β· write_env_file Β· materialize_file Β· http_request

Sealed (you ↔ vault)

request_credential (missing login: dialogs for path + fields) Β· sealed_save (path + masked value) Β· sealed_fetch (clipboard or dialog)

Manage

create_tenant Β· create_project Β· create_service Β· bind_project_path Β· set_secret Β· set_credential Β· generate_secret Β· put_file Β· tag_item

Versions & archive

list_versions Β· rollback_secret Β· archive Β· restore

Reveal (opt-in)

reveal_secret: only when you've enabled it per project with the CLI


Concepts

Refs & environments

tenant/[env/]project/service/key[@version][#field]

Form

Example

with environment (5 levels)

acme/staging/api/admin-panel/root#password

without environment (4 levels)

acme/api/stripe/api_key@2

inside a linked directory

staging/admin-panel/root or stripe/api_key

  • The environment is optional. The same service name can exist once per environment (staging/admin-panel, develop/admin-panel), each with its own items, versions and allowed hosts.

  • A directory link can carry a default environment: cvault project bind acme/api --env staging. Then admin-panel/root means staging.

  • Listing and export scopes accept acme/staging (everything in staging), acme/staging/api or acme/api (all environments).

  • cvault service-move acme/api/staging-panel acme/staging/api/panel moves an existing service into an environment (items, versions and hosts move with it).

  • Credentials default to the password field.

Missing credentials

When a task needs a login that isn't in the vault, the session hook, server instructions and "not found" errors all point Claude at request_credential. It opens native dialogs:

  1. Path: pre-filled with Claude's suggestion and editable. Paths are corrected automatically, with no second dialog. Project and environment typed in the wrong order (rezilens/digrc-api-service/develop/admin-panel/systemadmin) become rezilens/develop/digrc-api-service/admin-panel/systemadmin; extra levels fold into the service name; messy characters are cleaned. It only asks again when the intent can't be worked out.

  2. Fields: username (visible), password (hidden), plus any extra fields Claude asks for. Masked fields (password, token, secret, key, pin, otp) are asked twice, and a mismatch asks again, so a typo can't be saved silently. For an existing item, leaving a field empty keeps its current value, and a new version is saved.

If a login is rejected, Claude is told to stop after one attempt (accounts often lock after a few), never to try another environment's credential, and to offer request_credential on the same path to re-enter it.

Claude only receives the resulting ref.

Enforced rules (code, not instructions)

The server enforces these itself, so Claude can't skip them:

Rule

How it's enforced

Missing secret β†’ ask the user

Before run_with_secrets / write_env_file / http_request / sealed_fetch runs, missing refs trigger the request_credential dialogs automatically, then the original call continues (using a different path if you saved it elsewhere).

Allowed hosts per service

cvault service t/p/s --allow-host core-staging.example.com --add-host "*.staging.example.com". http_request checks the final URL; run_with_secrets checks the URLs, -h/--host flags and user@host in the command. Anything else is blocked, so a develop password can't reach staging. Only you can set this (CLI or explorer, not an MCP tool).

Lock after a rejected login

An HTTP 401 from http_request locks every credential used in that request. Locked items are refused for Claude until you re-enter them (a new version unlocks) or run cvault unlock <ref>. You can still view and copy them in the CLI and explorer.

Use budget

After 5 uses of a password/token field within 10 minutes (any tool, including clipboard fills), a dialog asks Allow / Block. Allow gives 30 more minutes; Block locks the credential. Usernames don't count.

No values through chat

set_secret / set_credential are refused unless you run cvault project chat-values t/p on.

Roles & defaults

Give credentials a role (admin, viewer, tester, …) and mark one per service as the default. "Log in as viewer" picks the viewer item; with no role named, Claude uses the default and only asks when neither applies.

Versioning

Every write creates version N+1. Old versions stay encrypted (and old files are kept as <uid>.vN.bin). @N reads any version; rollback copies an old version forward as a new version, so history is never rewritten.

Archive, never delete

Archive a tenant, project, service or single item. It's hidden and unusable but fully restorable. Archiving a tenant hides everything under it.

Export & import

Format

Encrypted?

Contents

Typical use

bundle (default)

βœ… passphrase β†’ scrypt β†’ AES-256-GCM

every item incl. files, roles, defaults, descriptions

backups, moving to another machine, handing a project to a teammate

env

❌ plaintext

KEY=value; credentials become KEY_FIELD; files skipped

seeding a .env

json

❌ plaintext

nested tenant β†’ project β†’ service β†’ item

scripts and other tools

cvault export acme/api                         # β†’ cvault-acme-api.cvault (asks for a passphrase)
cvault import-bundle cvault-acme-api.cvault    # on the other machine
cvault import-bundle backup.cvault --into acme-staging/api   # remap tenant/project
cvault export acme/api/stripe -f env --yes --stdout          # STRIPE keys as KEY=value

.env names are built from the path below the export scope: exporting a project gives STRIPE_API_KEY and ADMIN_PANEL_SUPERADMIN_PASSWORD, while exporting one service gives API_KEY and SUPERADMIN_PASSWORD. Name clashes are an error, never a silent overwrite. Plaintext exports need confirmation (--yes in scripts), are written with mode 600, and are refused inside a git repo unless the file is gitignored. For scripts, set the bundle passphrase with CVAULT_BUNDLE_PASSPHRASE. Export is deliberately not an MCP tool, so Claude can't dump the vault. In the explorer: Export… on the home page and on each project page.

Directory linking

cvault project bind links a directory (and its subdirectories; the most specific link wins) to a project. This drives short refs, the explorer's start page and the SessionStart hook.


Security model

  • Crypto: master password β†’ scrypt (N=2¹⁷) β†’ key-encryption key, which wraps a random 256-bit data key. Every value and file is encrypted with AES-256-GCM, with the item's UUID as additional authenticated data. Metadata (names, roles, bindings) is stored unencrypted, so the session hook needs no password.

  • Master password comes from the macOS Keychain via VAULT_MASTER_PASSWORD_CMD, and is removed from the server's environment at startup so child processes never inherit it.

  • Claude never sees values by default. Injected secrets are scrubbed (raw, base64 and URL-encoded) from command and HTTP output. reveal_secret is off per project, and only the CLI can turn it on.

  • Sealed mode: values travel through native macOS dialogs or the clipboard, never through tool arguments or results. They're passed to osascript via env, not argv.

  • Guardrails: .env and file writes into a git repo are refused unless the file is gitignored, and files are written with mode 0600.

  • Audit log of every use, reveal, copy and edit. It never records values.

Limit: scrubbing keeps secrets out of the conversation and transcripts. It is not a sandbox, so a command can still misuse an injected secret on purpose. Review commands that come from untrusted content.


Development

npm run build        # tsc β†’ dist/
npm test             # vitest (crypto, versioning, archive, roles, export/import, injection, scrubbing)
npx @modelcontextprotocol/inspector node dist/index.js
src/
  index.ts    MCP server (tools, sealed mode, reveal gate)
  cli.ts      cvault CLI + SessionStart hook
  ui.ts       interactive explorer
  store.ts    vault: hierarchy, versioned items, archive, roles, audit
  crypto.ts   scrypt + AES-256-GCM
  inject.ts   run / .env / file / http injection + scrubbing
  sealed.ts   macOS dialogs + clipboard with auto-clear
  export.ts   encrypted bundles, .env / JSON export, bundle import
  enforce.ts  server-side rules: auto-prompt, allowed hosts, use budget, 401 lock
  policy.ts   host matching / extraction, budget constants
  table.ts    table rendering
  db.ts       SQLite schema + migrations

License

MIT Β© 2026 shahroz Dhillon

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A secure secrets management server that enables LLMs to execute CLI commands using injected credentials while protecting sensitive data through output redaction and user-approved session permissions. It features an encrypted vault, secret capture from command outputs, and a macOS menu bar app for native notifications and dialogs.
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Secrets vault for Claude Code. Encrypt API keys, tokens and passwords with AES-256. Full audit logs, MCP access rules, and zero-knowledge mode. Secrets never appear in chat.
    17
    53 npm
    1
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    A cross-platform secrets manager that stores named credential sets in the system keychain and injects them as environment variables, enabling secure secret management for AI coding assistants and CLI tools.
    5
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables agents to securely use credentials for GitHub, Cloudflare, OpenAI, Stripe, and xAI without ever reading the secret values, including credential health, rotation, and audit features.
    -