# Changelog (agent-facing)

This changelog is written for autonomous agents and integrators. It focuses on surface-area changes that matter for HTTP clients.

## phase6-v6 (2026-09-23) -- Switchboard Retired, Live Prices Only, Confirm Gates Enforced

- **Prices come only from Pyth (Solana) and Chainlink (Monad).** Every priced entry of `accepts[]` on `POST /v1/storage/request` carries `extra.price_observation` (`provider`, `feed`, `price`, `observed_at`, `retrieved_at`, `cross_check`). Stablecoin legs are priced at parity and carry no observation. There is no static fallback: an asset whose feed is unavailable, or whose two feeds disagree, is omitted from the quote instead of being priced from a constant.
- **New anchors are written with the gateway's own key.** Production anchors every attestation with an SPL Memo plus a Solana Attestation Service account (`anchor.type` = `solana_sas_v1`; credential `7Hr3vTH9zYVmUK5xMGYfy2ZP5KmbTXjKzt1UEbBYmiKm`, schema `Ac3M7szFQkBVaNWAHxvXbhsnWUQtW3rUnQPAZzKK8nru`). The `switchboard_slot`, `switchboard_tx`, and `switchboard_feed` fields are never populated on new records; they remain on historical records only.
- **`POST /v1/attest/simulate` is retired.** It answers `410` with `error: endpoint_retired` and `use_instead: /v1/storage/evidence-bundle`. It stays in the OpenAPI bundle marked deprecated.
- **Payment attestation pushes stopped.** The `payment_attestation` object is no longer written for new receipts. Verify a payment with `GET /v1/receipts/{receipt_id}/verify-payment`.
- **Confirm gates enforced.** `POST /v1/files/confirm` and `POST /v1/files/confirm-batch` without a valid `locker:write` envelope return `401 SIGNED_ENVELOPE_REQUIRED`. `POST /v1/storage/confirm` that binds `operator_pubkey` without `payer_signature` returns `401 CONFIRM_INTENT_REQUIRED` (or `CONFIRM_INTENT_INVALID`); the body's `sign_this` is the exact string to sign.
- **Confirm rejections, stated with their codes.** A leg not offered in the quote: `422 PAYMENT_OPTION_NOT_IN_QUOTE`. A transaction made before the quote existed or long after it expired: `402 PAYMENT_OUTSIDE_QUOTE_WINDOW` (pay within the quote's 300-second life; the check allows 300 seconds before and 3600 seconds after). A transaction that already bought another pay request: `409 PAYMENT_ALREADY_ALLOCATED`.
- Discovery contract version stamped `phase6-v6-2026-09-23` in `.well-known/nukez.json`.

## phase6-v5 (2026-09-21) -- Provider-Neutral Anchors, Evidence Bundle, Signing Keys, One Payment per Request

Everything in this entry is additive. No existing field changed meaning, and no existing field was removed.

- **What the on-chain anchor proves, stated plainly.** The anchor proves that the gateway's published key committed this exact snapshot on Solana at this slot. With the Solana Attestation Service account, it also proves that the current commitment is readable on-chain at a predictable address. It does not prove that a third party inspected the files. Whether the file bytes were hashed again for a snapshot is reported separately, as `check_mode` in the envelope and as the `bytes_checked` fact. Anchors that the gateway publishes itself are not oracle-verified, and no documentation should describe them that way. Historical Switchboard anchors keep their original wording and their original meaning.
- **New provider-neutral `anchor` object on every proof response.** It appears on `GET` and `POST /v1/storage/verify`, `GET /v1/retrieve`, `GET /v1/storage/merkle-proof`, `GET /v1/storage/recompute-verify`, `GET /v1/attest-code`, `GET /v1/self-verify`, and inside the stored attestation object of new records. It is `null` when the record has no on-chain anchor. Its `type` is `solana_sas_v1` or `solana_memo_v2` for anchors that the gateway publishes itself, and `switchboard_v2` for historical records. The object carries `chain`, `tx`, `slot`, `block_time`, `signer`, `fee_lamports`, `commitment`, `finalized`, `statement_digest`, `snapshot_id`, `payload`, and `envelope_version`, plus a `sas` block with the attestation account address when the type is `solana_sas_v1`.
- **How new anchors are written.** The gateway signs a version-two evidence envelope: an RFC 8785 canonical JSON body, a SHA-256 statement digest, and an Ed25519 signature over the ASCII tag `NUKEZ_CONTENT_ENVELOPE_V2`, one zero byte, and the 32 digest bytes. It then sends one Solana transaction, signed by its publication key, that carries an SPL Memo with a compact statement: the public proof id, the snapshot id, the statement digest, and the Merkle root. For `solana_sas_v1`, the same transaction also writes a Solana Attestation Service account for the receipt under the schema `nukez.content.v2`. The memo never carries the receipt id, a filename, or any access handle. The Merkle algorithm (`nukez-merkle-v1`) is unchanged.
- **Reader rule for the legacy fields.** `switchboard_slot`, `switchboard_tx`, and `switchboard_feed` are populated only for historical Switchboard anchors. Read the transaction from `anchor.tx` first, and fall back to `switchboard_tx` only when the `anchor` object is absent. Never label a `solana_sas_v1` or `solana_memo_v2` anchor as Switchboard. A client that reads only the legacy fields will see `null` for every record anchored today.
- **New `verification` facts object.** It appears next to `anchor` on `/v1/storage/verify`, on `/v1/retrieve`, and in the evidence bundle. The facts are `commitment_computed`, `signature_valid`, `anchor_submitted`, `anchor_confirmed`, `anchor_finalized`, `payment_verified`, `bytes_checked`, and `witness`. Every value is one of `passed`, `failed`, `not_checked`, or `unavailable`. The boolean `verified` on `/v1/storage/verify` keeps its historical meaning, which is that an attestation record exists and is not pending. A reader that wants to say "anchored on-chain" must require `verification.anchor_confirmed` to be `passed`.
- **New stored attestation fields on version-two records:** `proof_id` (32 hex characters, random and public, never the receipt id), `snapshot_id`, `evidence_seq`, `envelope`, `anchor`, `check_mode` (`recorded_hashes` or `bytes_hashed`), `manifest_file_count`, `files_skipped`, and `last_anchor_error`.
- **New endpoint `GET /v1/storage/evidence-bundle?receipt_id=...`.** It returns a portable bundle (`schema: "nukez/evidence-bundle/v2"`) with the file list, the commitment, the signed envelope, the anchor, the verification facts, the signing keys, and plain-language verification steps. It is open, with no authentication, and it is designed to be saved and checked later. Records from the Switchboard era are exported with `bundle_version` 1 and their original signature.
- **New endpoint `GET /v1/keys`, also served at `/.well-known/nukez-signing-keys.json`.** It lists the gateway's statement-signing keys as `{key_id, alg, public_key_hex, use, status}`. Pin these keys out of band. A key list served by the same gateway that produced the evidence proves self-consistency only.
- **Attest results.** `POST /v1/storage/attest` and `POST /v1/attest/push` keep `push_result.tx_signature` and `push_result.slot`, and they add `push_result.anchor` and `push_result.anchor_type`.
- **`POST /v1/storage/confirm`: one payment transaction buys one pay request.** A second pay request with the same transaction returns `409` with `error_code: "PAYMENT_ALREADY_ALLOCATED"`. Retrying the same pay request is still idempotent and returns the same receipt. The transaction must have been made for the quote: a transaction that predates the quote returns `402` with `error_code: "PAYMENT_OUTSIDE_QUOTE_WINDOW"`. Paying SOL against a quote that did not offer SOL returns `422 PAYMENT_OPTION_NOT_IN_QUOTE`. Receipts gain `funding_authority`, which is evidence only.
- **`POST /v1/storage/confirm`: payer-signed confirm intent.** When `operator_pubkey` is sent, the payer should also send `payer_signature`: the payer's signature over the exact UTF-8 bytes `nukez/confirm/v1|<pay_req_id>|<tx signature or hash>|<operator_pubkey>`, with `-` 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. The rollout is staged by `CONFIRM_INTENT_MODE`. In the default `shadow` mode an unsigned request still works and is logged. In `enforce` mode it returns `401 CONFIRM_INTENT_REQUIRED` or `401 CONFIRM_INTENT_INVALID`, and the 401 detail carries `sign_this`.
- **Quote changes on `POST /v1/storage/request` (the 402 body).** When the gateway runs the version-two price source, each entry of `accepts[]` gains `extra.price_observation` with `asset`, `quote`, `price` (a string), `exponent`, `conf`, `observed_at`, `retrieved_at`, `provider` (`pyth`, `chainlink`, `emergency_override`, or `static_fallback`), `feed`, `verification`, `accepted`, `degraded`, and `cross_check`. `extra.oracle_rate` is unchanged. An asset whose price is unavailable is simply absent from `accepts[]`, and the stablecoin options remain.
- **New spec `/specs/nukez-evidence-v2.json`.** It is the normative description of the envelope, the on-chain statement, the `anchor` object, the `verification` facts, the attestation service schema, the evidence bundle, and the signing key list. `/specs/nukez-merkle-v1.json` is unchanged.
- **Verifier.** The `nukez-verify` command checks a saved evidence bundle. It reports each fact separately and never folds them into one verified flag. The offline facts need only the bundle, a pinned key, and optionally your own copies of the files. The online facts need a Solana RPC endpoint that you choose.
- Discovery contract version: this entry did not change the label in `.well-known/nukez.json` when it shipped; the label moved to phase6-v6 with the entry above.

## phase6-v4 (2026-08-09) -- Confirm Auth, Recompute-Verify Auth, Unified Attest Push Path

- **`GET /v1/storage/recompute-verify` now requires a signed envelope and no longer mutates.** The endpoint requires a payer-signed envelope (`X-Nukez-Envelope` + `X-Nukez-Signature`) with the `locker:read` operation, because it causes the backend to re-download stored file bytes. The envelope's `receipt_id` must match the query `receipt_id`. The recompute is strictly read-only: it runs entirely in memory and never overwrites the stored attestation, so a tamper signal can no longer be laundered into a later "match" by calling the endpoint. Every previously published unsigned example is obsolete.
- **`POST /v1/files/confirm` and `POST /v1/files/confirm-batch` require a payer-signed `locker:write` envelope.** This is the same authority as the create-file step; a bare `receipt_id` is bearer access, not proof of ownership, and no longer authorizes confirm on its own. The envelope binds the request's method, path, and query string, plus the canonical body `{}` (clients literally send the two characters `{}` as the POST body), and the envelope's `receipt_id` must match the query `receipt_id`. Sign the envelope at confirm time, after the upload completes. Enforcement is being rolled out in stages via `CONFIRM_AUTH_MODE` (shadow first, then enforce), but the signed request is the contract and new clients must sign. When enforcement is active, missing headers on confirm return `401 SIGNED_ENVELOPE_REQUIRED`.
- **Missing-header status codes, stated plainly (both behaviors are intentional).** On `/v1/storage/attest`, `/v1/attest/push`, and `/v1/storage/recompute-verify` the envelope 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 invalid. On the confirm endpoints, missing headers deliberately return `401 SIGNED_ENVELOPE_REQUIRED`.
- **`POST /v1/storage/attest?sync=true` no longer pushes on-chain inside the request.** The sync form enqueues through the same managed queue path as the async form — the worker is the single code path that pushes on-chain — and then polls the persisted attestation server-side, returning the terminal state in the familiar blocking-response shape. If the poll ceiling (90 seconds by default) expires while work is still in flight, the call returns a 202-style in-flight response pointing at `/v1/storage/verify`. The attestation status lifecycle is `pending`, then `computed`, then `pushing`, then `complete`; a truthy `merkle_root` remains the completion signal for pollers.
- **Envelope TTL ceiling documented.** The gateway rejects any envelope whose `exp - iat` exceeds 300 seconds (`400 ENVELOPE_TTL_TOO_LONG`). Canonical JSON for envelopes and bodies is sorted-key, compact-separator, `ensure_ascii=False` UTF-8 — non-ASCII characters travel as raw UTF-8 bytes, not `\uXXXX` escapes.
- **Short URL tokens are method-bound, and upload responses carry new headers.** A token is valid only for the HTTP method it was minted for (a read token cannot be replayed as a write or delete). A proxied PUT (`?proxy=1`) records the gateway-computed sha256 into the manifest and reports it via `X-Nukez-Content-Hash-Recorded`; the plain-PUT 307 redirect carries `X-Nukez-Confirm-Required: true`, meaning the caller must still confirm after the redirected upload.
- **Verification guidance hardened.** Receipt-proof verification checks both `manifest_signature` (over the bare merkle root string) and `receipt_sig` (over the canonical receipt-plus-manifest) against `receipt_signer_pubkey` served on `GET /v1/receipts/{id}` — a 64-character hex Ed25519 key with 128-character hex signatures (not base58). A robust verifier pins that key out of band: `/v1/short-url/verify-key` serves a different key (the short-URL signer), and a gateway that chose to tamper could serve both a forged object and a matching key.
- **Operational status made explicit.** The periodic cron re-attestation sweep is implemented but not currently enabled in production (no `CRON_ATTEST_*` variables are set), and the Monad cross-chain bridge is unconfigured and inert in production. Neither should be treated as live.
- **SDK (pynukez ≥ 4.0.22):** the SDK now signs the confirm-flow envelopes (`confirm_file` / `confirm_files`) and the `recompute_verify` envelope, matching the auth changes above — upgrade to keep confirm working once enforcement is active. (4.0.21 was a docs-only release: the README went mainnet-only, dropping devnet/testnet examples.)
- Discovery contract version stamped `phase6-v4-2026-08-09` in `.well-known/nukez.json`.

## phase6-v3 (2026-06-15) -- Attestation Performance, Byte-Level Verification, Self-Contained Proofs

- **Attestation latency is now flat as lockers grow.** `POST /v1/storage/attest` no longer re-downloads file bytes on every call — it trusts the per-file `content_hash` recorded at confirm time. A multi-gigabyte locker now attests in roughly the same few seconds as a small one. No request-shape change. (Update 2026-08-09: the sync form no longer waits on the on-chain confirmation inside the request at all — it enqueues and polls server-side; see phase6-v4.)
- **Auto-reattestation skips unchanged lockers.** The gateway's automatic re-attest after each file confirm is now suppressed when the manifest has not changed since the last attestation, eliminating redundant on-chain transactions. Explicit `POST /v1/storage/attest` calls always re-attest, unchanged.
- **`GET /v1/storage/recompute-verify` is now a true byte-level integrity check.** It re-downloads every file from storage, re-hashes the bytes, rebuilds the Merkle tree, and compares to the persisted attestation. Use it when you need to prove stored bytes still match what was attested; latency scales with locker bytes. Plain `POST /v1/storage/verify` remains the cheap, cached structural check. (Update 2026-08-09: this endpoint now requires a payer-signed `locker:read` envelope and is strictly read-only; see phase6-v4.)
- **`GET /v1/lockers/{locker_id}/files/{filename}` now verifies object presence.** If the manifest has an entry but the underlying object is missing from storage, the endpoint returns a typed `404 CONTENT_NOT_FOUND` instead of a download URL that fails later. Clients get an actionable, distinguishable error.
- **Attestation skips files whose bytes are missing from storage.** The attestation compute step now confirms object presence (parallel existence checks) before including a file, so a green attestation means "manifest + bytes present," not just "manifest entry exists."
- **`verification-bundle.payment_proof` is fully populated for every asset.** `paid_amount`, `pay_asset`, `paid_raw`, and `confirmed_at` are present for all payments (including SPL tokens). The SOL-specific `amount_sol` / `amount_lamports` fields are populated for native-SOL payments and null for SPL-token payments by design — use `paid_amount` + `pay_asset` for an asset-generic view.
- **`manifest_signature` is now a normative, fully-specified field.** The Merkle spec (`/specs/nukez-merkle-v1.json`, `/docs/verify/merkle-v1.md`) now documents the algorithm (Ed25519), the signing key (the gateway service key, published as `receipt_signer_pubkey` on `GET /v1/receipts/{id}`), the exact signed payload (the bare `merkle_root` with the `sha256:` prefix stripped, UTF-8), the hex encoding, and verification steps. Third parties can now verify the gateway's signature independently.
- **Merkle spec hardening: `file_count` binding is now a MUST.** Verifiers MUST confirm the leaf count they used equals the attested `file_count` (present in the attestation object and the on-chain SPL Memo), closing the odd-node (CVE-2012-2459) ambiguity. Filenames cannot contain `:`, so the `filename:size_bytes:content_hash` leaf material is unambiguous.
- **Discovery surfaces corrected to mainnet-only with full asset set.** `tools.json` and `tool_contract.json` now list solana-mainnet and monad-mainnet (no devnet/testnet) and include BETA (Solana) and USDT0 (Monad) in the payment enums, matching the discovery doc and `llms.txt`.
- **SDK (pynukez ≥ 4.0.20):** new methods `recompute_verify(receipt_id)`, `attest_async(receipt_id)` (sync=false + poll), `confirm_files(receipt_id, filenames)` (batch confirm — one attestation per batch instead of per file), and `download_to_file(...)` (streams to disk). Client timeouts widened; new `attest_timeout` handling.
- Discovery contract version stamped `phase6-v2-2026-06-12` in `.well-known/nukez.json` (the version label still reads phase6-v2; these are additive, backward-compatible changes — no previously-working HTTP shape broke).

## phase6-v2 (2026-05-19) -- Signed Envelope Required on Attestation Endpoints

- **BREAKING: `POST /v1/storage/attest` and `POST /v1/attest/push` now both require a signed envelope.** Previously both accepted unauthenticated requests. Because the `X-Nukez-Envelope` + `X-Nukez-Signature` headers are declared required on these routes, a request with the headers entirely missing returns `422 VALIDATION_ERROR`; the 401 family is returned when the headers are present but fail verification.
  - `/v1/storage/attest` computes the file-set Merkle root over confirmed files.
  - `/v1/attest/push` anchors the computed root on Switchboard (PullFeed write + SPL Memo).
  - Both endpoints are gated by the same new op: `locker:attest`.
  - Envelope `receipt_id` must equal the request's `receipt_id` (JSON body for push; query parameter for attest); mismatch returns 401 `RECEIPT_ID_MISMATCH` on both.
  - Rationale: both endpoints cause the gateway to spend server-held SOL on oracle / anchor writes. Authentication binds the spend to the receipt's payer.
- **New op name `locker:attest`** — issued only by the receipt's payer key. Covers `/v1/storage/attest` and `/v1/attest/push` (and any future endpoints that anchor attestations on-chain using server resources).
- **Migration**: most clients call attestation via the SDK (`client.attest(receipt_id=…)`) or `/v1/storage/verify` — both wrappers must be updated to construct signed envelopes with `ops=["locker:attest"]`. Direct HTTP callers of either endpoint must do the same. Unsigned calls are rejected: 422 when the headers are entirely absent, and a 401-family error when they are present but invalid.
- Discovery contract version stamped at `phase6-v2-2026-05-19` in `.well-known/nukez.json`.

## phase6-v1 (2026-04-06) -- EVM Auth + Sponsored Provisioning + Developer-Owned Lockers

- **EVM/secp256k1 auth**: Full multi-chain signed envelope support. EVM wallets (0x-prefixed) sign envelopes with `personal_sign`. Gateway infers `sig_alg` from identity format — `0x` prefix = secp256k1, otherwise ed25519.
- **Sponsored provisioning**: `POST /v1/storage/sponsored-confirm` — OIDC-authenticated endpoint for authorized services to provision lockers on behalf of developers. Developer owns the locker, no operator added.
- **Batch sponsored provisioning**: `POST /v1/storage/sponsored-confirm-batch` — provision N lockers from a single on-chain payment. All pay_req_ids must be on the same network. Payment verified against sum of all quotes.
- **Developer-owned locker model**: Lockers are owned by the developer's pubkey. Services like NukezAgent orchestrate but never hold keys — the developer signs all envelopes.
- **Per-owner locker limit**: MAX_LOCKERS_PER_OWNER (default 50) prevents runaway locker creation. Returns `LOCKER_LIMIT_EXCEEDED` (429) when exceeded.
- **Provider-aware locker terms**: `locker_terms.py` module with per-provider capacity limits (storage_limit_bytes, file_limit, egress_included_bytes). Env var overrides per provider. Not yet enforced at write time — reserved error code `LOCKER_CAPACITY_EXCEEDED` (507).
- **SOL payment error field**: `verify_sol_payment` returns `"error"` (not `"err"`) for consistency with EVM path. `storage_confirm` accepts both field names for backward compat.
- **Tags on locker records**: Tags persisted to locker index via `_record_state`. Included in `/v1/owner/lockers` response.
- **Async event loop fixes**: Blocking `time.sleep()` replaced with `asyncio.sleep()` / `asyncio.to_thread()` in operators.py, short_url.py, public_file.py. Arweave download no longer blocks the event loop for up to 14s.
- **Nonce store fix**: AppError shadow removed — nonce replay errors now go through deterministic error formatting.
- **Dead code removed**: `locker_routes.py` (broken imports, never mounted), `_save_manifest` (zero callers), deprecated GCS MetaStore migration shims.
- **Sidecar deploy**: Helius API key moved from hardcoded env var to Secret Manager (`turbo-gateway-url:latest`).
- **ENV=development guard**: Gateway rejects `ENV=development` on Cloud Run at startup.

## phase5-v1 (2026-03-06) -- Operator Delegation + Verification Bundles

- **Operator delegation (ADR-3/5)**: Register Ed25519 operator keys to perform file operations on behalf of locker owner. Cross-chain delegation supported (EVM owner → Ed25519 operator).
  - Provision-time: include `operator_pubkey` in `POST /v1/storage/signed_provision` body
  - Post-provision add: `POST /v1/lockers/{locker_id}/operators` (ops: `locker:admin`)
  - Remove: `DELETE /v1/lockers/{locker_id}/operators/{pubkey}` (ops: `locker:admin`)
  - Operators use signed envelopes with their own Ed25519 key for file ops (read, write, list)
- **Verification bundle**: `GET /v1/storage/verification-bundle?receipt_id=X` — self-contained proof package (payment proof + content proof + algorithm spec + DIY steps). No auth required. Portable and shareable.
- **Confirm upload tool**: `POST /v1/files/confirm` — explicit SHA-256 verification after upload (ops: `locker:write`). Required for sandbox/chunked uploads and content-addressed providers.
- **Manifest endpoint**: `GET /v1/lockers/{locker_id}/manifest` — full locker manifest with all file metadata (ops: `locker:read`).
- **Fetch and store**: `POST /v1/files/fetch-and-store` — server-side URL fetch + store to locker (ops: `locker:write`). SSRF-protected.
- **Multi-chain asset expansion**: WETH and USDT added to Monad testnet payment options (was: MON, USDC; now: MON, USDC, USDT, WETH).
- **FileInfo expansion**: File metadata in manifest now includes `size_bytes`, `uploaded_at`, `content_hash`, `provider`, `provider_ref` fields.
- **Batch URL operations**: `POST /v1/lockers/{locker_id}/files/urls` — batch download URL minting for multiple files.
- Discovery contract version bumped to `phase5-v1-2026-03-06`.

## phase4-v2 (2026-02-25) -- Merkle Inclusion Proofs

- **New endpoint**: `GET /v1/storage/merkle-proof?receipt_id=X&filename=Y` — returns cryptographic proof of file membership in the attested merkle tree
- Open auth (no headers required), matching `/v1/storage/verify`
- Proof response includes: `leaf_hash`, `proof` (sibling hash path), `merkle_root`, `tree_depth`, `file_entry`, `switchboard` tx data
- Walk the proof to independently verify: start with `leaf_hash`, apply each sibling step, final hash must equal `merkle_root`
- SDK: `client.get_merkle_proof(receipt_id, filename)` added to pynukez
- MCP: `nukez_get_merkle_proof` tool added (read-only, idempotent)
- Core `build_merkle_root()` function UNCHANGED — new `build_merkle_tree_with_levels()` added alongside with intentional duplication for on-chain safety
- Switchboard impact: NONE (oracle job, memo schema, push mechanism unchanged)

## phase4-v1 (2026-02-24) -- Multi-Coin Quotes + Cap Token Elimination

- **payment_options** array in 402 response: agents discover all available payment methods (SOL, USDC, USDT, MON, WETH) with live oracle-priced amounts
- CoinGecko oracle integration for MON and WETH with 60s cache + env var fallbacks
- Agent-selectable payment: `payment_chain`/`payment_asset` in confirm body
- `sig_alg` cross-check: envelope `sig_alg` validated against receipt network
- Quote schema bumped to `dl_quote_v3`
- **Cap token eliminated**: all auth now uses signed envelopes only
- Challenge/login/whoami endpoints deprecated
- Receipt-based file proxy: `GET /v1/r/{receipt_id}/f/{filename}` — permanent, no-auth access to files
- Viewer URLs use stable receipt-based download URLs (no TTL expiry)
- Gateway file viewer fix: `locker_record_path` → `locker_index_path`

## phase3-v1 (2026-02-24) -- Nukez Rebrand + Multi-Chain

- Full rebrand from DataLocker/Lockerz to Nukez
- Multi-chain payment support: Solana + EVM/Monad
- Chain-agnostic receipt fields: paid_amount, paid_raw, pay_asset
- New storage request params: pay_network, pay_asset
- Updated all headers: X-DL-* -> X-Nukez-*
- SDK renamed: pylocker -> pynukez (v3.0.0)
- Discovery contract: `.well-known/nukez.json` (schema `nukez.discovery.v1`)
- All URLs updated: `api.nukez.xyz`, `www.nukez.xyz`
- Envelope auth supports Ed25519 (Solana) and secp256k1 (EVM)

## phase2-v2 (2026-02-23)

### Phase 1 Multi-Provider Storage
- Added storage-provider selection at request time (`provider` on `POST /v1/storage/request`).
- Active provider set now includes: `gcs`, `mongodb`, `storj`, `arweave`, `filecoin`, `firestore`.
- Locker manifests now include provider metadata for file entries (`provider`, `provider_ref` for content-addressed providers).

### Arweave integration model update
- Arweave uploads are now documented as sidecar-backed Turbo signer flow (no API-key raw upload model).
- `provider_ref` stores Arweave `tx_id` for later retrieval.

### Filecoin integration notes
- Filecoin provider path uses Lighthouse/IPFS upload API and stores CID as `provider_ref`.
- Download path resolves via configured gateway with CID.

### Firestore provider notes
- Firestore database env accepts `FIRESTORE_DATABASE_ID` (also supports `FIRESTORE_DATABASE`).
- `FIRESTORE_COLLECTION` is optional; provider defaults to `nukez_lockers`.

## phase2-v1 (2026-02-08)

### Short URL Service (Provider Opacity)
- **All upload_url/download_url are now short URLs**: `https://api.nukez.xyz/f/{token}` (~170 chars vs ~600 chars for raw GCS URLs). Context window savings: ~3,000 tokens per 10-file operation.
- Agents PUT/GET to short URLs directly; gateway handles provider routing via 307 redirect. This is transparent to standard HTTP clients.
- Short URL tokens are Ed25519-signed -- independently verifiable with no Nukez account. (Update 2026-08-09: for a fully trust-free check, pin the verify key out of band; see phase6-v4.)
- New endpoint: `GET /v1/short-url/verify-key` returns the public key for token verification.
- Provider identity (GCS, S3, Azure) is never exposed to agents.

### Token Format (v2)
- v2 tokens include the filename directly in the payload (not a hash).
- v1 tokens (fn_hash) still decoded for backward compatibility but no longer created.
- Tokens encode: locker_id, filename, method, provider, expiry, nonce, ed25519 signature.

### Storage Cleanup
- Removed `payload.bin` references -- agents use named files via create_file().
- `provision_locker_v2()` alias added for transition.
- `refresh_space()` returns clean locker metadata with file_api hints.
- Dead legacy endpoints removed.

### SDK (pynukez 2.0)
- `validate_signed_url()` now accepts short URLs and legacy signed URLs.
- Updated URL validation pattern for `api.nukez.xyz/f/{token}` format.

## phase1-v4 (2026-01-19)

- Unified discovery contract now points to `/openapi.json` as canonical OpenAPI bundle.
- File endpoints documented as **signed-envelope OR cap-token** authorized.
- `ttl_min` documented for file URL minting.
- Added explicit cap-token renewal flow (`/challenge` -> `/login`) and `cap/whoami`.

## phase1-v3

- Initial "file-first" flow: signed_provision + per-file URL minting.
