Agent ID CLI Reference
Alien Agent ID v3 ships as four plugins, each with its own CLI binary. There is no
single aggregate cli.mjs; every command belongs to exactly one plugin:
| Plugin | Binary | Responsibility |
|---|---|---|
agent-id-core | plugins/agent-id-core/bin/cli.mjs | Bootstrap, lifecycle, and universal operations (sign / verify / export) |
agent-id-git | plugins/agent-id-git/bin/cli.mjs | SSH-signed commits with provenance trailers + v3 bundle |
agent-id-vault | plugins/agent-id-vault/bin/cli.mjs | Encrypted credential storage |
agent-id-auth | plugins/agent-id-auth/bin/cli.mjs | DPoP-signed (RFC 9449) calls to Alien-aware services |
Invocation
Run a command by passing the subcommand as the first argument to its plugin binary:
node plugins/agent-id-<plugin>/bin/cli.mjs <command> [flags]For example:
node plugins/agent-id-core/bin/cli.mjs bootstrap
node plugins/agent-id-git/bin/cli.mjs setup
node plugins/agent-id-vault/bin/cli.mjs store --service github
node plugins/agent-id-auth/bin/cli.mjs header --url https://service.example.com/api/whoamiEvery binary prints usage when run with no command, help, --help, or -h. Output
follows a strict convention: structured JSON goes to stdout (so it can be piped to
jq), and human-readable progress goes to stderr.
Common flags
Only one flag is genuinely common to every command across every plugin:
| Flag | Type | Default | Description |
|---|---|---|---|
--state-dir <path> | string | $AGENT_ID_STATE_DIR, else ~/.agent-id | State directory holding keys, owner session, audit trail, and vault. |
Other flags such as --provider-address, --sso-url, and --oidc-origin are scoped to
specific commands and documented inline below.
State directory resolution
The state directory is resolved in this order:
--state-dir <path>flagAGENT_ID_STATE_DIRenvironment variable~/.agent-id(default)
A resolved path is always absolutized. Directories are created with mode 0700; private
files (keys, owner session, vault records) are written with mode 0600.
agent-id-core — bootstrap, lifecycle, and universal operations
The core plugin establishes and maintains the agent identity. The global alien-agent-id
bin points only at this plugin.
bootstrap
One-shot setup: runs init, auth, and bind in sequence — generates the keypair, starts
OIDC authorization, surfaces a deep link, then blocks while waiting for Alien App approval.
Git signing is not part of bootstrap; run agent-id-git setup separately afterward.
node plugins/agent-id-core/bin/cli.mjs bootstrap [--provider-address <addr>]Blocking: Yes — up to ~5 minutes while polling for approval (the bind step’s
--timeout-sec, default 300).
| Flag | Type | Default | Description |
|---|---|---|---|
--provider-address <addr> | string | resolved (see below) | Alien provider address. |
The provider address is resolved from --provider-address, then ALIEN_PROVIDER_ADDRESS,
then a default-provider.txt file next to the CLI.
Output (fresh bootstrap):
{
"ok": true,
"providerAddress": "...",
"stateDir": "/Users/you/.agent-id"
}Known source gap (v4.0.0): bootstrap tries to surface fingerprint, ownerSessionSub,
and bindingId, but the underlying bind step returns ownerSub and jkt (not those
keys), so the three fields resolve to undefined and are omitted from the JSON. Read the
agent thumbprint and owner subject off bind (or status) instead.
If an identity is already bootstrapped, it short-circuits and returns:
{
"ok": true,
"alreadyBootstrapped": true,
"jkt": "...",
"ownerSub": "...",
"providerAddress": "...",
"stateDir": "/Users/you/.agent-id"
}init
Generate the Ed25519 agent keypair without starting the auth flow. Idempotent — re-running loads the existing key.
node plugins/agent-id-core/bin/cli.mjs initOutput:
{
"ok": true,
"fingerprint": "...",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...",
"stateDir": "/Users/you/.agent-id"
}auth
Start OIDC authorization and emit a deep link + QR code for human approval. Auto-runs init
first if no keypair exists, and advertises the agent key’s RFC 7638 thumbprint as dpop_jkt
on the authorize URL. Persists pending auth state for the subsequent bind.
node plugins/agent-id-core/bin/cli.mjs auth --provider-address <addr>| Flag | Type | Default | Description |
|---|---|---|---|
--provider-address <addr> | string | — (required) | Alien provider address. |
--sso-url <url> | string | https://sso.alien-api.com | SSO base URL. |
--oidc-origin <origin> | string | http://localhost | OIDC redirect origin. Falls back to http://localhost if the origin is rejected. |
Output:
{
"ok": true,
"deepLink": "...",
"qrCode": "<ascii QR>",
"pollingCode": "...",
"expiredAt": 1234567890,
"message": "Ask the user to open the deep link or scan the QR code with Alien App"
}bind
Poll for human approval, exchange the authorization code for tokens (with a DPoP proof),
verify the id_token (including the cnf.jkt binding per RFC 9449 §6.1 and the OIDC nonce),
and persist the owner session. Requires a prior auth to have written pending auth state.
node plugins/agent-id-core/bin/cli.mjs bind [--timeout-sec 300] [--poll-interval-ms 3000]Blocking: Yes — up to --timeout-sec.
| Flag | Type | Default | Description |
|---|---|---|---|
--timeout-sec <n> | number | 300 | Max seconds to poll for approval. |
--poll-interval-ms <n> | number | 3000 | Polling interval in milliseconds. |
Output:
{
"ok": true,
"ownerSub": "...",
"issuer": "...",
"providerAddress": "...",
"jkt": "..."
}setup-owner-session
Force a fresh OAuth flow that produces a DPoP-bound, cnf-carrying id_token. Used to
rebind pre-cutover agents whose existing owner session carries a cnf-less id_token
that the v3 verifier rejects. The keypair is preserved — only the owner session and pending
auth state are cleared so bind can rewrite them. Any stale pre-v3 owner-binding.json is
also removed.
node plugins/agent-id-core/bin/cli.mjs setup-owner-session [--provider-address <addr>] [--verbose]| Flag | Type | Default | Description |
|---|---|---|---|
--provider-address <addr> | string | resolved | Alien provider address (resolved like bootstrap). |
--verbose | boolean | false | Print extra progress (provider address, fingerprint, polling code) to stderr. |
Output:
{
"ok": true,
"rebound": true,
"providerAddress": "...",
"stateDir": "/Users/you/.agent-id"
}Same source gap as bootstrap: the documented fingerprint, ownerSessionSub, and
bindingId fields are read off the bind result, which does not return them, so they are
omitted from the emitted JSON.
refresh
Refresh the SSO access_token and id_token (and refresh_token if the authorization
server rotates it) without human interaction. The refreshed id_token is re-verified
(cnf.jkt + sub) before being persisted.
node plugins/agent-id-core/bin/cli.mjs refreshOutput:
{
"ok": true,
"refreshedAt": 1234567890,
"ownerSessionSub": "...",
"providerAddress": "..."
}If the authorization has been revoked, the command fails with ok: false and an error
directing you to run bootstrap to re-authenticate.
status
Show whether an identity exists and is bound to a human owner.
node plugins/agent-id-core/bin/cli.mjs statusOutput (no identity yet):
{
"ok": true,
"initialized": false,
"bound": false,
"stateDir": "/Users/you/.agent-id"
}Output (initialized and bound):
{
"ok": true,
"initialized": true,
"bound": true,
"dpopBound": true,
"jkt": "...",
"ownerSub": "...",
"providerAddress": "...",
"issuer": "...",
"nextSeq": 7,
"nonceAgents": 1,
"stateDir": "/Users/you/.agent-id"
}dpopBound is true only when the owner session’s id_token carries
cnf.jkt = jwkThumbprint(agent_jwk).
sign
Sign an arbitrary operation into the local hash-chained audit trail.
node plugins/agent-id-core/bin/cli.mjs sign \
--type TOOL_CALL --action "bash.exec" --payload '{"command":"deploy"}'| Flag | Type | Default | Description |
|---|---|---|---|
--type <type> | string | — (required) | Operation type (e.g. TOOL_CALL, API_CALL). |
--action <action> | string | — (required) | Action identifier. |
--payload <json> | string | — (required) | JSON-encoded payload to sign. |
--agent-id <id> | string | main | Agent identity context for the entry. |
--meta <json> | string | — | Optional JSON metadata attached to the entry. |
Output:
{
"ok": true,
"operationId": "...",
"seq": 7,
"nonce": 3,
"agentId": "main",
"signatureShort": "...",
"envelopeHashShort": "..."
}verify
Run a chain-integrity check over the local audit trail.
node plugins/agent-id-core/bin/cli.mjs verifyEmits the verification result as JSON. On failure (ok: false), the process exits with
code 1.
export-proof
Emit the owner session and the complete audit trail to stdout as plain (indented) JSON.
node plugins/agent-id-core/bin/cli.mjs export-proofOutput:
{
"exportedAt": 1234567890,
"stateDir": "/Users/you/.agent-id",
"ownerSession": { "...": "..." },
"operations": [ { "...": "..." } ]
}The portable, base64url-encoded v3 bundle (
{ version: 3, id_token, agent_jwk }) is produced byagent-id-git commitand attached as a git note — not byexport-proof.
agent-id-git — SSH-signed commits with provenance
setup
Write the agent keypair into SSH-format files (ssh/agent-id, ssh/agent-id.pub,
ssh/allowed_signers) under the state directory and print the public key to add to your git
host. The signing committer email is auto-derived from the agent’s JWK thumbprint
(agent-<jkt>@agent-id.local); there is no --email, --name, or --global flag, and
setup does not run git config.
node plugins/agent-id-git/bin/cli.mjs setupRequires an agent keypair (run agent-id-core bootstrap first). Add the printed public key
to GitHub under Settings → SSH and GPG keys → New SSH key → Key type: Signing Key.
Output:
{
"ok": true,
"privateKeyPath": "/Users/you/.agent-id/ssh/agent-id",
"publicKeyPath": "/Users/you/.agent-id/ssh/agent-id.pub",
"allowedSignersPath": "/Users/you/.agent-id/ssh/allowed_signers",
"sshPublicKey": "ssh-ed25519 AAAA... agent-id:...",
"jkt": "...",
"ownerSub": "..."
}ownerSub is included only when a bound owner session is present.
commit
Create an SSH-signed commit carrying Agent-ID-JKT, Agent-ID-Owner, and Co-Authored-By
trailers, log the commit to the audit trail, and attach a v3 bundle as a git note on
refs/notes/agent-id.
node plugins/agent-id-git/bin/cli.mjs commit --message "feat: implement feature" [--push]Precondition: the owner session must be DPoP-bound. If it is missing or predates the
v3.0.0 DPoP cutover, the command refuses to sign and points you at agent-id-core auth and
agent-id-core bind to start a fresh DPoP-bound session. (The live error string still names
the retired agent-id-setup binary — read it as agent-id-core.)
| Flag | Type | Default | Description |
|---|---|---|---|
--message <msg> | string | — (required) | Commit message. Also accepts -m. |
--push | boolean | false | Push the commit and the refs/notes/agent-id note to the remote. |
--remote <name> | string | origin | Remote to push to (only used with --push). |
--allow-empty | boolean | false | Allow an empty commit. |
Output:
{
"ok": true,
"commitHash": "...",
"signed": true,
"jkt": "...",
"proofAttached": true,
"pushed": false,
"notesPushed": false,
"auditSeq": 7,
"signatureShort": "..."
}auditSeq and signatureShort appear only when the audit-trail entry was logged
successfully.
verify
Verify a commit’s v3 attestation chain. Reads the v3 bundle from refs/notes/agent-id, runs
the universal bundle verifier, then adds git-specific checks: the Agent-ID-JKT /
Agent-ID-Owner trailers must agree with the bundle, and the SSH commit signature must
validate against agent_jwk.
node plugins/agent-id-git/bin/cli.mjs verify [--commit HEAD] [--sso-url <url>]The chain traced: SSH signature → agent_jwk → cnf.jkt-bound id_token (DPoP-bound owner
session) → SSO RS256 id_token signature. The exp/aud claims are intentionally skipped —
commit attestation is historical, not runtime resource access. Pre-v3 commits (no
Agent-ID-JKT trailer) are not supported.
| Flag | Type | Default | Description |
|---|---|---|---|
--commit <hash> | string | HEAD | Commit to verify. |
--sso-url <url> | string | null (auto from bundle) | Override the SSO base URL for JWKS lookup. |
Output:
{
"ok": true,
"commit": "...",
"jkt": "...",
"ownerSub": "...",
"issuer": "...",
"aud": "...",
"iat": 1234567890,
"summary": "Commit abc123def456 signed by agent <jkt>... owned by <sub>"
}agent-id-vault — encrypted credential storage
Credentials are encrypted with AES-256-GCM under a key derived (HKDF-SHA256) from the agent’s private key, so a record is only readable on the same machine as the agent that stored it.
store
Store an encrypted credential.
# From file (most secure — never touches CLI args)
node plugins/agent-id-vault/bin/cli.mjs store --service github --credential-file /tmp/tok
# From an environment variable
node plugins/agent-id-vault/bin/cli.mjs store --service github --credential-env GITHUB_TOKEN
# From stdin
echo 'ghp_xxx' | node plugins/agent-id-vault/bin/cli.mjs store --service github
# Direct (least secure — visible in the process list)
node plugins/agent-id-vault/bin/cli.mjs store --service github --credential "ghp_xxx"The credential value is resolved by the first source present, in this precedence:
--credential-file → --credential-env → stdin → --credential.
| Flag | Type | Default | Description |
|---|---|---|---|
--service <name> | string | — (required) | Service identifier (filename-sanitized). |
--type <type> | string | api-key | Free-form descriptive tag (stored as-is, not a validated enum). Conventional values: api-key, password, oauth, bearer, custom. |
--credential-file <path> | string | — | Read the credential from a file (most secure). |
--credential-env <var> | string | — | Read the credential from an environment variable. |
--credential <value> | string | — | Credential value as a CLI argument (least secure). |
--username <user> | string | — | Associated username (applies to any type). |
--url <url> | string | — | Associated service URL. |
Output:
{
"ok": true,
"service": "github",
"type": "api-key",
"updated": false
}updated is true when an existing credential for the service was overwritten.
get
Retrieve and decrypt a credential.
node plugins/agent-id-vault/bin/cli.mjs get --service github| Flag | Type | Default | Description |
|---|---|---|---|
--service <name> | string | — (required) | Service identifier. |
Output:
{
"ok": true,
"service": "github",
"type": "api-key",
"credential": "ghp_xxx",
"url": null,
"username": null
}url and username are always present (null when unset).
list
List stored credentials’ metadata. Never decrypts or returns plaintext.
node plugins/agent-id-vault/bin/cli.mjs listOutput:
{
"ok": true,
"credentials": [
{
"service": "github",
"type": "api-key",
"url": null,
"username": null,
"createdAt": 1234567890,
"updatedAt": 1234567890
}
]
}An empty or absent vault returns { "ok": true, "credentials": [] }.
remove
Remove a credential.
node plugins/agent-id-vault/bin/cli.mjs remove --service github| Flag | Type | Default | Description |
|---|---|---|---|
--service <name> | string | — (required) | Service identifier. |
Output:
{
"ok": true,
"service": "github"
}Removing a non-existent service fails with ok: false and No credential stored for "...".
agent-id-auth — DPoP-signed service calls
Each call carries an Authorization: DPoP <access_token> header plus a fresh DPoP: <proof>
header (RFC 9449). The proof is single-use and bound to the specific request method and URL,
so a target URL is always required. These commands require a bound session with an
access_token (run agent-id-core bootstrap or bind first); the auth plugin attempts a
transparent session refresh before signing.
header
Emit the Authorization and DPoP headers for one request.
# JSON output
node plugins/agent-id-auth/bin/cli.mjs header --url https://service.example.com/api/whoami
# Raw headers (one per line) for curl
node plugins/agent-id-auth/bin/cli.mjs header --url https://service.example.com/api/whoami --rawBecause the DPoP proof is single-use and bound to (method, URL), prefer call when you
just want to send a request — see below.
| Flag | Type | Default | Description |
|---|---|---|---|
--url <url> | string | — (required) | Target request URL (binds the proof’s htu). |
--method <verb> | string | GET | HTTP method (binds the proof’s htm). |
--raw | boolean | false | Print raw Authorization: and DPoP: lines instead of JSON. |
Output (JSON):
{
"ok": true,
"authorization": "DPoP <access_token>",
"dpop": "<proof-jws>",
"method": "GET",
"url": "https://service.example.com/api/whoami"
}Output (--raw):
Authorization: DPoP <access_token>
DPoP: <proof-jws>call
One-shot signed HTTP request (preferred). Generates the DPoP headers, sends the request, and
returns the response — eliminating the single-use-jti / two-header footgun.
node plugins/agent-id-auth/bin/cli.mjs call \
--url https://service.example.com/api/posts --method POST \
--body '{"title":"Hi"}'| Flag | Type | Default | Description |
|---|---|---|---|
--url <url> | string | — (required) | Target request URL. |
--method <verb> | string | GET | HTTP method (uppercased; must be alphabetic). |
--body <value> | string | — | Inline request body (visible in process listings). |
--body-file <path> | string | — | Read the request body from a file (preferred for non-trivial payloads). |
--content-type <type> | string | application/json | Content-Type sent when a body is present. |
Output:
{
"ok": true,
"status": 200,
"method": "POST",
"url": "https://service.example.com/api/posts",
"contentType": "application/json",
"body": { "...": "..." }
}ok mirrors the HTTP response’s res.ok. body is the parsed JSON when the response is
application/json, otherwise the raw text.
discover
Fetch and validate a service’s /.well-known/alien-agent-id.json manifest.
node plugins/agent-id-auth/bin/cli.mjs discover --url https://service.example.com| Flag | Type | Default | Description |
|---|---|---|---|
--url <url> | string | — (required) | Service URL (also accepts --service). |
--allow-insecure | boolean | false | Permit plaintext HTTP. |
--timeout-ms <n> | number | — | Request timeout in milliseconds. |
Output:
{
"ok": true,
"manifestUrl": "https://service.example.com/.well-known/alien-agent-id.json",
"allowedHost": "service.example.com",
"manifest": { "...": "..." }
}capabilities
Render a service manifest’s api.operations[] as actionable markdown. Output is markdown
text on stdout (not JSON).
node plugins/agent-id-auth/bin/cli.mjs capabilities --url https://service.example.com| Flag | Type | Default | Description |
|---|---|---|---|
--url <url> | string | — (required) | Service URL (also accepts --service). |
--allow-insecure | boolean | false | Permit plaintext HTTP. |
--timeout-ms <n> | number | — | Request timeout in milliseconds. |
support
Probe a page for the <meta name="alien-agent-id"> support signal.
node plugins/agent-id-auth/bin/cli.mjs support --url https://service.example.com| Flag | Type | Default | Description |
|---|---|---|---|
--url <url> | string | — (required) | Page URL to probe. |
--allow-insecure | boolean | false | Permit plaintext HTTP. |
--timeout-ms <n> | number | — | Request timeout in milliseconds. |
Output:
{ "ok": true, "supported": true, "version": "v1" }supported is false (with version: null) when the tag is absent or its content is not a
recognized version.
Environment Variables
| Variable | Purpose |
|---|---|
AGENT_ID_STATE_DIR | State directory (default: ~/.agent-id). Overridden by --state-dir. |
ALIEN_PROVIDER_ADDRESS | Default provider address for bootstrap / auth / setup-owner-session (avoids --provider-address). |
Provider address resolution
For commands that need a provider address (bootstrap, auth, setup-owner-session), it is
resolved in this order:
--provider-address <addr>flagALIEN_PROVIDER_ADDRESSenvironment variabledefault-provider.txtfile next to the CLI
Error Output
Errors are emitted as JSON on stdout with { "ok": false, "error": "<message>" } and set a
non-zero exit code.
| Error | Action |
|---|---|
No provider address | Set --provider-address, ALIEN_PROVIDER_ADDRESS, or default-provider.txt. |
No pending auth found | Run auth (or bootstrap) first. |
No agent keypair | Run agent-id-core bootstrap (or init). |
No owner session / Session is not DPoP-bound | Run agent-id-core auth then bind to start a fresh DPoP-bound session. |
Session refresh failed (authorization revoked …) | Run bootstrap again to re-authenticate. |
No bound session with access_token | Run agent-id-core bootstrap or bind before making signed calls. |
No credential stored for "…" | Store it first with agent-id-vault store. |