Passman Documentation

Complete reference for the secure credential proxy MCP server. Learn how to install, configure, and use all 13 tools across 4 protocols.

Introduction

Passman is a secure credential proxy that lets AI agents use your passwords, API tokens, SSH keys, and other secrets without ever seeing them. It implements the Model Context Protocol (MCP), which means it works with any MCP-compatible client: Claude Code, Cursor, VS Code Copilot, Claude Desktop, and Windsurf.

How It Works

Passman sits between the AI agent and your external services. Secrets never appear in the conversation:

StepWhat Happens
1. AI RequestsThe agent calls an MCP tool, referencing a credential by name or UUID
2. Policy CheckPassman validates the request against per-credential policies (URL patterns, command patterns, rate limits)
3. Secret InjectionThe credential is decrypted from the vault and injected into the outbound request server-side
4. Execute & ScrubThe request executes and the response is sanitized — all secret traces removed across 6 encodings
5. Safe ResultThe AI receives the clean result. Secrets were never visible to the agent.

Security Model

LayerProtection
No Raw Secret AccessThe MCP protocol never exposes secret values. No read_secret tool exists.
Output SanitizationAll proxy responses scrubbed across 6 encoding variants (raw, Base64, URL-encoded, hex, upper hex, partial)
Policy EnginePer-credential rules: URL whitelists, command patterns, read-only SQL, recipient restrictions, rate limits
Rate LimitingPer-credential rate limits prevent runaway usage and abuse
Audit TrailEvery operation logged with timestamp, credential, operation type, and outcome
Memory SafetyBuilt in Rust with zeroize for cryptographic memory hygiene. Secrets wiped from RAM after use.

Installation

One-Line Installer (Recommended)

Downloads a pre-built binary for your platform. No Rust toolchain required.

bash
$ curl -fsSL https://raw.githubusercontent.com/ahmadzein/passman/main/install.sh | bash

The binary is installed to ~/.local/bin/passman-mcp-server.

Build from Source

Requires the Rust toolchain.

bash
$ git clone https://github.com/ahmadzein/passman.git $ cd passman $ cargo build --release -p passman-mcp-server $ cp target/release/passman-mcp-server ~/.local/bin/

Desktop GUI

A Tauri v2 + React desktop app for managing your vault visually. Shares the same vault file as the MCP server.

PlatformDownload
macOS (Apple Silicon).dmg (ARM64)
macOS (Intel).dmg (x64)
Windows.exe Installer · .msi
LinuxAppImage · .deb · .rpm
 macOS Gatekeeper

The app is not signed with an Apple Developer certificate. After installing, run this in Terminal:

bash
$ xattr -cr /Applications/Passman.app

Verify Installation

bash
$ passman-mcp-server --version

Configuration

Add Passman to your MCP client. Pick your client and paste the config.

Claude Code

Create .mcp.json in your project root, or use the CLI:

bash
$ claude mcp add --transport stdio passman -- passman-mcp-server

Or manually add to .mcp.json:

.mcp.json
{ "mcpServers": { "passman": { "command": "passman-mcp-server", "args": [] } } }

Cursor

.cursor/mcp.json
{ "mcpServers": { "passman": { "command": "passman-mcp-server", "args": [] } } }

VS Code Copilot

.vscode/mcp.json
{ "servers": { "passman": { "type": "stdio", "command": "passman-mcp-server", "args": [] } } }

Claude Desktop

claude_desktop_config.json
{ "mcpServers": { "passman": { "command": "passman-mcp-server", "args": [] } } }

Windsurf

~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "passman": { "command": "passman-mcp-server", "args": [] } } }

Getting Started

Walk through the basic flow: unlock, store, use, lock.

1. Unlock the Vault

On first run, a new vault is created automatically.

MCP Tool Call
vault_unlock(password="your-master-password") // Response: "Vault unlocked. 0 credentials loaded."

2. Store a Credential

MCP Tool Call
credential_store( name="GitHub API", kind="api_token", environment="production", secret={ "token": "ghp_xxxxxxxxxxxx", "header_name": "Authorization", "prefix": "Bearer " }, tags=["github", "api"] ) // Response: "Stored: GitHub API [a1b2c3d4-...]"

3. Use the Credential

The AI agent makes an HTTP request. Passman injects the token and scrubs the response.

MCP Tool Call
http_request( credential_id="a1b2c3d4-...", method="GET", url="https://api.github.com/user" ) // Response (sanitized — token never visible): { "status": 200, "headers": { "authorization": "[REDACTED]" }, "body": "{ \"login\": \"username\", \"id\": 12345, ... }" }

