# Cairn CLI Design Specification
This document provides a comprehensive specification for the Cairn CLI. It is language-agnostic and contains all necessary details to rebuild the CLI from scratch in any programming language (e.g., Go, Python, TypeScript).
## 1. Overview and Principles
**Cairn** (`cairn`) is an agent-native command-line tool for autonomous settlement workflows.
1. **API-First**: The CLI is a pure presentation layer over the Backpac REST APIs. It holds no complex state other than saved configuration and JWT authentication credentials.
2. **Machine-Readable**: All command outputs default to structured JSON. A `--output=text` flag is supported for flat shell piping.
3. **No Interactive Prompts**: The CLI must never block for interactive `stdin` prompts unless strictly necessary. It expects all inputs via arguments or flags.
4. **Deterministic Exits**: It strictly adheres to a predefined set of 16 exit codes for machine-readable error handling.
## 2. Configuration and State
The CLI manages local state in the user's home directory under `~/.backpac/`.
### `~/.backpac/config.json`
Stores user preferences and defaults.
```json
{
"chain": "ethereum",
"network": "mainnet",
"tier": "standard",
"default_confirmations": 24,
"default_rebroadcast_max": 3,
"default_valid_for": 600,
"key_file": null,
"credentials_path": null,
"output": "json",
"api_url": "https://api.backpac.xyz"
}
```
### `~/.backpac/credentials.json`
Stores the active authentication session.
```json
{
"jwt": "eyJhbGci...",
"agent_id": "agent_123...",
"wallet": "0xABC...",
"expires_at": "2026-12-31T23:59:59Z"
}
```
## 3. Global Flags Configuration
The following flags must be available on *all* commands globally:
| `--output` | `CAIRN_OUTPUT` | Output formatting (`json` or `text`) | `json` |
| `--quiet`, `-q` | None | Suppress all `stdout`/`stderr` outputs, exit code only | `false` |
| `--api-url` | `BACKPAC_API_URL` | Override the default API base URL | none |
| `--jwt` | `BACKPAC_JWT` | Complete override of the saved JWT token | none |
| `--chain` | `CAIRN_CHAIN` | Chain identifier (e.g., `ethereum`) | from config |
| `--network` | `CAIRN_NETWORK` | Network within chain (e.g., `mainnet`) | from config |
## 4. HTTP Client and Auth Injection
1. **Base URL**: Resolve via `--api-url` -> `BACKPAC_API_URL` -> `config.json` -> fallback to `https://api.backpac.xyz`.
2. **JWT Injection**: Resolve via `--jwt` -> `BACKPAC_JWT` -> `~/.backpac/credentials.json`.
3. If a JWT is found, inject it as an HTTP header: `Authorization: Bearer <jwt>`.
4. All requests must send `Content-Type: application/json`.
## 5. Command Hierarchy and API Mapping
### `auth`
| `auth challenge` | `--wallet`, `--chain`, `--did` | `POST /v1/agents/challenge` | Request an EIP-4361 signing challenge payload. |
| `auth connect` | `--wallet`, `--chain`, `--did`, `--nonce`, `--signature`, `[--key_file]` | `POST /v1/agents/connect` | Submit challenge signature. Saves JWT to `credentials.json`. Auto-signs if `--key_file` provided. |
| `auth refresh` | None | `POST /v1/agents/refresh` | Re-mints JWT using the currently valid JWT. Updates `credentials.json`. |
| `auth status` | None | `GET /v1/agents/status` | Checks token health, expiry, and scopes. |
### `identity`
| `identity register` | `--did`, `--wallet`, `--public-key`, `[--display-name]` | `POST /v1/agents/identity` | Anchor a DID to the wallet. |
| `identity rotate` | `--did`, `--current-key`, `--new-key` | `PUT /v1/agents/identity/rotate` | Rotate the Ed25519 signing key for the DID. |
| `identity get` | `<did>` | `GET /v1/agents/identity/:did` | Look up state and current key for a registered DID. |
### `poi` (Proof of Intent)
| `poi create` | `[--chain]`, `[--network]`, `[--parent]`, `[--max-depth]`, `[--ttl]`, `[--metadata]` | `POST /v1/pois` | Create a new Proof of Intent tracking record. |
| `poi get` | `<poi_id>` | `GET /v1/pois/:poi_id` | Retrieve an existing PoI. |
### `intent`
| `intent send` | `--method`, `--params`, `[--poi-id]`, `[--confidence]`, `[--id]` | `POST /` (RPC Route) | Submit JSON-RPC payload. Binds to PoI if `--poi-id` provided (via `X-Backpac-Poi-Id` header). |
| `intent status` | `<intent_id>` | `GET /v1/intents/:intent_id` | Check state of a specific execution intent. |
| `intent verify` | `<intent_id>`, `[--receiver-did]`, `[--min-confidence]` | `GET /v1/intents/:intent_id/verify` | Receiver-side verification endpoint for a settled PoI. |
| `intent wait` | `<intent_id>`, `[--interval]`, `[--timeout]` | Polling `GET /v1/intents/:intent_id` | Client-side poll until status is `FINALIZED`, `ABORTED`, or `EXPIRED`. |
| `intent list` | `[--status]`, `[--since]`, `[--limit]` | `GET /v1/intents` | List and filter authenticated agent's intents. |
### `watch`
| `watch intent` | `<intent_id>` | `GET /v1/intents/:intent_id/stream` | Stream live SSE state transitions for a specific intent. |
| `watch agent` | None | `GET /v1/agents/stream` | Stream live SSE account-wide agent events and notifications. |
### `proof`
| `proof get` | `<intent_id>`, `[--include-telemetry]`, `[--include-children]`, `[--verify-signature]`, `[--raw]` | `GET /v1/proofs/:intent_id` | Fetches the structured cryptographic PoT bundle. |
| `proof verify` | `<intent_id>` | `GET /v1/proofs/:intent_id` + `GET /.well-known/jwks.json` | Fetches bundle, fetches JWKS from issuer, performs local Ed25519 cryptographic signature verification against the payload hash. |
### `receive`
| `receive` | `--poi-id`, `[--from]`, `[--recipient]`, `[--expect-value]`, `[--require-finalized]` | `GET /v1/pois/:poi_id` | Combined fetch and verify step for receiver agents. Validates sender, recipient, and value contexts. |
### `config`
| `config set` | `<key>`, `<value>` | Local config.json | Edits values in `~/.backpac/config.json`. |
| `config get` | `[<key>]` | Local config.json | Fetches a specific value, or dumps the full configuration structure. |
## 6. Exit Codes
Errors must be strictly mapped from HTTP responses to integer exit codes to allow programmatic behavior.
| Success | `0` | `200`, `201`, `204` |
| General Error (Serialization, IO) | `1` | `500` or OS-level errors |
| Value Mismatch | `2` | PoI context validation failure |
| Not Finalized | `3` | `require-finalized` set but state is pending |
| Forbidden / Unauthorized | `4` | `401 Unauthorized` / `403 Forbidden` |
| Not Found | `5` | `404 Not Found` |
| Conflict | `6` | `409 Conflict` |
| Intent Expired | `7` | `410 Gone` (if message contains "expired") |
| Intent Aborted | `8` | `410 Gone` |
| Chain Depth Exceeded | `9` | Payload constraint check |
| Operation Timeout | `10` | `intent wait` duration exceeded |
| Signature Verification Error | `11` | Cryptographic match failure |
| PoT Not Ready | `12` | Proof missing signatures/payloads |
| Network Error | `13` | Unreachable endpoint, connection reset |
| Token Expired | `14` | `401 Unauthorized` (if message contains "expired") |
| Challenge Expired | `15` | Spec constraint |
| Insufficient Funds / x402 | `16` | `402 Payment Required` |
## 7. Cryptographic Requirements
To rebuild this CLI, your language must support the following cryptographic operations:
1. **EIP-4361 (Sign-In with Ethereum) Message Signing**: For authentication connecting a wallet. Standard `secp256k1` keccak256 signature recovery.
2. **Ed25519 Signing / Verification**: For DID usage (`did:key` base58 encoding) and local `proof verify` bundle verification using a remote JSON Web Key Set (JWKS).