CLI Tool¶
The SigID CLI (sigid-cli) is the command-line interface for agent identity
workflows. It covers local key management, agent registration,
challenge-response authentication, token caching, signing, verification, and key
lifecycle operations.
This CLI is not a Dashboard administration tool or a general Management API
client. For interactive human login from a terminal it supports OAuth device
authorization via auth --mode device. For HTTP API integration patterns, see
API And SDK Reference.
What this page is for¶
Use this page when you need to:
- generate and store agent signing keys locally
- initialize a self-registered agent from a terminal
- authenticate an agent with challenge-response
- inspect cached agent tokens
- buy x402-protected resources as an authenticated agent
- sign and verify local payloads
- register, rotate, revoke, or remove agent keys
- script agent identity operations in development or CI
Install and verify¶
From npm (recommended)¶
Prebuilt native binaries for Linux and macOS (x64 / arm64):
npx @sigid/cli --version
npx @sigid/cli setup --name my-agent --redirect-uri http://localhost:3000/
# or install globally / as a project dependency
npm install -g @sigid/cli
sigid-cli --help
The @sigid/cli package is a thin Node wrapper; the real tool is the
platform-specific sigid-cli binary shipped via optionalDependencies
(@sigid/cli-linux-x64, @sigid/cli-linux-arm64, @sigid/cli-darwin-x64,
@sigid/cli-darwin-arm64).
The Linux x64 and arm64 packages use statically linked musl binaries and do not require a particular glibc version from the host environment.
From source¶
Use source install on unsupported platforms (e.g. Windows) or when developing the CLI itself.
Verify the installed binary:
Before you start¶
The CLI stores local configuration and sensitive data under ~/.config/sigid/
unless your environment overrides the platform config directory.
| File | Purpose | Notes |
|---|---|---|
~/.config/sigid/config.toml |
CLI defaults | Plain TOML configuration |
~/.config/sigid/keys.enc |
Local private-key keystore | Encrypted with Argon2id + AES-256-GCM-SIV |
~/.config/sigid/tokens.enc |
Cached access and refresh tokens | Encrypted token cache |
~/.config/sigid/tokens.key |
Token-cache master key | Private file permissions |
Passphrase sources are checked in this order:
--passphrase--passphrase-stdin--passphrase-fileSIGID_PASSPHRASE- interactive prompt
--passphrase exists for automation and tests, but it can be captured in shell
history. Prefer an interactive prompt, --passphrase-stdin,
--passphrase-file, or SIGID_PASSPHRASE in controlled CI environments.
Initialize an agent¶
init generates a key pair, registers the agent, and caches credentials in one
flow.
sigid-cli init \
--idp https://auth.example.com \
--tenant-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111 \
--name my-agent \
--anchor-type did_key \
--algo ed25519
| Option | Description | Default |
|---|---|---|
--idp |
Identity Provider URL | default_idp from config, otherwise required |
--tenant-id |
Tenant ID bound into the key-ownership proof | default_tenant_id from config, otherwise required |
--name |
Agent name and local key label | Required |
--anchor-type |
Agent anchor type | did_key |
--algo |
Key algorithm | ed25519 |
The CLI:
- generates a local key pair
- registers the agent with
/api/v1/agents/auth/register - falls back to PoW registration through
/api/v1/agents/auth/register/powand/api/v1/agents/auth/register/pow/completewhen required - stores the private key in
keys.enc - caches returned tokens when the tenant admits the agent
Hand Off An Agent-Built Workspace¶
After provisioning and testing, transfer durable ownership of the agent and grant the developer the resources needed to inspect the integration:
sigid-cli handoff \
--idp https://auth.example.com \
--organization-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111 \
--agent-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a222 \
--application-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a333
--application-id is repeatable. Organization co-ownership is granted by
default; pass --grant-org-owner=false only for an agent/application-only
transfer. Send the returned fragment-bearing link privately to the intended
developer. Acceptance is single-use and transfers the agent's durable owner.
This differs from invite: an invitation grants organization/application
access but never transfers the agent identity. See
Provision, Test, And Hand Off for the fixture-user,
branding, test-manifest, and acceptance sequence.
Authenticate¶
auth supports three modes via --mode:
| Mode | Purpose |
|---|---|
agent (default) |
Challenge-response with an existing local agent key |
autonomous |
Create a new agent identity (keygen + register), then authenticate |
device |
OAuth device flow: print a link, user logs in in a browser, CLI polls for tokens |
Agent mode (challenge-response)¶
| Option | Description | Default |
|---|---|---|
--mode |
agent, autonomous, or device |
agent |
--idp |
Identity Provider URL | default_idp from config, otherwise error |
--key |
Local key label to use | default_key from config, otherwise default |
--scope |
Requested scopes | None |
The CLI:
- loads the local key from the encrypted keystore
- requests
/api/v1/agents/auth/challenge - signs the canonical challenge
- submits
/api/v1/agents/auth/verify - stores the returned tokens in
tokens.enc
Autonomous mode (self-register)¶
Creates a new agent identity the same way as init, then runs challenge-response
auth with the new key. Emits one result (registration facts + bearer).
Non-interactive use requires a passphrase source (SIGID_PASSPHRASE,
--passphrase-file, or --passphrase-stdin). There is no plaintext key-on-disk
mode; the local keystore always stays encrypted. Interactive prompts work when a
TTY is available.
export SIGID_PASSPHRASE='...'
sigid-cli auth --mode autonomous \
--idp https://auth.example.com \
--tenant-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111 \
--name my-agent \
--algo ed25519
| Option | Description | Default |
|---|---|---|
--name / --key |
Agent name and local key label | default_key / default |
--tenant-id |
Tenant bound into the key-ownership proof | default_tenant_id, otherwise required |
--algo |
Key algorithm | ed25519 |
--anchor-type |
Agent anchor type | did_key |
Device mode (OAuth link login)¶
RFC 8628 device authorization for human login from a terminal. The application
must allow the device_code grant and the tenant must have device flow enabled.
sigid-cli auth --mode device \
--idp https://auth.example.com \
--client-id YOUR_CLIENT_ID \
--scope "openid profile offline_access"
| Option | Description | Default |
|---|---|---|
--client-id |
OAuth application client id | Required |
--scope |
Requested scopes (include offline_access for a refresh token) |
None |
--tenant-slug |
Optional tenant routing on the token poll (persisted for refresh) | None |
--interval |
Initial poll interval seconds | Server interval or 5 |
The CLI:
- calls
POST /oauth/device/code - always prints the absolute verification link and user code on stderr
(including under
--output json|yaml|quiet) so the human can open the link while the CLI blocks on the poll loop - polls
POST /oauth/tokenwith granturn:ietf:params:oauth:grant-type:device_code - honors
authorization_pending,slow_down,access_denied, andexpired_token - caches the human token in
tokens.enc(subject_kind=user, withclient_idand optionaltenant_slugsosigid-cli token --refreshuses/oauth/token) - omits refresh tokens and ID tokens from every output format; refresh tokens remain available only through the encrypted token cache
This is distinct from sigid-cli delegation, which is agent-initiated AAL2
approval for a delegated agent token, not a human OAuth session.
Inspect identity and tokens¶
Show the current local identity and cached agent context:
Show a cached token for an IDP:
Refresh a cached token:
Clear cached tokens:
# Clear tokens for one IDP
sigid-cli token --clear --idp https://auth.example.com
# Clear all cached tokens
sigid-cli token --clear
Buy x402-protected resources¶
x402 buy quotes and immediately executes an outbound x402 purchase using the
cached agent token for the IDP.
sigid-cli x402 buy \
--idp https://auth.example.com \
--url https://merchant.example.com/resource \
--header "Accept: application/json"
The CLI calls the agent-authenticated /agent/x402/pay/quote route first, then
executes the returned quote through /agent/x402/pay/execute. Run
sigid-cli auth first if no valid cached token is available.
For merchants using SigID Commerce payment links, use x402 commerce-link.
The CLI builds the commerce x402 endpoint and pays it through the same agent
outbound x402 flow:
sigid-cli x402 commerce-link \
--idp https://auth.example.com \
--merchant https://merchant.example.com \
--payment-link-token plink_abc123 \
--buyer-country US \
--buyer-type business
| Option | Description | Default |
|---|---|---|
--idp |
Identity Provider URL | default_idp from config, otherwise error |
--url |
x402-protected resource URL | Required |
--method |
Outbound method: GET or HEAD |
GET |
--header |
Outbound header; only Accept and Accept-Language are supported |
None |
--payment-route |
facilitator or wallet |
facilitator |
--payer-mode |
sigid_funded, user_authorized, or tenant_funded |
sigid_funded |
--idempotency-key |
Caller idempotency key bound to the quote | None |
--payment-required-file |
JSON file containing a captured x402 402 response body | None |
--payment-payload-file |
JSON file containing a signed x402 payment payload for user_authorized mode |
None |
x402 commerce-link additionally accepts:
| Option | Description | Default |
|---|---|---|
--merchant |
Merchant SigID Commerce origin | Required |
--payment-link-token |
SigID Commerce payment-link token | Required |
--buyer-country |
Buyer country for merchant policy checks | None |
--buyer-region |
Buyer region for merchant policy checks | None |
--buyer-type |
unknown, consumer, or business |
None |
For manual quote and execution flows:
sigid-cli x402 quote \
--idp https://auth.example.com \
--url https://merchant.example.com/resource
sigid-cli x402 execute \
--idp https://auth.example.com \
--quote-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111
sigid-cli x402 status \
--idp https://auth.example.com \
--payment-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111
Outbound x402 quotes are short lived. Execute a quote immediately, or use
x402 buy for the normal one-step purchase path.
Manage local keys¶
Generate a local key pair:
| Option | Description | Default |
|---|---|---|
--algo |
Key algorithm: ed25519, es256, es256k, or bip340 |
ed25519 |
--label |
Local key label | default_key from config, otherwise default |
keygen prints the public key and fingerprint. It does not print the private
key. The private key is encrypted and stored in the local keystore.
List local keys:
Remove a key from the local keystore:
remove-key only deletes the local keystore entry. To revoke a registered key
on the IDP, use revoke-key.
Register, rotate, and revoke keys¶
Register an additional local key with the IDP:
sigid-cli register-key \
--idp https://auth.example.com \
--tenant-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111 \
--key backup-key
register-key requires a valid cached token. Run sigid-cli auth first if no
valid token is available. --tenant-id (or default_tenant_id in config) is
required for the key-ownership proof.
Rotate a key:
sigid-cli rotate-key \
--idp https://auth.example.com \
--tenant-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111 \
--old my-agent \
--new-label my-agent-v2
| Option | Description | Default |
|---|---|---|
--tenant-id |
Tenant ID for the new key's ownership proof | default_tenant_id from config, otherwise required |
--old |
Label of the old local key | Required |
--new-label |
Label for the new key | {old}_v2 |
--new-algo |
Algorithm for the new key | Same as old key |
The CLI generates a new key pair, registers the new key, saves it locally, and deprecates the old key on the IDP.
Deprecate a registered key without rotating:
Revoke a key:
sigid-cli revoke-key \
--idp https://auth.example.com \
--key compromised-key \
--reason "key exposed"
deprecate-key soft-retires a key on the IDP (normal lifecycle). revoke-key
marks the registered key as compromised. Both preserve the local key; use
remove-key to delete the local keystore entry.
Change the keystore passphrase (re-encrypts all local keys under a new Argon2id salt):
Sign and verify payloads¶
Sign a message:
Sign a file:
The signature output includes a base64-encoded signature, algorithm, fingerprint, and timestamp.
Verify a message signature:
sigid-cli verify \
--message "Hello, SigID!" \
--signature "base64-signature" \
--public-key "hex-encoded-public-key" \
--algo ed25519
Verify a file signature:
sigid-cli verify \
--file transaction.json \
--signature-file transaction.sig \
--public-key "hex-encoded-public-key" \
--algo ed25519
--signature expects a base64-encoded signature. --signature-file reads a raw
binary signature file.
Create a GPG-compatible detached signature:
sigid-cli gpg-sign --file artifact.tar.gz --key my-agent
sigid-cli gpg-sign --file artifact.tar.gz --key my-agent --armor
Without --armor, the CLI writes a binary .sig file. With --armor, it
writes a .sig.asc file.
Configuration and storage¶
Set defaults with the config subcommand (preferred) or by editing
~/.config/sigid/config.toml:
sigid-cli config set \
--default-idp https://auth.example.com \
--default-key my-agent \
--default-tenant-id 018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111 \
--output-format json
sigid-cli config show
default_idp = "https://auth.example.com"
default_key = "my-agent"
default_tenant_id = "018f3d44-7d4d-7d5d-8d3b-9f0c4c37a111"
output_format = "json"
| Field | Description |
|---|---|
default_idp |
Default Identity Provider URL (--idp → config → error; setup/invite/handoff/verify-setup fall back to https://auth.sigid.org) |
default_key |
Default local key label |
default_tenant_id |
Default tenant ID for key-ownership proofs on init / register-key / rotate-key |
output_format |
Default output format when --output is omitted: json, yaml, text, table, or quiet |
Supported environment variables:
| Variable | Purpose |
|---|---|
SIGID_PASSPHRASE |
Keystore passphrase for automation |
SIGID_LOG_LEVEL |
CLI logging level |
RUST_LOG |
Fallback logging configuration |
Supported global flags:
Non-localhost IDP URLs must use HTTPS. Local development URLs such as
http://localhost:3000 and http://auth.sigid.localhost:3000 are allowed;
the CLI bypasses proxies and pins .localhost names to loopback addresses.
Output formats and scripting¶
JSON is the default output format:
Other formats are available for humans and scripts:
sigid-cli list-keys --output table
sigid-cli token --output quiet # raw access token only
sigid-cli sign --message hi --output quiet # raw signature only
sigid-cli whoami --output yaml
Quiet mode emits primary machine-parseable values (raw bearer for token /
auth, raw signature for sign, newline-separated labels for list-keys)
instead of the human Display block. Table uses the same columnar layout as text
for list-like results.
The CLI exits with 0 on success and a non-zero code on errors.
Keystore security¶
The local keystore is designed for agent development and operator workflows:
- private keys are encrypted at rest in
keys.enc - the keystore uses Argon2id-derived keys and AES-256-GCM-SIV encryption
- keystore and token files are written atomically with restrictive permissions
- the token cache is encrypted separately from the private-key keystore
- non-localhost IDP URLs must use HTTPS to avoid exposing bearer tokens
Keep the keystore passphrase outside source control and avoid passing it through shell history.
Common errors¶
| Error | Meaning | Fix |
|---|---|---|
no IDP URL specified |
No --idp flag and no default_idp in config |
Pass --idp or sigid-cli config set --default-idp … |
no tenant ID specified |
No --tenant-id and no default_tenant_id |
Pass --tenant-id or set default_tenant_id |
no valid token - authenticate first with sigid-cli auth |
The IDP operation needs a cached token | Run sigid-cli auth |
no refresh token available |
The cached token cannot be refreshed | Re-authenticate with sigid-cli auth |
key '<name>' has no server_key_id |
The local key has not been registered with the IDP | Run sigid-cli register-key first |
default_idp must use https for non-localhost hosts |
Remote IDP URL uses plain HTTP | Use HTTPS for non-localhost IDPs |
provide --message or --file |
Signing or verification input is missing | Pass one input source |
provide --signature or --signature-file |
Verification signature is missing | Pass one signature source |
| Signature verification failure | Payload, public key, algorithm, or signature does not match | Re-check the exact bytes and algorithm |
Command reference¶
| Command | Purpose |
|---|---|
setup |
Workspace bootstrap (agent + sandbox org/env/app) |
init |
Generate a key, register an agent, and cache tokens |
invite |
Issue operator co-owner invitation link |
handoff |
Transfer agent ownership plus optional organization/application access to a human |
verify-setup |
OIDC discovery + local token check after setup |
app create\|list\|get\|update\|rotate-secret |
Tenant OAuth application management (update merges onto current state) |
delegation create\|poll\|revoke |
Agent device-flow human delegation + issued-delegation revocation |
keygen |
Generate a local key pair |
sign |
Sign a message or file |
verify |
Verify a signature |
gpg-sign |
Create a GPG-compatible detached signature |
auth |
Perform agent challenge-response authentication |
token |
Show, refresh, or clear cached tokens |
register-key |
Register a local key with the IDP |
list-keys |
List local keystore entries |
whoami |
Show local identity and cached agent context |
rotate-key |
Generate and register a replacement key, then deprecate the old key |
deprecate-key |
Soft-retire a registered key on the IDP |
revoke-key |
Revoke a registered key on the IDP |
remove-key |
Remove a key from the local keystore |
change-passphrase |
Re-encrypt the local keystore under a new passphrase |
config show\|set\|unset |
Read or write CLI defaults (default_idp, default_tenant_id, …) |
x402 |
Quote/execute outbound x402 payments |
schema export |
Export database schema for development or operator workflows |