One file, one container, one verification result.
Docs · Portal
Viewer routes.
Viewer links are unauthenticated by design.
The viewer family is three routes on nukez.xyz that render a single file, a container, or a verification result entirely from query parameters. They are not a separate product and there is no share-link generator in the portal. Because they carry no session, the link itself is the credential — treat one like a bearer token.
No passkey, no session, no prompt on any of the three.
The recipient can recompute the chain from public endpoints.
Surfaces
Three query-driven routes
There is no separate viewer product and no share-link generator. Three routes on the main site render a file, a container, or a verification result from query parameters alone.
nukez.xyz/view ?locker_id= &filename= &receipt_id= &download_url=nukez.xyz/verify ?receipt_id= (also accepts /verify/<receipt_id>)nukez.xyz/viewer ?request_type= &locker_id= &receipt_id= &filename= &payload= # /viewer is the agent return window, not a share link. Its payload is decoded# from the URL in four attempts - raw JSON, decodeURIComponent, atob, base64url -# and rendered as header, stats, links, table, status, kv, proofs, file_meta,# json and file_preview blocks. With locker_id but no payload it shows# "Payload Not Embedded" and links to /owner. Nothing in this repo builds one. # firebase.json rewrites /view, /verify, /verify/**, /viewer and /owner on# nukez.xyz. There is no /viewer/** rewrite, so a path segment under /viewer# resolves to nothing. app/robots.ts disallows /view and /viewer; /verify is# left indexable and ships its own OG metadata. # portal.nukez.xyz rewrites ** to /agent-portal.html, so portal.nukez.xyz/viewer/...# renders the agent portal shell and never a viewer. The agent portal reads its# own file list through the session-gated GET /v1/portal/files instead.
Code notes
There is no separate viewer product and no share-link generator. The viewer family is three distinct routes served from the nukez.xyz Firebase site, each driven entirely by query parameters: /view renders one stored file, /verify renders the proof chain behind one receipt, and /viewer is the generic container an agent fills with an inline payload. The agent portal at portal.nukez.xyz/agent-portal is a fourth, unrelated surface that lists files through a session-gated endpoint and never links to these routes.
Nothing in this repository or in the agent runtime constructs a /viewer URL. Treat /view and /verify as the links you share, and /viewer as the container an agent renders its own result into.
Contract
The parameters a file link carries
A link is a plain query string. The filename is always required, and at least one of receipt id or locker id must accompany it.
https://nukez.xyz/view?locker_id=locker_2e1f783f4208&filename=report.pdf&receipt_id=232fed413a4d83fd&download_url=https%3A%2F%2Fapi.nukez.xyz%2Fv1%2Fr%2F232fed413a4d83fd%2Ff%2Freport.pdf # filename required. Absent -> "Missing filename query parameter."# receipt_id required unless locker_id is supplied. Neither present -># "Missing locker_id or receipt_id query parameter."# locker_id optional. Derived as locker_ + sha256(receipt_id).hex()[:12],# which is how locker_2e1f783f4208 comes from 232fed413a4d83fd.# download_url optional. Read without decodeURIComponent, so encode it once. # The shortest link that works is receipt_id plus filename:https://nukez.xyz/view?filename=report.pdf&receipt_id=232fed413a4d83fd # With no download_url, /view probes api.nukez.xyz then staging.nukez.xyz for the# receipt and builds the fetch URL itself as# BASE + /v1/r/232fed413a4d83fd/f/report.pdf# then hands that URL straight to an img, video, audio or iframe element.
Code notes
A /view link is a plain query string. filename is always required, and at least one of receipt_id or locker_id must be present; every other parameter is an optimisation. When only a receipt is supplied, the client derives the locker id locally and resolves the file URL against the public API bases before rendering, which is why a two-parameter link is enough to open a file.
locker_id only affects the header display and the Back to Locker link, which points at /owner. It is never used to fetch the bytes.
Builders
Construct a link in code
Two helpers in the navigation module assemble these URLs, and they are the only link builders in the codebase.
import { buildFileViewUrl, buildVerifyUrl, openViewerPage } from "@/lib/navigation"; // lib/navigation.ts:60 - the only /view link builder in the repositoryexport function buildFileViewUrl(params: { filename: string; receiptId: string; lockerId?: string; downloadUrl?: string;}): string { const query = new URLSearchParams(); if (params.lockerId) query.set("locker_id", params.lockerId); query.set("filename", params.filename); query.set("receipt_id", params.receiptId); if (params.downloadUrl) query.set("download_url", params.downloadUrl); return "/view?" + query.toString();} // lib/navigation.ts:77 - the matching /verify link builderexport function buildVerifyUrl(receiptId: string): string { return "/verify?receipt_id=" + encodeURIComponent(receiptId);} // McpChatModal.tsx:471 - chat caller, holds a receipt and nothing elseconst handleViewFile = (filename: string) => { if (!receiptId) return; openViewerPage(buildFileViewUrl({ filename, receiptId, lockerId: lockerId || undefined }));}; // OwnerViewerClient.tsx:285 - owner caller, attaches a resolved download_urlconst downloadUrl = await getDownloadUrl(filename);const query = new URLSearchParams({ locker_id: lockerId.trim(), filename, receipt_id: receiptId.trim() || "", download_url: downloadUrl,});openViewerPage("/view?" + query.toString()); // openViewerPage is same-tab navigation: window.location.href = url
Code notes
Two functions in lib/navigation.ts are the only link builders in the codebase, and each has exactly one caller: the MCP chat modal. The chat path has nothing but a receipt and refuses to build a link without one; the owner path bypasses the helpers entirely, resolving a download URL first and assembling the query string itself. Both hand the result to openViewerPage, which navigates the current tab.
buildFileViewUrl requires receiptId in its type signature even though /view itself will accept locker_id plus download_url with no receipt, which is why the owner path builds its URLSearchParams directly.
Access
The receipt is the credential
None of these routes prompts for anything. Anyone holding the link can read what it addresses, which is the whole security model to reason about before sharing one.
# None of the three routes imports an auth module. Their complete import surface is# next/link, next/navigation, react, the shared components and lib/api - no# lib/portal-auth, no PortalAuthContext, no PasskeyConnect. Passkey code lives only# under app/agent-portal/. # The receipt-scoped file endpoint answers a request with no Authorization header:curl -i https://api.nukez.xyz/v1/r/doesnotexist123/f/test.txt HTTP/2 404{"error_code":"LOCKER_NOT_FOUND","message":"locker_not_found","details":{}, "request_id":"a429139a-91ab-474d-8a19-8d380d7d89f2"} # That is a domain-level miss, not an auth challenge. A real receipt_id returns the# bytes to whoever presents it, so treat a /view or /verify link as a bearer secret:# forwarding the link forwards the read capability, and there is no revoke button. # The one auth prompt in this family targets the owner, not the recipient. It fires# when a link carries neither download_url nor receipt_id (ViewPageClient.tsx:143):# "This file requires a download URL or receipt ID. Open the Owner Portal to# authenticate with your wallet keypair, then navigate to this file from there."
Code notes
Opening a /view, /verify or /viewer link never prompts for anything. None of the three clients imports a session, passkey or auth module, and the receipt-scoped file endpoint on the gateway serves requests with no Authorization header. Access control is possession of the receipt id, or of an owner-minted download URL, which makes any such link a bearer credential.
Share these links the way you would share a password. No route in this family exposes a revoke, expire, or unshare control, and only /view and /viewer are kept out of the index by robots.txt - /verify is deliberately crawlable.
Independence
Recompute the proof yourself
A recipient does not have to trust the rendered page. Two unauthenticated gateway endpoints return the receipt hash and the verification bundle behind it.
import httpx RECEIPT = "232fed413a4d83fd" # one of the three sample receipts shipped on /verifyBASES = ["https://api.nukez.xyz", "https://staging.nukez.xyz"] def resolve(receipt_id): for base in BASES: r = httpx.post(base + "/v1/storage/verify", json={"receipt_id": receipt_id}) if r.status_code == 200: return base, r.json() raise SystemExit("receipt not found on any public API base") base, verified = resolve(RECEIPT)att = verified.get("attestation") or {}print(verified["verified"], verified.get("locker_id"))print(att.get("attestation_status"), att.get("merkle_root"), att.get("file_count")) bundle = httpx.get( base + "/v1/storage/verification-bundle", params={"receipt_id": RECEIPT},).json() content = bundle.get("content_proof") # null until the locker has been attestedprint(bundle["payment_proof"]["tx_signature"])if content: print(content["merkle_root"], content["files"])print(bundle["merkle_algorithm"]["pseudocode"])print(bundle["verify_yourself"]["steps"]) # These are the same two calls the browser makes for# https://nukez.xyz/verify?receipt_id=232fed413a4d83fd - neither sends a token.
Code notes
A recipient does not have to trust the rendered page. The verification page is backed by two unauthenticated endpoints that anyone can call directly: POST /v1/storage/verify returns the receipt and its attestation, and GET /v1/storage/verification-bundle returns a self-contained bundle carrying the payment proof, the content proof, the on-chain anchor, the Merkle algorithm specification and a written verify-yourself procedure.
The path form https://nukez.xyz/verify/232fed413a4d83fd loads the same page: the client matches the segment with a regex and runs the same two lookups. It does not rewrite the address bar - only submitting the form or clicking a sample receipt pushes the query form into history.
Sharing a link shares the data.
These routes perform no access check, so forwarding a link forwards the access. When a record should stay private, do not distribute its URL — there is no revocation short of deleting the file. Owner-scoped views live behind the passkey session described in the owner portal guide.
