Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Public React build configuration. REACT_APP_* values are embedded in the
# browser bundle and MUST NOT contain secrets.

# The current FirebaseResources implementation contains the production web
# config directly; these names document the intended environment-specific
# migration and match existing local files. Firebase web config is public, but
# Development/test accept only `local` (or an absent selector) and use the
# demo-mprc-local emulators; the hosted fields below are ignored there.
# Optimized builds must select `staging` or `production` and supply the complete
# matching public web config. Never use CI's reserved synthetic staging values
# as evidence of an owned staging project. Firebase web config is public, but
# every environment still needs correct Auth, Rules, and App Check controls.
REACT_APP_FIREBASE_ENVIRONMENT=local
REACT_APP_FIREBASE_API_KEY=
REACT_APP_FIREBASE_AUTH_DOMAIN=
REACT_APP_FIREBASE_PROJECT_ID=
Expand Down
2 changes: 1 addition & 1 deletion .firebaserc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"projects": {
"default": "mid-peninsula-running-club"
"default": "demo-mprc-local"
}
}
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,33 @@ jobs:
run: |
ruby -e 'require "yaml"; YAML.safe_load(File.read(".github/workflows/deploy.yml"), permitted_classes: [], permitted_symbols: [], aliases: true)'
ruby -e 'require "yaml"; YAML.safe_load(File.read(".github/workflows/ci.yml"), permitted_classes: [], permitted_symbols: [], aliases: true)'
node --test tests/ci-workflow.test.js tests/release-workflow.test.js tests/firebase-release-verification.test.js tests/test-artifact-safety.test.js tests/root-dependency-security.test.js
node --test tests/ci-workflow.test.js tests/release-workflow.test.js tests/firebase-release-verification.test.js tests/firebase-hosting-foundation.test.js tests/test-artifact-safety.test.js tests/root-dependency-security.test.js
- name: Build
env:
CI: 'true'
# Lint runs as its own step above; the webpack eslint pass would
# otherwise fail CI=true builds on style warnings.
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'
REACT_APP_RECAPTCHA_SITE_KEY: ${{ secrets.REACT_APP_RECAPTCHA_SITE_KEY }}
REACT_APP_SENTRY_DSN: ${{ secrets.REACT_APP_SENTRY_DSN }}
run: npm run build
- name: Verify synthetic staging Firebase artifact
env:
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'
run: node scripts/firebase-hosting-contract.js verify build

test-artifact-scrubber:
name: Test artifact scrubber
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,21 @@ jobs:
CI: 'true'
DISABLE_ESLINT_PLUGIN: 'true'
SITE_ORIGIN: 'https://runmprc.com'
REACT_APP_FIREBASE_ENVIRONMENT: 'production'
REACT_APP_FIREBASE_API_KEY: 'AIzaSyD2u17HMhDPZ0Tn9D3H71fep1vZgT-njnw'
REACT_APP_FIREBASE_AUTH_DOMAIN: 'mid-peninsula-running-club.firebaseapp.com'
REACT_APP_FIREBASE_PROJECT_ID: 'mid-peninsula-running-club'
REACT_APP_FIREBASE_STORAGE_BUCKET: 'mid-peninsula-running-club.firebasestorage.app'
REACT_APP_FIREBASE_MESSAGING_SENDER_ID: '253289716314'
REACT_APP_FIREBASE_APP_ID: '1:253289716314:web:dcad9766d820044d7f9663'
REACT_APP_FIREBASE_MEASUREMENT_ID: 'G-ECN7TT0BGF'
REACT_APP_RECAPTCHA_SITE_KEY: ${{ vars.REACT_APP_RECAPTCHA_SITE_KEY }}
REACT_APP_SENTRY_DSN: ${{ vars.REACT_APP_SENTRY_DSN }}
SOURCE_COMMIT: ${{ needs.preflight.outputs.source_commit }}
run: |
set -euo pipefail
npm run build
node scripts/firebase-hosting-contract.js verify build
printf '%s\n' "$SOURCE_COMMIT" > build/release-commit.txt
- name: Reject server-credential material in the website artifact
run: |
Expand Down
15 changes: 12 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,25 @@ npm --prefix functions run test:run -- --runInBand
CI=true npm test -- --watchAll=false --runInBand
npm run test:spa-navigation
npm run test:rules
CI=true DISABLE_ESLINT_PLUGIN=true npx --no-install react-scripts build
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 build
```

The Rules and commerce-emulator suites need Java 21. The direct `react-scripts build` command is preferred for a diagnostic build because normal `npm run build` regenerates the sitemap. Run dependency audits for security/dependency issues, but do not apply forced automatic upgrades.
The Rules and commerce-emulator suites need Java 21. The synthetic Firebase values above authorize no provider call or deployment. The direct `react-scripts build` command avoids regenerating the sitemap; the following verifier must pass before its output is trusted as a staging-shaped artifact. Normal `npm run build` validates its selected hosted environment and regenerates the sitemap. Run dependency audits for security/dependency issues, but do not apply forced automatic upgrades.

The deterministic frontend Jest baseline is available through the command above. Hosted CI runs it as the blocking `Run frontend Jest tests` step under #124 and runs the standalone SPA suite under #126. #135 adds a tested manual, exact-commit, backend-first GitHub release gate and pauses Git-triggered Netlify production builds. This proves source shape only: protected environments/OIDC (#133), staged/live deployment (#136), fail-closed lint, required checks, Netlify publication, provider configuration, and production behavior remain separate work under #105 and WEB-001.

For Firebase-backed local development, run `npm run emulators` first and wait until Auth, Firestore, and Functions are ready under `demo-mprc-local`. In a second terminal, run `npm start` and use `http://localhost:3000`. Stop if any browser Firebase request uses a non-loopback host. The emulator suite does not isolate Stripe, Strava, email, or other provider calls made by Functions; those flows remain forbidden until their test configuration and safe sink are separately proven.

