# Run402 CLI -- Agent Reference — Core commands > Slice `commands` of the Run402 CLI reference. Index (start here): https://docs.run402.com/llms-cli.txt · Whole reference: https://docs.run402.com/llms-cli-full.txt ## Command Reference ### up - `run402 up [repo-or-path] [--name ] [--project ] [--manifest ] [--dir ] [--tier ] [-y|--yes] [--check|--print-spec|--print-manifest|--plan|--require-plan ] [--verify] [--propagation-budget-s ] [--no-propagation-wait] [--quiet]` — SDK-owned recursive app deploy. Validates `run402.deploy.json`/`app.json`, requires explicit `--manifest` for executable `.ts/.js` configs, ensures missing Cloud prerequisites when approved, resolves/creates/links a project, then applies the manifest. Output includes `steps[]`; success has no top-level `status`. Use `--check` for local-only validation, `--print-spec` for normalized `ReleaseSpec`, `--plan` for a gateway-reviewed non-deploying plan, and `--require-plan` for exact reviewed apply. App HTTP verification reports fresh edge misses as `propagation_pending`, waits up to `--propagation-budget-s` (default 120), and `--no-propagation-wait` returns the pending state immediately. Add `--verify` to wait for gateway/edge release coherence after the deploy and attach `edge_coherence`; non-coherence exits 2. - `run402 up verify [repo-or-path] [--project ] [--manifest ] [--dir ] [--propagation-budget-s ] [--no-propagation-wait] [--quiet]` — rerun manifest HTTP verification (app manifest `verify.http[]` or deploy-manifest top-level `verify`) without deploying, uploading, creating a project, or mutating resources. Use it after `propagation_pending` or before declaring a consumer copy healthy. ### init - `run402 init` — set up with x402 (Base Sepolia). Creates the local wallet, requests faucet, checks tier, lists projects. A blocked or unconfirmed faucet is reported in `funding`; next actions carry the server cooldown or balance-check recovery instead of recommending immediate deploy. Identity setup can succeed while funding remains blocked. - `run402 init --api-base ` — configure the active profile to target a Run402 Core/API base. For Core, this does not create a wallet, request faucet funds, or require a Cloud tier. - `run402 init mpp` — set up with MPP: pathUSD on Tempo, faucet-funded on the Moderato testnet. Same steps, different payment rail; a purchase pays the first Tempo chain its 402 offers that the wallet can cover. - `run402 init lightning` — the Lightning wallet: the same Base wallet (the x402 fallback) plus a budgeted Lightning wallet the platform mints on Run402's own Hub (`POST /agent/v1/lightning-wallet`; custody `run402_hub`, a starter amount funded by the platform). The one-time pairing is stored in `wallet.json` beside the Base key and never printed; `rail` becomes `lightning`, so tier purchases and image generation answer a Lightning challenge first and fall back to x402. Image generation pays the x402 `exact` scheme on Lightning when Run402 offers it, and MPP Lightning otherwise. The summary carries `lightning: { wallet_id, status, lightning_address, budget_sats, starter_sats, balance_sats, custody, outcome }`; `outcome: "minting"` means rerun in a few seconds, `"unavailable"` means no Hub on this gateway (x402 keeps paying). - `run402 init --switch-rail` — switch the persisted rail; re-running with the same rail is idempotent. ### pay `run402 pay [--method ] [--body ] [--max-usd ] [--max-sats ] [--lightning-pairing ] [--idempotency-key ] [--require-receipt]` calls an arbitrary x402-priced HTTP endpoint through the SDK buyer. On the `lightning` rail a 402 that names an `lnbtc` entry on the wallet's network is paid in sats from the agent's Lightning wallet, bounded by `--max-sats` (invoice plus routing headroom) or the rail's ceiling; an `lnbtc` entry on another network is refused with `LIGHTNING_NETWORK_MISMATCH` and nothing is paid. `--lightning-pairing ` pays with a Nostr Wallet Connect pairing read from a file or stdin (never the command line, never printed) on whatever network its node is on, which is how a testnet desk or an operator's own node is paid. A host's priced routes, with the methods that buy each and the networks it takes (`eip155:…`, `lnbtc:…`), are listed at `https:///.well-known/x402`; a route priced on Lightning alone is listed only there, never in the x402 Bazaar. `--require-receipt` cannot be satisfied by a Lightning entry (`LIGHTNING_RECEIPT_UNAVAILABLE`). The request body goes in `--body` and nowhere else: `--json` is the CLI-wide output-format flag, takes no value, and is a no-op here because `pay` always prints JSON. Writing `--json ''` does not send the payload — it fails `BAD_USAGE` with a hint naming `--body`. The default ceiling is `$0.10`; `--max-usd` accepts up to six decimal places and is converted exactly to USD micros. `--require-receipt` requires a verified wallet-rooted offer before payment and a matching receipt afterward. JSON output is the complete `x402-commerce-result.v1` envelope with settlement, movement/replay, delivery, offer, merchant-receipt, signer-relationship, policy, and portable evidence. Unpriced URLs return `payment: null`. A seller that settles and then answers a non-2xx with a valid `PAYMENT-RESPONSE` receipt prints a normal result: `http_status` is the seller's status, `body` is the seller's body, and `payment.funds_moved: true` with `payment.delivery.status: "failed"`. For `PAYMENT_INTENT_PENDING` on a trusted Run402 host, wait for `Retry-After` and repeat the identical command with the same payer, request, and key. Never substitute a fresh key. Custom/arbitrary hosts and other `funds_moved: "unknown"` outcomes remain ambiguous and require reconciliation. ```bash run402 pay https://seller.example/translate --method POST \ --body '{"text":"hello"}' --max-usd 0.05 \ --idempotency-key translation:1 --require-receipt ``` A route priced in sats, paid with a pairing on its network (the pairing on stdin): ```bash run402 pay https://kychange.com/api/usdc/10k --method POST \ --body '{"destination":"0x0000000000000000000000000000000000000001"}' \ --max-sats 10500 --lightning-pairing - < ./payer.nwc ``` ### status `run402 status` — show full organization state in one shot (wallet, rail, balances, tier, projects, active project). Read-only, JSON output. Includes a `wallet: { local_label, server_label, address }` object naming the active named wallet (`local_label` is the local selector, `server_label` the server-synced display name or null), a top-level `rail`, and a `balances: { on_chain_usd_micros, on_chain_by_network, on_chain_token, allowance_usd_micros, held_usd_micros }` object: `on_chain_usd_micros` is the wallet's on-chain balance summed over the networks its rail pays on, `on_chain_by_network` each of those networks apart (`base_mainnet_usd_micros` and `base_sepolia_usd_micros` on x402, `tempo_mainnet_pathusd_micros` and `tempo_moderato_pathusd_micros` on mpp; null where a read failed), and `allowance_usd_micros` the organization's allowance. The on-chain token tracks the rail (USDC on x402, pathUSD on mpp); the allowance is rail-independent. Remote reads carry `remote_status` entries for tier, billing and projects (`available` or `unavailable`, with safe error code/category). `projects_source` distinguishes server inventory from `local_cache`. Null remote values after failed reads are unknown, not evidence that initialization or funding is needed. ### wallets Manage multiple named wallets (profiles) on one machine. Keys never leave the machine (non-custodial). The `default` wallet lives at the config-dir root; named wallets live under `{config_dir}/profiles//`. - `run402 wallets list` — JSON array of `{ local_label, server_label, address, address_short, rail, active }`. Reads non-secret `meta.json`; never loads private keys. - `run402 wallets current` — the resolved active wallet `{ local_label, source, source_detail, address, server_label, configured, created, rail, faucet_used, path, warnings }`; with no local wallet yet, `configured: false` and an `initialize_wallet` next action. `faucet_used` records faucet use, not pay-readiness. `source` ∈ flag|env|binding|config|default. `warnings` surfaces env-vs-binding conflicts and local-name-vs-server-label drift. - `run402 wallets new [--mpp | --rail ]` — create a new wallet (generates a key); `wallets new default` creates the root wallet without funding it. `{ local_label, address, rail, created: true, next_actions }`. A `lightning` wallet is minted on the platform by `run402 --wallet init lightning`. - `run402 wallets fund` — request testnet funds from the faucet into the active wallet (Base Sepolia USDC, or Tempo pathUSD on the `mpp` rail) and wait briefly for them to land. - `run402 wallets balance` — the active wallet's on-chain balances (`onchain`: `base_mainnet_usd_micros`, `base_sepolia_usd_micros`, `tempo_mainnet_pathusd_micros`, `tempo_moderato_pathusd_micros`; null where a read failed) plus its organization's `allowance_usd_micros` (null with no organization). - `run402 wallets lightning [status|mint|revoke]` — the active wallet's Lightning wallet. `status` reads the platform record plus a best-effort balance and remaining budget over NWC (`{ rail, lightning: { wallet_id, status, custody, lightning_address, budget_sats, starter_sats, has_pairing, balance_sats, budget_remaining_sats } }`); `mint` is `init lightning`'s wallet step alone; `revoke` deletes the sub-wallet on Run402's Hub, forgets the pairing, and returns the rail to x402. The pairing secret is never printed. - `run402 wallets use ` — set the global default wallet (`config.json` `active_wallet`). `{ name, active: true }`. - `run402 wallets rename ` — rename a wallet; renaming `default` migrates its root files into `profiles//`. `{ from, to, renamed: true }`. - `run402 wallets bind []` — write `./.run402.json` binding this directory to a wallet (defaults to the active one). Safe to commit (holds only a name). `{ wallet, file, bound: true, safe_to_commit: true }`. - `run402 wallets unbind` — remove `./.run402.json`. `{ file, unbound }`. - `run402 wallets import --key ` — adopt an existing 0x-prefixed 64-hex private key (file path or `-` for stdin) as a named wallet. `{ local_label, address, imported: true, signer: "local" }`. - `run402 wallets import --kms [--aws-profile ]` — adopt an AWS KMS key (`ECC_SECG_P256K1`, `SIGN_VERIFY`) by reference: reads its public key, derives the address, and writes a profile that holds the ARN and no secret. Every command then signs through KMS under `--wallet ` (SIWX, x402, MPP on Tempo, transactions); the key never leaves KMS. `{ local_label, address, imported: true, signer: "aws_kms", key_arn }`. Errors: `KMS_KEY_UNSUITABLE` (wrong key type), `KMS_ACCESS_DENIED` (the AWS identity is not on the key policy; names the caller), `KMS_SIGNER_UNAVAILABLE` (expired session: `aws sso login --profile

