Your agents get tokens. Your secrets stay behind a controlled boundary.
WispKey is a local-first, open-source credential firewall for AI agents. Agents receive opaque wk_* placeholders; WispKey resolves them to real credentials only at an explicit proxy or owner-run injection boundary. Configure credential host restrictions and policies to constrain where an untrusted agent may use a token.
Install with Cargo, or follow the Homebrew instructions on macOS and Linux:
cargo install wispkey --lockedYou can also build from source:
git clone https://github.com/rankupgames/wispkey.git
cd wispkey
cargo build --release
export PATH="$PWD/target/release:$PATH"Then create a vault and attach a selected secret:
# Create your vault
wispkey init
# Find and attach selected secrets from an existing .env
wispkey env list .
wispkey env attach .env --project my-app --key OPENAI_API_KEY --hosts api.openai.com
wispkey project use my-app
# Start the proxy
wispkey serveIn this proxy workflow, the agent supplies a token and WispKey substitutes the credential upstream within the configured host and policy boundary. This is not a universal output-redaction guarantee; see the security model. Signed GitHub Release archives, SHA-256 checksums, Sigstore signatures, and verification steps are in docs/install.md.
The attached .env stays in place: selected secret values become wk_* tokens while ordinary settings remain unchanged. Tokens belong to the local vault and are not portable team secrets. They are placeholders, not plaintext environment injection. Requests must use a WispKey substitution path; for HTTPS, send them through reverse proxy mode with X-Target-Url. Use wispkey run, exec, or inject for non-HTTP consumers. Because attachment cannot infer target hosts, --hosts is required when it creates credentials. Use a specific hostname or bounded glob such as *.example.com; empty or wildcard-only scopes such as * are rejected. Existing matching credentials must already have a meaningful host allowlist, and attachment never broadens their stored restrictions.
[AI Agent] --> "Authorization: Bearer wk_openai_prod_a7x9m2k4"
|
[WispKey Proxy @ localhost:7700]
|
Decrypts real key, swaps it in
|
[OpenAI API] <-- "Authorization: Bearer sk-real-key..."
- You store credentials in an encrypted local vault (AES-256-GCM, Argon2id key derivation)
- WispKey generates wisp tokens (
wk_*) for each credential - AI agents use wisp tokens in their requests
- The proxy swaps tokens it can inspect in HTTP requests or HTTPS reverse proxy requests, then forwards upstream
- Credential lookup surfaces expose metadata or tokens rather than stored plaintext; host restrictions and policies constrain token use. Explicit output exceptions include owner-run injection and newly generated leaf private keys from MCP certificate issuance, described below
- Encrypted local vault -- AES-256-GCM at rest, Argon2id master key derivation, SQLite backend, configurable session timeout (default 30 min), machine-bound encrypted session file by default, optional OS-backed or file remembered unlock
- Wisp token proxy -- HTTP forward proxy + blind HTTPS CONNECT tunneling + HTTPS reverse proxy mode (
X-Target-Urlheader) on localhost:7700 - CLI -- Credential lifecycle, project and partition management, encrypted bundle import/export, proxy serving, subprocess/template injection, instance administration, and audit export/tail
- MCP server -- Native integration with Cursor, Claude Code, Windsurf via stdio JSON-RPC, including first-class env-sideloaded credentials for locked-vault use
- Environment sideload -- MCP/proxy processes can receive
WISPKEY_SIDELOAD_<SLUG>values and expose only deterministicwk_env_<slug>tokens - Multi-instance access -- Enroll ephemeral VMs or worker instances with per-request identity, least-privilege credential scope, scoped bootstrap-token self-enrollment, cross-platform TCP, Unix, Linux AF_VSOCK, and Firecracker UDS-backed vsock listeners, plus host-approved access escalation
- Rotation-ready instance identities -- Schedule due-aware 48-character CSPRNG secret rotation with a bounded rollout grace window; the previous secret is retired as soon as the new secret first authenticates
- Native .env attachment -- Recursively discover
.env*files, import explicitly selected secrets into project/environment partitions, and atomically replace plaintext values withwk_*tokens in the same file - .env importer -- One-command migration with auto-detection of OpenAI, GitHub, Slack, AWS, and bearer token patterns
- Optional tray GUI -- Background desktop tray for owner credential entry over authenticated local IPC, including an atomic OVH API template; see
docs/tray.md
- Projects -- Top-level credential isolation by team or engagement (
project create,use,current,list,delete) - Partitions -- Logical credential grouping within projects, with encrypted
.wkbundleexport/import (partition create,list,delete,assign,export,import) - Environments --
env attachmaps.envto adefaultpartition and.env.<name>to a<name>partition; organization/account scope remains external to the local vault
- Policy engine -- TOML-defined rules with per-credential, per-host, per-path, per-method restrictions, deny rules, time windows, and sliding-window rate limiting; deterministic offline checks are documented in
docs/policy-testing.md - Audit log -- Every credential use and denial logged with timestamp, target host/path, method, and status; vault-backed events are queryable by credential and date range, bulk export supports JSONL/JSON for SIEM egress,
audit tail --followstreams without skipping same-timestamp events, and vault-less env sideload use writes a local fallback JSONL audit file - Host restrictions -- Glob-pattern allowlists per credential (e.g.
api.openai.comor*.amazonaws.com) - Cross-OS local file protection -- Vault directories, attached
.envfiles, generated.env.wispkeyfiles, and other sensitive outputs are owner-only on Linux/macOS and restricted with Windows ACLs where supported - Management API token checks -- The proxy compares management tokens in constant time
- Native shell leak guard -- the Cursor plugin calls
wispkey guard shellthroughPATHto checkbeforeShellExecutionpayloads for known plaintext secret patterns and unsafe secret-environment-variable use on every supported OS - Secret injection for subprocesses and templates --
wispkey exec,wispkey run, andwispkey injectare audited, owner-only plaintext-egress tools that resolve credentials in-process without placing plaintext in argv, parent env, WispKey stdout except explicitinject --stdout, or audit logs - Security model -- The current boundary and intentional limits are documented in
docs/security-model.md
- Session and status --
cloud loginverifies account/plan claims; local status andcloud status --remotedistinguish pending changes, conflicts, and acknowledged revisions - Encrypted partitions --
cloud push,pull, andsyncuse a separate bundle passphrase, conditional revisions, exact retry journals, and atomic imports. Explicitcloud resolvepreserves encrypted recovery copies. See setup and recovery; the compatible backend migration must be deployed first
WispKey stores arbitrary encrypted secret values, not only API keys from .env files. Use api_key as the generic opaque secret type for passwords, database URLs, SSH/private-key files, webhook secrets, OAuth tokens, service-account JSON, and anything else that should stay out of the agent process. The type mainly controls how the proxy injects or substitutes the value at request time.
For non-interactive adds, prefer --value-file <path> or --value-file - for stdin. --value still works, but WispKey warns on stderr because command-line arguments can be exposed through shell history and process listings.
| Type | CLI Flag | Injection |
|---|---|---|
| Bearer Token | --type bearer_token |
Authorization: Bearer <value> |
| API Key | --type api_key |
Header or body replacement |
| Basic Auth | --type basic_auth |
Authorization: Basic <base64> |
| Custom Header | --type custom_header --header-name X-Api-Key |
Named header |
| Query Param | --type query_param --param-name key |
URL query parameter |
| Website Login | wispkey login generate |
Encrypted username+password for a bound HTTPS origin. Not injected by the HTTP proxy; local browser fill preview requires human approval with Windows Hello or macOS Touch ID. |
Website logins are generated by WispKey so the password never appears in argv, stdout, MCP results, or audit logs:
wispkey project create "career-ops" --description "Career Ops credentials"
wispkey partition create "job-applications" --project career-ops
wispkey login generate employer-name \
--username user@example.com \
--url https://careers.example.com \
--project career-ops \
--partition job-applications \
--review-after 180dThe same generator works for any website login, not only job portals. Output is metadata only. Lifecycle is pending until you wispkey login activate, and wispkey login archive / restore never auto-delete. wispkey login list --due lists review-dated logins without deleting them.
The local browser handoff preview adds Chrome/Edge and Firefox extensions for a separate human-controlled profile on Windows or macOS with enrolled Touch ID. Agents request a one-time fill and poll metadata-only status; the person verifies with Windows Hello or Touch ID. The optional tray also offers Generate website login with a job application preset. Build/install the preview from source; it is not yet a browser-store release.
Examples:
printf '%s' "$DB_PASSWORD" | wispkey add "db-password" --type api_key --value-file - --tags "database"
printf '%s' "$DATABASE_URL" | wispkey add "db-url" --type api_key --value-file - --tags "database"
wispkey add "ssh-private-key" --type api_key --value-file ~/.ssh/id_ed25519 --partition "ssh-keys"
wispkey add "service-account-json" --type api_key --value-file ./service-account.json --tags "gcp"
printf '%s' "$BASIC_AUTH_VALUE" | wispkey add "basic-auth-api" --type basic_auth --value-file - --hosts "api.example.com"For non-HTTP consumers such as sudo, ssh, git, database CLIs, and local tools, wispkey exec resolves a vault credential in-process and injects it only into the child process:
# Stdin channel, useful for commands such as sudo -S.
wispkey exec --credential laptop-password --stdin -- sudo -S -p "" whoami
# Child-only environment variable.
wispkey exec --credential db-password --env DB_PASSWORD -- psql "$DATABASE_URL"
# Askpass helpers for sudo/ssh/git. Use sudo -A so sudo calls SUDO_ASKPASS.
wispkey exec --credential laptop-password --askpass -- sudo -A whoami
wispkey exec --credential git-token --askpass -- git fetchAt least one channel is required: --stdin, --env <VAR>, or --askpass. Channels can be combined. The credential is resolved within the active project, or within --project <name> when provided.
--stdin writes the secret followed by one newline and closes the child's stdin. Commands that also need interactive stdin should use --env or --askpass instead.
exec is a deliberate, owner-only plaintext-egress path for tools that cannot use the WispKey proxy. It is audited with CredentialExec events, but the audit row contains only the credential name, child program name, channel summary, project, and exit status. WispKey does not put the plaintext value in argv, the parent environment, WispKey stdout/stderr, tracing logs, or audit fields. The hidden askpass helper is not a standalone secret oracle: exec --askpass creates a per-exec owner-only handoff file and passes its path through WISPKEY_ASKPASS_HANDOFF; the helper refuses to run without a valid handoff from that child launch.
For tools that need multiple child-only environment variables, wispkey run reads a TOML manifest and resolves every cred:<name> reference before spawning the child:
# wispkey.toml
[env]
OPENAI_API_KEY = "cred:openai-key"
DATABASE_URL = "cred:db-url"
APP_ENV = "development"wispkey run -- npm test
wispkey run --manifest ./secrets/wispkey.toml --project client-alpha -- sh -c 'psql "$DATABASE_URL"'Manifest values without the cred: prefix are passed through as literal child environment values. run fails closed when the manifest is missing, invalid, has no [env] entries, or any referenced credential cannot be resolved. It writes a CredentialRun audit event with the credential names, child program name, project, and exit status.
For config files or templates, wispkey inject replaces {{ cred:<name> }} references and writes the rendered plaintext to an owner-only output file:
wispkey inject -i .env.template -o .env.local
wispkey inject -i config.template --stdout--stdout is an explicit plaintext disclosure to the caller. The safer default is -o <outfile>, which uses WispKey's owner-only file writer. inject writes a CredentialInject audit event with the credential names, output destination, and project.
The Cursor plugin's beforeShellExecution hooks invoke wispkey guard shell through PATH. The command reads the hook JSON from stdin and returns the existing {"permission":"allow"} or {"permission":"deny",...} contract. It does not open the vault or require a password. One native implementation performs both checks: known plaintext secret signatures and commands that print or directly export protected environment variables. Structurally valid lowercase wk_* tokens are treated as placeholders, but a command that also contains a plaintext secret is denied.
This is a bounded, best-effort pattern detector, not a shell parser or a general command sandbox. It cannot identify every secret, shell transformation, indirect disclosure, or process behavior. Invalid, empty, oversized, or unrecognized hook input fails closed. Keep wispkey available on the plugin process PATH.
Configure in Cursor, Claude Code, or any MCP-compatible tool. Keep the command as wispkey so the client uses the normal installed binary from PATH; do not hardcode a user-specific absolute path. Vault-backed credentials use the current WispKey session; run wispkey unlock before starting the client, or set WISPKEY_PASSWORD only for trusted automation.
wispkey doctor
wispkey integrate cursor --print
wispkey integrate codex --print
wispkey integrate claude-code --print
wispkey integrate generic-mcp --printDoctor reports binary.path when the first executable or shell shim on the current PATH differs from the running CLI. It inspects paths without running the other installation; shell aliases and another client's environment are outside the check. proxy.version compares the CLI package version with an authenticated response from the owned proxy, including when the vault is locked. Older proxies without version diagnostics report an unknown version. These checks never edit PATH, install software, unlock the vault, or restart a service. Different builds with the same package version are not distinguished.
integrate writes the matching client config by default and is idempotent: it updates only the WispKey MCP entry and leaves unrelated servers and settings in place. --print shows the snippet without writing. JSON clients warn that env blocks are plaintext; Codex uses env_vars instead of storing secret values.
{
"mcpServers": {
"wispkey": {
"command": "wispkey",
"args": ["mcp", "serve"]
}
}
}For env-sideloaded MCP credentials, pass WISPKEY_SIDELOAD_<SLUG> to the WispKey MCP process instead of passing the vault master password. WispKey lists the env key and returns a wk_env_<slug> token; it never returns the env value. In Codex, use env_vars so Codex forwards the variable from its own environment instead of storing the secret in config:
[mcp_servers.wispkey]
command = "wispkey"
args = ["mcp", "serve"]
env_vars = ["WISPKEY_SIDELOAD_OPENAI"]Start the WispKey proxy with the same WISPKEY_SIDELOAD_<SLUG> env var if you want the proxy to substitute the wk_env_<slug> token in outbound requests. Env sideloads are limited to the trusted local workflow; identity-authenticated instances cannot use them because sideloads have no persisted credential ID that can be enrolled or approved.
For JSON-style MCP configs that do not support env_vars, set the sideload variable in the client process environment or in the MCP server's env block:
{
"mcpServers": {
"wispkey": {
"command": "wispkey",
"args": ["mcp", "serve"],
"env": { "WISPKEY_SIDELOAD_OPENAI": "..." }
}
}
}Treat MCP env blocks as plaintext client config. Prefer process environment forwarding or an OS credential manager when available.
Available local stdio MCP tools (11):
wispkey_list-- List credentials (filter by tag, project)wispkey_get_token-- Get wisp token for a credentialwispkey_proxy_status-- Check vault/session/proxy statewispkey_project_list-- List all projects with partition countswispkey_set-- Create or update a credential (requiresoverwrite: trueto replace)wispkey_signup_profile_list-- List profile IDs, revisions and labels only; explicit project/partition scope requiredwispkey_generate_login-- Generate and store an encrypted website login; return non-secret metadata, never the passwordwispkey_request_browser_fill-- Request a five-minute one-use fill in a separate human-controlled browser profile; requires owner approval with Windows Hello or macOS Touch IDwispkey_browser_fill_status-- Read metadata-only fill status; completed means filled, not submittedwispkey_delete-- Delete a credential by namewispkey_issue_cert-- Issue a leaf certificate from a vault-held CA key. Generating a leaf keypair returns its new private key; signing a supplied CSR returns no private key. The CA private key is never returned
MCP is not a universal “no secrets in results” boundary: certificate issuance
intentionally returns a generated leaf private key. Owner exec, run and
inject are separate plaintext-egress paths. The fixed-operation executor has a
separate allowlisted result contract; see operation runtime.
WispKey supports HTTPS in two ways:
CONNECT tunneling (standard forward proxy) -- the client sets HTTPS_PROXY=http://localhost:7700 and the proxy tunnels the TLS connection. CONNECT is a blind tunnel: the proxy cannot inspect or rewrite headers, bodies, or query strings inside the TLS stream. Use CONNECT only when the request does not need wisp token substitution.
Reverse proxy mode -- use X-Target-Url for explicit HTTPS targeting:
curl http://localhost:7700 \
-H "X-Target-Url: https://api.openai.com/v1/chat/completions" \
-H "Authorization: Bearer wk_openai_prod_a7x9m2k4" \
-d '{"model": "gpt-4", "messages": [...]}'Reverse proxy mode substitutes wisp tokens in headers, supported text bodies, and the X-Target-Url query string before forwarding upstream. It forwards upstream response headers and bodies; it is not a hidden browser-session broker or universal output-redaction boundary.
WispKey can serve untrusted ephemeral VMs and worker instances without giving them plaintext secrets. The host enrolls each instance, gives it a one-time id and secret plus wk_* tokens, and runs the proxy on one or more listeners:
wispkey instance enroll worker-acme-001 --tag company:acme --credential openai-key
wispkey serve --listen tcp://127.0.0.1:7700 --listen unix:/run/wispkey/proxy.sockFor fleets, the host can mint a scoped bootstrap token and let each VM self-enroll for its own instance id and secret. Successful redemptions are atomic, so TTL and max-use limits are enforced under concurrent joins:
wispkey instance bootstrap create --tag company:acme --ttl 1h --uses 50
printf '%s' "$BOOTSTRAP_TOKEN" | wispkey instance join --token-file - --name worker-acme-001Remote first-contact self-enrollment can use POST /api/instances/join; that endpoint is authenticated by the bootstrap token and does not require a management token or existing instance identity.
Unix domain socket, Linux vsock, Firecracker vsock, and non-loopback TCP listeners require instance identity by default. Loopback TCP keeps the original trusted-local behavior unless --require-identity is set. For Windows or another server, keep WispKey on loopback and reach it through an SSH tunnel, or use an identity-required host-only TCP network. Credential selectors and approvals bind to the resolved credential ID rather than its project-local display name. Out-of-scope vault-token use returns 403 out_of_scope, queues an access request, and can be approved by the host; env-sideload tokens are always out of scope for authenticated instances:
wispkey instance requests --pending
wispkey instance approve req_...Instance secrets can be rotated safely from cron, systemd timers, CI, or Windows Task Scheduler. The command emits a new one-time secret only when rotation is due:
wispkey --format json instance rotate-secret worker-acme-001 \
--if-older-than 30d \
--grace 15mDeliver the JSON result through a protected deployment channel and do not log its stdout. During the grace window both secrets work; the first successful request with the new secret retires the previous secret immediately.
See docs/multi-instance-deployment.md for the deployment model, listener options, and a Firecracker microVM example.
WispKey is not a traditional secrets manager. Traditional vaults are built to deliver plaintext secrets to trusted applications; WispKey is built for agents that should never hold plaintext secrets at all.
- Versus agent credential proxies -- WispKey is a local Rust binary with a policy engine, audit trail, explicit HTTP token substitution, audited non-HTTP injection, first-class MCP tooling, and env sideload support for locked-vault workflows.
- Versus enterprise access platforms -- WispKey does not require a cloud account, sales motion, or hosted control plane for local use. Secrets can stay on the user's machine.
- Versus plaintext
.envfiles -- WispKey can attach selected HTTP-facing secrets in place as tokens while preserving ordinary settings. Non-HTTP secrets stay behind explicitrun,exec, orinjectboundaries.
Security claims are intentionally scoped: CONNECT is a blind tunnel, loopback TCP keeps the trusted-local default, authenticated instance listeners are scoped and fail closed, text-body substitution is limited to text-like content types, and the machine-bound session store does not defend against a same-user process that can read all local WispKey files or inspect memory. See docs/security-model.md for the public security model.
Define credential access rules in ~/.wispkey/policies.toml:
[[policy]]
name = "restrict-production"
credential = "aws-prod"
allowed_methods = ["GET"]
denied_paths = ["/admin/**", "/delete/**"]
allowed_hosts = ["api.aws.com"]
rate_limit = "10/minute"
time_window = "09:00-17:00" # local machine timeManage policies via CLI:
wispkey policy init # Create starter policies.toml
wispkey policy list # Show loaded policies
wispkey policy check # Validate policy fileAgent-scoped policies fail closed when the requester agent identity is unavailable. The proxy does not currently have a trusted agent identity source, so a policy with an agent = "..." scope still applies to proxy requests.
Credentials are isolated by project. Each project contains partitions, which contain credentials.
Each project gets its own personal partition, so partition names are project-scoped.
Credential names are unique within a project, not across the whole vault. The same credential name can exist in different projects. CLI name lookups such as get, remove, and rotate resolve against the active project; API lookups can use an explicit ?project= scope. Existing vaults migrate to schema v11 automatically.
For .env attachment, the WispKey project represents the source project and the filename maps to an environment partition: .env becomes default, while .env.production becomes production. Organization/account scope remains external to the local vault.
env attach cannot infer the intended upstream host. New attached credentials require --hosts with a meaningful hostname or bounded glob. A matching environment-prefixed credential may be reused only when it already has a meaningful host allowlist; env attach preserves that stored allowlist and never widens it. Wildcard-only scopes such as * are rejected.
wispkey project create "client-alpha" --description "Client Alpha credentials"
wispkey project use "client-alpha"
wispkey project current
wispkey project list
wispkey project export "client-alpha" --output client-alpha.wkbundle
wispkey project import client-alpha.wkbundle
wispkey list --all-projects
wispkey serve --all-projectsOverride per-terminal with export WISPKEY_PROJECT=client-alpha.
The proxy management API also honors project scope for GET /api/credentials, GET /api/credentials/{name}, DELETE /api/credentials/{name}, GET /api/partitions, and DELETE /api/partitions/{name} by passing ?project=<name>.
| Command | Purpose |
|---|---|
wispkey init |
Create vault and master password |
wispkey unlock [--timeout N] [--remember] [--protector-timeout N] [--password-file PATH|-] |
Unlock vault for the current session; --remember stores a password-free re-unlock protector |
wispkey lock [--forget] |
Revoke the current session; --forget also deletes the remembered protector |
wispkey tray [--ipc-only] |
Start authenticated owner IPC; optionally spawn the tray GUI |
| `wispkey add [--type TYPE] [--value-file PATH | -] [--hosts H] [--tags T] [--partition P] [--project P]` |
wispkey list [--partition P] [--project P] [--all-projects] |
List credentials |
wispkey get <name> [--show-token] |
Show credential metadata and wisp token |
wispkey remove <name> |
Delete a credential |
wispkey rotate <name> |
Regenerate a wisp token |
wispkey exec --credential <name> [--project P] [--stdin] [--env VAR]... [--askpass] -- <command> [args...] |
Inject a credential into a child process through audited stdin, child-only env, or askpass channels |
wispkey run [--manifest PATH] [--project P] -- <command> [args...] |
Run a child process with manifest-defined child-only environment variables |
| `wispkey inject -i <infile | -> [-o ] [--project P] [--stdout]` |
| `wispkey serve [--port 7700] [--random-port] [--listen SPEC]... [--require-identity | --no-require-identity] [--all-projects] [--daemon]` |
wispkey proxy status/stop/cleanup |
Inspect, stop, or clean up local proxy lifecycle state |
wispkey env list [directory] |
Recursively list attachable .env* files without reading their contents; use --format json for automation |
wispkey env attach <path> --project P --key NAME... [--environment E] [--hosts H] |
Import selected secrets into a project/environment with safe host scopes and replace their values with WispKey tokens in the same file |
wispkey import <path> [--prefix P] [--partition P] [--project P] |
Legacy whole-file import that writes a sibling .env.wispkey file |
wispkey login generate <name> --username U --url HTTPS [--project P] [--partition P] [--review-after 180d] |
Generate and store a unique website login without printing the password |
wispkey login list [--due] [--project P] [--all-projects] |
List website-login metadata |
wispkey login archive/restore/activate <name> |
Lifecycle changes; archive never deletes |
wispkey status |
Show vault, session, and proxy status |
wispkey doctor |
Run secret-safe diagnostics (version, permissions, session, proxy, policy, audit, MCP, substitution) |
wispkey operation identity/check/authorize/execute/status/cancel/reconcile/audit |
Owner-approved one-use SSH, Kubernetes Secret and PostgreSQL operations; runtime guide |
wispkey integrate <client> [--print] [--path FILE] |
Generate or write MCP client config (cursor, codex, claude-code, generic-mcp) |
wispkey log [--last N] [--credential C] [--since DATE] |
Query audit events |
| `wispkey audit export [--since TS] [--until TS] [--credential C] [--encoding jsonl | json] [-o FILE]` |
wispkey audit tail [--follow] [--credential C] |
Stream newest audit events as JSONL; --follow uses a forward (timestamp,id) cursor |
wispkey policy list/init/check |
List, initialize, or validate local access policies |
wispkey partition create/list/delete/assign/export/import |
Manage partitions |
wispkey project create/list/delete/use/current/export/import |
Manage projects and encrypted project bundles |
wispkey credential export/import |
Export or import one encrypted credential bundle |
wispkey backup create/inspect/verify/restore |
Encrypted full-vault backup, inspection, verification, and atomic restore |
wispkey instance enroll <name> [--description D] [--partition P]... [--project P]... [--credential C]... [--tag T]... |
Enroll a host-managed instance identity |
wispkey instance list/show/scope/revoke/requests/approve/deny |
List, inspect, scope, revoke, and approve or deny instance access requests |
wispkey instance rotate-secret <name> [--if-older-than 30d] [--grace 10m] |
Schedule-safe instance-secret rotation with bounded overlap |
wispkey instance bootstrap create/list/revoke |
Manage scoped, atomic bootstrap tokens for fleet self-enrollment |
| `wispkey instance join [] [--token-file <path | ->] --name ` |
wispkey cloud status/login/logout |
Manage local Cloud session groundwork |
wispkey cloud push/pull/sync |
Conditional encrypted partition sync; requires a bundle passphrase |
wispkey cloud resolve/recover |
Explicit conflict choice and encrypted local recovery |
wispkey mcp serve |
Start the MCP server over stdio |
The optional wispkey-tray binary is a desktop extension of the CLI. Start owner IPC with wispkey tray (or wispkey tray --ipc-only for tests), then add single credentials or the OVH API template from a native dialog. Secrets stay off argv and never pass through an unauthenticated localhost form. Closing a dialog leaves the tray running. See docs/tray.md.
Export and import encrypted credential bundles for sharing or backup:
wispkey partition create "staging" --description "Staging API keys"
wispkey partition assign "my-credential" --to "staging"
wispkey partition export "staging" --output staging.wkbundle
wispkey partition import staging.wkbundleExports are encrypted with a separate bundle passphrase, not the vault master password. The bundle file contains real secrets after decryption, so share the file and passphrase through different channels. New exports require a 12+ character bundle passphrase.
For non-interactive bundle operations, use WISPKEY_BUNDLE_PASSPHRASE or a protected passphrase file:
export WISPKEY_BUNDLE_PASSPHRASE='a-long-export-passphrase'
wispkey project export "client-alpha" --output client-alpha.wkbundle
wispkey project import client-alpha.wkbundle \
--bundle-passphrase-file ~/.wispkey/client-alpha.bundle-passphraseExport and import one encrypted credential for narrow sharing:
wispkey credential export "openai-key" --output openai-key.wkcred
wispkey credential import openai-key.wkcred --project client-alpha --partition personalCreate an encrypted full-vault backup for disaster recovery. This is separate from project, partition, and credential sharing bundles:
wispkey backup create --output vault.wkbackup
wispkey backup inspect vault.wkbackup
wispkey backup verify vault.wkbackup
wispkey backup restore vault.wkbackup --dry-run
wispkey backup restore vault.wkbackup --target /tmp/wispkey-restore-testThe backup passphrase is separate from the vault master password (WISPKEY_BUNDLE_PASSPHRASE or --bundle-passphrase-file). Credential blobs stay encrypted with the original master key. After restore, unlock with that master password. Session files, instance bearer secrets, and bootstrap token secrets are not restored; see docs/vault-backup.md for the format, scope, and recovery limits.
Do not pass the master password as a CLI argument. Prefer a remembered protector or an owner-only password file:
wispkey unlock --remember --password-file ~/.wispkey/master.pass
wispkey lock # revoke the 30-minute session
wispkey unlock # remint a session from the protector, no password prompt
wispkey lock --forget # drop the protector tooWISPKEY_PASSWORD still skips interactive prompts for trusted local automation:
export WISPKEY_PASSWORD='your-master-password'
wispkey init
wispkey unlock
printf '%s' "$SECRET_VALUE" | wispkey add "key" --type api_key --value-file -For non-interactive secret input, prefer wispkey add "key" --type api_key --value-file ./secret.txt or pipe the value to --value-file -. Passing secrets with --value emits a warning because the value can be captured by shell history or process listings.
WISPKEY_PASSWORD only unlocks or initializes the vault. It is intentionally not used for encrypted bundle export/import or vault backup; use WISPKEY_BUNDLE_PASSPHRASE or --bundle-passphrase-file for those commands.
wispkey mcp serve does not require WISPKEY_PASSWORD when you only need env-sideloaded credentials. Set WISPKEY_SIDELOAD_<SLUG> in the MCP server environment and ask for credential name <slug> (case and separators are normalized).
Older WISPKEY_FALLBACK_<SLUG> names are not supported. Rename those variables to WISPKEY_SIDELOAD_<SLUG> before upgrading.
src/
core/ # Vault engine (encrypt/decrypt, CRUD, wisp tokens, projects, partitions)
proxy/ # HTTP/HTTPS proxy (tokio + hyper, credential injection, policy eval, env sideload)
mcp/ # MCP server (stdio JSON-RPC transport)
cli/ # CLI interface (clap subcommands)
audit/ # Audit logging (SQLite, credential + time filtering)
migrate/ # .env discovery, attachment, and legacy import workflows
partition/ # Encrypted bundle export/import (.wkbundle)
secure_files.rs # Cross-platform owner-only local file protection
sharing/ # Project and single-credential encrypted share bundles
cloud/ # Cloud sessions, conditional encrypted sync and recovery
policy/ # Policy engine (TOML rules, rate limiting, time windows)
tests/ # Command-focused integration tests
plugin/ # Cursor plugin (rules, skills, hooks, agents)
- Current stable Rust via rustup
- SQLite is bundled via
rusqlite-- no system install needed
git clone https://github.com/rankupgames/wispkey.git
cd wispkey
cargo build # Debug build
cargo build --release # Optimized release build
cargo test --all-features # Run the full test suite
cargo clippy --all-targets --all-features -- -D warnings -W clippy::suspicious -W clippy::style -W clippy::perf -W clippy::complexity
cargo fmt --all -- --check # Format check
cargo audit # Dependency advisory check (install with: cargo install cargo-audit --locked)rustup target add x86_64-unknown-linux-gnu
rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-pc-windows-msvc
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
cargo build --release --target aarch64-apple-darwin| Component | Crate | Purpose |
|---|---|---|
| Async runtime | tokio |
Concurrent proxy connections |
| HTTP proxy | hyper + hyper-rustls |
HTTP/HTTPS request interception and CONNECT tunneling |
| Encryption | ring + argon2 |
AES-256-GCM vault, Argon2id key derivation |
| Database | rusqlite (bundled) |
Zero-config credential store + audit log |
| CLI | clap |
Subcommand parsing |
| Serialization | serde + serde_json + toml |
Config, policy, and MCP protocol |
| HTTP client | reqwest |
Cloud authentication and encrypted partition sync (rustls-tls) |
| Logging | tracing |
Structured logging with env filter |
| Patterns | glob-match + regex |
Host restriction globs, wisp token scanning |
| Browser | open |
Clerk login flow (opens default browser) |
Local vault, proxy, MCP, injection, and encrypted backup/sharing workflows work offline without an account. Optional account-owned partition sync is implemented in the CLI; it requires a compatible, configured backend. See encrypted sync and recovery and Cloud sign-in requirements for setup, compatibility and preview limits. Cloud sign-in does not unlock the local vault or authorize credential execution.
See CONTRIBUTING.md for development workflow and guidelines.
Apache-2.0 -- see LICENSE for details.
Opt existing credentials into explicit provider expiry, local use deadlines and local revocation with wispkey auth register. Use auth list for metadata-only inventory and auth bundle for explicitly chosen account/project-scoped alternatives. See the auth registry guide for safe registration, atomic encrypted recovery and the boundaries of this foundation. OAuth refresh, browser-session reuse and cloud credential execution are not included.