Skip to main content
Glama
vishnutvm

Password Space

by vishnutvm

Password Space

A local encrypted credential vault for developers and AI agents.

Store secrets once. Unlock them explicitly. Let agents access credentials through MCP without routinely exposing secret values in chat transcripts.

Encryption at rest and MCP inject handles reduce how often secrets appear in chat — they do not make secrets invisible to a compromised machine, a stolen unlock session, or every possible log/tool path. See Security model and SECURITY.md.

Why Password Space?

Anti-pattern

What goes wrong

Paste secrets into chat

Persist in transcripts, screenshots, logs

Commit .env files

Leak via git and shared workspaces

Plaintext MCP configs

Readable by anything with config access

Password Space keeps ciphertext on disk, an explicit unlock gate, and an agent side channel (MCP + short-lived inject files + Skill + optional redaction hook).

Related MCP server: secrets-mcp-server

Features

Feature

What it does

Encrypted local storage

Secrets in SQLite (vault.db), AES-256-GCM under a passphrase-wrapped vault key

Explicit unlock

vault_unlock (MCP) or CLI unlock before tools can use the vault; vault_lock / lock clears the session

Metadata-only listing

list / MCP list_secrets return names, ids, tags — not values

Day-1 MCP setup

vault_status, vault_init, vault_unlock, vault_lock, vault_set_secret — no clone/pnpm required after plugin install

Secret injection

inject_secret writes a short-lived 0600 env file and returns a handle + path — not the value

Session TTL

Unlock session expires (default 8 hours); expired sessions are rejected and cleared

Redaction hook

Optional beforeSubmitPrompt hook strips known inject values from outbound prompts

Bundled plugin MCP

plugins/password-space/dist/index.js is a self-contained esbuild bundle (vault + MCP)

When the vault is locked, mutating/read tools fail until unlock again. Restarting the MCP process does not unlock the vault — it only re-reads an existing valid session file.

Security model

