{
  "$schema": "https://docs.nukez.xyz/schemas/nukez.discovery.v1.schema.json",
  "type": "nukez.discovery.v1",
  "name": "Nukez",
  "version": "phase6-v6-2026-09-23",
  "service": {
    "provider": "Nukez",
    "canonical_base_url": "https://api.nukez.xyz",
    "environment": "production",
    "description": "Protocol-first, HTTP-native, pay-per-use storage for agents (x402 receipts + signed requests + provider-opaque short URLs). Multi-chain payments (Solana and EVM/Monad). Operator delegation for cross-chain access control.",
    "updated_utc": "2026-09-23"
  },
  "discovery": {
    "well_known": "/.well-known/nukez.json",
    "http_link_headers": [
      {
        "rel": "service-meta",
        "href": "/.well-known/nukez.json",
        "type": "application/json",
        "title": "Nukez discovery document"
      },
      {
        "rel": "service-doc",
        "href": "/docs/AUTH_SIGNED_ENVELOPE.md",
        "type": "text/markdown",
        "title": "Signed envelope authentication (CRITICAL for authenticated endpoints)"
      },
      {
        "rel": "service-desc",
        "href": "/openapi.json",
        "type": "application/vnd.oai.openapi+json",
        "title": "Nukez OpenAPI (canonical bundle)"
      },
      {
        "rel": "service-desc",
        "href": "/v1/tools.json",
        "type": "application/json",
        "title": "Nukez tool schema (LLM-friendly)"
      },
      {
        "rel": "service-doc",
        "href": "/docs/AGENT_BRIEFING.md",
        "type": "text/markdown",
        "title": "Agent briefing"
      },
      {
        "rel": "service-doc",
        "href": "/docs/AGENT_FLOW.md",
        "type": "text/markdown",
        "title": "Agent flow"
      },
      {
        "rel": "status",
        "href": "/health",
        "type": "application/json",
        "title": "Service status"
      },
      {
        "rel": "llms",
        "href": "/llms.txt",
        "type": "text/plain",
        "title": "LLM index (short)"
      }
    ]
  },
  "agent_start_here": {
    "summary": "For autonomous agents: Read tools.json first, then AGENT_FLOW.md. OpenAPI specs are supplementary.",
    "recommended_reading_order": [
      {
        "order": 1,
        "url": "/v1/tools.json",
        "why": "LLM-optimized tool definitions with full auth guidance and flow descriptions"
      },
      {
        "order": 2,
        "url": "/docs/AGENT_FLOW.md",
        "why": "Step-by-step walkthrough of the complete storage flow"
      },
      {
        "order": 3,
        "url": "/docs/AUTH_SIGNED_ENVELOPE.md",
        "why": "CRITICAL: Read before calling any authenticated endpoint"
      }
    ],
    "critical_auth_warning": {
      "issue": "HTTP 422 from signed_envelope endpoints (e.g., /v1/storage/signed_provision)",
      "cause": "Missing X-Nukez-Envelope and X-Nukez-Signature HTTP headers",
      "common_mistake": "Constructing the signed envelope correctly but then making an HTTP request WITHOUT attaching the headers",
      "fix": "However you construct the envelope, the X-Nukez-Envelope and X-Nukez-Signature values MUST be included as HTTP headers. If your HTTP tool has a 'headers' parameter, use it.",
      "status_code_divergence": "The missing-header status code differs by route, and both behaviors are intentional. On most signed endpoints (including /v1/storage/attest, /v1/attest/push, and /v1/storage/recompute-verify) the two headers are declared required, so a request with the headers entirely missing returns 422 VALIDATION_ERROR, and the 401 family fires only when headers are present but fail verification. On /v1/files/confirm and /v1/files/confirm-batch, missing headers deliberately return 401 SIGNED_ENVELOPE_REQUIRED once enforcement is active."
    },
    "do_not_read_first": [
      "/openapi.json - verbose, prefer tools.json"
    ]
  },
  "contracts": {
    "recommended": {
      "primary": {
        "url": "/v1/tools.json",
        "notes": "START HERE. LLM-optimized tool definitions with auth guidance, error handling, and flow descriptions."
      },
      "auth_docs": {
        "url": "/docs/AUTH_SIGNED_ENVELOPE.md",
        "notes": "MUST READ before calling signed_envelope endpoints. Explains header construction."
      }
    },
    "openapi": {
      "canonical": {
        "url": "/openapi.json",
        "media_type": "application/vnd.oai.openapi+json",
        "notes": "Full OpenAPI bundle. Verbose - prefer tools.json for LLM consumption."
      },
      "extra": []
    },
    "verification_spec": {
      "name": "nukez-merkle-v1",
      "schema_version": "1.0",
      "status": "normative",
      "canonical_doc": "/docs/verify/merkle-v1.md",
      "machine_readable": "/specs/nukez-merkle-v1.json",
      "media_type": "application/json",
      "notes": "Canonical Merkle / attestation spec for third-party independent verification. Covers leaf hashing, sort order, parent formula, odd-level duplication, result_hash, att_code (a nine-digit integer display value only — NOT a security primitive), inclusion proof verification, the historical Solana/Switchboard anchor format plus the Monad anchor format (the Monad bridge is specified but not currently operational in production), and a conformance test vector. att_code is derived from result_hash, not merkle_root; merkle_root is the Ed25519-signed field and the value written to the on-chain SPL Memo. Anchors that the gateway publishes itself are described by evidence_spec."
    },
    "evidence_spec": {
      "name": "nukez-evidence-v2",
      "schema_version": "2.0",
      "status": "normative",
      "machine_readable": "/specs/nukez-evidence-v2.json",
      "media_type": "application/json",
      "notes": "Normative description of the version-two evidence: the signed commitment envelope (RFC 8785 canonical JSON, SHA-256 statement digest, Ed25519 signature over a domain tag plus the digest), the compact statement written in an SPL Memo on Solana, the provider-neutral anchor object, the separate verification facts, the Solana Attestation Service schema nukez.content.v2, the evidence bundle, and the signing key list. The anchor proves that the gateway's published key committed this exact snapshot on Solana at this slot, and, with the attestation service account, that the current commitment is readable on-chain at a predictable address. It does not prove that a third party inspected the files."
    },
    "tools": {
      "preferred": {
        "url": "/v1/tools.json",
        "notes": "LLM-friendly tool definitions. Includes auth requirements and next-step guidance."
      },
      "full": {
        "url": "/v1/tool_contract.json",
        "notes": "Extended tool contract with detailed auth + error recovery guidance."
      }
    },
    "docs": {
      "agent_flow": "/docs/AGENT_FLOW.md",
      "agent_briefing": "/docs/AGENT_BRIEFING.md",
      "auth_signed_envelope": "/docs/AUTH_SIGNED_ENVELOPE.md",
      "error_recovery": "/docs/ERROR_RECOVERY.md",
      "changelog": "/docs/CHANGELOG.md",
      "merkle_v1": "/docs/verify/merkle-v1.md"
    },
    "llm_index": {
      "llms_txt": "/llms.txt",
      "optional": [
        "/llms-full.txt"
      ]
    }
  },
  "endpoints": {
    "health": "/health",
    "docs_swagger": "/docs",
    "docs_redoc": "/redoc",
    "price": "/v1/price",
    "storage_request": "/v1/storage/request",
    "storage_confirm": "/v1/storage/confirm",
    "receipt_get": "/v1/receipts/{receipt_id}",
    "receipt_verify": "/v1/receipts/{receipt_id}/verify",
    "storage_signed_provision": "/v1/storage/signed_provision",
    "locker_file_collection": "/v1/lockers/{locker_id}/files",
    "locker_file_item": "/v1/lockers/{locker_id}/files/{filename}",
    "locker_record": "/v1/lockers/{locker_id}/record",
    "locker_urls": "/v1/lockers/{locker_id}/urls",
    "storage_metadata": "/v1/storage/metadata/{locker_id}",
    "storage_refresh": "/v1/storage/refresh",
    "storage_verify": "/v1/storage/verify",
    "storage_merkle_proof": "/v1/storage/merkle-proof",
    "attest_code": "/v1/attest-code",
    "attest_code_jwt": "/v1/attest-code/jwt",
    "short_url_verify_key": "/v1/short-url/verify-key",
    "short_url_resolve": "/f/{token}",
    "files_confirm": "/v1/files/confirm",
    "files_confirm_batch": "/v1/files/confirm-batch",
    "storage_recompute_verify": "/v1/storage/recompute-verify",
    "files_fetch_and_store": "/v1/files/fetch-and-store",
    "locker_operators": "/v1/lockers/{locker_id}/operators",
    "locker_operator_item": "/v1/lockers/{locker_id}/operators/{pubkey}",
    "locker_manifest": "/v1/lockers/{locker_id}/manifest",
    "locker_files_batch": "/v1/lockers/{locker_id}/files/batch",
    "locker_files_urls": "/v1/lockers/{locker_id}/files/urls",
    "receipt_file_proxy": "/v1/r/{receipt_id}/f/{filename}",
    "receipt_verify_payment": "/v1/receipts/{receipt_id}/verify-payment",
    "storage_verification_bundle": "/v1/storage/verification-bundle",
    "storage_evidence_bundle": "/v1/storage/evidence-bundle",
    "signing_keys": "/v1/keys",
    "signing_keys_well_known": "/.well-known/nukez-signing-keys.json"
  },
  "multi_chain": {
    "supported_networks": [
      {
        "name": "solana-mainnet",
        "pay_asset": [
          "SOL",
          "USDC",
          "USDT",
          "WETH",
          "BETA"
        ],
        "sig_alg": "ed25519",
        "slot_field": "slot"
      },
      {
        "name": "monad-mainnet",
        "pay_asset": [
          "MON",
          "USDC",
          "USDT0",
          "WETH"
        ],
        "sig_alg": "secp256k1",
        "slot_field": "block_number"
      }
    ],
    "request_fields": {
      "pay_network": "Optional network identifier (e.g., 'solana-mainnet', 'monad-mainnet')",
      "pay_asset": "Optional asset to pay with (e.g., 'SOL', 'MON', 'USDC', 'USDT', 'USDT0', 'WETH', 'BETA')"
    },
    "price_endpoint_behavior": "Returns amounts for multiple chains when no pay_network is specified",
    "price_provenance": "Every priced entry of accepts[] carries extra.price_observation: provider ('pyth' on Solana or 'chainlink' on Monad), feed, price, observed_at, retrieved_at, and cross_check. Stablecoin legs are priced at parity and carry no observation. When an asset's live feed is unavailable or the two feeds disagree, that asset is omitted from accepts[] rather than priced from a constant; choose another leg or request a fresh quote.",
    "receipt_fields": {
      "paid_amount": "Human-readable payment amount",
      "paid_raw": "Atomic units (lamports, wei, etc.)",
      "pay_asset": "Asset used for payment",
      "network": "Network where payment was made",
      "slot": "Solana slot (present for Solana payments)",
      "block_number": "EVM block number (present for EVM payments)"
    },
    "confirm_behavior": "X402-TX header carries the tx signature (Solana base58) or tx hash (EVM 0x-hex). Confirm body must include pay_asset (and pay_network for multi-chain) matching the leg actually paid; gateway falls back to the quote's default leg only if both are omitted, so any non-default leg requires explicit pay_asset/pay_network — otherwise confirm returns 400 with '<ASSET> on <NETWORK> not in quote'.",
    "confirm_body_fields": {
      "pay_asset": "Required when paying any non-default leg of accepts[]. Must match the asset actually transferred (e.g., 'USDC', 'BETA', 'WETH'). Aliases: 'payment_asset' is also accepted.",
      "pay_network": "Required for multi-chain quotes when paying a non-default leg. Must match the network actually transferred on (e.g., 'solana-mainnet', 'monad-mainnet'). Aliases: 'payment_chain' is also accepted.",
      "operator_pubkey": "Optional Ed25519 pubkey to bind as locker operator at confirm time.",
      "payer_signature": "Expected whenever operator_pubkey is sent. The payer's signature over the exact UTF-8 bytes 'nukez/confirm/v1|<pay_req_id>|<tx signature or hash>|<operator_pubkey>', with a single hyphen in place of the operator when there is none. Solana payers send an Ed25519 signature in base58. EVM payers send an EIP-191 personal_sign signature in 0x hex. Enforcement is being rolled out in stages (CONFIRM_INTENT_MODE): unsigned requests still work in shadow mode, and they return 401 CONFIRM_INTENT_REQUIRED or CONFIRM_INTENT_INVALID once enforcement is on. The 401 detail carries sign_this, which is the exact text to sign."
    },
    "sig_alg_values": {
      "ed25519": "Solana keypair signatures",
      "secp256k1": "EVM wallet signatures"
    }
  },
  "compute_egress": {
    "required_domains": [
      "api.nukez.xyz"
    ],
    "why": "Sandbox mode uploads file bytes via curl to Nukez short URLs, bypassing LLM context. Without egress, uploads fall back to base64 (limited to ~32KB).",
    "upload_modes": {
      "sandbox": "Agent brokers a short URL, executes curl PUT with ?proxy=1 — binary bytes never enter context (~500 tokens regardless of file size)",
      "inline": "Base64-encoded in tool parameter — suitable for files under ~32KB",
      "url": "Server-side fetch from external HTTPS URL via /v1/files/fetch-and-store"
    },
    "setup_instructions": {
      "claude_ai": "Settings → Capabilities → Network egress → Additional domains → add api.nukez.xyz"
    }
  },
  "usage_guidance": {
    "when_to_use": "Use Nukez for persistent storage over HTTP with cryptographic receipts. No cloud credentials needed — just pay with Solana or EVM (x402) and start storing.",
    "best_for": "Agent-driven file storage, cross-session persistence, verifiable data provenance, multi-chain payment workflows, and any scenario where you want receipts without managing infrastructure.",
    "decision_heuristic": [
      "Need to store bytes for later?",
      "Need a receipt / public verifiability?",
      "No privileged cloud credentials available?",
      "Then Nukez is a good fit."
    ],
    "provider_selection": {
      "request_field": "provider",
      "supported_values": [
        "gcs",
        "mongodb",
        "storj",
        "arweave",
        "filecoin",
        "firestore"
      ],
      "note": "Provider is selected at /v1/storage/request and bound to the locker at provision/confirm."
    }
  },
  "auth": {
    "modes": {
      "public": {
        "description": "No auth headers required."
      },
      "signed_envelope": {
        "description": "Request-bound Ed25519 signature using payer key (envelope + signature headers).",
        "headers": {
          "X-Nukez-Envelope": "base64url(canonical_json(envelope))",
          "X-Nukez-Signature": "base58(ed25519_sign(envelope_bytes))"
        },
        "envelope_fields": [
          "v",
          "locker_id",
          "receipt_id",
          "nonce",
          "iat",
          "exp",
          "ops",
          "method",
          "path",
          "body_sha256"
        ],
        "envelope_construction": {
          "v": "Always 1",
          "locker_id": "Deterministic: 'locker_' + sha256(receipt_id).hex()[:12]",
          "receipt_id": "The receipt_id from confirm_storage response",
          "nonce": "Random 16 bytes as hex string (32 chars)",
          "iat": "Current Unix timestamp (seconds)",
          "exp": "iat + TTL (typically 60 seconds; the gateway rejects any envelope whose TTL exceeds 300 seconds)",
          "ops": "Array of operation strings from endpoint's required_ops (e.g., ['locker:provision'])",
          "method": "HTTP method uppercase (GET, POST, PUT, DELETE)",
          "path": "API path exactly as called (e.g., '/v1/storage/signed_provision')",
          "body_sha256": "SHA256 hex digest of the canonical JSON request body (or empty string hash if no body)"
        },
        "canonical_json": {
          "description": "Canonical JSON serialization for both envelope and request body",
          "rules": [
            "Keys sorted alphabetically",
            "No whitespace between elements",
            "Separators: comma without space, colon without space",
            "UTF-8 bytes, not ASCII-escaped: serialize with ensure_ascii=False (or the equivalent), so non-ASCII characters are sent as raw UTF-8 rather than \\uXXXX escapes"
          ],
          "example_python": "json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode('utf-8')",
          "example_js": "JSON.stringify(obj, Object.keys(obj).sort())"
        },
        "body_hash_critical": {
          "warning": "The body_sha256 in the envelope MUST match the SHA256 of the actual HTTP request body.",
          "common_mistake": "Computing body_sha256 from one JSON string but sending a differently-formatted body causes 401 errors.",
          "solution": "Serialize body to canonical JSON ONCE. Hash that string for body_sha256. Send that EXACT string as the request body."
        },
        "construction_steps": [
          "1. Build request body object (if any)",
          "2. Serialize body to canonical JSON string (sorted keys, no whitespace)",
          "3. Compute body_sha256 = sha256(canonical_body_string).hex()",
          "4. Build envelope object with all fields",
          "5. Serialize envelope to canonical JSON bytes",
          "6. base64url-encode envelope bytes (no padding) -> X-Nukez-Envelope header",
          "7. Ed25519-sign envelope bytes, base58-encode -> X-Nukez-Signature header",
          "8. Send HTTP request with headers AND the canonical body string from step 2"
        ],
        "ops_by_endpoint": {
          "/v1/storage/signed_provision": [
            "locker:provision"
          ],
          "/v1/lockers/{locker_id}/files (POST)": [
            "locker:write"
          ],
          "/v1/lockers/{locker_id}/files (GET list)": [
            "locker:list"
          ],
          "/v1/lockers/{locker_id}/files/{filename} (GET)": [
            "locker:read"
          ],
          "/v1/lockers/{locker_id}/files/{filename} (DELETE)": [
            "locker:write"
          ],
          "/v1/lockers/{locker_id}/urls": [
            "locker:urls"
          ],
          "/v1/files/fetch-and-store (POST)": [
            "locker:write"
          ],
          "/v1/lockers/{locker_id}/operators (POST)": [
            "locker:admin"
          ],
          "/v1/lockers/{locker_id}/operators/{pubkey} (DELETE)": [
            "locker:admin"
          ],
          "/v1/lockers/{locker_id}/manifest (GET)": [
            "locker:read"
          ],
          "/v1/lockers/{locker_id}/files/urls (POST)": [
            "locker:read"
          ],
          "/v1/files/confirm (POST)": [
            "locker:write"
          ],
          "/v1/files/confirm-batch (POST)": [
            "locker:write"
          ],
          "/v1/storage/recompute-verify (GET)": [
            "locker:read"
          ],
          "/v1/attest/push (POST)": [
            "locker:attest"
          ],
          "/v1/storage/attest (POST)": [
            "locker:attest"
          ]
        },
        "notes": [
          "All authenticated endpoints require signed_envelope auth (request-bound, no bearer tokens).",
          "Use x-nukez-ops from OpenAPI to populate envelope.ops for each endpoint.",
          "Signed-envelope auth is request-bound: X-Nukez-Envelope and X-Nukez-Signature must be attached to the same HTTP request they authorize.",
          "If the envelope includes body_sha256, the signature is only valid for the exact body bytes sent over the wire. Prefer canonical JSON and send those exact bytes.",
          "A 422 from a signed_envelope endpoint usually means the envelope headers were entirely missing: on routes where the headers are declared required (attest, attest/push, recompute-verify, signed_provision, and the locker file endpoints), FastAPI returns 422 VALIDATION_ERROR before the auth code runs. A present-but-invalid envelope (bad signature, expired window, body hash mismatch, receipt mismatch) returns the 401 family instead. The confirm endpoints are the deliberate exception: /v1/files/confirm and /v1/files/confirm-batch return 401 SIGNED_ENVELOPE_REQUIRED when the headers are missing and enforcement is active.",
          "POST /v1/files/confirm and POST /v1/files/confirm-batch require the same locker:write authority as file creation. The envelope binds the request's method, path, query string, and the canonical body \"{}\" (clients literally send {} as the POST body), and the envelope's receipt_id must match the query receipt_id. Sign the confirm envelope at confirm time, after the upload finishes: the gateway rejects envelopes whose TTL exceeds 300 seconds, so a long upload must re-sign rather than reuse a stale envelope. Enforcement is being rolled out in stages (CONFIRM_AUTH_MODE), but the signed request is the contract.",
          "GET /v1/storage/recompute-verify requires a payer-signed envelope with locker:read. It is no longer a public endpoint, because it makes the backend re-download stored bytes; it is also strictly read-only and never overwrites the stored attestation.",
          "Op locker:attest authorizes payer-signed attestation operations: POST /v1/storage/attest (compute Merkle root over confirmed files) and POST /v1/attest/push (anchor the root on Solana). Only the receipt's payer can issue it. The envelope's receipt_id must equal the request body's (or query param's) receipt_id; mismatch returns 401 RECEIPT_ID_MISMATCH. Rationale: both endpoints cause the gateway to spend server-held SOL on oracle/anchor writes."
        ],
        "full_documentation": "/docs/AUTH_SIGNED_ENVELOPE.md"
      },
      "signed_urls": {
        "description": "upload_url/download_url are provider-opaque short URLs (https://api.nukez.xyz/f/{token}) that resolve to storage via gateway proxy or 307 redirect.",
        "notes": [
          "Agents PUT/GET to the short URL directly; no cloud credentials are exposed.",
          "Short URL tokens are Ed25519-signed and independently verifiable via /v1/short-url/verify-key. Note that the verify-key endpoint serves the short-URL token signer specifically; it is a different key from receipt_signer_pubkey, which is served on GET /v1/receipts/{receipt_id} and verifies receipt and manifest signatures.",
          "Each token is bound to one HTTP method: the signed payload commits to the method it was minted for, and the resolver rejects any other method with 403. A download (GET) token cannot be replayed as an upload or delete.",
          "If a URL fails with 403/404, assume expiry and call GET /v1/lockers/{locker_id}/files/{filename} for fresh URLs.",
          "Append ?proxy=1 to PUT URLs to proxy uploads through the gateway (single-domain egress for sandbox environments). On a proxied PUT the gateway computes the sha256 of the received bytes, records it into the locker manifest, and reports the outcome in the X-Nukez-Content-Hash-Recorded response header.",
          "A plain (non-proxied) PUT is answered with a 307 redirect to a provider signed URL. That redirect carries X-Nukez-Confirm-Required: true, which means the caller must still call POST /v1/files/confirm (or confirm-batch) after the redirected upload so the content hash is recorded."
        ]
      }
    }
  },
  "flows": {
    "provision_and_use_file_first": {
      "summary": "Request storage, pay on-chain, and confirm to obtain a receipt. Provision the locker with signed_provision, create a file to mint short URLs, PUT the bytes to the upload_url, confirm the upload, and GET the download_url to read the bytes back.",
      "notes": [
        "Expect HTTP 402 from /v1/storage/request (it is part of the x402 payment protocol).",
        "Persist receipt_id; locker_id is deterministic from receipt_id.",
        "Provision returns a manifest; treat its file_api sub-object as runtime truth for file endpoints.",
        "Storage request accepts optional pay_network and pay_asset for multi-chain payment selection.",
        "Confirm: tx_sig goes in X402-TX header; confirm body must echo pay_asset (and pay_network for multi-chain) matching the leg you actually paid, or gateway rejects the mismatch.",
        "After uploading bytes, confirm each file with POST /v1/files/confirm (or confirm-batch) using a payer-signed locker:write envelope so the content hash lands in the manifest. Uploads that went through the gateway proxy (?proxy=1) already have their hash recorded, which the proxy reports via X-Nukez-Content-Hash-Recorded; uploads that followed the 307 redirect must still be confirmed, which the redirect signals via X-Nukez-Confirm-Required: true."
      ]
    },
    "operator_delegation": {
      "summary": "Register an operator key at provision time (operator_pubkey in the signed_provision body) or afterwards; the operator then signs file operations with their own Ed25519 key.",
      "notes": [
        "Operators are Ed25519 keys that can perform file operations on behalf of the locker owner.",
        "Useful for cross-chain delegation: EVM owner delegates to Ed25519 operator for file ops.",
        "Add operators after provisioning via POST /v1/lockers/{locker_id}/operators (requires locker:admin).",
        "Remove operators via DELETE /v1/lockers/{locker_id}/operators/{pubkey}.",
        "Operator auth uses same signed envelope headers; gateway verifies the operator key against the locker record."
      ]
    },
    "verification_bundle": {
      "summary": "GET /v1/storage/verification-bundle?receipt_id=X returns a self-contained proof package.",
      "notes": [
        "The bundle contains the payment proof, the content proof, the verification algorithm spec, and do-it-yourself verification steps.",
        "No authentication is required, but the endpoint is receipt-scoped: the caller must hold the receipt_id, so verification is permissionless in the sense of requiring no Nukez account, not anonymous access to arbitrary lockers.",
        "For fully trust-free verification, pin receipt_signer_pubkey (served on GET /v1/receipts/{receipt_id}) out of band: a gateway that chose to tamper could serve both a forged object and a matching key, and /v1/short-url/verify-key serves a different key (the short-URL signer).",
        "Use the bundle when you need a portable, self-contained verification package."
      ]
    },
    "evidence_bundle": {
      "summary": "GET /v1/storage/evidence-bundle?receipt_id=X returns the portable evidence for a receipt, designed to be saved and checked later without asking the gateway anything else.",
      "notes": [
        "No authentication is required. The endpoint is receipt-scoped in the same way as the verification bundle.",
        "The bundle contains the file list, the commitment, the signed version-two envelope, the anchor object, the separate verification facts, the signing keys, and plain-language verification steps.",
        "Records that were anchored in the Switchboard era are exported with bundle_version 1, their original signature, and their original meaning.",
        "Read the on-chain transaction from anchor.tx first. The switchboard_slot, switchboard_tx, and switchboard_feed fields are populated only for historical Switchboard anchors. Never label a solana_sas_v1 or solana_memo_v2 anchor as Switchboard.",
        "To say that a record is anchored on-chain, require verification.anchor_confirmed to be passed. The boolean verified on /v1/storage/verify keeps its historical meaning: an attestation record exists and is not pending.",
        "GET /v1/keys (also served at /.well-known/nukez-signing-keys.json) lists the gateway's statement-signing keys. Pin them out of band: a key list served by the gateway that also produced the evidence proves self-consistency only.",
        "The anchor proves that the gateway's published key committed this exact snapshot on Solana at this slot, and, with the attestation service account, that the current commitment is readable on-chain at a predictable address. It does not prove that a third party inspected the files.",
        "The normative description is /specs/nukez-evidence-v2.json."
      ]
    },
    "merkle_inclusion_proof": {
      "summary": "GET /v1/storage/merkle-proof?receipt_id=X&filename=Y returns an inclusion proof for a single file.",
      "notes": [
        "No authentication required.",
        "Returns leaf_hash, proof (list of {hash, position} sibling steps), merkle_root, tree_depth, file_entry.",
        "Verify by walking the proof: for each step, if position=='left' then sha256(sibling || current) else sha256(current || sibling); final must equal merkle_root (after stripping the 'sha256:' prefix).",
        "Canonical algorithm spec at /docs/verify/merkle-v1.md; machine-readable at /specs/nukez-merkle-v1.json.",
        "Includes conformance test vector — implementers MUST reproduce the published root and proof exactly."
      ]
    }
  },
  "real_world_failure_modes": {
    "tx_propagation_delay": {
      "symptom": "confirm_storage returns 402 tx_not_found",
      "strategy": "Retry with exponential backoff (2s, 4s, 8s, 16s, 32s)."
    },
    "pay_leg_mismatch": {
      "symptom": "confirm_storage returns 400 with message '<ASSET> on <NETWORK> not in quote'",
      "strategy": "The pay_asset/pay_network in your confirm body must match the leg of accepts[] you actually paid. If you paid USDC on monad-mainnet, send {\"pay_asset\":\"USDC\",\"pay_network\":\"monad-mainnet\"} in the confirm body. Default fallback only applies when both are omitted."
    },
    "envelope_expiry": {
      "symptom": "401/422 from signed_envelope endpoints with stale timestamp",
      "strategy": "Rebuild envelope with fresh timestamp and re-sign."
    },
    "signed_url_expiry": {
      "symptom": "403/404 from upload_url/download_url",
      "strategy": "Re-mint URLs via GET /files/{filename} or re-run create_file."
    },
    "idempotency": {
      "symptom": "409 on request_storage with reused Idempotency-Key but different body",
      "strategy": "Use a new Idempotency-Key or resend the identical body."
    },
    "body_hash_mismatch": {
      "symptom": "401 with error mentioning body hash or envelope verification failed",
      "strategy": "Ensure body_sha256 in envelope matches SHA256 of the EXACT request body bytes sent. Use canonical JSON (sorted keys, no whitespace) for both."
    },
    "payment_already_allocated": {
      "symptom": "confirm_storage returns 409 PAYMENT_ALREADY_ALLOCATED",
      "strategy": "One payment transaction buys one pay request. Retrying the same pay request is idempotent and returns the same receipt, but a second pay request needs its own payment. Request a new quote and pay it with a new transaction."
    },
    "payment_outside_quote_window": {
      "symptom": "confirm_storage returns 402 PAYMENT_OUTSIDE_QUOTE_WINDOW",
      "strategy": "The transaction was not made for this quote, for example because it was sent before the quote existed. Request the quote first, pay it, and then confirm."
    },
    "payment_option_not_in_quote": {
      "symptom": "confirm_storage returns 422 PAYMENT_OPTION_NOT_IN_QUOTE",
      "strategy": "The pay_asset / pay_network you echoed was not one of the legs offered in that quote's accepts[]. Send the leg you actually paid, exactly as offered."
    },
    "confirm_intent_required": {
      "symptom": "confirm_storage returns 401 CONFIRM_INTENT_REQUIRED or CONFIRM_INTENT_INVALID",
      "strategy": "Binding operator_pubkey at confirm requires payer_signature: the payer key's signature over the UTF-8 bytes 'nukez/confirm/v1|<pay_req_id>|<tx>|<operator_pubkey>' (base58 for Ed25519, 0x-hex EIP-191 for EVM). The 401 body's sign_this field is the exact string to sign. Confirms without operator_pubkey need no signature."
    },
    "endpoint_retired": {
      "symptom": "POST /v1/attest/simulate returns 410 endpoint_retired",
      "strategy": "Oracle simulation was retired. Download GET /v1/storage/evidence-bundle?receipt_id=... and verify it independently, or read GET /v1/storage/verify."
    },
    "asset_missing_from_quote": {
      "symptom": "The asset you intended to pay with is absent from accepts[]",
      "strategy": "Its live price feed was unavailable or failed the cross-check at quote time. Pay with another offered leg, or request a fresh quote a little later."
    }
  }
}
