The MPRC platform is a React single-page application backed by Firebase Authentication, Cloud Firestore, and Firebase Cloud Functions. It publishes club content and events and contains an in-progress platform for member accounts, race registration, Stripe-hosted payments, merchandise orders, administration, and Strava connections.
- Production informational site: runmprc.com
- Repository: Run-MPRC/Run-MPRC.github.io
Commerce status: the repository contains a substantial Stripe/race/shop prototype, but live payments are not production-ready. Do not configure or enable live Stripe keys until the P0 gates in STRIPE_COMMERCE_DESIGN.md and SECURITY.md are closed and a controlled pilot is approved.
| You are… | Start here |
|---|---|
| A club officer or backup maintainer | OFFICER_START_HERE.md — no coding required |
| An AI agent working a claimed issue | AGENTS.md — safety, ownership, documentation, and proof rules |
| A developer | Continue with the technical documents below |
| Document | Purpose |
|---|---|
| SYSTEM_DESIGN.md | Current and target architecture, boundaries, data model, invariants, workflows, and decisions |
| STRIPE_COMMERCE_DESIGN.md | Stripe configuration, checkout saga, webhooks, capacity, inventory, refunds, and launch checklist |
| SECURITY.md | Dated risk register, threat model, required controls, privacy, secure delivery, and incident response |
| IMPLEMENTATION_PLAN.md | Dependency graph, phases, gates, first implementation tranche, and definition of done |
| GITHUB_ISSUES.md | Ordered system-level trackers and directly assignable small/medium issues |
| GITHUB_ISSUE_SLICES.md | Atomic one-agent child tickets for every large tracker, with dependencies and required proof |
| OPERATIONS_RUNBOOK.md | Environments, secrets, testing, deployment, launch, reconciliation, refunds, incidents, and restore |
| AGENTS.md | Repository-specific safety and execution instructions for coding agents |
| OFFICER_START_HERE.md | Plain-language change, approval, live-verification, access, and emergency guides |
| OFFICER_HANDBOOK.md | Concise officer decision guide and index to the short step-by-step procedures |
Historical developer/content/LLM guides remain under docs/. They predate portions of the commerce implementation; use the root documents above for target architecture and launch decisions.
src/: React 18 UI, routes, services, Firebase client, account/admin/event/shop experiences.functions/: Node.js 20 Firebase Functions for identity, checkout, Stripe webhooks, admin commands, exports, email, rate limiting, and Strava.firestore.rulesandfirestore.indexes.json: browser data boundary and query indexes.tests/firestore-rules/: emulator-based allow/deny coverage..github/workflows/: frontend, Functions, Rules CI and deployment automation.public/404.html,public/index.html, andpublic/spa-navigation.js: current tested GitHub Pages callback handoff. It preserves safe same-origin path, query, and fragment state.
Deployment reality checked 2026-08-14: merges run CI but do not start the manual release workflow. The protected gate accepts one exact current merged commit, rechecks its newest CI run after approval, uses one fixed Firebase target set, fails when protected authority/configuration is missing, verifies Firebase before publishing GitHub Pages, and gives no server credential to website preparation or publication. Ordinary Git-triggered Netlify production builds are paused. WEB-002D #659 completed one bounded accessibility release: exact merge 46e23647d8e0bf9fa3a574ea5c5f993be10a419d published deploy 6a7ece87c5ca4d0007c1a3fc from source 7496fe0881fb52908c4ff2f40f488df09c94c908, tree ccac4c189c195db8ab594e0eefe256ea9fa04996, 62 files, and artifact digest e4c26e6f0fbcd086663d86238675f0be228fb649a00628c1c97d1166612f49c7. Signed-out desktop and phone route-focus/menu checks passed. Repause merge 3138a00c1c48e1d5d1dcda0b44722b09a2194ff7 passed exact-main CI run 31783808994; attempt 6a7ed0ddb00a46000818878d published nothing, deploy 6a7ece87c5ca4d0007c1a3fc remained live, and the manifest is inactive. #623 deploy 6a7e072f8f346b0008510d29 remains rollback history and its inert directory interface is unchanged beneath the accessibility delta. No Firebase, Rules, Functions, indexes, provider configuration, account, sign-in state, role, member data, payment, or connected directory backend changed. The prior bounded #473 deploy 6a6dc9ea588b0c0008036312 remains older history. The source stops adding a Pages CNAME, but GitHub Pages currently still claims runmprc.com and its default URL redirects there; only a controlled #136/WEB-001 publication and provider readback can clear that conflict. Reusable protected publication to the live Netlify-served runmprc.com is not configured yet. Treat GitHub Pages, Netlify, runmprc.com, Firebase, and outside providers as separate states.
Owned staging checked 2026-08-26: WEB-001A2 #665 created the isolated run-mprc-staging Firebase project under the club account and published its first static Hosting version. CI-001C1 #667 adds a protected staging-only OIDC verifier with two named reviewers and a keyless principal that can read only the exact project identity. CI-001D1 #669 adds the empty delete-protected (default) Firestore database in us-west2 and deploys exact Rules/indexes from source ee16bd16220ab58bd3a2add80dd2f39a1d514dd7; provider definitions matched, fixed anonymous server-only reads were denied, and the root collection count remained zero. CI-001D2 #671 deployed exact merged source 42542303d043f87a8f1a04be2f0b4f2a88e0318c to Auth only. Live readback shows instrumentless Firebase Authentication with Identity Platform, billing disabled, password-required email/password, improved email privacy, only the two staging authorized domains, and no other sign-in provider. Disposable API and browser sign-in/sign-out checks passed, both identities were removed, no email or SMS was requested, and the final user and Firestore root-collection counts were zero. CI-001D3 #674 adds a source-only guard for exactly createMemberOnSignUp and ensureMemberProfile; billing remains unapproved, so no Functions-related API was enabled and no Function was deployed. CI-001D4 #676 merged its argument-closed Hosting/App Check source guard through PR #677 as exact source bce911a7083201cfe2141edaa9660f210287bf57; all five exact-main jobs in run 33018136439 passed. The bounded provider phase enabled only Firebase App Check and reCAPTCHA Enterprise, created exactly one score-based key restricted to the two staging hosts, registered it to the one web app with a one-hour token lifetime, and published that exact source as Hosting version 8556fc51210bdc66. A made-up reserved-domain browser account completed create, sign-out, fresh sign-in, and post-enforcement sign-in; the empty Events read succeeded both signed in and signed out with no browser warning or error. Authentication plus Firestore now read ENFORCED with replay protection OFF; the same valid Auth credentials without App Check were rejected with HTTP 401, and a tokenless Firestore Events request was rejected with HTTP 403. The disposable user was deleted and final users plus root collections are zero. Billing remains disabled, Functions-related APIs and deployed Functions remain absent, the account page fails closed while those Functions are unavailable, and production Firebase Hosting plus the Netlify-served runmprc.com are unchanged.
The #99 local Firebase boundary is available for synthetic source development. It uses a non-addressable demo-mprc-local configuration and loopback Auth, Firestore, and Functions emulators. It stops startup when an emulator connection cannot be configured. App Check, Analytics, and Sentry stay off locally.
Node.js 20 lockfile installation remains the baseline for maintainers preparing isolated, non-Firebase checks:
npm ci --legacy-peer-deps
npm --prefix functions ciStart it in two terminals:
# Terminal 1
npm run emulators
# Terminal 2 — only after all three emulators report ready
npm startOpen only http://localhost:3000. Stop if Firebase traffic uses a non-loopback host. Use synthetic records only. This does not make checkout, refunds, email, Strava, or other outside-provider calls safe; follow OPERATIONS_RUNBOOK.md before any provider test.
Do not use a Netlify preview, locally served optimized build/, or run-mprc-staging with real identities, private/member records, admin work, or outside-provider actions. WEB-001A1 #663 makes optimized builds choose staging or production explicitly. WEB-001A2 #665 verifies the owned static stage, CI-001D1 #669 verifies its empty Firestore Rules/index boundary, CI-001D2 #671 proves bounded disposable email/password Auth behavior, and CI-001D4 #676 enforces App Check for Authentication plus Firestore. Functions and outside providers are still not isolated for end-to-end use; App Check does not authorize member, admin, payment, or provider behavior.
npm --prefix functions run lint
npm --prefix functions run test:run -- --runInBand
CI=true npm test -- --watchAll=false --runInBand
npm run test:spa-navigation
npm run test:firebase-auth-staging
npm run test:firebase-profile-functions-staging
npm run test:rules
CI=true DISABLE_ESLINT_PLUGIN=true \
REACT_APP_FIREBASE_ENVIRONMENT=staging \
REACT_APP_FIREBASE_API_KEY=synthetic-ci-api-key-not-a-credential \
REACT_APP_FIREBASE_AUTH_DOMAIN=mprc-staging-ci.firebaseapp.com \
REACT_APP_FIREBASE_PROJECT_ID=mprc-staging-ci \
REACT_APP_FIREBASE_STORAGE_BUCKET=mprc-staging-ci.firebasestorage.app \
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=100000000001 \
REACT_APP_FIREBASE_APP_ID=1:100000000001:web:abcdef0123456789 \
npx --no-install react-scripts build
node scripts/firebase-hosting-contract.js verify buildRules and commerce-emulator tests require Java 21. The repository pins Firebase CLI 15.24.0; use only that lockfile copy with Node 20 and explicit demo-* projects. The synthetic values above authorize no provider call or deployment. The direct react-scripts build command avoids regenerating public/sitemap.xml; the following verifier proves that executable JavaScript selected the synthetic staging project and contains no known production Firebase identity. The normal npm run build validates its selected hosted environment and runs the sitemap generator. Hosted CI runs the frontend Jest suite under #124, the SPA callback suite under #126, and the release-gate source tests under #135. #667 supplies a separate protected staging project-identity check only. #669 proves the exact Rules/index files on an empty staging database but grants no reusable deploy authority. #671 adds a focused source test and an Auth-only deploy guard; its separate exact-main provider evidence proves the live narrow Auth configuration and disposable sign-in cleanup, while source and CI alone still prove neither fact. #674 adds a separate source-only two-Function guard; passing it does not prove billing, API enablement, Function deployment, App Check, or backend behavior. Parent #133 still owns Firebase deployment roles and production protection; #136 owns the actual staged profile-recovery release. Required branch checks, complete backend/provider isolation, protected publication, security headers, rollback proof, and hosting consolidation remain open under #105 and their atomic children.
These safety changes do not repair a missing member profile or prove deployed Firebase Rules/Functions. The reported profile-save failure remains #118.
- Select one ready
S/Missue from GITHUB_ISSUES.md, or one atomic child from GITHUB_ISSUE_SLICES.md, and confirm its dependencies. - Follow AGENTS.md, including no production credentials/data and preserving unrelated worktree changes.
- Add positive, negative, retry, and authorization/concurrency tests appropriate to the risk.
- Use additive/idempotent migrations and backend-first expand-and-contract deployment.
- Update the affected design/runbook and record residual risk.
Business owners—not coding agents—must approve legal text, waiver/insurance policy, tax, shipping/returns, retention, live credentials, account ownership, DNS, and the live-mode pilot.
Pushing to main runs CI and does not start .github/workflows/deploy.yml. #667's separate manual verifier proves only short-lived staging project-identity access. #669's one-time bounded operation proves only the empty staging Firestore Rules/index boundary, and #671's one-time bounded operation proves only staging Auth; neither is a release button. #674's guarded Function command is source only and must not be run while billing is unapproved. The backend release workflow remains NOT AVAILABLE YET until #133 grants only the reviewed deployment roles and protects production. Ordinary Git-triggered Netlify production builds are paused. The completed #623 web-only exception is inactive and not reusable; it published no Firebase or provider change. The older #473 exception is retained only as rollback history. A reusable protected live-Netlify publication path is still NOT AVAILABLE YET. Production changes still require protected approval, a compatible backend-first rollout, separate live-host proof, a named observer, and the runbook's post-deploy/reconciliation steps.