CRUMB a card from devarno-cloud

x402 Paid Anonymous Links: One Payment Buys One Link

smo1 advanced 8 min read

ELI5

Someone without an account can pay a tiny crypto payment to make one short link. Cloudflare sits in front and turns purr-api’s “please log in” into “please pay”. The caller pays and retries with a receipt. purr-api checks that the request really came through Cloudflare, checks the receipt itself, asks a payment verifier to confirm it, and only then makes a 30-day link owned by a special account nobody can log into. The same receipt can never buy a second link.

Technical Deep Dive

Source: smo1-io docs/decisions/2026-10-12-x402-paid-anonymous-link-create.md (status accepted, sign-off flight@arno.host 2026-10-14) (1f19667); purr-api internal/handler/paid_link_handler.go:24-33, 86-257, internal/x402/provenance.go:10-20, internal/x402/payment.go:116-172, internal/service/link_service_paid.go:40-97, internal/config/config.go:91, 164-178, 222-258, internal/router/router.go:154-164, internal/middleware/auth.go:628-640, cmd/server/main.go:265-303, migrations/000011_x402_paid_create.up.sql (origin/main bb6cfc2), read 2026-10-25.

Flow

sequenceDiagram
autonumber
participant C as Keyless caller
participant G as Cloudflare zone and Gateway
participant P as purr-api POST /api/links
participant F as x402 facilitator
C->>G: POST purr.smo1.io/api/links with url
G->>P: request plus provenance header from a Transform Rule
P-->>G: 401 Authentication required
G-->>C: 402 with x402 payment terms
C->>G: retry with X-PAYMENT proof
G->>P: request, provenance header, X-PAYMENT
P->>P: provenance, per-IP limit, body allowlist, decode, replay lookup, local checks
P->>F: verify payment
F-->>P: valid
P->>P: one transaction, link and x402_payments row under the daily cap
P-->>C: 201 short URL and payment_ref

The Gate

With X402_PAID_CREATE_ENABLED on, POST /api/links is registered as PaidLink.Gate() → RequireAuth → tier limit → CreateLink (router.go:158-164). The Gate passes a request through untouched only when it carries a credential and no payment header. Any request with a payment header is answered by the paid path even if it also has a stale cookie, because a 401 on a paid request becomes another 402 and the caller pays twice (paid_link_handler.go:101-115).

Status discipline

flowchart TD
A[serve] --> B{"provenance header matches, constant time ?"}
B -->|No| F403[403 forbidden]
B -->|Yes| C{"per-IP limit ok ?"}
C -->|No| R429[429]
C -->|Yes| D{"body valid, allowlisted fields, at most 4 KiB ?"}
D -->|No| B400[400]
D -->|Yes| E{"payment header present ?"}
E -->|No| U401[401, the price signal]
E -->|Yes| G{"replay of a created payment ?"}
G -->|Yes| OK200[200 original link]
G -->|No| H{"local checks and facilitator valid ?"}
H -->|No| P403[403 payment refused with reason]
H -->|"facilitator down"| S503[503, proof stays retryable]
H -->|Yes| I{"under daily cap ?"}
I -->|No| C429[429 daily_cap, proof stays usable]
I -->|Yes| K201[201 created]
  • Provenance (provenance.go): a secret header (default name X-SMO1-Edge-Auth) that a Cloudflare Transform Rule sets on purr.smo1.io only; at least 32 bytes; compared with subtle.ConstantTimeCompare; an empty or short secret never matches. Requests to purr-api-production.up.railway.app never carry it, so the paid path rejects them.
  • Local payment checks (payment.go:144-172): scheme exact, network on the allowlist, payTo equal to X402_PAY_TO, amount at least X402_PRICE_ATOMIC, validity window covers now. Then the facilitator, under a global budget of X402_GLOBAL_VERIFY_PER_MIN calls (default 60).
  • Body allowlist: url, display_name, content_classification and the five utm_* fields. Custom slug, domain, password, PIN, hint and redirect mode are refused by construction.
  • Config fails closed (config.go:222-258): with the flag on, every price, wallet, asset, network, facilitator URL, cap and limit must be set explicitly, and boot refuses otherwise. There is no default price.

Identity and storage

  • Every paid link is owned by user x402-anon, seeded by migration 000011 with an .invalid email, no login and no key. Boot refuses to serve paid creates if the row is missing (main.go:271-276). passAuthenticated refuses any credential that resolves to it, so nobody can list, edit or read analytics on paid links (auth.go:628-640).
  • Links are plain redirects, unprotected, active, expiring after X402_LINK_TTL_DAYS (default 30, max 365), with no tier check (link_service_paid.go:40-97).
  • x402_payments keeps one row per decodable attempt, with a unique idempotency_key of (network, payer, authorization nonce), outcome created or refused, and a receipt_id slot in TOLL’s x402:<network>:<txHash>:<logIndex> format. payment_ref (x402: plus the key) is logged with every refusal for refunds.

Live status

The flag defaults to off (config.go:164). Whether it is on in Railway production is UNVERIFIED; it was not probed. The edge Worker on smo1.io is guarded by a regression test that it never answers 402 (zoomies-edge/src/handlers/redirect.no402.test.ts), and purr-api has the same guard on its redirect handlers.

Key Terms

  • x402 — HTTP 402 payment protocol: terms in the 402, proof in a retry header
  • Monetization Gateway — Cloudflare feature that rewrites an origin 401 into a priced 402 (waitlist-only per the decision)
  • Provenance header — shared secret proving a request crossed the Cloudflare zone
  • Facilitator — external service that confirms an x402 payment proof
  • x402-anon — the system owner of every paid link; unusable as a login

Q&A

Q: Why does a paid request never get a 401 from purr-api? A: The Gateway converts any 401 into a 402, so a 401 on a request that already carries a payment would charge the caller again. Only a request with no payment header gets the 401; payment and provenance failures are 403, caps and limits 429.

Q: What two checks must both pass before a paid link is created? A: Provenance (the Cloudflare-set secret header, compared in constant time) and payment (local checks on scheme, network, payee, amount and validity window, then facilitator verification).

Q: What happens if the same payment proof is presented twice? A: The idempotency key is unique, so the second request is answered from the first: 200 with the original link, or 403 if that proof was refused before, or 410 if the link it bought was taken down. No second link is created.

Q: Is the paid path live in production? A: Unknown. The code is merged and the decision accepted, but X402_PAID_CREATE_ENABLED defaults to off and its production value was not checked (UNVERIFIED).

Examples

A request straight to the Railway hostname with a valid-looking X-PAYMENT header gets 403 forbidden before the body is even read, because only the Cloudflare rule on purr.smo1.io adds the provenance header. The same request through purr.smo1.io on a day the cap is already spent gets 429 with reason daily_cap, stored unkeyed so the proof can buy a link after the UTC midnight reset.

neighbors on the map