🎭 Theater & Live Performance · performance recording

3009 Agent

Agents pay to unlock performance recording deliverables: sign an EIP-3009 authorization, base64 the x402 v2 envelope, and PayAI settles USDC on Base Sepolia before the endpoint returns.

Base x402· agent-native micropayment
Section · Agentic

The kernel.

full primer →

An agent runs the protocol server-side and directors get a signed, auditable result they can act on without leaving the app.

Why this primitiveBase x402 on Base Sepolia fits performance recording in Theater & Live Performance because directors can settle 0.01 USDC via a signed EIP-3009 authorization before the agent endpoint returns anything.

Kernel
HTTP 402 + `PAYMENT-SIGNATURE` on Base Sepolia — the server issues a v2 challenge, the agent signs an EIP-3009 USDC authorization via a Privy embedded EVM wallet, the PayAI facilitator verifies and settles on-chain, and the server returns the protected payload with a `PAYMENT-RESPONSE` tx hash
Drives the UI as
a four-step flow log — Challenge · Sign · Retry · Settle — with the unlocked payload and a Basescan tx link at the end
Appendix · Secrets

Required key.

LOVABLE_API_KEY
Auto-provisioned by Lovable when you enable the Lovable AI add-on. Powers the buyer, seller and merchant agents through an OpenAI-compatible gateway in front of every frontier LLM. Zero copy-paste.
open ↗

Add this in your Lovable project under Settings → Secrets before pasting the prompt below.

Appendix · Mega-prompt

The build prompt.

Paste into a fresh Lovable project. Make sure the key above is set first. read the build strategy →

Build "3009 Agent · x402" as a ONE-SHOT Lovable build. The participant has only
5 credits — this single message must produce a working demo with no follow-ups.
Single-page TanStack Start app. Cut scope ruthlessly.

CONCEPT
Lovable AI runs the agent that clears and pays archival recording rights via UCP so producers legally capture shows for posterity.
Reframed as a paid, agent-callable HTTP endpoint on Base Sepolia x402 (0.01 USDC via Privy embedded EVM wallet + EIP-3009 + PayAI facilitator).
Discipline: Theater & Live Performance (performance recording).

LOVABLE BUDGET (HARD CAP: ONE-SHOT, ~5 CREDITS TOTAL):
The participant has FIVE Lovable credits for the whole build. This prompt MUST
ship a working demo on the FIRST message with zero follow-ups. Engineer for that.
- ONE TanStack Start app, ONE route (`src/routes/index.tsx`). No extra pages, no auth, no nav.
- ONE TanStack server route under `src/routes/api/public/` that runs the protocol.
- ONE client surface (a button + flow log) that triggers the payment and renders the result.
- NO database, NO Lovable Cloud, NO extra integrations beyond what's listed below.
- NO tests, NO docs pages, NO settings screens.
- Demo-fallback contract: every code path must boot with NO secrets and return
  realistic simulated envelopes flagged `simulated: true`. Real signing activates
  only when the user signs in and the wallet is funded.

PROTOCOL SURFACE — x402 on Base Sepolia (self-contained; participant has no skills)

CONFIG (src/data/x402.json) — copy exactly:
```json
{
  "endpoint":     "https://x402.payai.network/api/base-sepolia/paid-content",
  "proxy":        "/api/public/x402-proxy",
  "usdcAddress":  "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
  "chainId":      84532,
  "network":      "eip155:84532",
  "faucetUrl":    "https://faucet.circle.com/",
  "explorer":     "https://sepolia.basescan.org",
  "amount":       "10000",
  "amountLabel":  "0.01 USDC"
}
```

RPC (src/data/rpc.json) — Alchemy Base Sepolia (falls back to public if no key):
```json
{ "baseSepolia": "https://base-sepolia.g.alchemy.com/v2/demo" }
```
The public RPC is rate-limited; wire an Alchemy key when you have one.

SAME-ORIGIN PROXY (src/routes/api/public/x402-proxy.ts) — CRITICAL: PayAI facilitator
sends no CORS headers, so a direct browser fetch throws "Failed to fetch" BEFORE the
402 lands. Every request MUST route through this same-origin proxy. Forward
`PAYMENT-SIGNATURE` (request) and `PAYMENT-RESPONSE` (response) verbatim:
```ts
import { createFileRoute } from "@tanstack/react-router";
import x402Cfg from "@/data/x402.json";

/** Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14 */
export const Route = createFileRoute("/api/public/x402-proxy")({
  server: {
    handlers: {
      GET: async ({ request }) => {
        const sig = request.headers.get("PAYMENT-SIGNATURE");
        const upstream = await fetch(x402Cfg.endpoint, {
          method: "GET",
          headers: sig ? { "PAYMENT-SIGNATURE": sig } : {},
        });
        const body = await upstream.arrayBuffer();
        const out = new Headers();
        const ct = upstream.headers.get("content-type");
        if (ct) out.set("Content-Type", ct);
        const pr = upstream.headers.get("PAYMENT-RESPONSE");
        if (pr) out.set("PAYMENT-RESPONSE", pr);
        out.set("Access-Control-Expose-Headers", "PAYMENT-RESPONSE");
        return new Response(body, { status: upstream.status, headers: out });
      },
    },
  },
});
```
The `/api/public/*` prefix bypasses Lovable's published-site auth gate — that's
the design. Do NOT add auth middleware here.

FOUR-STEP FLOW (client lib src/lib/x402.ts + UI in src/routes/index.tsx):