4. Lock the Vault

MCP Tool Call
vault_lock() // Encryption key cleared from memory

Tools Reference

Passman exposes 13 MCP tools across 5 categories.

Vault Management

vault_unlock

Unlock the vault with the master password. Creates a new vault if none exists.

ParameterTypeRequiredDescription
passwordStringrequiredMaster password to unlock the vault

Output: Confirmation message with credential count.

Example
vault_unlock(password="my-secret-password") // "Vault unlocked. 12 credentials loaded."

vault_lock

Lock the vault, clearing the encryption key from memory.

Parameters: None

Example
vault_lock() // "Vault locked."

vault_status

Check vault status: locked/unlocked, credential count, environments in use.

Parameters: None

Example
vault_status() // { "locked": false, "credentials": 12, "environments": ["production","staging"] }

Discovery

credential_list

List credentials with optional filters. Never returns secret values.

ParameterTypeRequiredDescription
kindStringoptionalFilter by kind: password, api_token, ssh_key, ssh_password, database_connection, certificate, smtp_account, custom
environmentStringoptionalFilter by environment: local, development, staging, production
tagStringoptionalFilter by tag
Example
credential_list(kind="api_token", environment="production") // Returns: [{ id, name, kind, environment, tags, created_at, updated_at }, ...]

credential_search

Search credentials by name, tags, or notes. Never returns secret values.

ParameterTypeRequiredDescription
queryStringrequiredSearch query (matches name, tags, notes)
Example
credential_search(query="github") // Returns matching credentials (metadata only, no secrets)

credential_info

Get detailed metadata for a credential. Never returns secret values.

ParameterTypeRequiredDescription
idStringrequiredCredential UUID
Example
credential_info(id="a1b2c3d4-...") // { id, name, kind, environment, tags, notes, created_at, updated_at }

Storage

credential_store

Store a new credential in the vault. The secret field structure depends on the credential kind (see Credential Types).

ParameterTypeRequiredDescription
nameStringrequiredHuman-readable name for the credential
kindStringrequiredCredential kind (see types)
environmentStringrequiredEnvironment: local, development, staging, production
secretObjectrequiredSecret data (structure depends on kind)
tagsString[]optionalTags for categorization
notesStringoptionalFreeform notes

credential_delete

Delete a credential from the vault. Requires explicit confirmation.

ParameterTypeRequiredDescription
idStringrequiredCredential UUID to delete
confirmBooleanrequiredMust be true to confirm deletion
Example
credential_delete(id="a1b2c3d4-...", confirm=true)

Protocol Proxies

http_request

Make an HTTP request using a stored credential for authentication. The credential's secret is injected as auth headers and never exposed. Response is sanitized.

ParameterTypeRequiredDescription
credential_idStringrequiredCredential UUID for authentication
methodStringrequiredGET, POST, PUT, PATCH, DELETE, HEAD
urlStringrequiredTarget URL
headersObjectoptionalAdditional HTTP headers (key-value pairs)
bodyStringoptionalRequest body

Auth modes depend on the credential type:

  • api_token — Injects header_name: prefix + token (e.g. Authorization: Bearer ghp_xxx)
  • password — Uses HTTP Basic Auth (Authorization: Basic base64(user:pass))
  • custom — Injects custom fields as headers
Example
http_request( credential_id="a1b2c3d4-...", method="POST", url="https://api.example.com/data", headers={"Content-Type": "application/json"}, body="{\"key\":\"value\"}" )

ssh_exec

Execute a command on a remote host via SSH. Supports both SSH key and SSH password credentials. Output is sanitized.

ParameterTypeRequiredDescription
credential_idStringrequiredCredential UUID (SSH key or SSH password)
commandStringrequiredShell command to execute on the remote host
Example
ssh_exec( credential_id="b2c3d4e5-...", command="uptime && df -h" ) // Returns: stdout, stderr, exit_code (all sanitized)

sql_query

Execute a SQL query using a stored database credential. Supports PostgreSQL, MySQL, and SQLite. Results are sanitized. Policy can enforce read-only mode.

ParameterTypeRequiredDescription
credential_idStringrequiredCredential UUID (database connection)
queryStringrequiredSQL query to execute
paramsArrayoptionalPositional query parameters for parameterized queries
Parameterized Queries

Always use parameterized queries to prevent SQL injection. Use $1, $2 placeholders and pass values via params.

