| name | agent-id-vault |
| description | Portable encrypted credential vault with LUKS-style slots (passkey/Touch ID, passphrase, agent-key, phone) and typed/domain-scoped credential records. Credentials can carry an ACCESS LEVEL (--access ro) so the agent can read a service but never write to it — enforced by the proxy and the sealed browser, widened only with the owner's out-of-band confirmation (set-access). Pairs with agent-id-proxy so the agent never sees credential values — the proxy injects them at request time. Can also GENERATE blockchain wallet keys (Solana ed25519, EVM secp256k1) inside the vault — the private key is sealed, only the address is printed, and transactions are signed by the proxy. Use whenever the user asks to save, fetch, or remove a service credential, create a crypto wallet for the agent, or whenever a downstream tool needs an external-service secret that must not appear in shell history, source files, or process arguments. |
| license | MIT |
| metadata | {"author":"Alien Wallet","version":"7.1.0"} |
| allowed-tools | Bash(node *agent-id-vault/bin/cli.mjs:*) Read |
Alien Agent ID — Vault
Portable single-file encrypted vault at ${AGENT_ID_STATE_DIR:-$HOME/.agent-id}/vault.enc.
The master key is held in slots, LUKS-style — passkey (Touch ID / Face ID /
security key), agent-key (auto-unlock), passphrase, mobile (phone),
owner-approval (Alien app).
Two modes (chosen at init, one-way):
- user mode (default) — NO passphrase, ever. Unlock by agent-key or
owner-approval/mobile (the Alien app). The agent cannot add a passphrase,
and a user-mode vault cannot be converted to dev mode (the mode is bound to
the master key and verified on every unlock).
- dev mode (
--dev, or providing a passphrase at init) — for developers /
power users: passphrase slots are allowed, plus all the user-mode methods.
Passphrase is the exceptional, opt-in path — never something the agent enables.
Pairs with the agent-id-proxy plugin — the proxy unlocks the vault and injects values into outbound HTTP requests. The agent itself does not retrieve plaintext during normal operation; show exists only for manual export.
Resolve the CLI
bin/cli.mjs lives in this plugin's directory. In the examples below, CLI is ${CLAUDE_PLUGIN_ROOT}/bin/cli.mjs — the ${CLAUDE_PLUGIN_ROOT} path is filled in for you when the skill loads.
Initialize the vault — pick how it unlocks
init --unlock <method> chooses the unlock. The passkey and passphrase
methods are hard boundaries — the agent does not hold them, so it can't
self-unlock; both default to no agent-key slot.
node CLI init --unlock passkey
node CLI init --unlock passphrase
node CLI init
--agent-key keeps an agent-key slot alongside a passkey/passphrase (convenience +
hard method). --no-agent-key removes it. Add more unlock methods later with
rekey add-passkey / add-passphrase (dev only) / add-mobile / add-owner-approval.
A user-mode vault never gains a passphrase and cannot be converted to dev mode.
Add a credential
Every credential needs a name, type, and domain allowlist (default-deny — the proxy refuses to inject for any host not on the list).
node CLI add --name github-pat --type bearer \
--domains '*.github.com,api.github.com' --value-file /tmp/tok
node CLI add --name openai --type header --header-name X-Api-Key \
--domains api.openai.com --value-file /tmp/key
node CLI add --name old-svc --type basic --domains svc.example.com \
--username admin --password-file /tmp/pw
node CLI add --name geocoder --type query --param-name api_key \
--domains api.example.com --value-env GEO_KEY
node CLI add --name session --type cookie --cookie-name sid \
--domains app.example.com --value-file /tmp/sid
node CLI add --name github-totp --type totp --domains '*.github.com' \
--secret-file /tmp/seed.b32
echo '{"sid":"abc","csrf":"xyz"}' | node CLI add --name gmail \
--type cookie-jar --domains mail.google.com
node CLI add --name deploy-key --type secret --form
node CLI add --name deploy-key --type secret --value-file ~/.ssh/id_ed25519
Login/password pairs use --type basic (--username + a password input). For
SSH/RSA keys and other key material use --type secret.
Value-input channels — pick the one with the smallest attack surface:
--form — out-of-band browser form (recommended when a human is present). You
supply --name/--type/--domains (and any metadata like --header-name); a
one-shot 127.0.0.1 form opens for the human to type the secret, which then goes
straight to the vault. It never enters your stdin, transcript, or process args.
Multi-field types (basic, oauth2, cookie-jar) collect all their secrets in
one form. The command prints the URL to stderr and waits up to 5 min.
--<field>-file <path> — read from a file
--<field>-env <VAR> — read from an environment variable
- stdin (piped) —
echo "secret" | node CLI add ...
--<field> <value> — visible in process list; avoid
node CLI add --name github-pat --type bearer --domains api.github.com --form
Never paste a secret into chat. The agent transcript persists.
Access levels — grant "read", not "everything"
Any credential can carry --access ro (default is rw, unrestricted). A
read-only credential can be used but not used to change anything:
- The proxy allows read-shaped requests only:
GET/HEAD/OPTIONS, plus
POST-tunneled reads it can classify (GraphQL query, JMAP */get/*/query,
JSON-RPC calls that don't submit transactions). Everything else → structured
403 access_denied.
- The sealed browser (agent-id-browser) enforces the same classification on
every request a
--access ro session makes, at the network layer.
- Plaintext egress is sealed:
show redacts and exec refuses a ro
credential's secret fields — otherwise the enforcement would be theater.
node CLI add --name fastmail --type bearer --access ro \
--domains api.fastmail.com --form
node CLI set-access --name fastmail \
--rules '[{"effect":"allow","methods":["POST"],"path":"/search"}]'
Changing the level later (set-access) is asymmetric by design:
node CLI set-access --name fastmail --access ro
node CLI set-access --name fastmail --access rw
Widening (ro→rw, adding an allow rule, dropping a deny rule) opens the
secure prompt and asks the owner to type the credential name — the agent
cannot approve it itself, and add/generate refuse to overwrite a record
with a wider level. When a human types the secret via --form, the form shows
the access level being granted.
Any credential the proxy restricts (level ro, or rw with rules) is also
sealed against show/exec — its plaintext can't be extracted to sidestep the
gate.
Where ro works — the operation must be legible in the request
Read/write can only be told apart when the request reveals the operation. It
fails safe: when it can't tell, it denies (never silently allows a write).
| API shape | Under ro |
|---|
| REST with real verbs (Gmail API, Stripe, GitHub REST) | reads pass, writes blocked — best case |
| GraphQL with the query text inline in the body | query passes, mutation/subscription blocked |
| JMAP (Fastmail) | */get,*/query pass; */set blocked |
| JSON-RPC with recognized read methods | reads pass; unknown methods blocked |
| GraphQL persisted queries (hash only, no query text — Reddit/X web) | over-blocks: reads AND writes denied |
| Opaque binary RPC (gRPC-web, Connect, Twirp) | over-blocks: denied |
Two things ro deliberately does not cover: a server that mutates on a
GET (per HTTP safe-method convention GET/HEAD/OPTIONS are reads —
constrain such an endpoint with an explicit deny rule), and raw IMAP/SMTP
(no HTTP method to gate — use an API or a web session).
For an over-blocked endpoint you have two levers: a scoped allow rule for a
read path (but if reads and writes share one URL, e.g. a single /graphql, that
also reopens writes there); or, for a token API, prefer a server-side
read-only scope at onboarding (e.g. Gmail gmail.readonly) — the only
enforcement even a client-side bug can't defeat.
The policy is enforced by the proxy / browser-session processes. With an
agent-key vault the agent could in principle open the vault directly, so for
a hard guarantee init the vault with --unlock passkey (no agent-key slot)
and let only the proxy hold it unlocked.
Generate a wallet keypair (the key never leaves the vault)
For blockchain wallets the vault can create the private key itself — the
key material is generated inside the vault process and sealed
(exportable: false): show redacts it, add refuses the type, and the only
way to exercise it is transaction signing inside the proxy. The command prints
only the public address.
node CLI generate --name sol-hot --type solana-keypair \
--domains api.mainnet-beta.solana.com
node CLI generate --name polygon-hot --type evm-keypair \
--domains polygon-bor-rpc.publicnode.com,ethereum-rpc.publicnode.com
Output (and list) carries the address (publicKey for Solana, address for
EVM) — that is all an agent ever needs: fund the address, build unsigned
transactions against it, and submit them through the proxy, which signs them
in-process (see the proxy skill, "Wallet credentials").
List, show, remove
node CLI list
node CLI show --name github-pat
node CLI remove --name github-pat
Run a command with credentials injected (env var or key file)
For tools that authenticate from environment variables or a key file
rather than HTTP — where the proxy can't help — exec materializes selected
credential fields and runs the command. The agent never sees the value; only the
variable names + their sources are logged (to stderr).
node CLI exec \
--env MODAL_TOKEN_ID=modal-token.username \
--env MODAL_TOKEN_SECRET=modal-token.password \
-- modal run gpu_job.py
node CLI exec --file GIT_SSH_KEY=deploy-key.value \
-- sh -c 'GIT_SSH_COMMAND="ssh -i $GIT_SSH_KEY -o IdentitiesOnly=yes" git fetch'
--file writes the field to a temporary 0600 file, sets VAR to its path,
runs the command, then shreds and removes the file on exit. The agent gets the
path, never the contents — ideal for the secret type (SSH/RSA keys, PEMs).
exec is a weaker boundary than the proxy — prefer the proxy when you can.
The child runs with inherited stdio, so if the command you run prints its
own environment, the secret comes straight back to your transcript. Treat the
value as exposed to the subprocess: only exec into a trusted tool that
authenticates from the env, never into something that echoes it (env,
printenv, sh -c 'echo $VAR'). The proxy, by contrast, never hands the
agent the value at all — reach for exec only for env-var-auth tools the
proxy genuinely can't reach.
--env / --file are repeatable and mixable. field is the record field to
read: basic → username/password; bearer/header/query/cookie →
value; totp → secret; oauth2 → refreshToken; secret → value.
(cred may contain dots — the split is on the last dot.)
- Everything after
-- is the command; it runs with inherited stdio, so
interactive commands keep their TTY (e.g. -- modal shell --gpu a10g).
- Sealed in-vault-generated keys (
solana-keypair/evm-keypair) refuse to leave
the vault — use the proxy to exercise those.
- Unlock +
--state-dir flags work as for any other subcommand.
Slot management, portability & migration
Less-common admin operations — adding/removing unlock slots (rekey), moving the vault between machines (export/import), and migrating a legacy v4 vault (migrate) — are documented in reference.md.
Common flag
--state-dir <path> — defaults to $AGENT_ID_STATE_DIR then ~/.agent-id.