Optimized builds and current deploy previews still use production Firebase configuration. Do not sign in, open private/admin pages, or exercise Firebase/provider behavior in a preview. Safe staging and provider isolation remain **NOT AVAILABLE YET** under #105/CONFIG.
Optimized builds must explicitly select `staging` or `production`. WEB-001A1 #663 provides and tests that source boundary, but it does not create an owned staging project or isolate outside providers. Do not sign in, open private/admin pages, or exercise Firebase/provider behavior in a preview until the protected staging and provider evidence required by #105/CONFIG exists.

For Stripe lifecycle work, tests must include relevant negative and retry cases: invalid signature, duplicate/out-of-order Event, unpaid completion, amount/currency/environment mismatch, terminal-state protection, async success/failure, expiration, cancellation, and refund retry. Capacity/inventory work must include concurrent final-unit tests.

Expand Down
6 changes: 3 additions & 3 deletions GITHUB_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Every issue inherits `AGENTS.md` and the definition of done in `IMPLEMENTATION_P
| 21 | DATA-002 | Create immutable, truthful waiver evidence | P1 | M | blocked_owner_decision | RACE-001, LEGAL-001 decision |
| 22 | ADMIN-001 | Move sensitive admin operations behind scoped APIs and durable audit | P1 | L | proposed | SEC-001, AUTH-003, PAY-002 |
| 23 | MERCH-002 | Configure shipping, tax, returns, and order communications | P1 | L | blocked_owner_decision | MERCH-001, LEGAL-001 |
| 24 | WEB-001 | Move to controlled hosting and add browser security policy | P1 | L | proposed | SAFETY-001, CI-001 |
| 24 | WEB-001 | Move to controlled hosting and add browser security policy | P1 | L | source foundation in progress through #460/#663; not deployed | SAFETY-001, CI-001 |
| 25 | OBS-001 | Add payment observability, alerts, SLOs, and redaction | P1 | M | proposed | PAY-003, PAY-005 |
| 26 | RESILIENCE-001 | Establish backup, restore, retention, and audited repair | P1 | L | proposed | DATA-001, PAY-005 |
| 27 | TEST-001 | Build the Stripe/Firebase security integration and E2E suite | P0 | L | in progress through #177; most children proposed | core security/payment/data children |
Expand Down Expand Up @@ -134,7 +134,7 @@ GitHub Pages routes unknown SPA paths through `public/404.html`. The original br

### Agent handoff

Do not redesign routing or hosting in this issue; WEB-001 owns that. Optimized previews still target production Firebase, so private preview behavior remains blocked on #105/CONFIG. Firebase emulators do not make Stripe, Strava, or email safe. ABUSE-001A must explicitly defer App Check enforcement on `lookupRegistration`, `lookupOrder`, and `stravaExchangeCode` while the initial capability guard is active. DATA-001A owns the two payment-confirmation handoffs; OAUTH-001C within live [#88](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/88) owns the Strava exchange handoff. Do not weaken the guard. Do not include any real callback token in fixtures. #118 still owns the reported profile failure.
Do not redesign routing or hosting in this issue; WEB-001 owns that. WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) now makes ordinary optimized previews select a reserved synthetic staging configuration, but private preview behavior remains blocked because no owned staging project or provider isolation is verified under #105/CONFIG. Firebase emulators do not make Stripe, Strava, or email safe. ABUSE-001A must explicitly defer App Check enforcement on `lookupRegistration`, `lookupOrder`, and `stravaExchangeCode` while the initial capability guard is active. DATA-001A owns the two payment-confirmation handoffs; OAUTH-001C within live [#88](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/88) owns the Strava exchange handoff. Do not weaken the guard. Do not include any real callback token in fixtures. #118 still owns the reported profile failure.

---

Expand Down Expand Up @@ -468,7 +468,7 @@ Use OAUTH-001A/B/C as bounded outcomes within canonical tracker [#88](https://gi
## AUTH-003 — Introduce scoped admin capabilities, MFA, and recent authentication

**Labels:** `priority:P1`, `type:security`, `area:auth`, `area:firebase`, `size:L`, `needs-migration`, `needs-external-config`
**Status:** Proposed
**Status:** In progress through tracker [#460](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/460). Source-only child WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) adds environment-explicit builds, artifact checks, and inert Firebase Hosting SPA configuration; no staging/production Hosting site, headers, release authority, deployment, domain, or cutover is verified.
**Depends on:** AUTH-001, AUTH-002, SEC-001

### Problem
Expand Down
2 changes: 1 addition & 1 deletion GITHUB_ISSUE_SLICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ An agent-ready child has one observable outcome, one trust boundary, explicit de
| MERCH-002B | proposed; A, MERCH-001B | Apply server shipping/tax config and actual total snapshots/reconciliation. | Test-mode taxable/pickup/shipping/mismatch fixtures. |
| MERCH-002C | proposed; A/B, ADMIN-001C | Add fulfillment/return/restock state and audited operator actions. | Forbidden transitions and stock adjustment tests. |
| MERCH-002D | proposed; A–C, MAIL-001B | Add minimum customer communications/UI and address minimization. | Lifecycle/email/PII projection tests. |
| WEB-001A | owner_action; CI-001B2 | Provision isolated staging hosting, rewrites, canonical/sitemap behavior, deploy and rollback. | ADR and deep-route staging evidence. |
| WEB-001A | owner_action; source foundation tracked by [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663); CI-001B2 | #663 adds environment-explicit builds, executable artifact checks, a demo-only CLI default, and inert Hosting rewrites. An authorized owner must still provision isolated staging hosting, short-lived authority, canonical/sitemap behavior, deploy, readback, and rollback. | #663 source tests and synthetic Hosting-emulator deep routes are not a hosted stage; later ADR plus exact deployed staging/readback/rollback evidence is required. |
| WEB-001B | proposed; A | Add CSP/HSTS/frame/referrer/permissions/MIME/cache policy. | Automated header/CSP and browser flow smoke. |
| WEB-001C | partial source retirement tracked by [#492](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/492); remaining work proposed; A/B, PAY-001A | Keep the dormant legacy raw-HTML renderer/parser removed, then migrate any legacy stored content to the structured #83 contract and validate external URLs. | #492 no-manifest/no-lock/no-source guard plus later XSS/protocol/legacy-content migration fixtures. No route, data, Rules, hosting, or live change is proved by #492. |
| WEB-001D | owner_action; A–C | Execute controlled DNS cutover/rollback and retire old deploy path without enabling commerce. | Dated owner-approved cutover evidence. |
Expand Down
2 changes: 2 additions & 0 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ MPRC can open a race or merchandise item for sale only when the platform can:

**WEB-002D completed release boundary:** [#659](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/659) completed one exact-artifact Netlify exception on 2026-08-14. Exact release merge `46e23647d8e0bf9fa3a574ea5c5f993be10a419d` published deploy `6a7ece87c5ca4d0007c1a3fc` from source `7496fe0881fb52908c4ff2f40f488df09c94c908`, tree `ccac4c189c195db8ab594e0eefe256ea9fa04996`, 62 files, and digest `e4c26e6f0fbcd086663d86238675f0be228fb649a00628c1c97d1166612f49c7`. Signed-out desktop route-focus and phone menu/route-focus checks passed. Repause merge `3138a00c1c48e1d5d1dcda0b44722b09a2194ff7` passed exact-main CI run `31783808994`; attempt `6a7ed0ddb00a46000818878d` remained unpublished and retained the verified deploy. The manifest is inactive, the temporary source/control/repause refs are absent, and rollback ref `codex/netlify-source-659-rollback` remains pinned to #623 source `c2d87d1f69f15e128a0bc9b1b9f915b7c8417aec`. #659 published only the reviewed #291 visible-focus, #490 phone-menu, and #657 route-focus behavior. It deployed no Firebase, Rules, Functions, or indexes; changed no outside-provider configuration, account, sign-in state, production data, payment, or connected directory behavior; and left directory availability literal `false`. Reusable hosting remains open under #460/#133/#136, and #507 still owns every connected-directory gate.

**WEB-001A1 Firebase Hosting source foundation:** [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) is the first bounded child of #460. It makes controlled optimized builds environment-explicit, keeps local/test on the demo emulators, requires complete staging-named public Firebase configuration, rejects production identities in staging, scans the executable synthetic CI artifact, changes the unqualified CLI default to `demo-mprc-local`, and adds the inert `build/` Hosting rewrite. It deliberately does not provision staging, configure short-lived authority or branch/environment protection, deploy Hosting/Firebase, automate `production-web`, add release readback/rollback/headers, alter Netlify/Pages, or touch DNS/TLS. Source/tests/merge and a synthetic build are not staging or production evidence.

## 3. Dependency map

```mermaid
Expand Down
2 changes: 2 additions & 0 deletions OFFICER_START_HERE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ As of **2026-08-13**, a merge runs checks but does not start the GitHub release.

