npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sethwebster/agent-password-manager

v0.4.0

Published

Local-first encrypted password manager for agents and operators

Readme

Agent Password Manager

A local-first password manager for agents and operators.

Now packaged for npm as @sethwebster/agent-password-manager, while keeping the same conservative vault, policy, audit, CLI, and MCP behavior intact.

Why

Because agents and operators still need secrets, but the usual options are full of nonsense:

  • shell history leaks
  • plaintext .env sprawl
  • shared browser password stores
  • overbuilt SaaS vaults when local control is enough
  • no clear audit trail for who revealed what and why

This repo exists to provide a boring, local-first secret layer for agent workflows:

  • encrypted at rest
  • masked reads by default
  • explicit about purpose when revealing secrets
  • policy-gated for sensitive reads/exports
  • usable from both CLI and MCP integrations

What it does

  • encrypted local vault
  • masked reads by default
  • explicit-purpose secret reveal
  • policy-gated approvals for reveal/export
  • append-only local audit log with hash chaining
  • MCP tool surface for local integrations

Install / run

One-shot via bunx

After publish:

export APM_MASTER_PASSPHRASE='your-passphrase'
bunx @sethwebster/agent-password-manager doctor
bunx @sethwebster/agent-password-manager init

One-shot via npx

After publish:

export APM_MASTER_PASSPHRASE='your-passphrase'
npx @sethwebster/agent-password-manager doctor
npx @sethwebster/agent-password-manager init

Install globally

npm install -g @sethwebster/agent-password-manager
apm doctor

Run from this repo during development

cd /Users/admin/.openclaw/workspace/repos/agent-password-manager
export APM_MASTER_PASSPHRASE='your-passphrase'
bun run build
node dist/apm.js doctor

CLI entrypoints after publish

Published package bins:

  • agent-password-manager — package-name-matched bin for npx/bunx @sethwebster/agent-password-manager ...
  • apm — short CLI alias after install
  • apm-mcp — MCP stdio server after install

Examples:

npx @sethwebster/agent-password-manager doctor
bunx @sethwebster/agent-password-manager get github-dougbot
apm doctor
apm-mcp

MCP server

Run directly from the published package

export APM_MASTER_PASSPHRASE='your-passphrase'
npx apm-mcp

If you do not want a global install, npx can invoke the named bin from the scoped package explicitly:

npx --package @sethwebster/agent-password-manager apm-mcp

Equivalent with Bun:

bunx --package @sethwebster/agent-password-manager apm-mcp

Run from this repo

export APM_MASTER_PASSPHRASE='your-passphrase'
bun run build
node dist/mcp-server.js

Or through the package script:

bun run mcp

Or through the CLI shim:

node dist/apm.js mcp-server

Vault path resolution

Default behavior is intentionally repo-local / working-directory-local:

  • CLI and MCP both use ./vault relative to the current working directory.
  • If you launch them from the same repo or project directory, they will use the same vault.
  • To pin the vault somewhere else explicitly, set APM_VAULT_DIR to an absolute or relative path.

Examples:

# default: use ./vault under the current directory
apm doctor

# explicit override: force a shared vault path
APM_VAULT_DIR=~/.apm/main-vault apm doctor
APM_VAULT_DIR=~/.apm/main-vault apm-mcp

Directory layout

  • vault/vault.enc.json — encrypted vault
  • vault/audit.log — append-only audit log with integrity hashes
  • vault/policy.json — local policy file for reveal/export/read rules
  • vault/sync-state.json — peer sync config and per-peer sync baselines
  • src/core.ts — shared vault, policy, and audit logic
  • src/apm.ts — CLI source
  • src/mcp-server.ts — stdio MCP server source
  • dist/apm.js — compiled CLI entrypoint
  • dist/mcp-server.js — compiled MCP entrypoint
  • spec.md — product spec
  • threat-model.md — security assumptions and risks

CLI usage

Doctor

npx @sethwebster/agent-password-manager doctor

Add an entry safely

Preferred order:

  1. --password-stdin
  2. interactive secret prompt on a TTY
  3. --password only if you knowingly accept shell history / process list exposure
printf 'super-secret\n' | npx @sethwebster/agent-password-manager add github-dougbot \
  --username '[email protected]' \
  --password-stdin \
  --url 'https://github.com' \
  --tag github --tag doug \
  --ownerType shared

Get an entry

Masked by default:

npx @sethwebster/agent-password-manager get github-dougbot

Reveal only with explicit purpose, and with approval when policy requires it:

npx @sethwebster/agent-password-manager get github-dougbot --reveal --purpose 'log into GitHub CLI'
npx @sethwebster/agent-password-manager get github-dougbot --reveal --approve --purpose 'break-glass production fix'

Edit an entry

printf 'rotated-secret\n' | npx @sethwebster/agent-password-manager edit github-dougbot \
  --password-stdin \
  --purpose 'rotate credential'

Search / list

npx @sethwebster/agent-password-manager list
npx @sethwebster/agent-password-manager list --tag github
npx @sethwebster/agent-password-manager search --query doug

Export / import

Plaintext export is intentionally annoying:

npx @sethwebster/agent-password-manager export \
  --output ./vault-export.json \
  --allow-plaintext-export \
  --approve \
  --purpose 'offline encrypted backup prep'

npx @sethwebster/agent-password-manager import --input ./vault-export.json --mode merge

Audit log

npx @sethwebster/agent-password-manager audit --limit 20
npx @sethwebster/agent-password-manager audit --verify

Secure peer sync

There are now two sync modes:

  1. Network start/connect for an actual live machine-to-machine exchange.
  2. Package push/pull for removable media / shared-folder workflows.

