AKM
Use macOS Keychain secrets in commands without copying their values into scripts, shell history, or agent conversations.
AKM is a small local CLI. It needs no account, server, background process, or separate vault. It uses your existing macOS Login Keychain and returns an error when access is unavailable, without opening Keychain dialogs.
Install
# Source build (requires a stable signing identity for seamless upgrades):
macOS only, with Apple Silicon and Intel CI coverage. The source requires Rust 1.85 or newer. CI runs on macOS 26 (Apple Silicon) and macOS 15 (Intel); this does not imply every older macOS release has been tested.
Optional instructions for Claude Code, Codex, and Gemini:
Updating the binary does not update an already installed skill automatically; run akm skill install after an upgrade.
Upgrade from 0.1 or 0.2
Homebrew and GitHub releases use a stable Developer ID signature. Older local builds used a signature tied to one executable; a replacement can lose access to its Keychain items. Keep a copy of the working binary before upgrading:
Migration copies values through private pipes into com.paperfoot.akm.v2,
verifies each write, skips existing destination entries, and preserves the old
com.paperfoot.akm entries. Values never appear in its output or temporary files.
Use --only NAME,NAME to select a subset. It stops on the first source failure or
five-second timeout and can be rerun. The older source executable may request
Keychain access; migration cannot suppress another process's dialogs.
New items take precedence; readable legacy entries remain a fallback. rm
removes both copies so an old value cannot reappear. After migration, write new
values with the new binary; old binaries still see the preserved original values.
Source builds need consistent code signing too; replacing an ad-hoc signed
binary can require authorizing the new executable in Keychain Access. Do not
remove the only binary that can read your keys before verifying its replacement.
Store and use a key
Supply new values through your subprocess API's stdin. For example, with a value already held by your application:
akm add NAME replaces an existing value. Names use [A-Z_][A-Z0-9_]*. AKM does not prompt for input; add/import with terminal stdin returns an error. Piped input to add has trailing CR/LF stripped.
Choose the transport the receiving command accepts:
| Need | Command |
|---|---|
| Environment variables | akm run --only OPENAI_API_KEY,ANTHROPIC_API_KEY -- npm test |
| Rename a stored key for one child | akm run --only API_KEY=PROJECT_API_KEY -- python script.py |
| Raw value on stdin | akm stdin TOKEN -- gh secret set TOKEN |
Shell-quoted NAME=value on stdin |
akm stdin TOKEN --format env -- flyctl secrets import |
| Find stored names | akm list --names-only |
| Discover one command | akm agent-info --command run |
run and stdin preserve child exit codes and redact exact supplied values from both child streams, including values split across reads. Normal progress output is forwarded as it arrives. For noninteractive jobs, SIGINT, SIGTERM, and SIGHUP received by AKM are forwarded to the child's process group.
Redaction pipes the child streams and matches exact bytes; transformed, encoded, or partial values are outside that match. --no-redact inherits the original output streams for tools that need a terminal or byte-for-byte output. --all explicitly selects every stored key.
Output contract
- Commands return JSON automatically when stdout is piped, or with
--json. Success uses the existing{"version":"1","status":"ok","data":...}envelope. - Errors go to stderr, include a code and recovery suggestion, and leave stdout available for data.
runandstdinforward child output without a completion message by default. Explicit--jsonadds a completion envelope to stderr, after any child stderr.--helpand--versionremain plain text.list --names-onlyexplicitly produces one name per line.- Exit codes:
0success,1runtime failure,3bad input,6missing key. Wrappers preserve the child's code (or128 + signal), so interpret it in the command's context.
get --raw means unmasked, not plain-text output: it still returns a JSON envelope when piped. Prefer run or stdin for ordinary credential use. get --raw and export intentionally expose values for requested retrieval or backup.
Other commands
| Command | Behavior |
|---|---|
akm get NAME |
Show a masked value. |
akm list --long |
Show names and last-write ages recorded in the audit log. |
akm import .env --dry-run |
Preview names and skipped lines without accessing Keychain. |
akm import .env |
Import literal dotenv values; never execute substitutions or delete the source. |
akm export --only NAME --format env |
Export raw values with shell quoting; AKM can import this format. |
akm rm NAME |
Remove a key; already absent succeeds. |
akm audit --limit 50 |
Read the latest complete audit records. |
akm guard install |
Install an optional staged-secret hook, preserving existing hooks and symlinks. |
akm guard uninstall |
Remove only an unchanged AKM-owned hook. |
akm guard scan |
Scan staged content for known prefixes followed by token-like text. |
Check an import's skipped entries and validate your replacement configuration before removing its source. Import is not transactional: a Keychain error can leave earlier entries stored. The optional guard is a heuristic, not a complete secret scanner; it does not flag bare prefix examples such as sk-....
macOS access and audit
Keep HOME set to your actual macOS account home when using Keychain. If AKM reports keychain_unavailable, check the existing Login Keychain in Keychain Access and unlock it if needed. AKM never resets a Keychain, changes its access controls, or disables system-wide security prompts. Dialog suppression is limited to the AKM process.
AKM keeps the file-based Keychain backend. Signed releases and the explicit migration above address access changes when replacing older executables. Apple's data-protection Keychain has different code-signing and entitlement requirements; switching backends would require a separate migration.
Successful operations and subprocess lifecycles are recorded in ~/.akm/audit.log with mode 0600, using names and metadata, never secret values or complete child arguments. Logs are best effort: failed lookups before execution and forced kills may have no terminal record. The log is writable by the same user and is not a tamper-proof access control.
AKM reduces accidental disclosure. A process running as your user can still retrieve keys, and child environment variables are accessible to same-user inspection. It does not provide isolation from an agent you have allowed to execute arbitrary commands as you.
Development
Integration tests create and remove uniquely named synthetic Keychain entries. Preserve the real HOME; redirecting it can hide macOS Keychain configuration. A dedicated regression verifies that unavailable Keychain access fails without a dialog.
See the September 2026 review for usage evidence, changes, and validation scope.
MIT. Built by Paperfoot.