# Nukez agent flow (HTTP-only, realistic conditions)

This document is intentionally concrete and operational. If you follow it, you can discover Nukez, decide to use it, and interact with it using **HTTP endpoints only** (plus standard Ed25519 or secp256k1 signing for auth).

## 0) Discover the contract

Recommended order:

- `GET https://api.nukez.xyz/.well-known/nukez.json`
- Then fetch `openapi.json` and `tools.json` from the URLs it references.

The discovery doc is the "table of contents" and is meant to be stable across UI changes.

## 1) Price check (optional)

```http
GET https://api.nukez.xyz/v1/price
```

Use this to estimate cost in SOL (or the native asset for your chosen chain).

## Receipt reuse guardrail (before purchase)

If your runtime already has a valid `receipt_id` + `locker_id`, reuse that locker for file operations.

Do not run a new `/v1/storage/request` flow unless the user explicitly asks to buy additional storage.

## 2) Request storage (x402 challenge)

```http
POST https://api.nukez.xyz/v1/storage/request
Content-Type: application/json
Idempotency-Key: <optional-uuid>

{"units":1}
```

Optional provider selection:

```json
{"units":1,"provider":"gcs"}
```

Supported values: `gcs`, `mongodb`, `storj`, `arweave`, `filecoin`, `firestore`.

Important: you may receive **HTTP 402** with a JSON body. Treat it as success for this step.

### The 402 response shape (x402-v2 `accepts[]`)

The 402 body is a standard x402-v2 payment request. Each entry in `accepts[]` is a single payment leg — one (network, asset) pair you can pay with. Networks use CAIP-2 identifiers (`solana:<genesis>` for Solana mainnet, `eip155:<chainId>` for EVM chains).

```json
{
  "x402Version": 2,
  "resource": {
    "url": "https://api.nukez.xyz/v1/storage/provision",
    "description": "Nukez storage locker provisioning",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "amount": "214846000",
      "asset": "So11111111111111111111111111111111111111112",
      "payTo": "HqrTLaNk89dHPVR5zyMPtEiRnQfabmqc8B9yR3KVbyVp",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "SOL",
        "decimals": 9,
        "human_amount": "0.214846",
        "pay_req_id": "f9d7a30edc2d",
        "quote_schema": "nukez_quote_v4",
        "oracle_rate": {"sol_usd": 93.09}
      }
    },
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "amount": "20000000",
      "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "payTo": "GT8UaKnQ9hM54assxCbhKYBUWtUarb6fxmu4EcrPMeMq",
      "maxTimeoutSeconds": 300,
      "extra": {"name": "USDC", "decimals": 6, "human_amount": "20.0", "pay_req_id": "f9d7a30edc2d", "quote_schema": "nukez_quote_v4"}
    },
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "amount": "1",
      "asset": "GBVdmQpooUmETgS8TiEqP9zuk3ajky2mabHrvSbYjR6s",
      "payTo": "7uuKkouh1PqEncfocPsbYVmW1arm2xk6sRmmdmBPa6uf",
      "maxTimeoutSeconds": 300,
      "extra": {"name": "BETA", "decimals": 0, "human_amount": "1", "pay_req_id": "f9d7a30edc2d", "quote_schema": "nukez_quote_v4"}
    },
    {
      "scheme": "exact",
      "network": "eip155:143",
      "amount": "578871202000000000000",
      "asset": "0x0000000000000000000000000000000000000000",
      "payTo": "0x744C9B35F7625A86ba75d2B62F9D0e257Dd43c2a",
      "maxTimeoutSeconds": 300,
      "extra": {"name": "MON", "decimals": 18, "human_amount": "578.871202", "pay_req_id": "f9d7a30edc2d", "quote_schema": "nukez_quote_v4", "oracle_rate": {"mon_usd": 0.03455}}
    }
  ],
  "error": "Payment required for storage provisioning",
  "extensions": {
    "nukez": {
      "terms": { "storage_limit_bytes": 10737418240, "ttl_days": 30, "...": "..." },
      "instructions": { "cli_example": "solana transfer <payTo> <human_amount> ..." },
      "price_summary": {"unit_price_usd": 20.0, "units": 1, "total_usd": 20.0, "provider": "gcs"}
    }
  }
}
```