Designed to protect: vault database on disk while locked (ciphertext + wrapped key); chat/transcript plane when agents use inject_secret / tool-arg secrets and follow the Skill (handles like ps://NAME instead of values).

Does not protect against: an attacker on the same unlocked user account; theft of a live session file; plaintext while present in process memory, inject *.env files, or CLI get / resolve_secret results; hooks that are disabled, misconfigured, or incomplete.

Area

MVP behavior

KDF / encryption

scrypt (N=2¹⁵, r=8, p=1) + AES-256-GCM via Node crypto — see SECURITY.md

Unlock session

$PASSWORD_SPACE_HOME/session, mode 0600, default TTL 8h; lock deletes it

Inject files

$PASSWORD_SPACE_HOME/inject/<token>.env, mode 0600, default TTL 5 minutes (consume then delete)

Passphrase loss

No recovery — ciphertext is unrecoverable without the unlock passphrase

Redaction

Only known values from inject files / explicit secret lists — not a universal DLP

Full threat model, trust boundaries, and reporting: SECURITY.md. Architecture / key hierarchy: docs/architecture.md.

Requirements

Requirement

Notes

Node.js

≥ 20 (uses built-in node:sqlite) — required to run the bundled MCP

Cursor plugin

Cursor with marketplace / MCP support

Package manager

pnpm 9.x — optional; only for CLI / developing this repo

OS

Developed and verified on macOS; should run anywhere Node 20 + node:sqlite works — treat Linux/Windows as best-effort until listed here as tested

Quickstart (Cursor plugin — primary)

Day-1 setup does not require git clone or pnpm.

  1. Open Cursor Settings → Customize (or Dashboard → Plugins & MCPs for team marketplaces).

  2. + Add Marketplace → Import from GitHub.

  3. Paste: https://github.com/vishnutvm/password-space

  4. Install the password-space plugin.

  5. Optional custom vault path: set a real PASSWORD_SPACE_HOME (absolute path) in the environment or a hand-written MCP env block. Leave it unset to use ~/.password-space. Never leave an unresolved ${PASSWORD_SPACE_HOME} (or other ${…}) as the path — the server treats those as unset and falls back to the default.

  6. Ask the agent, for example:

    Set up Password Space for me: check status, init the vault if needed, unlock it, then store my API token.

The Skill drives this MCP flow:

vault_status → vault_init (if needed) → vault_unlock → vault_set_secret → inject_secret

Pass the passphrase and secret values only as MCP tool arguments (or UI secret prompts / PASSWORD_SPACE_PASSPHRASE). Never paste them into chat.

Official public listing (later): cursor.com/marketplace/publish.

Layout: .cursor-plugin/marketplace.json → plugins/password-space/.

Quickstart (CLI — optional power-user)

git clone https://github.com/vishnutvm/password-space.git
cd password-space
pnpm install && pnpm build

pnpm password-space init          # interactive passphrase (min 8 chars)
pnpm password-space unlock        # writes session file
pnpm password-space set GITHUB_TOKEN --tags api,github   # prompts for value on a TTY
pnpm password-space list          # names / tags only
pnpm password-space status
pnpm password-space lock

How set reads the secret value

Input

When

Interactive hidden prompt

TTY and no --value

stdin (one value)

Non-TTY (pipes)

--value <text>

Explicit flag (avoid in shell history for real secrets)

Prefer piping from a protected source rather than putting a real token on the command line:

printf '%s' "$GITHUB_TOKEN" | pnpm password-space set GITHUB_TOKEN --tags api,github

pnpm build also regenerates the marketplace MCP bundle at plugins/password-space/dist/index.js.

Throwaway demo

export PASSWORD_SPACE_HOME=/tmp/ps-demo
rm -rf "$PASSWORD_SPACE_HOME"

# WARNING: inline PASSWORD_SPACE_PASSPHRASE is for demos only — it can end up in shell history.
# Do not use this pattern with a real vault passphrase.
PASSWORD_SPACE_PASSPHRASE='demo-pass-ok' pnpm password-space init
PASSWORD_SPACE_PASSPHRASE='demo-pass-ok' pnpm password-space unlock
printf 'ghp_demo_secret_value' | pnpm password-space set DEMO_TOKEN --tags demo
pnpm password-space list
pnpm password-space status
pnpm password-space lock

Or run pnpm demo (throwaway vault under /tmp/password-space-demo).

Manual MCP configuration

Without the marketplace, point Cursor (or another MCP client) at the bundled server (Node only — no monorepo install):

{
  "mcpServers": {
    "password-space": {
      "command": "node",
      "args": ["/absolute/path/to/password-space/plugins/password-space/dist/index.js"]
    }
  }
}

PASSWORD_SPACE_HOME is optional — omit env for the default ~/.password-space. If you override, use a concrete absolute path only (never an unresolved ${PASSWORD_SPACE_HOME} placeholder).

Also see examples/mcp.cursor.json. Skill: plugins/password-space/skills/password-space/. Hooks: plugins/password-space/hooks/hooks.json.

MCP tools

Tool

Returns secret value?

Use

vault_status

No

Initialized? Unlocked? Next step hint

vault_init

No

Create vault (passphrase via arg or PASSWORD_SPACE_PASSPHRASE)

vault_unlock

No

Write unlock session

vault_lock

No

Clear unlock session

vault_set_secret

No (metadata only)

Store/update name + value + tags

list_secrets

No

Discover names / ids / tags

get_secret_metadata

No

One secret’s metadata

inject_secret

No (writes env file)

Preferred agent access

resolve_secret

Yes, if confirm_redaction: true

Last resort only

Secret-injection lifecycle

This is the intended agent path (conversation plane stays on refs; runtime plane reads a file).

1. Human/agent: vault_status → vault_init (once) → vault_unlock
2. Agent: vault_set_secret (values as tool args — not chat)
3. Agent: list_secrets / get_secret_metadata   → names only
4. Agent: inject_secret({ name })
5. MCP: decrypt in-process → write inject/<token>.env (0600)
6. Tool result: handle + env_file path + env_var + expires_at  (no value)
7. Runtime: source/read env file outside chat → use credential
8. Runtime: delete env file when done
9. Human/agent: vault_lock when finished

Question

Answer

What does inject_secret return?

JSON with handle (ps://NAME), env_file (absolute path), env_var, expires_at, redact_in_transcript: true — not the secret value

Where is the file?

$PASSWORD_SPACE_HOME/inject/<random>.env

Permissions?

Written with mode 0600 (best-effort chmod)

How to use without printing?

Read/source the file in a shell or tool runtime; do not cat it into chat

When deleted?

Caller should delete after use; TTL metadata defaults to 5 minutes (file is not auto-swept by a daemon in MVP)

Vault locked / missing secret?

Tool returns an error payload (Vault is locked… / Secret not found…); no env file is written

Avoid CLI get and MCP resolve_secret in agent workflows — both can put plaintext into the tool/CLI result stream.

Architecture

flowchart TB
  Human[Human + agent prompts] -->|vault_init / unlock / set| MCP[Bundled MCP]
  MCP --> Vault[(Encrypted SQLite vault.db)]
  MCP -->|session 0600| Session[Unlock session]
  Agent[Agent + Skill] -->|list / metadata / inject| MCP
  MCP -->|ciphertext decrypt| Vault
  MCP -->|env file handle| Inject[inject/*.env]
  Agent -->|consume outside chat| Inject
  Hook[redact.mjs hook] -->|strip known values| Chat[Chat transcript]
  Agent -.->|prefer handles only| Chat
  CLI[Optional CLI] -.->|same vault| Vault

Key hierarchy: passphrase → scrypt(salt) → unwrap vault key → AES-256-GCM per secret.

Detail: docs/architecture.md.

Troubleshooting

Symptom

Likely cause

What to do

node:sqlite / MCP fails to start

Node &lt; 20

Upgrade to Node 20+ (node -v)

Vault not initialized

No vault_init yet

Ask the agent to call vault_init, or CLI password-space init

Vault is locked / No unlock session

Locked or expired session

Call vault_unlock, or CLI password-space unlock

Unlock session expired

TTL elapsed (default 8h)

Unlock again; use ttl_hours / --ttl if you need longer

Wrong passphrase

Typo or wrong vault

Confirm PASSWORD_SPACE_HOME matches the vault you meant

Plugin installed but MCP errors “locked”

Session missing/expired

Call vault_unlock (same PASSWORD_SPACE_HOME)

Secret not found

Wrong name / empty vault

list_secrets after unlock

Unexpected data dir

Env override or unresolved ${…}

Check PASSWORD_SPACE_HOME is a real path or unset; vault_status shows data_dir (placeholders fall back to ~/.password-space)

MCP won’t start

Bad path to dist/index.js

Rebuild with pnpm build, or reinstall the plugin

Development

pnpm install && pnpm build && pnpm test

Path

Role

packages/vault

Crypto, store, session, inject, redact

packages/cli

Optional human CLI

packages/mcp-server

MCP server sources (bundled into the plugin)

plugins/password-space

Cursor plugin (bundled MCP + skill + hooks + logo)

See CONTRIBUTING.md. License: MIT.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Encrypts and stores API keys and environment variables locally, providing them to AI agents via MCP with tools for listing, describing, getting secrets, and running commands with secret values redacted.
    2
    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
    C
    maintenance
    Enables AI coding agents to securely inject API keys and secrets into environment files, configs, or commands without the agent ever seeing the secret values.
    2 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to scan and audit environment files for leaked secrets, sanitize them, encrypt/decrypt vaults, and run processes with zero-disk secret injection.
    MIT