Example
sql_query( credential_id="c3d4e5f6-...", query="SELECT * FROM users WHERE email = $1", params=["user@example.com"] ) // Returns: { columns: [...], rows: [[...], ...] }

send_email

Send an email using a stored SMTP credential. Recipients can be restricted by policy.

ParameterTypeRequiredDescription
credential_idStringrequiredCredential UUID (SMTP account)
toString[]requiredRecipient email addresses
subjectStringrequiredEmail subject
bodyStringrequiredEmail body (plain text)
ccString[]optionalCC recipients
bccString[]optionalBCC recipients
Example
send_email( credential_id="d4e5f6g7-...", to=["team@company.com"], subject="Deploy Complete", body="Production deploy finished successfully." )

Audit

audit_log

View the audit log of proxy operations. Filter by credential, limit results, or filter by time range.

ParameterTypeRequiredDescription
credential_idStringoptionalFilter by credential UUID
limitIntegeroptionalMaximum number of entries to return
sinceStringoptionalOnly entries after this RFC3339 datetime (e.g. 2025-01-15T00:00:00Z)
Example
audit_log(limit=10, since="2025-01-15T00:00:00Z") // Returns: [{ timestamp, credential_id, credential_name, action, tool, success, details }, ...]

Credential Types

Passman supports 8 credential types. Each type has a specific JSON schema for the secret field when storing a credential.

Password

Username/password pairs for web services, APIs, or any login.