1. CHALLENGE — `GET /api/public/x402-proxy` → expect HTTP 402 with JSON
   `{ x402Version: 2, accepts: [PaymentRequirement, ...], error }`. Pick the
   `accepts[]` entry where `network === "eip155:84532"` AND `scheme === "exact"`.
   Amount is `req.amount` (atomic string) — for USDC 6-decimals, `"10000"` = 0.01 USDC.
   NOTE: v2 uses `amount`, NOT v1's `maxAmountRequired`. Network id is CAIP-2
   (`"eip155:84532"`), NOT `"base-sepolia"`.

2. SIGN — Build EIP-3009 `TransferWithAuthorization` typed data and sign it via
   the Privy embedded EVM wallet's EIP-1193 provider (NOT React hooks):
   ```ts
   const provider = await embedded.getEthereumProvider();
   const sig = await provider.request({
     method: "eth_signTypedData_v4",
     params: [address, JSON.stringify(typedData)],
   });
   ```
   Typed data domain uses `chainId: 84532`, `verifyingContract: req.asset`,
   and — CRITICAL — `name` and `version` come from `req.extra.name` /
   `req.extra.version` (e.g. `"USDC"` / `"2"`), NEVER hardcoded to `"1"`.
   Message fields:
   - `from` = the Privy wallet address
   - `to`   = `req.payTo`
   - `value` = `req.amount` (string, atomic units)
   - `validAfter`  = `Math.floor(Date.now()/1000) - 60`
   - `validBefore` = `Math.floor(Date.now()/1000) + (req.maxTimeoutSeconds ?? 300)`
   - `nonce` = fresh 32-byte hex (`crypto.getRandomValues(new Uint8Array(32))` → `"0x…"`)
   NEVER reuse a nonce.

3. RETRY — Wrap the signed authorization into the x402 v2 envelope. CRITICAL:
   v2 wraps under `accepted`, echoing the FULL PaymentRequirement you picked.
   A top-level `{scheme, network, payload}` (v1 shape) is rejected as
   `invalid_payload`:
   ```json
   {
     "x402Version": 2,
     "accepted": { /* the full PaymentRequirement you picked, verbatim */ },
     "payload": {
       "signature": "0x…",
       "authorization": { "from": "…", "to": "…", "value": "10000",
                          "validAfter": "…", "validBefore": "…", "nonce": "0x…" }
     }
   }
   ```
   Base64-encode this JSON, send as `PAYMENT-SIGNATURE` header (exact case),
   retry `GET /api/public/x402-proxy`.

4. SETTLE — On 200, read the `PAYMENT-RESPONSE` response header, base64-decode
   to `{ success: true, transaction: "0x…", network: "eip155:84532", payer: "0x…" }`.
   Link the tx to `${explorer}/tx/${transaction}`.

PRIVY MOUNT (SSR-safe, non-negotiable):
- NEVER import `@privy-io/react-auth` at module scope of a route or a file
  reachable from `src/router.tsx` — it crashes workerd under SSR.
- Load it via `lazy(() => import("./privy-client-entry"))` inside a
  `<ClientOnly>` + `<Suspense>` boundary in `src/components/privy-root.tsx`.
- Configure with `loginMethods: ['email', 'google']` and
  `embeddedWallets: { ethereum: { createOnLogin: 'users-without-wallets' } }`.
- Read the wallet with `useWallets()`; get the EIP-1193 provider with
  `await wallets[0].getEthereumProvider()`.

USER FLOW (the entire app):
1. Sign In (email/google) — Privy creates an embedded EVM wallet.
2. Show the wallet address + a "Fund with Circle faucet" link that opens
   `https://faucet.circle.com/` with a "choose Base Sepolia + USDC" hint.
3. "Refresh USDC balance" button — reads balance via viem `createPublicClient`
   against `rpcCfg.baseSepolia`.
4. "Unlock content" button — runs steps 1-4 above, streams each into a live
   flow log with clear labels (Challenge · Sign · Retry · Settle).
5. On success, render the unlocked JSON payload + a Basescan link to the tx.

FAILURE MODES (surface the facilitator body verbatim in the flow log):
- `invalid_payload` → sent v1 envelope shape; wrap under `accepted` (step 3).
- `invalid_signature` → wrong `chainId` or hardcoded `extra.version`; use `84532`
  and read `extra.version` from the picked requirement.
- `insufficient_funds` → wallet has ETH but no USDC; fund via Circle faucet.
- `TypeError: Failed to fetch` at step 1 → called facilitator directly, bypassing
  the proxy; ALL requests must go through `/api/public/x402-proxy`.

SIMULATE MODE (default when user isn't signed in): the flow log walks through
the four steps with fake data flagged `simulated: true`, no real fetch, no
signing prompt. The button label switches from "Unlock (simulate)" to
"Unlock (live · Base Sepolia)" once the wallet is ready and funded.

STACK
- TanStack Start app, the index route only.
- Tailwind + shadcn. Editorial look: gold accent on a dark or warm-cream background,
  generous type, one strong headline, one primary action.
- Footer renders: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14".
- Libraries: template defaults + `@privy-io/react-auth` + `viem` + `zod`. Nothing else.

CREDIT (UI footer + JSDoc on the proxy handler): Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
Appendix · Market

Market sizing.

TAM
$500M
live performance archival and media
SAM
$100M
theatrical union recording rights
SOM
$10M
professional stage archival captures

Indicative figures for hackathon pitches — refine with your own research before raising.

See also

Adjacent entries.