vestige/docs/launch/waitlist-setup.md
Sam Valladares cfe8d03d36 feat(landing): full-viewport GPU node-engine hero + waitlist + dashboard auth removal
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>
2026-06-24 22:54:51 -05:00

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

  1. Create a project at https://supabase.com (free tier is fine for launch).
  2. 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.