Keep going, build OIDC + VC + federation in-house as planned
Bespoke OIDC Cloudflare Worker in 1–2 weeks, VC signing keys on Cloudflare with joint control, runtime-themed federation portal, single Next.js static-export app.
A pretty avatar picker dressed up as an identity platform, with the actually-hard parts deferred to "phase 2" and underestimated by an order of magnitude. The "1–2 week Cloudflare Worker OIDC issuer" is the single most dangerous decision in the codebase — and the only good news is that it is not yet in code.
pending_profiles RLS is USING (true)The public anon key is in your shipped JS bundle. Anyone can SELECT * and live-watch every registration in flight: handles, traits, geo, locales. Calling this "v0 known gap" in a migration comment is professional malpractice.
lookup_signin_email_by_handle is a phishing oracleSECURITY DEFINER RPC maps public handle → real recovery email. Enumerate handles, build (handle, email) pairs, run against haveibeenpwned. Tuesday for any attacker.
Zero OIDC code. Zero VC issuance. Federation router returns 501. Federation portal and admin are empty. @sporthead/identity empty. You built the gift wrapping before the gift.
output:"export" turns off every feature Next.js exists to provide. Vite + React or Astro would give the same output in a third of the bundle and seconds-not-minutes builds.
PIN-as-password, synthetic @anon.sporthead.id emails, mailer_autoconfirm=true, RPC to leak email by handle. You pay the cost of both Supabase Auth and a bespoke flow with the benefit of neither.
No SERIALIZABLE txn or advisory lock around founder-number assignment. Turnstile secret is unset because both wrangler configs have <TODO> placeholders. A 90-second bot run destroys the cohort meaning.
No ESLint config, Prettier, Husky, lint-staged, commitlint. Changesets configured in package.json but .changeset/ never bootstrapped. Sentry/PostHog/OTel all absent.
One Next.js codebase serving all federations means one bad deploy nukes SLFA + WAB + everything. Per-tenant SLA, analytics, audit, rate-limit are impossible without re-inventing per-tenant deploys badly. ADR-0005 is wrong.
pin_hash exists, nothing writes to itSignature of a half-finished mental model. Either populate it server-side or drop the column. Dead schema becomes a future midnight incident.
Six criteria, weighted toward the December 2026 launch reality. Each option scores 1–5 against each.
Four shapes, ordered by size of change. A is the implicit baseline; B/C/D are the counter-proposals.
Darker green = stronger fit · rust = weak. Bottom row shows weighted totals.
| A · status quo | B · small | C · medium | D · big | |
|---|---|---|---|---|
| Closes critical security (0.25) | 1 | 5 | 5 | 5 |
| Match to product vision (0.22) | 2 | 2 | 4 | 5 |
| Likelihood of Dec 2026 launch (0.20) | 1 | 5 | 3 | 2 |
| Engineering cost (0.13) | 4 | 5 | 3 | 2 |
| Operational simplicity (0.12) | 2 | 4 | 3 | 3 |
| Reversibility (0.08) | 3 | 5 | 4 | 2 |
| Weighted total | 1.85 | 4.04 | 3.80 | 3.57 |
Same six criteria projected as a hexagon. Larger area is stronger overall fit. B dominates the launch-critical axes; D wins on vision but pays for it elsewhere.
Weighted-total order. Not a decision — surfaced for the architecture review on 2026-05-20.
Recommended path: B now → C next quarter. B ships registration + founder cohort + numbered VC by December without compromising security; C earns the "single sport identity" billing in Q1 2027 with Hydra in front of the PIN-first UX. D is the right shape if you were starting tomorrow but is too disruptive given Sierra Leone fight-night is six months out.
pending_profiles open RLS shipped?If not, anything pulled while the policy was open should be treated as already-exposed. Rotate + audit logs from the Supabase project before we close the policy.
@liam · before next commitOption B explicitly defers OIDC, VC issuance, and federation portal. The launch story becomes "claim your Sport Head + your number" — not "Sign in with SHOT". Is that the right narrative for Sierra Leone?
@jonny · architecture reviewOption C and D both depend on this. Need a binding ops decision — who runs it, what's the SLO, how is the postgres backup story coupled to the Supabase one?
@liam · spikeThe seed in migration 08 has slug='slfa' + vanity_domain='westafricaboxing.com' + sport='Boxing'. SLFA is the Sierra Leone Football Association. Either the seed is wrong or the federation model conflates two distinct partners.