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: passkey-mcp

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:

    Key

    Action

    ↑ ↓

    move between rows (stays in the same column)

    ← β†’

    move between columns in a row, e.g. tenant β†’ project β†’ environment on the tenant page

    β†’ on the last column / ⏎

    open the highlighted cell (project, environment, service, item)

    ← on the first column / Esc

    back one screen; inside an action it cancels without changing anything

    Space then s

    search from any screen; Esc returns to where you were

    q

    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.

Values from the chat need your OK

set_secret / set_credential (values you typed in the chat) open an Allow / Deny dialog showing the path and field names, never the values. cvault project chat-values t/p on skips the dialog for a project.

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
    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
    18 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
  • A
    license
    C
    quality
    C
    maintenance
    Enables AI agents to securely store and manage encrypted secrets locally while using them indirectly through environment-variable injection or file writes, so plaintext values never enter the agent's context.
    8
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to list entries and metadata, run commands with secrets injected as environment variables while scrubbing every value from the output, copy secrets straight to the clipboard, and generate or store new secrets without their plaintext ever entering the conversation. Obvious dumpers are refused and destructive edits require explicit confirmation, closing off accidental exposure paths.
    92 PyPI
    MIT