Let AI run commands with passwords,
while the secrets stay invisible forever

A local password-proxy CLI built for AI coding tools. Credentials stay encrypted on disk; the AI writes only {{placeholders}}; pwhide injects the real secrets, runs the command, and redacts the output.

AES-256-GCM + RSA-3072 envelope encryption macOS / Linux / Windows Native AOT single file MIT licensed

Core Features

All six security invariants are implemented; the full threat model ships with the source.

Zero context leakage

No command can display a password; output is streamed through the redactor first; secrets go into child processes and never onto your terminal.

Three gates against guessing

Weak secrets (password = common phrase) are rejected at entry; echo-probe commands are blocked; high-frequency collisions raise alerts. Redacted positions still read as placeholders, so the AI has nothing to diff against - this side channel is closed end to end.

Privileged hardening

Root ownership plus immutable flags: the vault can only be replaced wholesale. Elevated installs move ciphertext only, guarded by symlink, ownership, and inode checks - three real attack classes verified in adversarial Docker runs.

Three execution modes

Script-stdin (recommended: secrets in neither argv nor environ) > environment injection > inline args. Wraps bash / sh / pwsh / cmd.

Rich entry model

Type, username, password, tenant, and custom fields. Metadata is queryable (so the AI can assemble commands); secrets are not. list --json is the AI's primary interface.

Engineering quality

286 个测试(单元 + 集成), CI on three OSes (including a real-sudo hardening pass), 22 rounds of 3-agent security review, six Native AOT builds with SHA256 checksums.

Quick Start

