Public talking points for the 501 Cloud Developer follow-up. Rehearse the 90-second tour first; use the sections below when they ask you to go deeper or extend the system.
Cover sheet for reviewers: SUBMISSION.md
- Live dashboard - open production (or staging). Point out session ID, player photos, scores, active status.
- Edit a score - bump a player score; show it persists after reload.
- Real-time - open a second tab; change a score in one tab; it appears in the other (Durable Object WebSocket).
- Match history -
/history→ click a completed session → video plays (poster, seek via byte-range). - Security - show
packages/db/src/schema.tsRLS policies;withPrincipal()in API routes. - Proof - run
npm run db:rls:checkornpm run test:rls(isolation is tested, not assumed). - Cloud - mention staging vs production: separate Neon branches, Worker envs, R2 buckets, custom domains.
- Analytics - expand live/final analytics panel; charts animate in on session end (rich seed data for realistic race lines).
- Operator polish - Share board (URL) vs Copy (UUID); keyboard
?shortcuts; undo appends correcting event (not delete). - History - filter panel + tagged cards; timeline click seeks video; export scorecard preview modal.
- Multi-venue - switch Venue A/B; overlay transition; each owner sees isolated data (RLS + owner-scoped cache keys).
One-liner: “I treat the dashboard like a venue screen - fast first paint, no jank on score updates, and bounded bundle size.”
Points to hit:
- Lazy-loaded routes; TanStack Query for cache + dedupe + optimistic score PATCH with rollback
- Virtualised history list for long session lists
- Images: fixed dimensions, lazy load,
srcsetwhere available - Video:
preload="metadata"only until play; poster frame first - CI enforces bundle budget (
npm run size)
If they push: mention PWA Workbox cache for read-only offline history; React Query persist to localStorage.
Arcade motion: intro splash sets brand; live scores use the same bubble/liquid language via ParticleBurst + milestone banners. Optimistic PATCH is never blocked; prefers-reduced-motion and an FX tier toggle keep it accessible on venue tablets.
One-liner: “Media goes to R2 behind the CDN; the browser only fetches what the viewport or playback position needs.”
Points to hit:
- Immutable hashed keys + long cache headers on public assets
- Signed, short-lived URLs for private uploads
- MP4 with HTTP Range for seek without full download
- HLS field in schema +
<source>support; offlinenpm run media:transcodefor local/demo pipeline - Media Capabilities API ranks sources by device support
Honest gap: automated queue transcoding is designed but not deployed (cost/complexity for take-home).
One-liner: “Today’s choices work for demo scale; I’d add pagination depth, async media, and observability before thousands of concurrent venues.”
Points to hit:
- Keyset/cursor pagination (already on list API) + composite indexes
- Hyperdrive connection pooling; Neon read replicas for read-heavy history
- Transcoding: R2 upload event → Queue → FFmpeg Container, retries + DLQ, multiple renditions (360p/720p/HLS)
- WebSockets: Durable Object sharding by session; hibernation for idle rooms
- KV cache for hot session reads; structured traces Worker → DO → Hyperdrive → Neon
One-liner: “Player names and photos are personal data; I assume a bug in application code and enforce isolation in the database.”
Points to hit:
- Zod strict on all inputs
- RLS forced on every table;
withPrincipal()setsapp.current_ownerper transaction - Append-only
score_eventsaudit trail - CORS allowlist, CSP, React XSS defaults
- Signed media URLs; upload mime/size limits
- Staging never seeded from production; secrets per environment
One-liner: “The take-home uses a demo API key; production swaps in real auth and layers WAF, monitoring, and rotation on top of RLS.”
Points to hit:
- JWT or venue SSO → Worker verification → Neon
authenticatedRole+ RLS onauth.uid() - Cloudflare WAF + rate limits; least-privilege deploy tokens
- Secret rotation; dependency scanning (CodeQL, Dependabot)
- Backups / Neon PITR; GDPR erasure path for player photos
- Error monitoring without leaking stack traces to clients
| Question | Direction |
|---|---|
| Why arcade motion in an operator dashboard? | Instant feedback for score changes; same 501 brand language as the intro; particles are non-blocking overlays with reduced-motion + tier controls |
| Share board vs Copy match ref? | Share copies a deep link (/?session=) for another device; Copy is the raw UUID for support/logs |
| What happens when you switch demo venues? | Clear persisted React Query cache, navigate off stale session routes, exchange auth, reload owner-scoped queries - RLS already isolates rows |
| Add auth + RBAC | Auth provider → JWT in Worker → map to owner UUID → RLS policies use auth.uid() instead of GUC |
| 10k concurrent WebSocket sessions | DO per session (already); shard hot sessions; hibernation; backpressure on broadcast; consider pub/sub fan-out |
| Thousands of sessions in history | Virtualised list (done); keyset pagination (done); add search index on created_at + owner |
| Real transcoding pipeline | R2 event → Queue → Container FFmpeg → write HLS manifest back to R2 → update session row |
| Observability | OpenTelemetry from Worker; trace IDs through DO and Hyperdrive; SLOs on PATCH latency and WS connect time |
| GDPR / data residency | EU Neon region; retention job on media; erasure endpoint that deletes R2 objects + DB rows |
| Why Postgres not Mongo/Firestore? | Relational session/player/score model; ACID score updates; RLS is a Postgres strength |
| Why Cloudflare not AWS? | Edge Workers + R2 zero egress + DO for stateful WS; Neon for managed Postgres branching |
| Why not Next.js? | Dashboard is authenticated SPA with live WS - no SEO need; Vite keeps Worker/Pages deploy simple |
| Topic | What to say |
|---|---|
| Demo API key in bundle | Take-home stand-in; production uses JWT and never ships secrets in the SPA |
WS ?key= query param |
Browser can’t set custom headers on WebSocket; production uses cookie session |
| FFmpeg offline only | Avoids paid Container hours in a take-home; architecture doc shows queue path |
| E2E in CI | May need DATABASE_URL secret; RLS tests prove the critical isolation path |
Full gap list: checklists/03-known-gaps.md.
| File | Why |
|---|---|
apps/web/src/routes/Overview.tsx |
Live scores + optimistic PATCH |
apps/api/src/routes/sessions.ts |
REST handlers + withPrincipal() |
apps/api/src/durable-objects/session-room.ts |
WebSocket broadcast |
packages/db/src/schema.ts |
Tables + RLS policy definitions |
packages/db/src/with-principal.ts |
How RLS context is set |
apps/web/src/routes/History.tsx |
Virtualised history + filters |
apps/web/src/components/HistorySessionCard.tsx |
Tagged cards + game-format accents |
apps/web/src/lib/auth.tsx |
Venue switch + owner-scoped cache reset |
- How does the 501 Hub split venue-operator vs player-facing surfaces today?
- Where does live game state originate - on-prem hardware, edge devices, or cloud?
- What media formats do venues upload today, and is transcoding centralised?
Shows curiosity about their domain without overstepping the take-home scope.