Secret Schema
{ "username": "admin", // required "password": "s3cret", // required "url": "https://example.com" // optional }

API Token

Bearer tokens, API keys, and other token-based authentication.

Secret Schema
{ "token": "ghp_xxxx", // required "header_name": "Authorization", // optional (default: Authorization) "prefix": "Bearer " // optional (default: Bearer ) }

SSH Key

SSH key-based authentication for remote server access.

Secret Schema
{ "username": "deploy", // required "host": "server.example.com", // required "port": 22, // optional (default: 22) "private_key": "-----BEGIN OPENSSH...", // required "passphrase": "optional-passphrase" // optional }

SSH Password

SSH password-based authentication for remote server access.

Secret Schema
{ "username": "admin", // required "host": "server.example.com", // required "port": 22, // optional (default: 22) "password": "s3cret" // required }

Database Connection

Connection credentials for PostgreSQL, MySQL, or SQLite databases.

Secret Schema
{ "driver": "postgres", // required: "postgres" | "mysql" | "sqlite" "host": "db.example.com", // required "port": 5432, // optional (default: 5432) "database": "myapp", // required "username": "app", // required "password": "dbpass", // required "params": {"sslmode": "require"} // optional }

Certificate

PEM-encoded certificates and private keys for mutual TLS or client certificate authentication.

Secret Schema
{ "cert_pem": "-----BEGIN CERTIFICATE-----...", // required "key_pem": "-----BEGIN PRIVATE KEY-----...", // required "ca_pem": "-----BEGIN CERTIFICATE-----..." // optional (CA certificate) }

SMTP Account

SMTP server credentials for sending email.

Secret Schema
{ "host": "smtp.gmail.com", // required "port": 587, // optional (default: 587) "username": "user@gmail.com", // required "password": "app-password", // required "encryption": "start_tls" // optional: "none" | "start_tls" | "tls" }

Custom

Arbitrary key-value pairs for credentials that don't fit other types.

Secret Schema
{ "fields": { // required "api_key": "xxx", "account_id": "12345", "region": "us-east-1" } }

Environments

Every credential is tagged with an environment for organizational purposes.

EnvironmentDescription
localLocal development machine
developmentShared development / sandbox environment
stagingPre-production staging environment
productionLive production environment
customAny custom string (e.g. "dr-staging", "eu-west")

Use environments to filter credentials with credential_list and to organize your vault when you have credentials for the same service across different environments.

Policy Engine

The policy engine provides per-credential access controls. When policy patterns are specified, unmatched requests are denied by default.

Policy Fields

FieldTypeDescription
allowed_toolsString[]Which MCP tools can use this credential (e.g. ["http_request"])
http_url_patternsString[]URL glob patterns allowed for HTTP requests (e.g. ["https://api.github.com/*"])
ssh_command_patternsString[]Command glob patterns allowed for SSH (e.g. ["ls *", "cat *"])
sql_allow_writeBooleanIf false (default), blocks INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE, REPLACE, MERGE
smtp_allowed_recipientsString[]Email glob patterns for allowed recipients (e.g. ["*@company.com"])
rate_limitObject{ "max_requests": 100, "window_secs": 3600 } — sliding window rate limit
Pattern Matching

All patterns use glob-style matching with * as wildcard. Example: https://api.github.com/* matches any path under that domain. If no patterns are specified for a category, all requests for that category are allowed.

Full Policy Example

Policy JSON
{ "allowed_tools": ["http_request", "credential_info"], "http_url_patterns": [ "https://api.github.com/*", "https://api.example.com/*" ], "ssh_command_patterns": [], "sql_allow_write": false, "smtp_allowed_recipients": ["*@company.com"], "rate_limit": { "max_requests": 100, "window_secs": 3600 } }

SQL Read-Only Enforcement

When sql_allow_write is false, the following SQL statements are blocked:

Output Sanitization

Every proxy response is scrubbed to ensure secrets cannot leak back to the AI agent, even if the external service echoes them.

Encoding Variants

For each secret value (4+ characters), the sanitizer checks and replaces across 6 encoding variants:

#EncodingExample
1Rawmy-secret-token
2Base64 (Standard)bXktc2VjcmV0LXRva2Vu
3Base64 (URL-safe)bXktc2VjcmV0LXRva2Vu
4URL-encodedmy-secret-token (percent-encoded if special chars)
5Hex (lowercase)6d792d7365637265742d746f6b656e
6Hex (uppercase)6D792D7365637265742D746F6B656E
Minimum Threshold

Secrets shorter than 4 characters are not sanitized to avoid false positives with common words or values.

What Gets Sanitized

ProxySanitized Output
http_requestResponse body and response headers
ssh_execstdout and stderr
sql_queryQuery result values
send_emailStatus messages

All matched values are replaced with [REDACTED].

Desktop GUI

The desktop app provides a visual interface for managing your Passman vault. Built with Tauri v2 and React, it shares the same vault file as the MCP server — changes sync automatically.

Screens

ScreenDescription
UnlockMaster password entry. Creates a new vault on first run.
Vault BrowserList, search, and filter all credentials. Displays metadata only — no secrets shown.
Credential EditorCreate and edit credentials with form fields matching each credential type's schema.
Policy EditorConfigure per-credential policies: URL patterns, command patterns, rate limits.
Audit LogBrowse and filter the audit trail of all proxy operations.
SettingsConfigure MCP server auto-install, vault file location, and other preferences.

MCP Auto-Install

The desktop GUI can automatically install the MCP server configuration for detected AI clients (Claude Code, Cursor, VS Code, Claude Desktop, Windsurf).

Install via CLI

bash
# Installs MCP server + Desktop GUI in one command $ curl -fsSL https://raw.githubusercontent.com/ahmadzein/passman/main/install.sh | GUI=1 bash

Build from Source

bash
$ cd gui $ npm install $ npm run tauri build

Architecture

Passman is a Rust workspace with a layered architecture. Each crate has a single responsibility.

Crate Structure

CrateResponsibility
passman-vaultEncryption, KDF, credential storage, policy engine, audit log
passman-proxyHTTP, SSH, SQL, SMTP proxy clients + output sanitizer
passman-mcp-serverMCP protocol implementation, tool definitions, stdio transport
gui/Tauri v2 + React desktop application

Tech Stack

ComponentTechnology
LanguageRust
EncryptionAES-256-GCM (aes-gcm crate)
Key DerivationArgon2id (64 MiB memory, 3 iterations, 4 parallelism)
HTTP Clientreqwest (rustls-tls)
SSH Clientrussh
SQL Clientsqlx (PostgreSQL, MySQL, SQLite)
SMTP Clientlettre
MCP Protocolrmcp (stdio transport)
Memory Safetyzeroize (cryptographic memory hygiene)
Desktop GUITauri v2 + React + TypeScript

File Locations

FilePurpose
~/.passman/vault.jsonVault file (credentials encrypted with AES-256-GCM)
~/.passman/audit.jsonlAudit log (JSON Lines, append-only)

Common Workflows

Store and Use an API Key

Workflow
# 1. Store the API key credential_store( name="OpenAI API", kind="api_token", environment="production", secret={"token":"sk-...", "header_name":"Authorization", "prefix":"Bearer "} ) # 2. Use it in a request http_request( credential_id="<returned-uuid>", method="POST", url="https://api.openai.com/v1/chat/completions", headers={"Content-Type": "application/json"}, body="{...}" )

SSH into a Server

Workflow
# 1. Store SSH credentials credential_store( name="Production Server", kind="ssh_key", environment="production", secret={"username":"deploy", "host":"prod.example.com", "private_key":"-----BEGIN..."} ) # 2. Run a command ssh_exec(credential_id="<uuid>", command="systemctl status nginx")

Query a Database

Workflow
# 1. Store database connection credential_store( name="App DB", kind="database_connection", environment="staging", secret={"driver":"postgres", "host":"db.example.com", "port":5432, "database":"myapp", "username":"app", "password":"dbpass"} ) # 2. Run a query with parameters sql_query( credential_id="<uuid>", query="SELECT id, name, email FROM users WHERE active = $1 LIMIT $2", params=[true, 10] )

Send an Email

Workflow
# 1. Store SMTP credentials credential_store( name="Company SMTP", kind="smtp_account", environment="production", secret={"host":"smtp.gmail.com", "port":587, "username":"bot@company.com", "password":"app-password", "encryption":"start_tls"} ) # 2. Send an email send_email( credential_id="<uuid>", to=["team@company.com"], subject="Weekly Report", body="Here is this week summary..." )

Review the Audit Trail

Workflow
# View last 20 operations audit_log(limit=20) # View operations for a specific credential audit_log(credential_id="a1b2c3d4-...", limit=50) # View operations since a date audit_log(since="2025-01-01T00:00:00Z")

Set Up Policies

Policies are set per-credential when storing via the Desktop GUI policy editor, or by including policy rules in the vault configuration.

Policy Example
// Restrict a GitHub token to only github.com API calls, max 100/hour { "allowed_tools": ["http_request"], "http_url_patterns": ["https://api.github.com/*"], "rate_limit": { "max_requests": 100, "window_secs": 3600 } }

Troubleshooting

Common Errors

ErrorCauseSolution
Vault is lockedTrying to use tools before unlockingCall vault_unlock first
Policy deniedRequest doesn't match the credential's policy rulesCheck the credential's policy — update URL patterns, command patterns, or allowed tools
Rate limit exceededToo many requests in the rate limit windowWait for the window to reset, or increase the max_requests / window_secs
Binary not foundpassman-mcp-server not in PATHEnsure ~/.local/bin is in your PATH, or use the full path in MCP config
macOS Gatekeeper blockUnsigned binary quarantined by macOSRun: xattr -cr /Applications/Passman.app
Connection refused (SSH)Wrong host, port, or firewall blockingVerify host/port in the SSH credential. Check that the remote server allows SSH connections.
SQL: write not allowedsql_allow_write is false in policySet sql_allow_write: true in the credential's policy
Invalid credential kindSecret fields don't match the kind schemaCheck the Credential Types section for the correct schema

Debugging Tips

File Permissions

The vault file at ~/.passman/vault.json should only be readable by your user:

bash
$ chmod 600 ~/.passman/vault.json

FAQ

Can AI agents read my passwords?

No. Passman has no read_secret tool. AI agents can only reference credentials by name or UUID. The actual secret values are decrypted server-side and injected into outbound requests. Furthermore, all responses are sanitized across 6 encoding variants to prevent accidental leaks.

Where is my data stored?

All data is stored locally on your machine:

  • ~/.passman/vault.json — Encrypted vault file
  • ~/.passman/audit.jsonl — Audit log

No cloud sync, no telemetry, no third-party access. Your secrets never leave your machine except when proxied to the target service.

What encryption is used?

Passman uses AES-256-GCM for encryption with Argon2id for key derivation (64 MiB memory, 3 iterations, 4 parallelism). The encryption key is derived from your master password and wiped from memory (using Rust's zeroize crate) when you lock the vault.

Can multiple AI clients use the same vault?

Yes. The vault is a single file on disk. Both the MCP server and the Desktop GUI can access it. However, only one process should write to the vault at a time — the MCP server and GUI coordinate through file locking.

What happens if I forget my master password?

The vault cannot be recovered without the master password. There is no password reset, no recovery key, and no backdoor. This is by design — it means even if someone obtains your vault file, they cannot access your credentials without the password.

Is Passman free?

Yes, free forever. Passman is open source under the MIT License. No paid tiers, no subscriptions, no usage limits.

Which MCP clients are supported?

Any MCP-compatible client that supports stdio transport. Tested with:

  • Claude Code (CLI)
  • Claude Desktop
  • Cursor
  • VS Code Copilot
  • Windsurf
How do I update Passman?

Run the installer again. It will download and replace the existing binary:

bash
$ curl -fsSL https://raw.githubusercontent.com/ahmadzein/passman/main/install.sh | bash

Your vault file is preserved across updates.