Conversation
Move all plan docs into docs/plans/ with consistent naming. Add new plans for cloud deployment (Fly.io + SQLite + Litestream), auth revised for cloud, hosted service (managed Pi-to-cloud), and race day edge cases. Rename cloud-sync to remote-access, certificate to achievement-certificate. Flesh out display-pubsub with event ideas. Reorganize vision.md into near/mid/long-term tiers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add DERBY_VIEWER_KEY env var for privacy-focused families. When set, all read-only content requires a viewer password — pack leaders share it privately via email/GroupMe. Two-tier cookie system: derby_admin (full access) and derby_viewer (read-only). Admin cookie implicitly satisfies viewer checks. Updated deployment.md and hosted-service.md to reference the new viewer password feature without duplicating auth details. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend auth module (src/auth.ts) with HMAC cookie validation, adminOnly/viewerRequired middleware wrappers, and auto key generation. All 17 mutation endpoints wrapped with adminOnly(), all GET API endpoints wrapped with viewerRequired() (active only when DERBY_VIEWER_KEY is set). Public mode (no key) keeps everything open. Auth routes: POST/GET /admin/login, POST /admin/logout, POST /viewer/login, GET /admin/status. Frontend context plumbed with isAdmin/isPublicMode/isPrivateMode from /admin/status endpoint (no UI gating yet). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace UI form interactions (click add, fill, submit, wait modal, close × 2) with direct fetch() calls to create racers. The test verifies sorting toggle behavior, not racer creation. 1.9s → 0.6s. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Stop logging admin key in plaintext; show masked form (first4..last4) - Protect /ws upgrade with viewer cookie check in private mode - Token login accepts HMAC so URL needn't contain raw secret - Safe fallback: publicMode=false on /admin/status fetch failure - Extract respondJson to shared export from auth.ts (remove duplicate) - Cache admin/viewer keys at module load (no per-request env reads) - Add /viewer/logout endpoint for symmetry with /admin/logout - Mark frontend auth context fields with "Phase 2" comment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Export ADMIN_PURPOSE/VIEWER_PURPOSE/ADMIN_COOKIE/VIEWER_COOKIE from auth.ts so integration tests import them instead of hardcoding - Add adminOnly/viewerRequired unit tests (public-mode pass-through) - Auth-enforcement paths covered by integration tests
Gate admin-only pages (Registration, Race Control) behind full-page walls. Hide mutation buttons (New Event, delete, generate heats) from viewers. Add AdminBanner component, filter nav items by role, and add 6 Playwright tests verifying viewer-mode behavior on a dedicated auth server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Admin Login button in nav when auth is configured (not public mode) - Login opens dialog with password field, refreshes auth on success - Add Logout button when authenticated as admin - Fix: viewers selecting non-complete events now navigate to /heats instead of /register (which showed an admin wall dead-end) - Remove AdminBanner from HeatsView (unnecessary for read-only page) - Position auth button outside scrollable nav area - Add login flow e2e test + viewer navigation redirect test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…enshots - Add unified /auth/login endpoint (detects admin vs viewer from password) - Add PrivateLoginGate screen for fully-private mode (both keys set) - Add PasswordInput component with show/hide toggle - Hide Print Certificates from viewers in StandingsView - Add README screenshot gallery (6 screenshots, 2-column grid) - Refresh screenshots Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a cookie/HMAC-based authentication layer (admin + optional viewer password) and wires it through backend routes and frontend UI gating to support public, admin-protected, and fully private deployments.
Changes:
- Introduces
src/auth.ts(key management, cookie helpers, auth wrappers) and appliesadminOnly/viewerRequiredacross API routes +/ws. - Adds frontend auth state (
authStatus,canEdit) and updates views/navigation to hide admin-only UI, add login/logout flows, and gate pages. - Adds unit/integration/E2E coverage for auth behaviors and updates test scripts/config accordingly; expands docs/README with auth + roadmap updates.
Reviewed changes
Copilot reviewed 32 out of 41 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/auth.test.ts | New unit tests for auth helpers (HMAC, cookie parsing, cached-key behaviors, wrapper pass-through). |
| tests/auth.integration.ts | New integration tests covering login/logout/status and admin/viewer access control against a running server. |
| src/auth.ts | New auth module: admin key resolution (incl. auto), HMAC cookie sessions, wrappers, status, startup logging. |
| src/index.ts | Applies auth wrappers to routes, restricts /ws, adds /auth/login, /admin/*, /viewer/*, /admin/status, logs auth config. |
| src/frontend/api.ts | Adds AuthStatus + auth endpoints (getAuthStatus, adminLogin, unified login, adminLogout). |
| src/frontend/context.tsx | Extends app context with auth state and refreshAuth. |
| src/frontend/main.tsx | Fetches auth status on hydrate, adds private-mode login gate, adds admin login dialog + logout, filters nav based on canEdit. |
| src/frontend/components/AdminBanner.tsx | New banner for read-only users indicating admin access is required for mutations. |
| src/frontend/views/EventsView.tsx | Hides “New Event” and destructive actions for non-admins; shows AdminBanner. |
| src/frontend/views/RegistrationView.tsx | Hard-gates registration behind canEdit with an “Admin access required” wall. |
| src/frontend/views/RaceConsoleView.tsx | Hard-gates race control behind canEdit and hides certificate printing for non-admins. |
| src/frontend/views/HeatsView.tsx | Hides mutation controls for non-admins; improves empty-state messaging for viewers. |
| src/frontend/views/StandingsView.tsx | Restricts certificates link visibility to canEdit on completed events. |
| src/frontend/views/CertificateView.tsx | Adds admin gating for batch printing (/certificates). |
| e2e/ui-sorting.playwright.ts | Speeds up setup by creating racers via API instead of UI interactions. |
| e2e/auth-ui-gating.playwright.ts | New Playwright suite validating viewer-mode UI gating and admin login flow. |
| playwright.config.ts | Adds a dedicated auth project for auth-gating UI tests. |
| package.json | Splits integration tests into API vs auth suites; adds test:ui:auth. |
| README.md | Adds screenshots section; documents auth modes/vars and updated test commands. |
| docs/vision.md | Reorganizes roadmap into near/mid/long-term and adds plan index tables. |
| docs/plans/*.md | Adds/updates multiple plan docs (auth/deployment/edge cases/hosted service/remote access/scoring/etc.). |
| docs/auth-plan.md / docs/certificate-plan.md | Removes older plan docs in favor of the new docs/plans/* structure. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ivate mode tests - Remove api.adminLogin(), nav dialog now uses unified api.login() - Add api.logout() that clears both admin and viewer cookies - Use crypto.timingSafeEqual for all password comparisons - Change PrivateLoginGate label to "Event Password" - Add private mode e2e tests (gate, wrong password, viewer/admin login) - Add test:ui:auth and test:ui:private to test:all - Refresh screenshots Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…docs alignment - Add Secure flag to cookies when request is HTTPS (X-Forwarded-Proto) - Use timingSafeEqual for cookie HMAC validation (not just login) - Write auto-generated admin key with mode 0o600 - Remove unused AdminBanner imports from RegistrationView and RaceConsoleView - Add private mode auth check in CertificateView (single + batch) - Skip auth unit tests when DERBY_ADMIN_KEY/DERBY_VIEWER_KEY are set - Fix auth.md: remove unimplemented "none" sentinel and /display token claims - Fix deployment.md typo: "cloaud" → "cloud" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clear DERBY_ADMIN_KEY/DERBY_VIEWER_KEY before importing the auth module via cache-busted dynamic import so tests always run in public mode regardless of ambient environment variables. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a cookie-based authentication system (admin + optional viewer) to support public, admin-protected, and fully-private deployments, and updates the UI/API/tests/docs accordingly.
Changes:
- Introduces
src/auth.ts(key management, HMAC cookies, auth wrappers, auth status helpers) and wires it into server routes. - Gates API mutations with
adminOnly, read endpoints withviewerRequired(only when viewer key is set), and adds login/logout/status endpoints plus WS gating. - Updates frontend to track auth status (
canEdit), gate admin-only views/actions, and adds integration + Playwright coverage and documentation updates.
Reviewed changes
Copilot reviewed 33 out of 42 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/auth.test.ts | Unit tests for auth helpers (HMAC, cookie parsing, cached mode behavior, wrapper pass-through in public mode). |
| tests/auth.integration.ts | End-to-end API auth verification (login/logout/status + protected endpoints). |
| src/index.ts | Wires auth into Bun routes, adds auth endpoints, wraps API routes, gates WS upgrades. |
| src/auth.ts | New auth module: key resolution (incl. auto), HMAC cookie sessions, middleware wrappers, auth status, startup logging. |
| src/frontend/api.ts | Adds auth API surface: getAuthStatus, unified login, logout helpers. |
| src/frontend/context.tsx | Extends app context with auth state and refreshAuth. |
| src/frontend/main.tsx | Fetches auth status on hydrate, private-mode login gate, nav updates, password input toggle. |
| src/frontend/components/AdminBanner.tsx | Viewer-mode banner indicating admin access required for mutations. |
| src/frontend/views/StandingsView.tsx | Hides batch certificate link unless user can edit. |
| src/frontend/views/RegistrationView.tsx | Hard-gates registration UI behind admin access. |
| src/frontend/views/RaceConsoleView.tsx | Hard-gates race control UI behind admin access; hides batch cert link for viewers. |
| src/frontend/views/HeatsView.tsx | Hides mutation controls for viewers; improves empty-state messaging for viewer mode. |
| src/frontend/views/EventsView.tsx | Hides “New Event” and delete controls for viewers; shows admin banner. |
| src/frontend/views/CertificateView.tsx | Adds auth-status checks (viewer required in private mode; batch requires admin). |
| README.md | Adds screenshots, expands test commands, documents auth modes and env vars. |
| playwright.config.ts | Adds dedicated Playwright projects for auth gating and private mode; updates ignores. |
| package.json | Splits integration tests (API vs auth), adds auth/private UI test scripts, updates test:all. |
| e2e/ui-sorting.playwright.ts | Speeds up test by creating racers via API instead of UI interactions. |
| e2e/screenshots.playwright.ts | Improves screenshot logging (new/updated/unchanged). |
| e2e/auth-ui-gating.playwright.ts | New E2E coverage for viewer-mode UI gating + admin login dialog flow. |
| e2e/auth-private-mode.playwright.ts | New E2E coverage for private-mode login gate behavior. |
| docs/vision.md | Restructures vision doc and plan index tables. |
| docs/plans/standings-scoring.md | Adds standings scoring research doc (points-per-place). |
| docs/plans/remote-access.md | Renames/reframes remote access plan (tunnel vs cloud). |
| docs/plans/race-day.md | Updates link to new standings scoring doc. |
| docs/plans/race-day-2026-analysis.md | Updates link to new standings scoring doc. |
| docs/plans/hosted-service.md | Adds hosted service plan doc. |
| docs/plans/edge-cases.md | Adds race-day edge cases plan doc. |
| docs/plans/display-pubsub.md | Expands display pub/sub plan detail and event taxonomy. |
| docs/plans/deployment.md | Adds Fly.io deployment plan doc. |
| docs/plans/auth.md | Adds/updates auth plan doc describing implemented phases. |
| docs/plans/achievement-certificate.md | Adds consolidated certificate plan doc. |
| docs/certificate-plan.md | Removes older certificate plan doc (superseded). |
| docs/auth-plan.md | Removes older auth plan doc (superseded). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Remove raw admin key acceptance from GET /admin/login (HMAC only)
- Export ADMIN_LOGIN_PURPOSE constant, use in index.ts and integration tests
- Remove unused imports from index.ts (parseCookies, isPublicMode, isPrivateMode)
- Add aria-label and remove tabIndex={-1} on password toggle button
- Add private mode auth check to display page
- Fix auth.md: Max-Age 30 days, remove /healthcheck, update display docs
- Update vision.md: auth plan status → Implemented
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Introduces a cookie-based authentication system (admin + optional viewer) and applies it across the Bun server routes and React UI to support public, admin-protected, and fully-private deployments.
Changes:
- Add
src/auth.tsand wire backend route protection (adminOnly,viewerRequired) plus login/logout/status endpoints. - Update frontend context/UI to respect auth state (hide mutation controls for viewers, add private-mode login gate).
- Add unit/integration/E2E coverage for auth behaviors and update docs/plans + test scripts.
Reviewed changes
Copilot reviewed 34 out of 43 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/auth.test.ts | Adds unit tests for auth helpers and public-mode behavior. |
| tests/auth.integration.ts | Adds integration tests for login flows, cookies, and protected routes. |
| src/index.ts | Wires auth into server startup, protects API routes + websocket upgrade, adds auth endpoints. |
| src/auth.ts | New auth module: key management (incl. auto), HMAC cookies, wrappers, status, logging. |
| src/frontend/views/StandingsView.tsx | Hides certificate-print link unless user can edit. |
| src/frontend/views/RegistrationView.tsx | Adds admin-only wall for registration in viewer mode. |
| src/frontend/views/RaceConsoleView.tsx | Adds admin-only wall for race control; hides certificate-print link for viewers. |
| src/frontend/views/HeatsView.tsx | Hides admin controls for viewers; adjusts empty-state copy for viewers. |
| src/frontend/views/EventsView.tsx | Hides “New Event” + delete controls for viewers; adds AdminBanner. |
| src/frontend/views/CertificateView.tsx | Adds auth checks (private-mode login requirement; batch print admin-only). |
| src/frontend/main.tsx | Adds auth status hydration, private-mode login gate, and nav login/logout dialog. |
| src/frontend/display.tsx | Adds private-mode auth check; shows “Authentication Required” screen when needed. |
| src/frontend/context.tsx | Extends app context with auth status (canEdit, mode flags) and refreshAuth. |
| src/frontend/components/AdminBanner.tsx | New banner shown to viewers indicating admin access required for changes. |
| src/frontend/api.ts | Adds auth APIs (getAuthStatus, login, logout) and AuthStatus type. |
| README.md | Documents auth modes, env vars, and updated test commands; adds screenshots section. |
| playwright.config.ts | Adds dedicated Playwright projects for auth-gating and private-mode tests. |
| package.json | Splits integration + UI tests into auth/private variants; updates scripts. |
| e2e/ui-sorting.playwright.ts | Speeds up setup by creating racers via API instead of UI form flows. |
| e2e/screenshots.playwright.ts | Improves screenshot logging output (new/updated/unchanged). |
| e2e/auth-ui-gating.playwright.ts | New E2E tests verifying viewer-mode UI gating and admin login dialog flow. |
| e2e/auth-private-mode.playwright.ts | New E2E tests verifying private-mode login gate behavior. |
| docs/vision.md | Reorganizes roadmap/plan links and clarifies near/mid/long-term direction. |
| docs/plans/standings-scoring.md | Adds standings scoring research doc (points-per-place). |
| docs/plans/remote-access.md | Updates/renames remote access plan and clarifies tunnel vs cloud. |
| docs/plans/race-day.md | Fixes reference to the new standings scoring plan location. |
| docs/plans/race-day-2026-analysis.md | Updates link to standings scoring plan doc. |
| docs/plans/hosted-service.md | Adds hosted service plan doc. |
| docs/plans/edge-cases.md | Adds race-day edge cases plan doc. |
| docs/plans/display-pubsub.md | Expands display pub/sub plan with event taxonomy and UX ideas. |
| docs/plans/deployment.md | Adds Fly.io deployment plan doc. |
| docs/plans/auth.md | Adds auth plan doc aligned with the implementation. |
| docs/plans/achievement-certificate.md | Adds consolidated certificate plan doc aligned with current implementation. |
| docs/certificate-plan.md | Removes older certificate plan doc (replaced by docs/plans/achievement-certificate.md). |
| docs/auth-plan.md | Removes older auth plan doc (replaced by docs/plans/auth.md). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Precompute HMACs at module load for sync cookie validation - Add per-IP rate limiting (10 attempts/60s) on all login endpoints - Remove dead /admin/login POST endpoint - Show login/logout button regardless of event selection - Nav logout works for both admin and viewer roles - Add viewer logout + read-only e2e tests - Fix test:integration:api glob, .gitignore, display.tsx auth state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a cookie-based authentication system (admin + optional viewer access) to protect mutation APIs and optionally require login for read-only views, with corresponding frontend UI gating and expanded automated test coverage.
Changes:
- Introduces
src/auth.ts(admin key management, HMAC cookies, auth wrappers, rate limiting) and wires it into backend routes (API +/ws+ auth endpoints). - Updates frontend context + navigation + views to hide/disable admin-only functionality based on auth status and to show private-mode login gating.
- Adds unit/integration/E2E coverage for auth flows, plus documentation updates describing auth modes and future plans.
Reviewed changes
Copilot reviewed 34 out of 44 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/auth.test.ts | Unit tests for auth helpers (HMAC, cookie parsing, mode flags, wrapper pass-through). |
| tests/auth.integration.ts | Integration tests for login/logout, status, route protection, and rate limiting. |
| src/index.ts | Applies adminOnly / viewerRequired, gates /ws, and adds /auth/login, /admin/login, /admin/logout, /viewer/login, /viewer/logout, /admin/status. |
| src/auth.ts | New auth module: key management (DERBY_ADMIN_KEY=auto), HMAC cookies, auth wrappers, rate limiting, startup logging. |
| src/frontend/views/StandingsView.tsx | Hides certificate printing link behind canEdit. |
| src/frontend/views/RegistrationView.tsx | Adds admin-only “wall” for non-edit users. |
| src/frontend/views/RaceConsoleView.tsx | Adds admin-only “wall” and hides certificate printing link behind canEdit. |
| src/frontend/views/HeatsView.tsx | Hides admin controls for viewers and adjusts empty-state messaging for read-only users. |
| src/frontend/views/EventsView.tsx | Hides event creation/deletion actions for viewers and adds AdminBanner. |
| src/frontend/views/CertificateView.tsx | Adds auth checks: requires login in private mode; restricts batch printing to admins. |
| src/frontend/main.tsx | Adds auth status hydration/refresh, private-mode login gate UI, and nav login/logout dialog + role-based nav filtering. |
| src/frontend/display.tsx | Adds private-mode auth check to block display view until authenticated. |
| src/frontend/context.tsx | Extends app context with auth fields (isAdmin, isViewer, public/private, canEdit) and refreshAuth. |
| src/frontend/components/AdminBanner.tsx | New banner shown when user lacks edit/admin privileges. |
| src/frontend/api.ts | Adds /admin/status fetch, unified login (/auth/login), and logout helpers. |
| playwright.config.ts | Adds dedicated Playwright projects for auth-gating and private-mode suites. |
| package.json | Splits integration tests into API vs auth; adds UI auth/private test scripts; updates test:all. |
| e2e/ui-sorting.playwright.ts | Uses API calls to create racers (reduces UI interaction in sorting test). |
| e2e/screenshots.playwright.ts | Improves screenshot runner output formatting. |
| e2e/auth-ui-gating.playwright.ts | New E2E tests validating viewer-mode UI gating + admin login dialog flow. |
| e2e/auth-private-mode.playwright.ts | New E2E tests validating private-mode login gate and viewer/admin behavior. |
| README.md | Adds auth documentation and expands testing commands list. |
| docs/vision.md | Restructures roadmap/vision and references new plan docs. |
| docs/plans/standings-scoring.md | New research doc on points-per-place scoring. |
| docs/plans/remote-access.md | Renames/reframes remote viewing doc around tunneling vs “cloud sync”. |
| docs/plans/race-day.md | Updates reference link to standings scoring research doc. |
| docs/plans/race-day-2026-analysis.md | Updates reference link to standings scoring research doc. |
| docs/plans/hosted-service.md | New hosted-service plan doc. |
| docs/plans/edge-cases.md | New plan doc covering common race-day disruptions and proposed features. |
| docs/plans/display-pubsub.md | Expands WebSocket pub/sub plan with richer display event concepts. |
| docs/plans/deployment.md | New Fly.io deployment plan doc. |
| docs/plans/auth.md | New auth plan doc describing intended design and phases. |
| docs/plans/achievement-certificate.md | New consolidated certificate plan doc (supersedes older docs). |
| docs/certificate-plan.md | Removes older certificate plan doc (replaced by plans/achievement-certificate.md). |
| docs/auth-plan.md | Removes older auth plan doc (replaced by plans/auth.md). |
| .gitignore | Ignores .derby_admin_key auto-generated admin key file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Derive _privateMode from both admin and viewer keys being set - Pass secure flag to clearAdminCookie/clearViewerCookie in logout - Update README and auth plan to reflect unified /auth/login endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
src/auth.tswith HMAC cookie-based authentication,adminOnly()andviewerRequired()middleware wrappers, auto key generation (DERBY_ADMIN_KEY=auto)adminOnly(), all GET API endpoints withviewerRequired()(active only whenDERBY_VIEWER_KEYis set)POST/GET /admin/login,POST /admin/logout,POST /viewer/login,GET /admin/statusisAdmin/isPublicMode/isPrivateMode(no UI gating yet)Test plan
tests/auth.test.ts) — HMAC, cookie parsing, key managementtests/auth.integration.ts) — login/logout, protected mutations, protected reads, status endpoint🤖 Generated with Claude Code