Network identifiers in `accepts[].network` (CAIP-2 ↔ friendly-name mapping; the discovery doc's `multi_chain.supported_networks` is the source of truth):

| CAIP-2 (`network`) | Friendly name (for confirm body) | sig_alg |
|---|---|---|
| `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | `solana-mainnet` | ed25519 |
| `eip155:143` | `monad-mainnet` | secp256k1 |

Currently supported assets (live):
- **Solana mainnet**: SOL, USDC, USDT, WETH, BETA
- **Monad mainnet**: MON, USDC, USDT0, WETH

BETA is flat 1-token-per-unit (no oracle, decimals 0); pay by SPL transfer to the BETA ATA in that leg's `payTo`.

Agents should:
1. Inspect `accepts[]` to enumerate available legs.
2. Pick a leg, then transfer `amount` (atomic units) of `asset` to `payTo` on that `network`.
3. Capture `extra.pay_req_id` — it's the same across all legs in one request and is what you send to `/v1/storage/confirm`.
4. In the confirm body, echo `pay_asset` (and `pay_network` if you picked a non-default leg). Aliases `payment_asset`/`payment_chain` are accepted but `pay_asset`/`pay_network` are canonical.

## 3) Pay on chain (external step)

For your chosen leg of `accepts[]`, transfer exactly the leg's `amount` (atomic units of `asset`) to `payTo` on `network`. The leg's `extra.human_amount` is the same value in display units.

### Solana payment flow

- Native SOL: `solana transfer <payTo> <extra.human_amount> --url <RPC>` (or equivalent in your SDK)
- SPL token (USDC, USDT, WETH, BETA): SPL transfer of `amount` atomic units of mint `asset` to the ATA address in `payTo` (the gateway hands you the ATA, not the wallet — that's the destination for SPL transfers)

Capture the resulting transaction signature → `tx_sig` (base58).

### EVM payment flow (Monad mainnet)

- Native MON (asset = `0x0000…0000`): standard EVM value transfer of `amount` wei to `payTo`
- ERC-20 (USDC, USDT0, WETH): `transfer(payTo, amount)` on the token contract at `asset`

Capture the resulting transaction hash → `tx_sig` (0x-prefixed hex; serves the same role as a Solana signature in confirm).

## 4) Confirm storage

The confirm endpoint works with both Solana transaction signatures and EVM transaction hashes.

**Preferred method (X402-TX header):**

```http
POST https://api.nukez.xyz/v1/storage/confirm
Content-Type: application/json
X402-TX: <tx_sig>

{"pay_req_id":"<pay_req_id>"}
```

**With agent-selected payment leg:**

If the agent picked a non-default leg from `accepts[]`, echo it in the confirm body. Canonical field names are `pay_network` and `pay_asset`; the aliases `payment_chain` and `payment_asset` are accepted for backward compatibility but new code should use the canonical names.

```http
POST https://api.nukez.xyz/v1/storage/confirm
Content-Type: application/json
X402-TX: <tx_sig>

{"pay_req_id":"<pay_req_id>", "pay_network":"monad-mainnet", "pay_asset":"MON"}
```

The server validates the agent's choice against the legs offered in the quote. If the (network, asset) pair wasn't in `accepts[]`, confirm returns 400 with message `<ASSET> on <NETWORK> not in quote`. The fallback-to-quote-default behavior only kicks in when both `pay_network` and `pay_asset` are omitted — paying any non-default leg requires explicitly echoing them.

**Fallback method (tx_sig in body):**

If your HTTP tool cannot set custom headers, include `tx_sig` in the JSON body:

```http
POST https://api.nukez.xyz/v1/storage/confirm
Content-Type: application/json

{"pay_req_id":"<pay_req_id>", "tx_sig":"<tx_sig>"}
```

Returns a permanent receipt:
- `receipt_id` (save it)
- `receipt` object (may include proof fields)
- `idempotent` boolean (true if you already confirmed earlier)
- `paid_amount` (chain-agnostic amount paid)
- `paid_raw` (raw amount in smallest denomination: lamports or wei)
- `pay_asset` (e.g., `SOL`, `USDC`, `USDT`, `USDT0`, `MON`, `WETH`, `BETA`)
- `network` (e.g., `solana-mainnet`, `monad-mainnet`)

If you get `tx_not_found`, retry with exponential backoff (2s, 4s, 8s, 16s, 32s). Note: EVM chains may require longer initial delays due to block confirmation times.

**Authentication: Signed Envelopes Only**

All authenticated Nukez endpoints require signed envelope headers. Cap tokens have been eliminated.

For each file operation:
1. Construct the signed envelope (using a signing helper if your runtime provides one, or manually)
2. Make HTTP request with the `X-Nukez-Envelope` and `X-Nukez-Signature` headers attached
3. If your signing helper returns a `body_canonical` string, use that exact string as the request body

Warning: You MUST include the headers in the HTTP request. If you construct the envelope but don't attach the headers, you'll get HTTP 422.

Do NOT call `/challenge` or `/login` — those endpoints are deprecated.

## 5) Provision a locker (file-first)

### 5.1 Compute deterministic locker_id

Locker IDs are derived from the receipt_id:

```
locker_id = "locker_" + sha256_hex(receipt_id)[:12]
```

### 5.2 Create a signed envelope

For this endpoint, required ops are:

- `locker:provision`

Headers:
- `X-Nukez-Envelope`: base64url(canonical_json(envelope))
- `X-Nukez-Signature`: base58(ed25519_sign(envelope_bytes)) (Solana) or hex(secp256k1_sign(envelope_bytes)) (EVM)

See `/docs/AUTH_SIGNED_ENVELOPE.md` for precise rules and supplemental information below, but before section 5.3.

#### Tool-runner pattern (common in agent runtimes)

If your environment uses a separate "signing tool" (e.g., `build_signed_envelope`) and a separate HTTP tool (e.g., `api_request`), treat the signing tool output as *authoritative* for the next request.

Rules:
1) Whatever `build_signed_envelope` returns as headers MUST be attached verbatim to the next HTTP request.
2) If `build_signed_envelope` returns a canonical body string (often named `body_canonical`), send that exact string as the HTTP request body bytes. Do not re-serialize JSON with different spacing/key order.
3) A 422 from a signed-envelope endpoint almost always means "missing/invalid envelope headers" (or body hash mismatch). Rebuild the envelope using the exact body bytes you will send, then retry once.

Example (tool-runner pseudocode):

1) Build envelope for signed_provision:

{
  "tool": "build_signed_envelope",
  "args": {
    "receipt_id": "<receipt_id>",
    "method": "POST",
    "path": "/v1/storage/signed_provision",
    "operations": ["locker:provision"],
    "body": {"receipt_id":"<receipt_id>","tags":[]}
  }
}

2) Use the returned headers + canonical body for the HTTP call:

{
  "tool": "api_request",
  "args": {
    "method": "POST",
    "url": "https://api.nukez.xyz/v1/storage/signed_provision",
    "headers": <headers_returned_by_build_signed_envelope>,
    "body_bytes": "<body_canonical_returned_by_build_signed_envelope>"
  }
}

### 5.3 Call signed_provision

```http
POST https://api.nukez.xyz/v1/storage/signed_provision
Content-Type: application/json
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>

{"receipt_id":"<receipt_id>","tags":[]}
```

Returns a manifest. Treat these fields as runtime truth:
- `space.locker_id`
- `space.file_api` (paths for create/list/get/delete)
## 5.4 Authentication for file operations

All file operations use signed envelopes:
- Sign each request with `X-Nukez-Envelope` + `X-Nukez-Signature`
- Strongest request binding (method/path/body hash bound to signature)
- Include `sig_alg` in envelope: `"ed25519"` (Solana) or `"secp256k1"` (EVM)

## 6) Create a file (mint short URLs)

All file endpoints require signed envelope authentication.

### 6.1 Create file

```http
POST https://api.nukez.xyz/v1/lockers/<locker_id>/files
Content-Type: application/json
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>

{"filename":"notes.txt","content_type":"text/plain","ttl_min":30}
```

Notes:
- `filename` is optional; server can generate one.
- `ttl_min` controls how long the returned short URLs are valid.
- Returned URLs are provider-opaque short URLs (`https://api.nukez.xyz/f/{token}`).

Response includes:
- `upload_url` (+ optional `upload_headers`)
- `download_url` (+ optional `download_headers`)
- `urls_expire_in_sec`

### 6.1b Create files in batch (recommended for N>1)

```http
POST https://api.nukez.xyz/v1/lockers/<locker_id>/files/batch
Content-Type: application/json
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>

{
  "ttl_min": 30,
  "files": [
    {"filename":"doc.html","content_type":"text/html"},
    {"filename":"guide.md","content_type":"text/markdown"},
    {"filename":"logo.png","content_type":"image/png"}
  ]
}
```

Use this when uploading multiple files because it:
- requires one signed request instead of N create calls
- returns all upload URLs at once
- preserves per-file content-type locks

After uploading all bytes, finalize hashes in one call:

```http
POST /v1/files/confirm-batch?receipt_id=<receipt_id>&filenames=doc.html&filenames=guide.md&filenames=logo.png
```

### 6.2 Upload bytes (raw HTTP)

```http
PUT <upload_url>
Content-Type: text/plain

hello world
```
## 6.3 Create a file: signed envelope method

### Step 1: Build the envelope

Call your signing tool:
```json
{
  "receipt_id": "",
  "method": "POST",
  "path": "/v1/lockers//files",
  "ops": ["locker:write"],
  "body": {"filename": "mydata.txt", "content_type": "text/plain"}
}
```

### Step 2: Make the HTTP request

Use the EXACT headers and body_canonical returned by the signing tool:
```http
POST /v1/lockers//files
Content-Type: application/json
X-Nukez-Envelope:
X-Nukez-Signature:

```

### Step 3: Upload to short URL

Response includes `upload_url` (a short URL). PUT your data there:
```http
PUT
Content-Type: text/plain

Hello from autonomous agent!
```

### Common Mistakes

- Calling /challenge between build_signed_envelope and POST
- Re-serializing the body instead of using body_canonical
- Adding Authorization header when using signed envelope
- Assuming cloud-provider URLs will be visible (they are intentionally hidden)

If the response included `upload_headers`, include them exactly.

## 7) Download bytes

If you still have a valid `download_url`:

```http
GET <download_url>
```

If it expired, refresh URLs via the API:

```http
GET https://api.nukez.xyz/v1/lockers/<locker_id>/files/notes.txt?ttl_min=30
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>
```

Then `GET` the new `download_url`.

## 8) List files

```http
GET https://api.nukez.xyz/v1/lockers/<locker_id>/files
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>
```

## 9) Delete a file

```http
DELETE https://api.nukez.xyz/v1/lockers/<locker_id>/files/notes.txt
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>
```

## 10) Receipt-based file proxy (stable URLs)

Files can be accessed without auth via permanent receipt-based proxy URLs:

```http
GET https://api.nukez.xyz/v1/r/<receipt_id>/f/<filename>
```

These URLs:
- Require no authentication headers
- Have no TTL expiry (permanent as long as locker exists)
- Return the file content directly with appropriate Content-Type
- Include CORS headers for browser access
- Are ideal for embedding, sharing, and viewer rendering

Use these when you need stable, shareable links to files. For time-limited signed URLs (for upload/download operations), use the file creation endpoints.

## 11) Verification & attestation (optional)

- Verify that storage matches the expected hash:

```http
POST https://api.nukez.xyz/v1/storage/verify
Content-Type: application/json

{"receipt_id":"<receipt_id>"}
```

- Get an attestation code (requires result_hash to exist):

```http
GET https://api.nukez.xyz/v1/attest-code?receipt_id=<receipt_id>
```

If you get `missing_result_hash_run_storage_verify_first`, run `/v1/storage/verify` first.

**Note on `att_code`**: this is a compact display value derived from `result_hash` (NOT directly from `merkle_root`) and is intended for UI/oracle convenience — it is **not a security primitive**. For cryptographic verification use `result_hash`, `merkle_root`, the file entries, and the on-chain anchors. See the canonical Merkle V1 spec at [/docs/verify/merkle-v1.md](/docs/verify/merkle-v1.md) (machine-readable: [/specs/nukez-merkle-v1.json](/specs/nukez-merkle-v1.json)).

## 12) Confirm upload (optional)

After uploading bytes to `upload_url`, you can explicitly confirm the upload to compute and record the content hash:

```http
POST https://api.nukez.xyz/v1/files/confirm
Content-Type: application/json
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>

{"receipt_id":"<receipt_id>","filename":"notes.txt"}
```

Returns:
- `size_bytes` — confirmed file size
- `content_hash` — SHA-256 hash of uploaded content (sha256:... prefixed)

Note: Many providers auto-confirm on upload. This endpoint is primarily useful for sandbox/chunked upload paths, explicit integrity verification, and content-addressed providers (Arweave, Filecoin).

## 13) Operator delegation (optional)

Operators are Ed25519 keys that can perform file operations on behalf of the locker owner. This enables cross-chain delegation (e.g., an EVM/secp256k1 owner delegates to an Ed25519 operator for file operations).

### 13.1 Add an operator at provision time

Include `operator_pubkey` in the signed_provision body:

```http
POST https://api.nukez.xyz/v1/storage/signed_provision
Content-Type: application/json
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>

{"receipt_id":"<receipt_id>","tags":[],"operator_pubkey":"<ed25519_pubkey_base58>"}
```

### 13.2 Add an operator after provisioning

```http
POST https://api.nukez.xyz/v1/lockers/<locker_id>/operators
Content-Type: application/json
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>

{"pubkey":"<ed25519_pubkey_base58>"}
```

Required ops: `["locker:admin"]`. Only the locker owner can add operators.

### 13.3 Remove an operator

```http
DELETE https://api.nukez.xyz/v1/lockers/<locker_id>/operators/<pubkey>
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>
```

Required ops: `["locker:admin"]`.

### 13.4 Using operator auth

Once registered, an operator signs requests with their own Ed25519 key using the same signed envelope format. The gateway verifies the operator's signature against the locker's operator list.

## 14) Verification bundle (optional)

Get a self-contained verification package with all proof components:

```http
GET https://api.nukez.xyz/v1/storage/verification-bundle?receipt_id=<receipt_id>
```

No authentication required. Returns payment proof, content proof, verification algorithm spec, and DIY verification steps.

## 15) Fetch and store from URL (optional)

Store content from an external HTTPS URL directly without downloading through the agent:

```http
POST https://api.nukez.xyz/v1/files/fetch-and-store
Content-Type: application/json
X-Nukez-Envelope: <...>
X-Nukez-Signature: <...>

{"locker_id":"<locker_id>","receipt_id":"<receipt_id>","source_url":"https://example.com/data.json"}
```

The server fetches the URL server-side and stores the content. SSRF-protected: only HTTPS URLs to public hosts are allowed.

## 16) Merkle inclusion proof (optional)

Prove that a specific file is included in the attested merkle tree without needing all other files:

```http
GET https://api.nukez.xyz/v1/storage/merkle-proof?receipt_id=<receipt_id>&filename=<filename>
```

No authentication required. Returns:
- `leaf_hash` — SHA256 hash of the target file's leaf node
- `proof` — list of `{hash, position}` sibling steps from leaf to root
- `merkle_root` — the attested root (matches on-chain attestation)

**Verification algorithm:**
1. Start with `leaf_hash`
2. For each step in `proof`:
   - If `position == "right"`: `current = SHA256(current + step.hash)`
   - If `position == "left"`: `current = SHA256(step.hash + current)`
3. Final `current` must equal `merkle_root`

**Canonical reference**: [/docs/verify/merkle-v1.md](/docs/verify/merkle-v1.md) is the normative spec — covers leaf hashing, sort order, parent formula, odd-level duplication, empty-list invalidity, on-chain anchors (Solana/Switchboard + Monad), and a conformance test vector. Machine-readable form: [/specs/nukez-merkle-v1.json](/specs/nukez-merkle-v1.json).

Use this when you need to verify a single file's membership without downloading or hashing all other files in the locker.