Both use the same conservative merge rules:

  • vault passphrases stay local
  • sync uses a separate shared secret/key
  • deletes replicate as tombstones
  • if both sides changed the same id since the last baseline, sync aborts instead of guessing
  • sync actions are written to the audit log

Live network sync

Intended UX:

# source machine
apm sync start --secret 'something'

# safer equivalents
printf 'something\n' | apm sync start --secret-stdin
APM_SYNC_SECRET='something' apm sync start

# client machine
apm sync connect http://source-host:8787 --secret 'something'

# safer equivalents
printf 'something\n' | apm sync connect http://source-host:8787 --secret-stdin
APM_SYNC_SECRET='something' apm sync connect http://source-host:8787

What sync connect does:

  • sends the client's current encrypted sync snapshot to the source server
  • the source applies only fast-forwardable changes from the client
  • the source returns its current encrypted snapshot
  • the client applies only fast-forwardable changes from the source
  • if either side detects a same-record/same-baseline divergence, the sync fails with the conflicting ids

The server is intentionally simple: an HTTP process you start when you want it, not a permanent secret daemon.

Legacy/local-first package sync

File-package sync still exists for conservative offline workflows:

# machine A: connect peer B to a package path
printf 'shared-sync-key\n' | npx @sethwebster/agent-password-manager sync connect laptop-b \
  --package ./laptop-b.apmsync \
  --sync-key-stdin

# write my current state for the peer to consume
printf 'shared-sync-key\n' | npx @sethwebster/agent-password-manager sync push laptop-b --sync-key-stdin

# read the peer package and apply only non-conflicting changes
printf 'shared-sync-key\n' | npx @sethwebster/agent-password-manager sync pull laptop-b --sync-key-stdin

Safety semantics:

  • vault passphrases are never transmitted by either sync mode
  • network sync authenticates with a shared secret and encrypts the exchanged sync body with that same secret-derived key material
  • package sync encrypts the sync package with the shared sync key
  • package contents and network payloads contain vault records/tombstones, not the master vault passphrase
  • conflict handling stays conservative on purpose; you reconcile locally, then retry

MCP tool surface

Minimal and conservative on purpose:

  • apm_doctor
  • apm_list
  • apm_search
  • apm_get
  • apm_add
  • apm_edit
  • apm_policy_show
  • apm_audit_tail

MCP safety model

  • apm_get returns masked passwords by default.
  • Reveals require reveal=true and purpose="...".
  • If policy says approval is required, also pass approve=true.
  • apm_add and apm_edit do not take inline plaintext passwords.
  • Secret writes must use one of:
    • passwordEnvVar: environment variable name containing the password
    • passwordFile: local file path containing the password
  • MCP calls reuse the same policy checks and audit log as the CLI.
  • MCP audit records are tagged with transport: "mcp".

Example MCP config

Example for a local MCP client that supports stdio servers:

{
  "mcpServers": {
    "agent-password-manager": {
      "command": "npx",
      "args": [
        "--package",
        "@sethwebster/agent-password-manager",
        "apm-mcp"
      ],
      "env": {
        "APM_MASTER_PASSPHRASE": "your-passphrase"
      }
    }
  }
}

If your client supports inherited environment instead of literal secrets in config, use that. Better than hardcoding the passphrase in JSON.

Example MCP tool calls

Masked read:

{
  "name": "apm_get",
  "arguments": {
    "id": "github-dougbot"
  }
}

Reveal with explicit purpose:

{
  "name": "apm_get",
  "arguments": {
    "id": "github-dougbot",
    "reveal": true,
    "purpose": "log into GitHub CLI"
  }
}

Add using an environment variable instead of inline plaintext:

{
  "name": "apm_add",
  "arguments": {
    "id": "github-dougbot",
    "username": "[email protected]",
    "ownerType": "shared",
    "tags": ["github", "doug"],
    "passwordEnvVar": "GITHUB_DOUGBOT_PASSWORD",
    "purpose": "initial vault import"
  }
}

Policy

Default local policy file:

{
  "version": 1,
  "defaults": {
    "requireApprovalForReveal": false,
    "requireApprovalForMaskedRead": false,
    "requireApprovalForExport": true,
    "denyReveal": false,
    "allowedOwnerTypes": ["agent", "operator", "shared", "service"]
  },
  "rules": [
    {
      "name": "prod secrets",
      "tag": "prod",
      "requireApprovalForReveal": true,
      "denyReveal": false,
      "allowedOwnerTypes": ["service", "operator"]
    }
  ]
}

Scripts

  • bun run build — compile TypeScript entrypoints to dist/ for Node execution
  • bun run build:clean — rebuild from scratch
  • bun run prepack — rebuild before publishing/packing
  • bun run pack:check — rebuild and inspect the npm tarball contents
  • bun test — run all Bun-native tests
  • bun run test — rebuild, then run all tests
  • bun run test:noninteractive — rebuild, then run CLI tests only
  • bun run test:mcp — rebuild, then run MCP tests only
  • bun run test:smoke — rebuild, then run the doctor command under Node
  • bun run mcp — rebuild and launch the MCP server under Node
  • bun run cli -- <args> — rebuild and run the CLI with arguments under Node

Testing

bun run test
bun run test:mcp
bun run test:noninteractive
bun run pack:check

Tradeoffs

  • This stays local-first and simple.
  • Approval is local friction, not a remote workflow system.
  • The MCP server is intentionally stdio-only and short-lived by default.
  • --password still exists in the CLI for compatibility, but MCP blocks inline plaintext secret writes on purpose.
  • Policy is a local guardrail, not a defense against a hostile machine owner.