As of **2026-08-14**, [#659](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/659) completed one accessibility-only Netlify release. Deploy `6a7ece87c5ca4d0007c1a3fc` is live from frozen source `7496fe0881fb52908c4ff2f40f488df09c94c908`; its exact 62-file marker and signed-out desktop/phone route-focus and menu checks passed. Repause merge `3138a00c1c48e1d5d1dcda0b44722b09a2194ff7` made the manifest inactive. Its attempt `6a7ed0ddb00a46000818878d` published nothing and retained the verified deploy. #623 deploy `6a7e072f8f346b0008510d29` is the recorded rollback, not current production. #659 changed no Firebase, provider configuration, account, sign-in state, production data, payment, or connected directory behavior. Officers do not run commands, sign in, enter data, or approve this as a reusable release. Follow the completed record in [Review, merge, release, and check a change](./docs/officers/PUBLISH_AND_CHECK.md).

As of **2026-08-23**, WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) adds source checks for an explicit staging-or-production website build and a Firebase Hosting SPA rewrite. This is **SOURCE ONLY, NOT LIVE**. There is no club-owned staging Hosting site, protected automatic Firebase Hosting release, custom domain, or officer publish control yet. Netlify still answers `runmprc.com`; a green #663 test or merged source change does not mean Firebase Hosting, Firebase backend services, DNS, GitHub Pages, Netlify, or the public website changed.

The optional profile-photo and officer People-finder functions are still **NOT AVAILABLE YET**. #621 makes the frontend default an inert preview: My Account shows the future photo and separate finder-choice controls disabled, while the People finder stays behind the administrator guard and shows its name field and Search button disabled. The preview reads no saved photo or setting, accepts or uploads no photo, searches no name, and saves nothing; it shows no people or sample results. #623 published exactly that disabled interface as deploy `6a7e072f8f346b0008510d29`. Officers inspect the protected layouts only in synthetic local artifacts. The completed signed-out production review proved only the exact revision, normal sign-in and administrator guards, and absence of a member-directory network request. Do not sign in to production, choose a real photo, enter a real name, or treat the preview as a directory. #623 changed no Firebase, provider configuration, account, sign-in, or production data. #507 must later prove the privacy, authorization, staging, backend-first deployment, and readback gates before a separate reviewed source change may connect it. Follow the preview and source-review procedure in [Events, shop, members, and money](./docs/officers/EVENTS_SHOP_MEMBERS.md).

For the concise handbook, see [OFFICER_HANDBOOK.md](./OFFICER_HANDBOOK.md). The expanded task index is [docs/officers/README.md](./docs/officers/README.md).
Loading