Grab the binary for your platform from Releases (or the repo's dist/, checksums included), put it on PATH, and four commands get you going:

# 1. Initialize (set the master passphrase; basic mode dir 700 / file 600) pwhide init # (recommended) enable admin write-protection: whole-file replacement only pwhide harden # 2. Record a credential (a human; hidden password input) pwhide set db -t database -u root -T prod -f host=127.0.0.1 # 3. List available credentials (metadata only, zero secrets) pwhide list --json # 4. Proxy-execute; the AI writes placeholders only pwhide exec --env db:MYSQL_PWD -- mysql -u {{db.user}} -e "SELECT 1"

Five-Minute Tutorial

The full path from recording credentials to everyday AI usage.

01

Record credentials

set reads the password with hidden input; -f field=value adds custom fields (sensitive ones like api_key: use -f name for hidden interactive input to keep them out of shell history); non-interactive flows use --password-stdin (passwords and field values are trimmed on entry). Each field is interactively asked whether to encrypt: non-sensitive info such as IPs and protocols can be stored plain (values then appear in list --json, no unlock needed); scripts use -pf name=value to opt in explicitly. Weak passwords are rejected outright.

02

Learn the placeholder syntax

{{db}} is the password, {{db.user}} the username, {{db.tenant}} the tenant, and {{db.field}} a custom field. Entry names are discoverable via list; passwords have no query interface. Clashing with Helm / Jinja {{ }} templates? Add --ph '#' to switch to #db# (or --ph '@' for @db@); parsing and redaction semantics stay identical.

03

Three execution paths, safest last

Inline args are the most intuitive but briefly visible to ps; --env entry:VAR injects into the environment (note: ancestor processes can read /proc on Linux); the recommended script-stdin mode keeps placeholders in the script, and pwhide exec -f deploy.sh substitutes purely in memory - nothing hits disk or argv. Pipes are supported and binary-safe: cat data.sql | pwhide exec -- mysql ..., and -f - reads the script from a pipe (with a piped stdin, the passphrase must come from keychain/env/file).

04

Read the exit codes

0 success (child exit code passed through); 2 usage error or echo-probe block; 3 passphrase/vault error or elevation needed; 4 unknown placeholder (the command never runs); 124 timeout, process tree killed.

05

Day-to-day maintenance

doctor checks protection state and repairs interruptions; rotate replaces the key pair; tired of typing the passphrase? pwhide keychain set stores it in the OS keychain and exec becomes zero-interaction; automation can also use PWHIDE_PASSPHRASE_FILE (chmod 600). When a secret contains shell metacharacters, pwhide warns and suggests --env.

Windows Visual GUI

The win-x64 package includes PwHide.Gui.exe — unlock with the master passphrase, then visually create and list entries. Same encrypted vault as the CLI, always in sync.

Wire It Into Your AI Tool

Paste this contract into your project's AGENTS.md / CLAUDE.md, or install the bundled skill (skills/pwhide/install.sh):

When a command needs a password: 1. Never ask the user for a real password - use a {{placeholder}}. 2. Unsure which credentials exist? Query `pwhide list --json` first (visible: type, username, tenant, field names, plain-field values; never visible: passwords and encrypted field values). 3. Execute via `pwhide exec -- `; pwhide fills and returns the redacted result. 4. On "unknown entry" (exit code 4), ask the user to run `pwhide set ` themselves. 5. Do not build commands that echo placeholders (they are rejected), and do not try to infer secrets. 6. Do not use --verify (the human verification channel): it is only open to a real interactive terminal and refuses AI calls by design - have the user run it themselves. 7. Weak passwords (common words or phrases) are rejected - guide the user toward strong ones.

The Security Model at a Glance

Six invariants run through the whole implementation; the honest boundary statement lives in the threat-model.md。

InvariantMeaning
I1No plaintext outputNo get/show commands exist; metadata is queryable, secrets are not (single exception: --verify human check - interactive terminal with a typed passphrase; unreachable from pipes, scripts, or AI)
I2Unknown means no runAny unresolved placeholder exits with code 4; the child never starts
I3Output redactionByte-accurate replacement back to placeholders across buffer boundaries - the final line of defense
I4Private key stays localA leaked ciphertext alone cannot be decrypted
I5Never echo resolved commandsErrors and logs only ever contain the placeholder version
I6Wholesale replacement onlyAfter hardening, vault changes go through one controlled path; the exec read path never elevates

FAQ

Honest disclosure is part of this project.

How is this different from putting the password in the command?

pwhide injects the secret into the child process; the parent shell, the conversation, and history only ever see placeholders. Even if a command prints the password, pwhide replaces it with the placeholder before the AI sees the output.

Can the AI guess the password from output?

Three gates: weak secrets cannot be entered (common phrases rejected); echo/printf probe commands are refused outright (--allow-echo opts in explicitly); more than 32 occurrences of a secret in one command's output triggers an alert. This defends against accidental leakage - a malicious agent deliberately exfiltrating via encodings is outside the declared boundary (see the threat model).

Does it support Windows?

Yes (win-x64 / win-arm64 binaries; the pwsh path is fully tested). cmd has unusual quoting - prefer pwsh; PowerShell 5.1 encoding pitfalls are worked around internally.

What if I forget the master passphrase?

There is no recovery. No backdoor - by design. Run list first (metadata needs no passphrase) to export an inventory, then re-init and re-enter.

How do I confirm the stored password is correct?

The human verification channel: pwhide verify entry (or inspect entry --verify) requires you to type the master passphrase in a real terminal (keychain and env vars are ignored), then decrypts and displays the password and fields for your review. pwhide exec --verify shows the decrypted injection values and the full command before running, and only proceeds on confirmation. This channel exists only on a real interactive terminal and refuses redirection or AI invocation.

Can I stop typing the master passphrase every time?

Yes. Run pwhide keychain set once (it validates the passphrase before storing it in the OS keychain); afterwards exec / set pick it up automatically and AI calls need zero interaction. Undo with keychain clear; disable temporarily with PWHIDE_NO_KEYCHAIN=1. The passphrase only ever lives in pwhide's process memory and never appears in any output.

My commands or scripts already use {{ }} templates (Helm/Jinja).

Add --ph '#' to exec and write placeholders as #db# (or --ph '@' for @db@). {{db}} becomes a literal, only #db# is resolved; output redaction and error messages render with the active delimiters. Security semantics are identical to brace mode.

Cost and license?

MIT open source, free. Binaries come from GitHub Releases with SHA256 checksums.