The /launch landing page (bleeding-edge waitlist hero): - Full-viewport WebGL node engine (src/lib/hero/nodeEngine.ts): 40k GPU particles on a two-FBO GPUComputationRenderer running an 18s looping cinematic — particles stream in from the screen edges, slam together and EXPLODE at center, reform into a brain / graph constellation / neural lattice, dissolve back out, loop. Glowing additive particles + UnrealBloom, fills the whole screen edge to edge. - Key GPGPU fix: custom DataTexture shape targets read black in GPUComputationRenderer (three.js #15882), so shape targets are computed PROCEDURALLY in-shader from the per-particle seed. Position integrates raw per-frame velocity (Codrops pattern), texel-center reference attribute. - AmbientField (god-ray glow + parallax starfield), phantomBrain (deterministic seed-from-identity memory graph), LandingHero/neuralFlow (WebGPU Cinema-storm reuse + WebGL fallback, kept as alternates). Manifesto copy, seed input, email capture. No em-dashes. SSR off + prerender for the WebGL route. Waitlist backend (Supabase, owned data): - supabase/migrations/0001_waitlist.sql (RLS-locked table, dedup), Edge Function waitlist-join (CORS, validation, honeypot, dedup, Resend confirmation), setup doc. Dashboard auth removed (reverts the launch-polish auth wall that 401'd the dashboard against itself): mod.rs/state.rs/websocket.rs back to no-token, api.ts/websocket.ts/ +layout.svelte stripped of the token client. Pending-review UX + Memory PR gating kept. Research/spec docs under docs/launch/. Frontend typechecks 0/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.8 KiB
Waitlist capture — setup (10 minutes)
The public waitlist form (/dashboard/waitlist) is already built. It POSTs JSON
to VITE_WAITLIST_ENDPOINT. This guide stands up the storage backend: a
Supabase table + Edge Function that stores every signup and (optionally) emails a
confirmation.
Owned data, queryable Postgres, free tier handles launch-day volume.
1. Create the project
- Create a project at https://supabase.com (free tier is fine for launch).
- Grab the Project Ref (Settings → General) and the service-role key (Settings → API). The service-role key is a secret — never commit it or expose it client-side.
2. Deploy the schema + function
supabase link --project-ref <your-project-ref>
supabase db push # applies supabase/migrations/0001_waitlist.sql
supabase functions deploy waitlist-join # deploys supabase/functions/waitlist-join
3. Configure function secrets
SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are injected automatically.
Email confirmation is optional — set these to enable it (reuses Resend):
supabase secrets set RESEND_API_KEY=<your-resend-key>
supabase secrets set WAITLIST_FROM_EMAIL="Vestige <waitlist@yourdomain>"
# Optional: lock CORS to your sites (comma-separated). Omit for "*".
supabase secrets set WAITLIST_ALLOWED_ORIGIN="https://samvallad33.github.io,https://vestige.dev"
4. Point the form at the function
The function URL looks like:
https://<project-ref>.functions.supabase.co/waitlist-join
Set it at build time for the dashboard:
# apps/dashboard/.env (or your Pages build env)
VITE_WAITLIST_ENDPOINT=https://<project-ref>.functions.supabase.co/waitlist-join
Rebuild the dashboard (pnpm --filter @vestige/dashboard build) and redeploy.
5. Verify
curl -X POST https://<project-ref>.functions.supabase.co/waitlist-join \
-H 'content-type: application/json' \
-d '{"email":"you@example.com","name":"You","plan":"solo","priority":"sync"}'
# -> {"ok":true,"deduped":false}
# Re-run the same command:
# -> {"ok":true,"deduped":true} (idempotent; no duplicate row)
6. Export the list on launch day
In the Supabase SQL editor:
select email, name, plan, priority, notes, created_at
from public.waitlist
order by created_at desc;
Or download a CSV from the Table Editor. That list is the asset that converts to paid over the weeks after the spike.
Notes
- RLS is on, anon can do nothing. Only the service-role key inside the function can insert. The public anon key (if ever used) has zero table access.
- Honeypot + email validation run both client-side (in the form) and server-side (in the function).
- Dedup is enforced by a unique index on the lowercased email; a re-submit
returns
{ok:true,deduped:true}instead of erroring or duplicating.