`), `SIGNER_ADDRESS_MISMATCH` (the key at the ARN is not the imported one). - `run402 wallets send --to

[--from ] [--network base|base-sepolia|tempo|tempo-moderato] [--confirm]` — send USDC (Base) or pathUSD (Tempo; the fee is paid in pathUSD too) from the active wallet, key-backed or KMS-backed. `--from` sends from a CoinbaseSmartWallet the wallet owns (Base only; refused with `SMART_ACCOUNT_NOT_OWNER` otherwise). Without `--confirm` it runs every check and prints a preview (`{ preview: true, network, asset, amount_usd_micros, from, to, sender, balance_usd_micros, estimated_gas, hint }`) without signing; with it, it reports `{ tx_hash, from, to, amount_usd_micros, network, asset, explorer_url }` once the transaction is mined, or `SEND_REVERTED` with the hash. A short balance is `SEND_INSUFFICIENT_BALANCE` naming the asset and shortfall. The MCP sandbox refuses it (`VALUE_TRANSFER_REQUIRES_CLI`). - `run402 wallets rm --yes` — delete a wallet and its keys. Requires `--yes` (agent-first: no interactive prompt). Refuses to remove `default`. Removing a KMS-backed wallet deletes the local profile only; the KMS key is untouched. `{ name, removed: true }`. - Server-side display label: `new`/`rename`/`import` push the wallet's name to a server-side label (signed by the wallet — proof of control) so the same name shows cross-machine and in the console (WEB). Best-effort and on by default; `RUN402_WALLET_LABEL_SYNC=0` opts out (fully offline wallet ops). The local folder name is the source of truth; the label is a mirror, and `wallets current` flags any drift. - Selection for ANY command: `--wallet ` (alias `--profile`) > `RUN402_WALLET` > nearest `./.run402.json`/`.run402.local.json` > `wallets use` default > `default`. A conflicting env + binding errors with `WALLET_SELECTION_CONFLICT` (resolve via `--wallet`, `unset RUN402_WALLET`, or `wallets unbind`). Selecting a non-existent wallet errors with `WALLET_NOT_FOUND`. ### tier Tier and quotas are per organization (not per project) — `set` is organization-wide, `status.pool_usage` is the pooled total across every project in the organization. `set` refetches status after the call and includes it as `status_after`. - `run402 tier status` - `run402 tier set ` ### credentials Two surfaces under one command. `credentials ` acts on PROJECT CREDENTIALS (rows on the gateway); `credentials project-keys ` acts on the LOCAL key cache on this machine. A project credential (`r402_…`) is named, listable, expiring and individually revocable, and several may be live per kind at once — that overlap is how you rotate with no downtime. It replaces the derived `anon_key`/`service_key`, which come from the platform signing key, never expire, and cannot be revoked one at a time. Secrets are returned EXACTLY ONCE, from `issue`, `rotate` and `token`. Full JSON goes to stdout so it can be piped (`| jq -r .secret`); warnings go to stderr. Never write one of these responses to a result cache or tmp file. - `run402 credentials status [--project ]` — am I still on the retiring key? Returns `state: "legacy"|"rotatable"`, `rotatable_credentials`, `credentials[]`, and `retirement.gated_on[]`. `retirement.deadline` is ALWAYS null and that is deliberate: retirement is gated on conditions (every project migrated, 30 consecutive days of zero legacy-key use, explicit owner approval), never a date — do not plan against one. Needs only `project.read`, so automation can check its own posture. - `run402 credentials issue --kind --name [--project ] [--expires ] [--import]` — mint one; secret printed ONCE. `--name` must be unique among LIVE credentials; re-using a live name returns `409 CREDENTIAL_NAME_TAKEN`, and that collision IS the idempotency story (a retried create never silently mints a second credential). `--expires` must be in the future and within one year. `--import` additionally writes the minted secret into this machine's local key cache (what deploys and data-plane commands read) — the cold-restart re-key path in one command per kind instead of issue-then-`project-keys import`. A first `--kind anon --import` on a machine with no cached entry still needs a service key first (the same rule as `project-keys import`), and that refusal comes BEFORE the mint, so a show-once secret is never burned on a usage error. - `run402 credentials list [--project ] [--include-revoked]` — metadata only; never a secret or a secret hash. - `run402 credentials rotate [--project ]` — mint a replacement and revoke the old one in one transaction, keeping the name and recording `replacement_of`. New secret printed ONCE. For a rotation with NO downtime window, prefer issuing a second credential, deploying it, then revoking the first; use `rotate` when the old secret is already compromised. - `run402 credentials revoke [--project ] [--reason ]` — immediate, and frees the name for reuse. - `run402 credentials token [--project ] [--kind ]` — mint a SHORT-LIVED token (defaults to `service`). This is the cold-restart recovery path and the ONE credential call an agent can make with no human present: a grant key is accepted. No step-up, because there is nobody to prompt; what it returns expires, so it cannot become a durable root. Authority split: `issue`/`rotate`/`revoke` require owner membership on the project's owning org PLUS a fresh step-up, and a grant key can NEVER satisfy them — otherwise a scoped agent credential could escalate itself into a permanent root. Authenticate with a wallet (SIWX) or a sign-in session (`run402 login`). `token` is the deliberate exception. - `run402 credentials project-keys list` — LOCAL CACHE read. Lists cached project-key entries with `source: "local_cache"`, `cache_path`, `wallet`/`profile`, key presence, prefixes, fingerprints, and timestamps. Never prints full keys. - `run402 credentials project-keys status --project ` — LOCAL CACHE read for one project id. `configured: false` means this selected wallet/profile lacks cached keys; it does not mean the server project is missing. - `run402 credentials project-keys import --project --service-key-stdin` — import a service key from stdin. Optional anon key comes from `--anon-key-env `. - `run402 credentials project-keys import --project --service-key-env ` — import a service key from an environment variable. Do not pass service keys as argv values. - `run402 credentials project-keys import --project --anon-key-env ` — anon-only rotation. Import writes the whole cache entry, so the FIRST import for a project must supply a service key; once an entry exists, `--anon-key-env` alone rotates the anon key and keeps the cached service key. Rotating the anon key therefore never requires exporting the service key with `--reveal` and passing it back through a shell. Passing `--anon-key-env` with no cached service key fails with `BAD_USAGE` naming that flag. - `run402 credentials project-keys export --project --reveal` — print cached secret key material. Requires `--reveal`. - `run402 credentials project-keys delete --project ` — delete one local cache entry without deleting or changing the server project. ### projects - `run402 projects quote` - `run402 projects list [--org ] [--all]` — SERVER read of the named, domain-aware inventory (NOT the local project-key cache). Membership-scoped by default: every project owned by an org your wallet is an active member of, each row `{ project_id, name, site_url, custom_domains, org_id, status, active }` (`active` from local state). `--org ` filters to one org (authorize-before-reveal: non-member/guessed id -> 403, non-UUID -> 400). `--all` reads every project you can reach across all your orgs (`GET /agent/v1/me/projects`) — as your sign-in session after `run402 login`, else as the current wallet — and echoes `scope`. Bare `run402 projects list` is the cold-start path (no login needed). Tier/lifecycle live on the organization — use `run402 status` / `run402 tier status`. - `run402 projects rename --name