Skip to Content
CLI Reference
View .md

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:

PluginBinaryResponsibility
agent-id-coreplugins/agent-id-core/bin/cli.mjsBootstrap, lifecycle, and universal operations (sign / verify / export)
agent-id-gitplugins/agent-id-git/bin/cli.mjsSSH-signed commits with provenance trailers + v3 bundle
agent-id-vaultplugins/agent-id-vault/bin/cli.mjsEncrypted credential storage
agent-id-authplugins/agent-id-auth/bin/cli.mjsDPoP-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/whoami

Every 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:

FlagTypeDefaultDescription
--state-dir <path>string$AGENT_ID_STATE_DIR, else ~/.agent-idState 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:

  1. --state-dir <path> flag
  2. AGENT_ID_STATE_DIR environment variable
  3. ~/.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).

FlagTypeDefaultDescription
--provider-address <addr>stringresolved (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 init

Output:

{ "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>
FlagTypeDefaultDescription
--provider-address <addr>string— (required)Alien provider address.
--sso-url <url>stringhttps://sso.alien-api.comSSO base URL.
--oidc-origin <origin>stringhttp://localhostOIDC 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.

FlagTypeDefaultDescription
--timeout-sec <n>number300Max seconds to poll for approval.
--poll-interval-ms <n>number3000Polling 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]
FlagTypeDefaultDescription
--provider-address <addr>stringresolvedAlien provider address (resolved like bootstrap).
--verbosebooleanfalsePrint 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 refresh

Output:

{ "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 status

Output (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"}'
FlagTypeDefaultDescription
--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>stringmainAgent identity context for the entry.
--meta <json>stringOptional 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 verify

Emits 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-proof

Output:

{ "exportedAt": 1234567890, "stateDir": "/Users/you/.agent-id", "ownerSession": { "...": "..." }, "operations": [ { "...": "..." } ] }

The portable, base64url-encoded v3 bundle ({ version: 3, id_token, agent_jwk }) is produced by agent-id-git commit and attached as a git note — not by export-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 setup

Requires 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.)

FlagTypeDefaultDescription
--message <msg>string— (required)Commit message. Also accepts -m.
--pushbooleanfalsePush the commit and the refs/notes/agent-id note to the remote.
--remote <name>stringoriginRemote to push to (only used with --push).
--allow-emptybooleanfalseAllow 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_jwkcnf.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.

FlagTypeDefaultDescription
--commit <hash>stringHEADCommit to verify.
--sso-url <url>stringnull (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.

FlagTypeDefaultDescription
--service <name>string— (required)Service identifier (filename-sanitized).
--type <type>stringapi-keyFree-form descriptive tag (stored as-is, not a validated enum). Conventional values: api-key, password, oauth, bearer, custom.
--credential-file <path>stringRead the credential from a file (most secure).
--credential-env <var>stringRead the credential from an environment variable.
--credential <value>stringCredential value as a CLI argument (least secure).
--username <user>stringAssociated username (applies to any type).
--url <url>stringAssociated 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
FlagTypeDefaultDescription
--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 list

Output:

{ "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
FlagTypeDefaultDescription
--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.

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 --raw

Because the DPoP proof is single-use and bound to (method, URL), prefer call when you just want to send a request — see below.

FlagTypeDefaultDescription
--url <url>string— (required)Target request URL (binds the proof’s htu).
--method <verb>stringGETHTTP method (binds the proof’s htm).
--rawbooleanfalsePrint 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"}'
FlagTypeDefaultDescription
--url <url>string— (required)Target request URL.
--method <verb>stringGETHTTP method (uppercased; must be alphabetic).
--body <value>stringInline request body (visible in process listings).
--body-file <path>stringRead the request body from a file (preferred for non-trivial payloads).
--content-type <type>stringapplication/jsonContent-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
FlagTypeDefaultDescription
--url <url>string— (required)Service URL (also accepts --service).
--allow-insecurebooleanfalsePermit plaintext HTTP.
--timeout-ms <n>numberRequest 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
FlagTypeDefaultDescription
--url <url>string— (required)Service URL (also accepts --service).
--allow-insecurebooleanfalsePermit plaintext HTTP.
--timeout-ms <n>numberRequest 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
FlagTypeDefaultDescription
--url <url>string— (required)Page URL to probe.
--allow-insecurebooleanfalsePermit plaintext HTTP.
--timeout-ms <n>numberRequest 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

VariablePurpose
AGENT_ID_STATE_DIRState directory (default: ~/.agent-id). Overridden by --state-dir.
ALIEN_PROVIDER_ADDRESSDefault 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:

  1. --provider-address <addr> flag
  2. ALIEN_PROVIDER_ADDRESS environment variable
  3. default-provider.txt file 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.

ErrorAction
No provider addressSet --provider-address, ALIEN_PROVIDER_ADDRESS, or default-provider.txt.
No pending auth foundRun auth (or bootstrap) first.
No agent keypairRun agent-id-core bootstrap (or init).
No owner session / Session is not DPoP-boundRun 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_tokenRun agent-id-core bootstrap or bind before making signed calls.
No credential stored for "…"Store it first with agent-id-vault store.
Last updated on