LightCue Automation
Record and trigger lighting cues onchain to verify show timing and enable automated playback.
The primitive.
The Solana primitive runs at the right moment in the flow and surfaces a clear, verifiable result that directors can act on without web3 jargon.
Why this primitiveA single-endpoint x402 paywall on Solana Devnet is the smallest possible monetization surface for 'LightCue Automation' — one 0.01 USDC signature unlocks the creative payload, settled by PayAI, verifiable on Solana Explorer, with no server-side subscription state to manage.
Required keys.
Add these in your Lovable project under Settings → Secrets before pasting the prompt below.
The build prompt.
Paste into a fresh Lovable project. Make sure all five secrets above are set first. read the build strategy →
Build "LightCue Automation" in ONE Lovable message. Single-page demo on Solana Devnet with an x402 paywall.
This prompt is self-contained — do NOT rely on any external Solana or x402 skill beyond what is inlined here.
CONCEPT
Record and trigger lighting cues onchain to verify show timing and enable automated playback.
Discipline: Theater & Live Performance (lighting cue logs).
Onchain primitive: x402 Paid Endpoint on Solana Devnet. Why this primitive: A single-endpoint x402 paywall on Solana Devnet is the smallest possible monetization surface for 'LightCue Automation' — one 0.01 USDC signature unlocks the creative payload, settled by PayAI, verifiable on Solana Explorer, with no server-side subscription state to manage.
5-CREDIT BUDGET (HARD LIMIT — one Lovable message, no follow-ups):
- ONE single-page app (index route only). No Lovable Cloud, no database, no auth flows
beyond Privy drop-in.
- Privy (Google login + embedded Solana wallet on Devnet) is ALWAYS the signing layer.
- Every unlock = 0.01 USDC on Solana Devnet, settled by PayAI's public facilitator.
- No custom Solana program. No Anchor. No Metaplex. No cNFT. Just SPL Token transfer
wrapped in the x402 exact scheme.
REQUIRED SECRETS (Lovable → Project Settings → Secrets — add BEFORE sending this prompt):
- PRIVY_APP_ID From https://dashboard.privy.io. In the SAME Privy app, whitelist BOTH
`https://*.lovable.app` AND your published `https://<slug>.lovable.app`
under Allowed Origins, or the signing iframe is killed by frame-ancestors
CSP and every tx fails with a blank "Something went wrong" modal.
Enable Solana gas sponsorship for cluster `solana:devnet` in the same
dashboard so users never see a fund-wallet prompt for SOL.
- SOLANA_RPC_URL Alchemy Solana Devnet RPC: https://solana-devnet.g.alchemy.com/v2/<KEY>.
The public https://api.devnet.solana.com is rate-limited and blocks
browser CORS on getLatestBlockhash — do NOT use it.
- LOVABLE_API_KEY Auto-provisioned by Lovable. Optional — only if the unlocked payload
needs one AI call per unlock (invoked from a server function, never the
browser).
Expose secrets to the browser in `.env.local` (Vite only ships VITE_* to the client):
```
VITE_PRIVY_APP_ID=<same value as PRIVY_APP_ID secret>
VITE_SOLANA_RPC_URL=<same value as SOLANA_RPC_URL secret>
```
STACK
- React + Vite + TanStack Start single page.
- `@solana/web3.js` + `@solana/spl-token` for tx building. `@privy-io/react-auth` v3+ for auth AND signing.
- Cluster: Devnet ONLY. Never mainnet.
- PayAI SVM facilitator: `https://x402.payai.network/api/solana-devnet/paid-content`.
- Devnet USDC mint: `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU`, 6 decimals.
- Amount: `"10000"` atomic units = 0.01 USDC per unlock.
- Network id (CAIP-2-ish): `"solana-devnet"` — the string PayAI uses in `accepts[].network`.
- Scheme: `"exact"`.
VITE + BUFFER POLYFILL (4 layers — omit any one and it works in dev but blanks the
published mobile build):
1. `bun add buffer vite-plugin-node-polyfills`.
2. `vite.config.ts`:
```ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [react(), nodePolyfills({ globals: { Buffer: true, global: true }, protocolImports: true })],
optimizeDeps: { esbuildOptions: { define: { global: "globalThis" } } },
resolve: { alias: { "rpc-websockets/dist/lib/client": "rpc-websockets/dist/lib/client.browser.js" } },
});
```
DO NOT add a top-level `define: { global: "globalThis" }` in vite.config — it conflicts with the
plugin's Rollup inject and leaves Buffer undefined in dependency chunks. DO NOT put `import`
statements inside `build.rollupOptions.output.intro` — top-level import inside a chunk wrapper is
illegal and blanks the page.
3. `index.html` — inline BEFORE any bundle loads:
```html
<script type="module">
import { Buffer } from "https://esm.sh/buffer@6";
globalThis.Buffer = globalThis.Buffer || Buffer;
</script>
```
4. `src/polyfills.ts` (imported FIRST in `src/main.tsx`, before anything else):
```ts
import { Buffer } from "buffer";
(globalThis as any).Buffer = (globalThis as any).Buffer || Buffer;
(window as any).Buffer = (window as any).Buffer || Buffer;
```
Then in every file that touches Solana, add `import { Buffer } from "buffer";` at the top — the
plugin cannot inject into your own TypeScript source.
PRIVY (browser auth + Solana signing) — v3, MUST include `solana.rpcs`:
```
bun add @privy-io/react-auth @solana/web3.js @solana/spl-token @solana/kit @solana-program/memo
```
(`@solana-program/memo` is an implicit peer dep of `@privy-io/react-auth/solana`; missing it
crashes the Privy modal at runtime.)
`src/components/PrivyRoot.tsx`:
```tsx
import { PrivyProvider } from "@privy-io/react-auth";
import { toSolanaWalletConnectors } from "@privy-io/react-auth/solana";
import { createSolanaRpc, createSolanaRpcSubscriptions } from "@solana/kit";
const rpcUrl = import.meta.env.VITE_SOLANA_RPC_URL!;
const wsUrl = rpcUrl.replace("https://", "wss://");
export function PrivyRoot({ children }: { children: React.ReactNode }) {
return (
<PrivyProvider
appId={import.meta.env.VITE_PRIVY_APP_ID!}
config={{
loginMethods: ["google", "email"],
embeddedWallets: { solana: { createOnLogin: "users-without-wallets" } },
externalWallets: { solana: { connectors: toSolanaWalletConnectors() } },
solanaClusters: [{ name: "devnet", rpcUrl }],
solana: {
rpcs: {
"solana:devnet": {
rpc: createSolanaRpc(rpcUrl),
rpcSubscriptions: createSolanaRpcSubscriptions(wsUrl),
},
},
},
}}
>{children}</PrivyProvider>
);
}
```
Without the `solana.rpcs` mapping, Privy throws "No RPC configuration found for chain solana:devnet".
Sign the x402 tx with the embedded wallet:
```tsx
import { useSolanaWallets } from "@privy-io/react-auth/solana";
const { wallets } = useSolanaWallets();
const embedded = wallets.find(w => w.walletClientType === "privy");
const signed = await embedded!.signTransaction(tx); // VersionedTransaction
```
X402 PROXY ROUTE (same-origin — the facilitator does NOT send CORS headers, direct
browser fetch throws `TypeError: Failed to fetch` before the 402 arrives):
```ts
// src/routes/api/public/x402-proxy.ts
import { createFileRoute } from "@tanstack/react-router";
const ENDPOINT = "https://x402.payai.network/api/solana-devnet/paid-content";
export const Route = createFileRoute("/api/public/x402-proxy")({
server: {
handlers: {
GET: async ({ request }) => {
const sig = request.headers.get("PAYMENT-SIGNATURE");
const upstream = await fetch(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);
return new Response(body, { status: upstream.status, headers: out });
},
},
},
});
```
The `/api/public/*` prefix bypasses Lovable's published-site auth gate — that's what we want.
Do NOT add auth middleware.
X402 SVM EXACT SCHEME — locked-in tx shape (PayAI rejects anything else):
Exactly 3 instructions in this order, no Memo, compute unit limit ≤ 40_000:
```ts
import { Connection, PublicKey, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } from "@solana/web3.js";
import { createTransferCheckedInstruction, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from "@solana/spl-token";
const conn = new Connection(import.meta.env.VITE_SOLANA_RPC_URL!, "confirmed");
const payer = new PublicKey(embeddedAddress);
const feePayer = new PublicKey(requirement.extra.feePayer); // facilitator's Solana pubkey
const mint = new PublicKey(requirement.asset); // Devnet USDC
const payTo = new PublicKey(requirement.payTo);
const sourceAta = getAssociatedTokenAddressSync(mint, payer, true);
const destAta = getAssociatedTokenAddressSync(mint, payTo, true);
const ixs = [
ComputeBudgetProgram.setComputeUnitLimit({ units: 40_000 }),
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }),
createTransferCheckedInstruction(sourceAta, mint, destAta, payer,
BigInt(requirement.amount), 6, [], TOKEN_PROGRAM_ID),
];
const { blockhash } = await conn.getLatestBlockhash("confirmed");
const msg = new TransactionMessage({
payerKey: feePayer,
recentBlockhash: blockhash,
instructions: ixs,
}).compileToV0Message();
const tx = new VersionedTransaction(msg);
```
Common failures if you deviate: extra Memo ix → "invalid_payload"; compute limit > 40_000 →
"invalid_payload"; wrong feePayer → "invalid_signature".
X402 V2 ENVELOPE (base64-encoded into the `PAYMENT-SIGNATURE` header):
```ts
const envelope = {
x402Version: 2,
accepted: requirement, // echo the exact requirement you picked from accepts[]
payload: { transaction: signedTxB64 },
};
const header = btoa(JSON.stringify(envelope));
```
NOT `{ scheme, network, payload }` at top level — that's v1 and PayAI rejects it as
`invalid_payload`.
4-STEP UI FLOW (always log each step visibly):
1. Challenge — `GET /api/public/x402-proxy` → expect 402 → parse `{ x402Version:2, accepts, error }`.
Pick `accepts[]` where `network === "solana-devnet"` && `scheme === "exact"`.
2. Sign — build the tx above, `embedded.signTransaction(tx)`, base64-serialize `tx.serialize()`.
3. Retry — `GET /api/public/x402-proxy` with header `PAYMENT-SIGNATURE: <base64 envelope>`.
4. Settle — on 200, read `PAYMENT-RESPONSE` header, base64-decode → `{ success, transaction, network, payer }`.
Link the tx to `https://explorer.solana.com/tx/${transaction}?cluster=devnet`.
On any non-200 retry, surface the response body verbatim in the flow log — facilitator errors
(`invalid_payload`, `insufficient_funds`, `invalid_signature`) are the only signal you get.
FUND FLOW UX (before unlock):
- Show the user's Privy Solana address prominently with a copy button.
- Link "Get Devnet USDC" to https://faucet.circle.com/ (choose Solana Devnet) — arrival ~10s.
- Link "Get Devnet SOL" to https://faucet.solana.com — only needed for ATA rent on first-ever unlock.
- "Refresh balance" button that polls USDC balance on click; do NOT auto-poll.
CREATIVE PAYLOAD (what the unlock reveals)
The unlocked payload is the working demo of 'LightCue Automation': Record and trigger lighting cues onchain to verify show timing and enable automated playback. Build a single card/panel that renders the creative artefact (text, image, audio snippet, JSON, or interactive canvas — pick whatever best fits the lighting cue logs discipline). Generate it deterministically client-side, OR make one optional LOVABLE_API_KEY call from a server function per unlock. Keep the payload self-contained — no external accounts, no user uploads, no storage.
TERMINOLOGY (user-facing copy)
- Say "Lovable Cloud", "backend", "sign in", "unlock". Never "Supabase", "wallet extension",
"seed phrase", "gas fee". Sponsored SOL + a 0.01 USDC unlock is the whole story.
DELIVERABLES
- Working demo at `/` — sign in → fund → unlock → see the creative payload.
- 4-step flow log visible during unlock.
- Every settled tx links to Solana Explorer (devnet cluster).
- No follow-up prompts. Ships in one message.
Market sizing.
Indicative figures for hackathon pitches — refine with your own research before raising.