From 07b229644b40467556940da59274021b676bea84 Mon Sep 17 00:00:00 2001 From: Dave Liu <7david12liu@gmail.com> Date: Wed, 26 Aug 2026 13:09:31 -0700 Subject: [PATCH] docs: record empty staging Firestore boundary (#669) --- GITHUB_ISSUES.md | 2 ++ IMPLEMENTATION_PLAN.md | 2 ++ OFFICER_START_HERE.md | 2 +- OPERATIONS_RUNBOOK.md | 12 ++++---- README.md | 8 +++--- SECURITY.md | 7 +++-- SYSTEM_DESIGN.md | 26 ++++++++++++++---- docs/officers/PUBLISH_AND_CHECK.md | 44 ++++++++++++++++++++++++++++-- docs/officers/README.md | 2 ++ 9 files changed, 85 insertions(+), 20 deletions(-) diff --git a/GITHUB_ISSUES.md b/GITHUB_ISSUES.md index 789dea2..0f9c0a2 100644 --- a/GITHUB_ISSUES.md +++ b/GITHUB_ISSUES.md @@ -146,6 +146,8 @@ Do not redesign routing or hosting in this issue; WEB-001 owns that. WEB-001A1 [ **Current CI-001C boundary:** CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) supplies the staging-only protected read-only OIDC proof: exact immutable repository/owner IDs, `main`, the `staging` environment, one verifier workflow, two named individual reviewers, and a keyless principal with only project-identity read permission. Parent #133 still owns Firebase deployment roles and production protection; #136 still owns an actual backend release. +**Current CI-001D boundary:** CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) supplies the first staging data boundary: empty Native/Standard `(default)` Firestore in `us-west2`, delete protection enabled, PITR disabled, and exact Rules/indexes from `ee16bd16220ab58bd3a2add80dd2f39a1d514dd7`. The 418-case Rules suite passed, provider definitions matched, fixed anonymous server-only reads were denied, and root collection count remained zero. This does not provision Auth, Functions, App Check, provider sandboxes, reusable deploy authority, application records, or production behavior. Parent #133 still owns keyless Firebase deployment roles and production protection; #136 still owns an actual synthetic backend release. + ### Problem Before #103, frontend tests failed because the Jest environment lacked `TextEncoder`. That child repaired the local baseline, and #124 adds it to hosted CI. #167 originally added a separate named Node 20/Java 17 job for the PAY-002B2A Firestore transaction suite and made both protected-release CI reads require it. SUPPLY-001D9/#455 moves the current source to Java 21 and exact Firebase CLI 15.24.0 while preserving one explicit demo project, Firestore-only scope, committed lockfiles, and no cloud/provider authority. CI-001B4/#186 replaces the mutating, ignored `lint:fix` step with a blocking exact-record check that initially covered 102 JS/JSX/TS/TSX files. It disables the repository-wide severity-masking `eslint-plugin-only-warn` hook inside the lint process. CI-001B4A/#227 removes exactly one reviewed `arrow-body-style` record without changing the rule or gate. CI-001B4B/#239 removes exactly one stale `AdminMembers` unknown-rule suppression record without changing executable statements, the rule set, or the gate. PAY-004C1/#359 removes one `no-alert` warning and two label-association errors by removing the obsolete reusable-link prompt and amount/tier controls. Intervening reviewed source additions expanded coverage from 102 to 108 files by #359. PR #391 then adds one TypeScript file and removes two TSX errors; PR #392 adds two TypeScript files and removes two more TSX errors. CI-001B4C/#449 replaces the four remaining stale `react-hooks/exhaustive-deps` directives with ordinary comments on the same lines, preserving every executable statement and dependency array. MEMBERS-CONTENT-001B/#492 then removes one finding-free dormant JSX file. The gate therefore scans 110 files and records 113 configured errors and 6 warnings as reviewable individual records. Any added, removed, moved, or changed record and every fatal parser/configuration failure rejects with one fixed diagnostic. The checker runs directly before application tests in an exact prefix that pins the hosted runner, read-only permission, credential-free checkout, Node version, lifecycle-disabled install, lint, and adjacent clean check. Global overrides and prefix changes reject. The clean check compares tracked files with `HEAD` plus non-ignored untracked files; ignored dependency/build paths are outside that proof. This is an explicit debt baseline, not a clean-lint claim. #135 removes the automatic/fail-open release source defects: merge and release are separate, exact CI checks are re-read, release targets are fixed, OIDC replaces JSON-key wiring, lockfile tooling is used, backend precedes Pages, and Git-triggered Netlify production builds stop. The protected identity/environments and actual staged/live release remain outside that source slice. diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md index b14ed43..446ad0c 100644 --- a/IMPLEMENTATION_PLAN.md +++ b/IMPLEMENTATION_PLAN.md @@ -42,6 +42,8 @@ MPRC can open a race or merchandise item for sale only when the platform can: **CI-001C1 protected staging identity proof:** [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) creates the protected `staging` GitHub environment, two named individual reviewers, exact-`main` policy, a dedicated GitHub OIDC pool/provider, and a keyless service account with only `resourcemanager.projects.get`. Its separate manual verifier fails on a wrong repository, immutable repository/owner ID, ref, environment, project, workflow, provider, or service account before authentication; after approval it reads only the exact staging project identity. It does not grant Firebase deployment roles, provision Auth/Firestore/Functions, configure outside providers, protect production, or make `.github/workflows/deploy.yml` available. +**CI-001D1 empty staging Firestore boundary:** [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) provisions `(default)` as an empty Native/Standard database in `us-west2`, enables delete protection, keeps PITR disabled, and deploys only exact Rules/indexes from `ee16bd16220ab58bd3a2add80dd2f39a1d514dd7` with Firebase CLI 15.24.0. The Node 20/Java 21 Rules suite passes 418/418; provider readback matches the active Rules digest, four composite indexes, and 28 field overrides; fixed anonymous server-only reads are denied; root collection count remains zero. This completes only the first staging data boundary. Auth test configuration, Functions, App Check, outside-provider sandboxes, keyless Firebase deployment roles, synthetic signed-in flows, website republication, production protection, and production behavior remain later children of #105/#133/#136. + ## 3. Dependency map ```mermaid diff --git a/OFFICER_START_HERE.md b/OFFICER_START_HERE.md index 1275195..46318ac 100644 --- a/OFFICER_START_HERE.md +++ b/OFFICER_START_HERE.md @@ -50,7 +50,7 @@ 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-26**, WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) is merged, and WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created the club-owned `run-mprc-staging` project and published exact source `a614c68d9a1a2be631a3be874a686d61e5d170a0` to [run-mprc-staging.web.app](https://run-mprc-staging.web.app). CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) adds a separate protected read-only identity check: Dave Liu or Jeff Chang reviews exact `main`, and a keyless five-minute identity may read only the staging project identity. It cannot deploy Firebase. This remains an **ENGINEERING STAGING SITE ONLY**. Desktop and phone public-route checks passed, but staging has no approved backend/provider test estate, App Check key, protected automatic release, release marker, tested predecessor rollback, security-header policy, custom domain, or officer publish control. Do not sign in or enter real information there. Netlify still answers `runmprc.com`; production Firebase Hosting remains empty, and no DNS, GitHub Pages, Netlify, backend, or production-data change occurred. +As of **2026-08-26**, WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) is merged, and WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created the club-owned `run-mprc-staging` project and published exact source `a614c68d9a1a2be631a3be874a686d61e5d170a0` to [run-mprc-staging.web.app](https://run-mprc-staging.web.app). CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) adds a separate protected read-only identity check: Dave Liu or Jeff Chang reviews exact `main`, and a keyless five-minute identity may read only the staging project identity. It cannot deploy Firebase. CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) adds an empty, delete-protected staging Firestore database and the exact reviewed Rules/index definitions. Anonymous reads of three fixed server-only paths were denied, and the database still had zero root collections after verification. This remains an **ENGINEERING STAGING SITE ONLY**. It has no Auth test setup, Functions, App Check, provider sandbox, protected automatic release, release marker, tested predecessor rollback, security-header policy, custom domain, or officer publish control. Officers do not sign in, enter data, run commands, or treat the empty database as a usable backend. Netlify still answers `runmprc.com`; production Firebase Hosting remains empty, and no DNS, GitHub Pages, Netlify, production Firebase, production data, or live website behavior 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). diff --git a/OPERATIONS_RUNBOOK.md b/OPERATIONS_RUNBOOK.md index 72d3613..fc99b11 100644 --- a/OPERATIONS_RUNBOOK.md +++ b/OPERATIONS_RUNBOOK.md @@ -63,13 +63,15 @@ Maintain an internal inventory with project/account IDs and console links: | --- | --- | --- | --- | --- | --- | | Local source runtime | `demo-mprc-local` emulators | Providers remain separately gated | `localhost:3000` | None | Available for synthetic Firebase work after all three loopback emulators report ready | | CI | Separate named demo/emulator projects | Fixtures/test only | None | CI | Frontend, Functions, Rules, SPA, and commerce-journal checks run; broader gates remain partial | -| Staging | `run-mprc-staging` | No provider sandbox or backend test configuration yet | `run-mprc-staging.web.app`; `dev.runmprc.com` not configured | `staging`: two reviewers, exact `main`, read-only OIDC verifier | Static signed-out Hosting and protected project-identity proof are available; backend/provider staging remains a launch blocker | +| Staging | `run-mprc-staging` | Empty Firestore Rules/index boundary only; no Auth, Functions, App Check, or provider sandbox | `run-mprc-staging.web.app`; `dev.runmprc.com` not configured | `staging`: two reviewers, exact `main`, read-only OIDC verifier | Static signed-out Hosting, protected project-identity proof, and empty delete-protected Firestore are available; usable backend/provider staging remains a launch blocker | | Production | `mid-peninsula-running-club` (verify) | Live + unique endpoint | `runmprc.com` | `production` | Informational site only until gates close | -WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created and provider-read back `run-mprc-staging`, one web app, and Hosting version `3ffadcf2ac8cc760` from source `a614c68d9a1a2be631a3be874a686d61e5d170a0` on 2026-08-26. Always name that project explicitly; `.firebaserc` still defaults to the demo namespace. The static host is not complete environment isolation: App Check, Auth test configuration, Firestore, Functions, Rules, indexes, Storage data, provider sandboxes, protected deployment authority, `dev.runmprc.com`, release-marker readback, and tested rollback remain unavailable. +WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created and provider-read back `run-mprc-staging`, one web app, and Hosting version `3ffadcf2ac8cc760` from source `a614c68d9a1a2be631a3be874a686d61e5d170a0` on 2026-08-26. Always name that project explicitly; `.firebaserc` still defaults to the demo namespace. The static host is not complete environment isolation. CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) adds a separate staging-only authority check. The `staging` environment admits exact `main` and requires one of two named individual organization administrators. The OIDC provider additionally requires immutable organization/repository IDs, the environment, ref, and one verifier workflow. Its keyless service account has no user-managed key and only `resourcemanager.projects.get`; it has no Firebase deployment role. The verifier performs one fixed read of the exact active project identity and emits no response body. This proof does not enable or deploy Auth, Firestore, Rules, indexes, Functions, Hosting, App Check, Storage, an outside provider, or production. +CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) provisioned only the staging `(default)` Firestore database and deployed exact Rules/indexes from source `ee16bd16220ab58bd3a2add80dd2f39a1d514dd7`. Readback on 2026-08-26 showed Native mode, Standard edition, `us-west2`, delete protection enabled, PITR disabled, Rules SHA-256 `fe3f53302db76df5febf7e9d242d82a6b6bec0c0b574ecc8253f1034f1a77dba`, four matching composite indexes, 28 matching field overrides, and zero root collections. Anonymous reads of fixed nonexistent `mail`, `stripeEvents`, and `auditEvents` documents returned `403`. The Rules emulator suite passed 418/418 before provider mutation. No application record, Auth provider/test user, Function, App Check setting, Storage data, outside provider, Hosting version, production Firebase surface, or website changed. + The repository's only `.firebaserc` default is now `demo-mprc-local`, a deliberately non-deployable local namespace. It is an accidental-deploy brake, not staging. Every future deployment must still pass one exact privately approved project explicitly. Do not infer backend/provider isolation or release authority from the static host; #113/#133 must protect, map, and verify those remaining boundaries. ## 4. Configuration and secrets @@ -97,7 +99,7 @@ REACT_APP_STRAVA_CLIENT_ID The Firebase web configuration is public application configuration. It must still be environment-specific and paired with Auth restrictions, Firestore rules, and App Check. WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) makes controlled optimized builds choose `staging` or `production`; staging project IDs must include a distinct `staging` segment and the complete artifact check rejects known production identities. Production accepts only the exact reviewed public values. These values are not server credentials, but do not paste provider-console screenshots, private links, tokens, or any server credential into source or an issue. -`firebase.json` has a Hosting block for `build/` and missing-path SPA rewrites. Its predeploy chain rejects a Firebase CLI project that differs from the selected browser project, then rebuilds and scans executable JavaScript before upload. **Static engineering staging and a protected read-only authority check are available; backend and production publication are NOT AVAILABLE YET.** #665 used that chain for the first `run-mprc-staging` release. #667's WIF identity can read only the project identity; it cannot run this deploy chain. No protected production authority, custom domain, release marker, tested rollback, production release, or DNS/TLS change exists. +`firebase.json` has a Hosting block for `build/` and missing-path SPA rewrites. Its predeploy chain rejects a Firebase CLI project that differs from the selected browser project, then rebuilds and scans executable JavaScript before upload. **Static engineering staging, a protected read-only authority check, and an empty Rules-protected Firestore boundary are available; a usable backend and production publication are NOT AVAILABLE YET.** #665 used that chain for the first `run-mprc-staging` Hosting release. #667's WIF identity can read only the project identity; it cannot deploy. #669 was a separate bounded database/Rules/index operation and did not grant reusable authority. No protected Firebase deploy role, Auth test setup, Function, App Check enforcement, provider sandbox, production authority, custom domain, release marker, tested rollback, production release, or DNS/TLS change exists. ### App Check release state — NOT AVAILABLE YET @@ -178,7 +180,7 @@ For each secret, record in a private inventory: - Emergency revoke procedure. - Evidence that the old version was disabled after overlap. -Never print secret values into CI output, issue bodies, screenshots, chat, or this runbook. +Never print secret values into CI output, issue bodies, screenshots, chat, or this runbook. With Firebase CLI 15.24.0, do not run `firebase login:list --json` in any captured or shared session because its account objects can contain OAuth tokens. Use a non-token identity check. If token-bearing output appears, stop, revoke every affected OAuth grant, and record only the redacted incident result. ## 5. Local development @@ -704,7 +706,7 @@ For expand-and-contract changes: 7. Run smoke and reconciliation checks. 8. Observe for the defined period before removing legacy fields/endpoints. -The GitHub release workflow is now manual and exact-current-commit. After protected approval it rechecks `main`, the newest exact CI run, and the retained credential-free artifact before cloud authentication. It fails on missing protected configuration, uses a lockfile Firebase CLI, verifies reviewed Rules and two named profile Functions before publishing the Pages branch, and never gives cloud authority to website preparation or publication. Future Pages artifacts omit the `runmprc.com` CNAME, but the existing Pages provider setting still claims that Netlify-served name until #136/WEB-001 publish and read it back. #667's separate staging verifier proves protected short-lived authentication only; its principal has no deployment permission and `.github/workflows/deploy.yml` still rejects staging. The release source gate remains unavailable until #133 supplies the remaining staging deployment roles and protected production environment/identity. It does not publish the live Netlify host. Treat the full pipeline as incomplete until #105 closes. +The GitHub release workflow is now manual and exact-current-commit. After protected approval it rechecks `main`, the newest exact CI run, and the retained credential-free artifact before cloud authentication. It fails on missing protected configuration, uses a lockfile Firebase CLI, verifies reviewed Rules and two named profile Functions before publishing the Pages branch, and never gives cloud authority to website preparation or publication. Future Pages artifacts omit the `runmprc.com` CNAME, but the existing Pages provider setting still claims that Netlify-served name until #136/WEB-001 publish and read it back. #667's separate staging verifier proves protected short-lived authentication only; its principal has no deployment permission and `.github/workflows/deploy.yml` still rejects staging. #669 proves the exact Rules/index content can be applied to the empty staging database, but it adds no reusable deploy authority and no Function/Auth/App Check/provider boundary. The release source gate remains unavailable until #133 supplies the remaining staging deployment roles and protected production environment/identity. It does not publish the live Netlify host. Treat the full pipeline as incomplete until #105 closes. WEB-PRIVACY-001AA [#582](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/582) changes only the executable Netlify release builder's outer caught-failure output. Source now uses an unbound catch, prints fixed `netlify_release_build_failed`, and preserves the failing exit. Synthetic black-box tests cover ordinary message detail, a hostile message getter, and a hostile prototype trap before any provider or release work can start. This proves only that the wrapper does not inspect or echo its caught value. At the #582 boundary, the child-process runner and two Git reads still inherited subprocess diagnostics; #596 below closes that separate residual inside the builder. The success evidence line, cleanup, authorization, build, artifact, and publication behavior are unchanged. Source, tests, and any merge do not prove a Netlify publication, website change, `runmprc.com` verification, Firebase deployment, provider configuration, production-data change, or live behavior; the fixed code says only that preparation failed. diff --git a/README.md b/README.md index 1fbb1ba..62a2d95 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Historical developer/content/LLM guides remain under [`docs/`](./docs/README.md) **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](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/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](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created the isolated `run-mprc-staging` Firebase project under the club account and published exact source `a614c68d9a1a2be631a3be874a686d61e5d170a0` as Hosting version `3ffadcf2ac8cc760` at [run-mprc-staging.web.app](https://run-mprc-staging.web.app). Signed-out desktop, phone, direct `/events`, and empty-data checks passed. CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) adds a protected staging-only OIDC verifier with two named reviewers and a keyless principal that can read only the exact project identity. This is still static engineering staging: Auth, Firestore, Functions, App Check enforcement, outside-provider sandboxes, Firebase deployment authority, application security headers, a release marker, and a tested predecessor rollback are not configured. Do not sign in or use real data there. Production Firebase Hosting remains empty and `runmprc.com` remains on its existing Netlify deployment. +**Owned staging checked 2026-08-26:** WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created the isolated `run-mprc-staging` Firebase project under the club account and published exact source `a614c68d9a1a2be631a3be874a686d61e5d170a0` as Hosting version `3ffadcf2ac8cc760` at [run-mprc-staging.web.app](https://run-mprc-staging.web.app). Signed-out desktop, phone, and direct `/events` checks passed. CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/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](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/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. This is still incomplete engineering staging: Auth test configuration, Functions, App Check enforcement, outside-provider sandboxes, reusable Firebase deployment authority, application security headers, a release marker, and a tested predecessor rollback are not configured. Do not sign in or use real data there. Production Firebase Hosting remains empty and `runmprc.com` remains on its existing Netlify deployment. ## Local setup status @@ -68,7 +68,7 @@ npm start Open 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](./OPERATIONS_RUNBOOK.md) before any provider test. -Do not use a Netlify preview, locally served optimized `build/`, or the static `run-mprc-staging` Hosting site for sign-in, private pages, admin work, or backend/provider testing. WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) makes optimized builds choose `staging` or `production` explicitly. WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) verifies an owned static stage, but does not configure its backend or isolate outside providers. +Do not use a Netlify preview, locally served optimized `build/`, or `run-mprc-staging` for sign-in, private pages, admin work, or provider testing. WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) makes optimized builds choose `staging` or `production` explicitly. WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) verifies the owned static stage, and CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) verifies only its empty Firestore Rules/index boundary. Auth, Functions, App Check, and outside providers are still not isolated for end-to-end use. ## Verification @@ -90,7 +90,7 @@ npx --no-install react-scripts build node scripts/firebase-hosting-contract.js verify build ``` -Rules 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](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/124), the SPA callback suite under [#126](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/126), and the release-gate source tests under [#135](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/135). #667 supplies a separate protected staging project-identity check only. Parent [#133](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/133) still owns Firebase deployment roles and production protection; [#136](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/136) owns the actual staged profile-recovery release. Required branch checks, backend/provider isolation, protected publication, security headers, rollback proof, and hosting consolidation remain open under [#105](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/105) and their atomic children. +Rules 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](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/124), the SPA callback suite under [#126](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/126), and the release-gate source tests under [#135](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/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 and proves no signed-in behavior. Parent [#133](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/133) still owns Firebase deployment roles and production protection; [#136](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/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](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/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](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/118). @@ -106,4 +106,4 @@ Business owners—not coding agents—must approve legal text, waiver/insurance ## Deployment warning -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. The backend release workflow is fixed to an exact merged commit and reviewed target set, but 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. +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; it is not a release button. 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. diff --git a/SECURITY.md b/SECURITY.md index 19b78de..364723b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -43,11 +43,11 @@ The findings below describe the repository at the start of the 2026-07-12 assess | RISK-003 | Paid Checkout Sessions are created before their registration/order record is persisted. The current handlers also relied on the resolved Session's raw ID and URL without checking its declared mode, amount, currency, buyer, labels, callbacks, state, or URL origin. The webhook only queries for the stored Session ID. | A fast webhook, lost reply, malformed result, function crash, or repeated browser request can leave a paid or payable Stripe transaction with no local confirmation, an arbitrary redirect, or duplicate Sessions. | Keep #357's immediate result/terminal-retry containment and #503's synchronous pre-render one-attempt browser guard, then replace the flow with a persistence-first deterministic-key saga, durable result/reconciliation evidence, approved URL/configuration binding, and metadata-direct lookup. | | RISK-004 | Race capacity uses a count-then-create sequence outside a transaction; `registeredCount` is displayed but not maintained. | Concurrent users can exceed capacity; stale pending Sessions can block capacity for up to seven days. | Introduce transactional capacity counters/reservations, matching expiry, backfill, and concurrency tests. | | RISK-005 | Merchandise has product status but no SKU/variant inventory reservation. | The club can sell unavailable size/color combinations or oversell stock. | Add canonical variants and atomic inventory holds before live merchandise. | -| RISK-006 | `main` source now removes the recursive browser-admin rule, but deployment repeatedly skipped Firebase and the live Rules revision is unproven. | A stale deployed ruleset may still expose secrets or financial/audit writes to a compromised admin session. | Use #105 to stage and deploy the exact tested #100 Rules revision, verify deny behavior with synthetic accounts, and record rollback. | +| RISK-006 | `main` source removes the recursive browser-admin rule. CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) deployed its exact tested Rules digest to the empty staging database and verified anonymous denial on fixed server-only paths, but the production Rules revision remains unproven. | A stale production ruleset may still expose secrets or financial/audit writes to a compromised admin session. | Preserve the staging proof, complete Auth/App Check and synthetic role-boundary drills there, then use #105 to deploy and verify the exact reviewed Rules in production with rollback evidence. | | RISK-007 | Repository source rejects unverified targets at the two existing role-grant endpoints. AUTH-001B [#196](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/196) adds exact verified-email checks to role-based Firestore Rules, and AUTH-001C [#209](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/209) tracks the matching gate at current Functions role consumers. Firebase deployment, verification-mirror refresh, revocation, authoritative membership, scoped capability/recent-auth controls, and replacement of the legacy static-key endpoint remain unproven. | Older deployed code or a remaining role-only server guard can let an unverified known-email account use member/admin authority. CSV export also retains separate minimization, recent-auth, and audit risks. | Deploy and verify the exact Rules/Functions through #105, complete the remaining AUTH-001 mirror/revocation work, require authoritative membership and scoped privileged access, harden roster export under #116, and retire the static-key endpoint. | | RISK-008 | `requireAppCheck` is controlled by optional `ENFORCE_APP_CHECK`; repository/deployment config does not prove it is true. Missing site key also disables client App Check. | Public callable functions may be scripted directly, enabling abuse and cloud/Stripe cost amplification. | Use Firebase runtime `enforceAppCheck: true` for sensitive callables, reCAPTCHA Enterprise, staged metrics, and no environment fail-open. | | RISK-009 | #99 source preserves and validates the complete same-origin Pages callback route, applies `strict-origin` before subresources, and suppresses initial callback App Check/telemetry startup; #126 adds that standalone suite as a blocking hosted CI step. #443 adds only the source/test Strava same-document handoff after the native and Router callback locations are clean. No production Stripe/Strava callback, provider-backed App Check token behavior, or live custom-domain handoff has been rehearsed, and the live custom domain uses a different host. | Hosting, provider, configuration, or release drift can still break return links or the App Check handoff even when source tests pass. Enforcing a callback callable without its matching released client handoff and staged provider evidence can also break confirmation or Strava exchange. | Keep the hosted callback gate blocking; keep `lookupRegistration`/`lookupOrder` deferred until DATA-001A; keep native enforcement for `stravaExchangeCode` deferred to ABUSE-001A2 after the exact #443 website source and provider-backed Enterprise token behavior are proven in protected staging; verify test-mode callbacks only after provider gates; consolidate hosting under WEB-001. | -| RISK-010 | Development/test use synthetic loopback Auth, Firestore, and Functions. WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) makes controlled optimized builds select `staging` or `production` explicitly and binds Hosting predeploy to the exact project. WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) creates owned `run-mprc-staging` with one web app and a scanned static Hosting release. App Check, Auth test configuration, Firestore, Functions, Rules, indexes, Storage data, provider sandboxes, protected authority, and a release marker remain absent; emulated Functions can still call outside providers. | A static visual stage, bypassed build path, or provider-capable emulator can be mistaken for complete staging and affect production/provider systems. | Keep private actions out of previews; preserve #663's fail-closed gate and #665's fixed project boundary; complete #113/#133 short-lived authority and backend/provider staging; prove test keys, sinks, provider isolation, exact marker readback, and rollback before end-to-end flows. | +| RISK-010 | Development/test use synthetic loopback Auth, Firestore, and Functions. WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) binds controlled Hosting builds to an explicit environment, and WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) supplies owned static staging. CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) adds an empty delete-protected Firestore database with exact Rules/indexes. App Check, Auth test configuration, Functions, Storage data, provider sandboxes, protected Firebase deployment authority, and a release marker remain absent; emulated Functions can still call outside providers. | A partially provisioned stage, bypassed build path, or provider-capable emulator can be mistaken for complete staging and affect production/provider systems. | Keep private actions out of previews; preserve the fixed project and exact Rules boundary; complete #113/#133/#136 Auth, Functions, App Check, short-lived deploy authority, and provider isolation; prove test keys, sinks, exact marker readback, and rollback before end-to-end flows. | | RISK-011 | Local cancellation only changes Firestore; it does not expire the active Stripe Session. The webhook can later move a cancelled record back to paid. | Customers can pay a supposedly cancelled registration/order; capacity/inventory and support state diverge. | Add explicit cancellation saga with Stripe Session expiry and allowed-state transitions. | | RISK-012 | Repository source now denies every admitted positive late-registration amount before identifier/token allocation, registration writes, Stripe construction, or Product/Price/Payment Link calls, and the Admin screen exposes only the exact-zero compatibility path. Firebase deployment is unproven, and legacy reusable Payment Links may still exist. | Older deployed code or an existing link can still create unreconciled or repeated charges/Sessions for one logical registration. The exact-zero local record is also marked paid without Stripe and is not payment, free, comp, or membership authority. | Keep paid late registration unavailable; inventory, deactivate, and reconcile legacy links privately; then replace them with one-off idempotent Checkout Sessions through the normal registration/payment lifecycle. | | RISK-013 | The assessment baseline permitted Stripe promotions while Firestore stored base price. Repository source now disables promotion entry and automatic tax, requires a complete adjustment breakdown, and quarantines unknown or nonzero discount/tax/shipping amounts; deployment and provider state are unproven. | An older open Session, provider drift, skipped backend release, or later guard regression could still make reporting, refunds, and local payment confirmation disagree. | Privately inventory pre-change Sessions/provider settings, release through the protected gate, verify Firebase and Stripe separately, and keep every adjustment disabled until an approved authoritative model exists. | @@ -88,6 +88,7 @@ The findings below describe the repository at the start of the 2026-07-12 assess | RISK-036 | #135 adds a manual exact-commit source gate, fixed profile-recovery targets, backend-first order, missing-config failure, and ordinary Git-triggered Netlify production containment. WEB-UX-001A [#457](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/457) used a temporary exact-artifact exception. On 2026-08-01 an overbroad WEB-002A artifact was merged after a late blocker and published; exact rollback merge `1099ee8` restored source `ed1b0833`, and `dee7951` paused the manifest. Bounded replacement [#473](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/473) then published exact source `39ab8649` as deploy `6a6dc9ea588b0c0008036312`; its delta is only Shop and Events/Calendar failure containment, and its public checks passed. Final control `cb6a8f0` made the manifest inactive; Netlify attempt `6a6dcdd47bc81e000859a249` stopped unpublished and left that bounded deploy as #623's rollback. WEB-002C [#623](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/623) then completed one exact-artifact release: merge `9d5cc8612b4321172370bd949d307e7e4ac0ec7d` published frozen inert source `c2d87d1f69f15e128a0bc9b1b9f915b7c8417aec`, tree `411aa6ec9a9459f5d923030533ffc7c007fe6908`, 62 files, and digest `d837272a1e5efc1575809e87f532276b38d1a63f1dd79ec1aef0533f6da8afb1` as deploy `6a7e072f8f346b0008510d29`. Signed-out marker, route, guard, and no-connected-symbol/request checks passed. Repause merge `c8678c623afdd9becf77d596b71f36f26f04b746` made the manifest inactive; attempt `6a7e081e73fdd60009f7ba57` stopped unpublished and retained the verified deploy. The release source is absent and the rollback ref remains. Protected environments/OIDC, backend/provider staging, a reusable live-Netlify path, and provider-owned atomic rollback remain unverified; #665 proves only the static host. | Require a final blocker re-read and an executable delta from the live artifact before every release merge. Preserve the exact #473 rollback evidence, keep ordinary publication paused, and keep the reviewed Git rollback projection available. Treat #623 as a completed one-off, not a reusable control. Complete #133 and #136, extend #665 into protected backend/provider staging, protect the reusable Netlify release path under WEB-001, and rehearse provider rollback before broader production work. | | WEB-002D completed exact-artifact containment for RISK-036 | [#659](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/659) completed one accessibility-only release on 2026-08-14. Exact release merge `46e23647d8e0bf9fa3a574ea5c5f993be10a419d` published deploy `6a7ece87c5ca4d0007c1a3fc`; its marker and all 62 artifact paths matched frozen source `7496fe0881fb52908c4ff2f40f488df09c94c908`, tree `ccac4c189c195db8ab594e0eefe256ea9fa04996`, digest `e4c26e6f0fbcd086663d86238675f0be228fb649a00628c1c97d1166612f49c7`, and exact six-path diff digest `462eeb01e7a9858678802464f7dd4b76cd2fcb3c13be827efb4f98fa53ca809c`. Signed-out desktop and phone route-focus/menu checks passed. Repause merge `3138a00c1c48e1d5d1dcda0b44722b09a2194ff7` passed exact-main CI run `31783808994`; attempt `6a7ed0ddb00a46000818878d` stayed unpublished and retained the verified deploy. Literal-false directory availability, Firebase, Rules, Functions, indexes, outside-provider configuration, accounts, sign-in, production data, payments, and connected directory behavior were unchanged. | The manifest is inactive; source/control/repause refs are absent; rollback ref `codex/netlify-source-659-rollback` remains pinned to #623 source `c2d87d1f69f15e128a0bc9b1b9f915b7c8417aec`. Treat #659 as a completed one-off, not reusable authority. Keep ordinary publication paused, preserve #507 connected-directory gates, and preserve active #616 OAUTH-001A2L files and its RISK-024 wording byte-for-byte. | | WEB-001A1/A2 boundary for RISK-010 and RISK-036 | [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) adds the dependency-free hosted-build contract and Hosting rewrite. [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) adds one club-owned staging project, web app, finalized static Hosting release, provider readback, and signed-out desktop/phone route checks from exact source `a614c68`. | This proves static engineering staging only. It creates no staging backend/provider isolation, App Check, WIF/IAM identity, protected branch/environment, automatic deployment, release marker, tested predecessor rollback, complete header policy, custom domain, DNS/TLS change, production Hosting, `runmprc.com` publication, or live protection. Complete #113/#133 and the remaining #460 children before any end-to-end staging or production claim. | +| CI-001D1 boundary for RISK-006 and RISK-010 | [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) creates empty Native/Standard staging Firestore in `us-west2`, enables delete protection, keeps PITR off, and deploys exact Rules SHA-256 `fe3f53302db76df5febf7e9d242d82a6b6bec0c0b574ecc8253f1034f1a77dba`, four composite indexes, and 28 field overrides from source `ee16bd16220ab58bd3a2add80dd2f39a1d514dd7`. The 418-case emulator suite passed; active Rules content matched; fixed anonymous server-only reads returned denial; root collection count remained zero. | This proves only an empty staging data boundary. It creates no Auth test user/provider, Function, App Check enforcement, provider sandbox, protected Firebase deploy role, application record, website release, production Rules change, or production behavior. | | RISK-037 | Account/registration deletion, export, retention, backup, and restore procedures are incomplete. | Approve retention matrix, automate minimization, support access/deletion requests, and test backup restoration. | | RISK-038 | Source-controlled secret scan is ad hoc; no continuous secret scanner, dependency update bot, SBOM, provenance, or branch protection is documented. | Add secret/dependency/code scanning, reviewed lockfile updates, protected environments/branches, and artifact provenance appropriate to project scale. | | RISK-039 | Some authenticated accounts can lack `members/{uid}` after the Firebase cutover; the account screen hid the read failure and exposed an update that could only fail. Manual database/account repair could corrupt roles or private data. | Use an authenticated create-once server bootstrap, keep browser creation denied, fail the UI closed, and prove backend-first deployment with synthetic accounts. | @@ -327,6 +328,8 @@ Backups and exports must honor eventual deletion/anonymization schedules and hav CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) establishes the first external proof of this pattern in staging only. The protected `staging` environment requires one of two named individual organization administrators and exact `main`; its GitHub OIDC provider also binds immutable organization/repository IDs, the environment, ref, and one verifier workflow. The dedicated service account has no user-managed key and only a custom `resourcemanager.projects.get` permission. The verifier performs one fixed project `GET` with a five-minute token and emits fixed success/failure markers without response data. This is not Firebase deployment authority, production protection, backend isolation, or permission to inspect staging data. +CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) used only the club account and an explicitly named staging project for the one-time database provision and bounded Rules/index deploy. Firebase CLI 15.24.0 JSON account diagnostics can include token-bearing account objects; never run `firebase login:list --json` in CI, a captured terminal, an issue, or an AI transcript. Use non-token identity checks, revoke any accidentally exposed OAuth grant immediately, and prefer the protected keyless deployment path once #133 grants the exact reviewed staging roles. + ## 10. Incident response ### Severity examples diff --git a/SYSTEM_DESIGN.md b/SYSTEM_DESIGN.md index f42031a..14d5460 100644 --- a/SYSTEM_DESIGN.md +++ b/SYSTEM_DESIGN.md @@ -97,7 +97,9 @@ The former workflow automatically published Pages before attempting Firebase and WEB-002D [#659](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/659) completed one exact-artifact exception on 2026-08-14. Pinned preview `6a7ec998bf8fde00086d2bfe` matched control `137d8a8721339a6ca1079283cc34c1bd7cc2706c`. Exact release merge `46e23647d8e0bf9fa3a574ea5c5f993be10a419d` published deploy `6a7ece87c5ca4d0007c1a3fc` from source `7496fe0881fb52908c4ff2f40f488df09c94c908`, tree `ccac4c189c195db8ab594e0eefe256ea9fa04996`, 62 files, and artifact digest `e4c26e6f0fbcd086663d86238675f0be228fb649a00628c1c97d1166612f49c7`; the exact six-path predecessor diff digest was `462eeb01e7a9858678802464f7dd4b76cd2fcb3c13be827efb4f98fa53ca809c`. Signed-out desktop route-focus and phone menu/route-focus checks passed. Repause merge `3138a00c1c48e1d5d1dcda0b44722b09a2194ff7` passed exact-main CI `31783808994`; attempt `6a7ed0ddb00a46000818878d` stayed unpublished and retained the verified deploy. The manifest is inactive; temporary refs are absent; #623 deploy `6a7e072f8f346b0008510d29` is the rollback target. #659 changed no route set, content, Firebase surface, outside-provider configuration, account, sign-in state, production data, payment, or directory behavior; directory availability remains literal `false` and #507 still owns connection. -**WEB-001A1/A2 Firebase Hosting foundation and isolated static staging:** [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) makes every controlled optimized browser build select `staging` or `production` explicitly. Local/test still use only `demo-mprc-local` and all three loopback emulators. A staging build requires a complete public Firebase web configuration whose project name has a `staging` boundary; both the browser startup boundary and build contract reject production-equal identities, and CI scans executable JavaScript for every selected staging field and absence of known production Firebase identities. Production requires the exact reviewed public web configuration. `firebase.json` describes `build/`, a missing-path rewrite to `/index.html`, and a predeploy chain that binds the selected web configuration to Firebase CLI's exact `GCLOUD_PROJECT`, rebuilds, and scans the artifact before upload. `.firebaserc` defaults to the non-deployable demo namespace. On 2026-08-26, [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created club-owned project `run-mprc-staging`, one web app, and finalized Hosting version `3ffadcf2ac8cc760` from exact merged source `a614c68d9a1a2be631a3be874a686d61e5d170a0`. Provider and rendered checks passed for [run-mprc-staging.web.app](https://run-mprc-staging.web.app), static assets, direct `/events`, desktop, and phone navigation; the empty staging data surface settled without an error. This is signed-out static visual staging only. It does not configure App Check, Auth test users, Firestore, Functions, Rules, indexes, Storage data, outside-provider sandboxes, protected short-lived authority, `production-web`, a release marker, tested predecessor rollback, response-header policy, custom domains, DNS/TLS, Netlify/Pages, production Hosting, or `runmprc.com`. +**WEB-001A1/A2 Firebase Hosting foundation and isolated static staging:** [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) makes every controlled optimized browser build select `staging` or `production` explicitly. Local/test still use only `demo-mprc-local` and all three loopback emulators. A staging build requires a complete public Firebase web configuration whose project name has a `staging` boundary; both the browser startup boundary and build contract reject production-equal identities, and CI scans executable JavaScript for every selected staging field and absence of known production Firebase identities. Production requires the exact reviewed public web configuration. `firebase.json` describes `build/`, a missing-path rewrite to `/index.html`, and a predeploy chain that binds the selected web configuration to Firebase CLI's exact `GCLOUD_PROJECT`, rebuilds, and scans the artifact before upload. `.firebaserc` defaults to the non-deployable demo namespace. On 2026-08-26, [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created club-owned project `run-mprc-staging`, one web app, and finalized Hosting version `3ffadcf2ac8cc760` from exact merged source `a614c68d9a1a2be631a3be874a686d61e5d170a0`. Provider and rendered checks passed for [run-mprc-staging.web.app](https://run-mprc-staging.web.app), static assets, direct `/events`, desktop, and phone navigation. + +**CI-001D1 empty staging Firestore boundary:** [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) provisioned the empty `(default)` Firestore Native, Standard database in `us-west2` with delete protection enabled and point-in-time recovery disabled. Exact `main` source `ee16bd16220ab58bd3a2add80dd2f39a1d514dd7` supplied Rules SHA-256 `fe3f53302db76df5febf7e9d242d82a6b6bec0c0b574ecc8253f1034f1a77dba`, four composite indexes, and 28 field overrides. Provider readback matched those definitions; fixed anonymous reads of `mail`, `stripeEvents`, and `auditEvents` were denied, and root collection count remained zero. This is a protected empty data boundary, not a usable application backend. Auth test configuration, Functions, App Check, Storage data, outside-provider sandboxes, protected Firebase deployment authority, `production-web`, a release marker, tested predecessor rollback, response-header policy, custom domains, DNS/TLS, Netlify/Pages, production Hosting, and `runmprc.com` were unchanged. ```mermaid flowchart TD @@ -245,9 +247,23 @@ flowchart LR Text alternative: a manual verifier from exact `main` waits for a named staging reviewer, exchanges its tightly restricted GitHub identity for a short-lived keyless service-account token, and reads only the staging project identity. +**CI-001D1 staging data boundary:** the database exists only in `run-mprc-staging`; no production data was inspected, copied, or created. + +```mermaid +flowchart LR + Main["Exact reviewed main\nRules and index files"] --> Emulator["Rules emulator\n418 allow/deny checks"] + Emulator --> Deploy["Staging-only Rules and index deploy"] + Browser["Anonymous browser"] --> Rules["Active Firestore Rules"] + Deploy --> Rules + Rules -- "server-only path" --> Deny["Deny"] + Rules --> Empty["Empty staging Firestore\nNative Standard, us-west2"] +``` + +Text alternative: exact reviewed Rules and indexes pass the emulator suite before a staging-only deploy; active Rules deny anonymous server-only reads, and the protected staging database remains empty. + ## 4. Target deployment topology -The target keeps React, Firebase, and Stripe, but places stronger boundaries around them. Migrating the frontend from GitHub Pages/Netlify to Firebase Hosting is recommended before live commerce because it supports controlled SPA rewrites, preview channels, and security headers. #663 supplies the fail-closed build selector and inert Hosting rewrite configuration. #665 owns the isolated static staging host. CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) adds only a protected, read-only staging identity proof; it grants no Firebase deployment permission. Backend deployment authority, automatic exact-commit release, readback, rollback, headers, production protection, and DNS/TLS cutover remain separate work under #113/#133/#460. The migration is not required to design or test the backend and is not live merely because its source configuration exists. +The target keeps React, Firebase, and Stripe, but places stronger boundaries around them. Migrating the frontend from GitHub Pages/Netlify to Firebase Hosting is recommended before live commerce because it supports controlled SPA rewrites, preview channels, and security headers. #663 supplies the fail-closed build selector and inert Hosting rewrite configuration. #665 owns the isolated static staging host. CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) adds only a protected, read-only staging identity proof; it grants no Firebase deployment permission. CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) adds only the empty staging Firestore database and exact Rules/index boundary. Backend Functions, Auth configuration, deployment authority, automatic exact-commit release, full readback, rollback, headers, production protection, outside-provider isolation, and DNS/TLS cutover remain separate work under #113/#133/#136/#460. The migration is not live merely because one staging layer exists. ```mermaid flowchart TB @@ -1732,7 +1748,7 @@ Returning a `2xx` response to Stripe means the event has been durably accepted o | --- | --- | --- | --- | --- | | Local source runtime | Firebase Emulator Suite under `demo-mprc-local` | Not safe by Firebase emulation alone | `localhost:3000` | Synthetic data only; browser Firebase traffic is loopback-only | | CI | Ephemeral emulators and mocks | Stripe fixtures/signature tests; optional isolated test account | None | Synthetic data, no production secrets | -| Staging | `run-mprc-staging`; static Hosting, one web app, and protected read-only OIDC proof | Not configured; no provider testing authorized | `run-mprc-staging.web.app`; `dev.runmprc.com` not configured | Signed-out public visual review plus synthetic identity verification only; no real data | +| Staging | `run-mprc-staging`; static Hosting, one web app, protected read-only OIDC proof, and empty delete-protected Firestore with exact Rules/indexes | Not configured; no provider testing authorized | `run-mprc-staging.web.app`; `dev.runmprc.com` not configured | Signed-out public visual review and fixed anonymous denial checks only; no sign-in or real data | | Production | Dedicated production Firebase project | Live restricted keys and production webhook secret | `runmprc.com` | Real data under documented retention and access policies | ```mermaid @@ -1753,9 +1769,9 @@ Text alternative: the local browser uses only the three loopback Firebase emulat #99 source selects a fully synthetic Firebase configuration for development/test, connects Auth, Firestore, and Functions to the loopback ports above, stops startup when connection setup fails, and leaves App Check, Analytics, and Sentry off locally. #139 additionally keeps Firebase Analytics off in every source environment and removes the direct Waiver SDK bypass pending an approved #110 policy. The Firebase CLI must still report all three emulators ready before the app is opened. Mocked endpoint tests alone do not prove listening processes, website publication, provider behavior, or deletion of earlier provider data/cookies. -Optimized builds must select `staging` or `production` explicitly. WEB-001A1 #663 rejects missing, local, incomplete, or cross-environment Firebase web configuration, and its artifact check rejects known production identities from a staging executable. Ordinary Netlify deploy previews receive and scan only the reserved synthetic configuration; `run-mprc-staging.web.app` receives and scans the real isolated staging web-app identity; the pinned release builder receives and scans the exact reviewed public production web configuration. The owned static staging project does not by itself isolate outside providers or create a backend test estate, so both preview types remain restricted to public, signed-out, read-only visual review until #105/CONFIG supplies protected backend/provider evidence. Do not sign in, open private/admin pages, or test Firebase/provider actions in those previews. +Optimized builds must select `staging` or `production` explicitly. WEB-001A1 #663 rejects missing, local, incomplete, or cross-environment Firebase web configuration, and its artifact check rejects known production identities from a staging executable. Ordinary Netlify deploy previews receive and scan only the reserved synthetic configuration; `run-mprc-staging.web.app` receives and scans the real isolated staging web-app identity; the pinned release builder receives and scans the exact reviewed public production web configuration. The owned staging project now has an empty Rules-protected Firestore boundary, but it still lacks Auth test configuration, Functions, App Check, and outside-provider isolation. Both preview types remain restricted to public, signed-out, read-only visual review until #105/CONFIG supplies complete backend/provider evidence. Do not sign in, open private/admin pages, or test Firebase/provider actions in those previews. -`SITE_ORIGIN`, Firebase project IDs, App Check keys, Stripe keys, webhook endpoints, Sentry environments, and email providers must be environment-scoped. Live and test webhook secrets are never interchangeable. `run-mprc-staging` now supplies only the public web-app and Hosting boundary; end-to-end rollout still needs protected staging backend/provider configuration and evidence. +`SITE_ORIGIN`, Firebase project IDs, App Check keys, Stripe keys, webhook endpoints, Sentry environments, and email providers must be environment-scoped. Live and test webhook secrets are never interchangeable. `run-mprc-staging` now supplies the public web-app, Hosting, and empty Firestore Rules/index boundary; end-to-end rollout still needs protected Auth, Functions, App Check, deployment authority, and outside-provider configuration and evidence. CONFIG-001A [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) adds the source boundary for current commerce and confirmation-mail Functions: local is exact loopback, tests use HTTPS `.test`, staging is exactly `https://dev.runmprc.com`, and production is exactly `https://runmprc.com`. Test/live expected mode must match the server-key marker on the four key-bound Functions. Webhook and mail receive no Stripe API key, and invalid settings stop before rate-limit, business, mail, event-ledger, or Stripe writes. This source boundary does not create staging, configure Secret Manager or Stripe, deploy Firebase, enable commerce, or prove live behavior. diff --git a/docs/officers/PUBLISH_AND_CHECK.md b/docs/officers/PUBLISH_AND_CHECK.md index 1259a77..be08931 100644 --- a/docs/officers/PUBLISH_AND_CHECK.md +++ b/docs/officers/PUBLISH_AND_CHECK.md @@ -10,7 +10,7 @@ **Protected release status:** **NOT AVAILABLE YET.** Issue #135 provides the fail-closed source gate. Issue #133 must still configure protected `staging` and `production` environments, their named reviewers, and a short-lived cloud identity. Public browser build values must be named repository or organization variables because artifact preparation has no protected-environment access; #133/#136 must record and verify them separately. Do not add a long-lived Firebase key as a shortcut. -**Firebase Hosting status:** **ENGINEERING STAGING AVAILABLE; OFFICER AND PRODUCTION RELEASE NOT AVAILABLE YET.** WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) requires controlled optimized builds to name staging or production and scans executable JavaScript for environment identity. WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created the club-owned `run-mprc-staging` project and published exact merged source `a614c68d9a1a2be631a3be874a686d61e5d170a0` to [run-mprc-staging.web.app](https://run-mprc-staging.web.app). The site is for signed-out public visual review only. It has no approved staging backend/provider data, App Check key, protected short-lived publication, release marker, tested predecessor rollback, complete security headers, custom domain, or DNS/TLS change. Officers do not sign in, enter information, or use the site as a publish control. Production Firebase Hosting remains empty, and Netlify still serves `runmprc.com`. +**Firebase staging status:** **STATIC HOSTING AND EMPTY FIRESTORE BOUNDARY AVAILABLE; OFFICER AND PRODUCTION RELEASE NOT AVAILABLE YET.** WEB-001A1 [#663](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/663) requires controlled optimized builds to name staging or production and scans executable JavaScript for environment identity. WEB-001A2 [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) created the club-owned `run-mprc-staging` project and published exact merged source `a614c68d9a1a2be631a3be874a686d61e5d170a0` to [run-mprc-staging.web.app](https://run-mprc-staging.web.app). CI-001D1 [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669) adds an empty delete-protected Firestore database and exact reviewed Rules/indexes; fixed anonymous server-only reads were denied and the root collection count remained zero. The site is still for signed-out public visual review only. It has no Auth test setup, Functions, App Check, provider sandbox, protected short-lived publication, release marker, tested predecessor rollback, complete security headers, custom domain, or DNS/TLS change. Officers do not sign in, enter information, or use the site as a publish control. Production Firebase Hosting remains empty, and Netlify still serves `runmprc.com`. **Live Netlify publication status:** a reusable protected release is **NOT AVAILABLE YET**. Ordinary Git-triggered production builds are paused by repository configuration. An overbroad #473 artifact was published and then rolled back on 2026-08-01; bounded #473 deploy `6a6dc9ea588b0c0008036312` is older history. #623 completed the inert-directory predecessor and remains the immediate rollback as deploy `6a7e072f8f346b0008510d29`, source `c2d87d1f69f15e128a0bc9b1b9f915b7c8417aec`. #659 completed one separate exact-artifact accessibility release. Deploy `6a7ece87c5ca4d0007c1a3fc`, source `7496fe0881fb52908c4ff2f40f488df09c94c908`, is production now. Its exact marker/artifact and signed-out route-focus/menu checks passed. The manifest is inactive, temporary refs are absent, and the rollback ref remains. Shop is the static catalog; Events and Calendar show a fixed retry-later notice; the directory remains inert. Event records remain unavailable because this did not deploy Firebase. GitHub Pages currently still claims the same custom domain; future source omits that claim, but #136/WEB-001 must publish and verify its removal. @@ -55,12 +55,13 @@ As of **2026-07-13**, with the internal tooling note below checked from source o - A caller cannot type a Firebase project or deployment target into the release form. - Missing environment configuration or cloud authority makes the release red before backend dependencies, cloud authentication, or deployment. A public website artifact may be prepared without cloud authority, but it cannot be published. - The separate #667 staging verifier uses a short-lived cloud identity after named approval. That identity can read only the staging project identity. The backend release still has no deployment authority. The website job receives public browser values only. +- #669 used one bounded maintainer operation to provision empty staging Firestore and deploy exact Rules/indexes. It did not give the #667 identity or the general release workflow a deploy role. - The Firebase CLI comes from the committed lockfile. The release does not install `latest`. - Source checked on 2026-07-22 pins the internal Firebase CLI to 15.24.0, and its emulator checks use Java 21. Officers do not install or run either tool; the platform maintainer owns them. This source change does not alter the release diagram or prove that Firebase was deployed. The provider and live-host facts below were not reverified for this tooling update. - Source checked on 2026-08-01 pins every root brace-expansion dependency family to reviewed compatible releases 1.1.18, 2.1.4, and 5.0.9 after a new advisory and follow-up bypass fixes. This is internal dependency evidence only. Officers do not install packages, run audit commands, or resolve dependency warnings; the platform maintainer owns those tasks. The separate minimatch finding remains open. This source change does not publish a website, deploy Firebase, change a provider, or prove live behavior. - A production Pages publication job cannot start until Firebase deployment and Function verification succeed. -- The club-owned `run-mprc-staging` project now supplies one static signed-out Hosting surface. Its first release is exact source `a614c68d9a1a2be631a3be874a686d61e5d170a0`; direct `/events` rewriting and desktop/phone public rendering passed. -- #663 source requires complete staging-named public browser configuration and gives CI a synthetic staging artifact scan. #665's provider readback proves only the static staging host. #667 adds a protected read-only identity check, not Firebase deployment authority. These slices do not supply backend/provider isolation, App Check, a release marker, predecessor rollback, or permission to test sign-in/private/admin/commerce behavior. +- The club-owned `run-mprc-staging` project supplies one static signed-out Hosting surface and one empty Firestore boundary. Its Hosting release remains exact source `a614c68d9a1a2be631a3be874a686d61e5d170a0`; direct `/events` rewriting and desktop/phone public rendering passed. Its Rules/index release comes from exact source `ee16bd16220ab58bd3a2add80dd2f39a1d514dd7`; the 418-case emulator suite, provider definition match, fixed anonymous denial probes, and zero-root-collection readback passed. +- #663 source requires complete staging-named public browser configuration and gives CI a synthetic staging artifact scan. #665 proves the static staging host. #667 adds a protected read-only identity check, not Firebase deployment authority. #669 proves only the empty Firestore Rules/index boundary. These slices do not supply Auth, Functions, App Check, provider isolation, a release marker, predecessor rollback, or permission to test sign-in/private/admin/commerce behavior. - `runmprc.com` is served by Netlify, not GitHub Pages. - GitHub Pages currently reports `runmprc.com` as its custom domain and redirects its normal address there. It is not an independently reachable copy today. - Future source stops writing that Pages domain claim. Only provider readback after #136/WEB-001 can prove it cleared. @@ -450,6 +451,43 @@ In words: the release preview and production must contain the same pinned fronte **Escalation:** platform owner, then security owner. Use the private incident path if output may contain a sensitive value. +## Review the empty staging Firestore boundary — AVAILABLE AFTER #669 + +**Purpose:** let a backup officer confirm what the staging database change proves without signing in to the website, entering data, or running a command. + +**Approver:** platform owner plus the named security reviewer for #669. + +**Prerequisites:** merged and closed issue #669; its public redacted evidence comment; private provider readback held by the platform owner; and confirmation that no real or made-up application record was created. + +```mermaid +flowchart LR + Source["Exact reviewed Rules and indexes"] --> Stage["Empty run-mprc-staging Firestore"] + Visitor["Signed-out browser"] --> Rules["Active staging Rules"] + Rules -- "server-only path" --> Deny["Denied"] + Rules --> Stage + Stage -. "no application records" .-> Empty["Zero root collections"] +``` + +Text alternative: exact reviewed Rules and indexes protect the empty staging database; signed-out requests to server-only paths are denied, and no application records exist. + +1. Open issue [#669](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/669). +2. Confirm its outcome names only project `run-mprc-staging` and exact source `ee16bd16220ab58bd3a2add80dd2f39a1d514dd7`. +3. Confirm the redacted evidence says Native, Standard, `us-west2`, delete protection on, and point-in-time recovery off. +4. Confirm the Rules digest, four composite indexes, and 28 field overrides matched the reviewed files. +5. Confirm the three fixed signed-out probes were denied and the final root collection count was zero. +6. Confirm the evidence separately says Auth, Functions, App Check, providers, Hosting, production Firebase, production data, and `runmprc.com` were unchanged. +7. Record the issue link, check date, approver, and any mismatch. Do not paste a console response, token, account record, or screenshot containing private details. + +**Expected result:** the officer can distinguish an empty protected staging database from a usable backend. The public staging website remains signed-out visual review only. + +**Stop conditions:** stop if the project, source commit, location, database mode, protection state, digest, index counts, denial result, or empty count differs; if any record exists; if evidence mentions a production mutation; or if anyone asks the officer to sign in, run a command, or handle a credential. + +**Success proof:** keep the redacted #669 evidence link, exact source commit, Rules digest, provider configuration summary, fixed denial statuses, zero collection count, approver, and check date. + +**Undo:** there is no casual officer undo. The chosen database location is permanent and delete protection is intentional. If Rules or indexes are wrong, stop use and open one reviewed security issue for a platform maintainer to deploy the exact known-good source. Do not delete the database or edit Rules in the console. + +**Escalation:** platform owner first, security owner second. Use the private incident path for unexpected records, access, or credential output. + ## Before a protected release — NOT AVAILABLE YET Do not use this section until #133 records that both GitHub environments are protected and tested. diff --git a/docs/officers/README.md b/docs/officers/README.md index c5b694e..f47eb70 100644 --- a/docs/officers/README.md +++ b/docs/officers/README.md @@ -69,6 +69,8 @@ WEB-002D [#659](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/659) compl The general protected GitHub release is still **NOT AVAILABLE YET**. #667 configures only a separate staging read-only identity check: Dave Liu or Jeff Chang reviews exact `main`, and a keyless five-minute identity may read only the staging project identity. It cannot deploy Firebase or publish a website. #133 still owns backend deployment roles and production protection. Missing authority stops the release with a red failure before Firebase or website publication. +The empty staging Firestore boundary is available after #669. Project `run-mprc-staging` has a delete-protected empty database with the exact reviewed Rules and indexes. Fixed signed-out requests to server-only paths were denied, and the final root collection count was zero. This is not a usable backend: Auth test setup, Functions, App Check, provider sandboxes, reusable deployment authority, and signed-in testing remain **NOT AVAILABLE YET**. Officers do not sign in, enter records, run commands, or edit the database. Use the no-terminal [staging Firestore review](./PUBLISH_AND_CHECK.md#review-the-empty-staging-firestore-boundary--available-after-669). + GitHub Pages currently still claims `runmprc.com`, so its normal address redirects to the Netlify-served name. The source stops adding that claim, but #136/WEB-001 must publish and verify the provider setting before officers call Pages an independent copy. The optional profile-photo and officer People-finder functions are **NOT AVAILABLE YET**. #621 makes the frontend default an inert preview. My Account shows the future photo and independent finder-choice controls disabled. The administrator-guarded People finder shows its name field and Search button disabled. Neither preview reads saved state, accepts a file or name, creates a request, calls the directory services, or shows people. #623 published only that inert interface as deploy `6a7e072f8f346b0008510d29`. Officers inspect the protected disabled layouts only in synthetic local artifacts. Completed signed-out public review proved only the exact revision, normal sign-in and administrator guards, and absence of a member-directory network request. Follow [Events, shop, members, and money](./EVENTS_SHOP_MEMBERS.md), and do not sign in to production or use a real name or photo. #623 changed no Firebase, provider configuration, account, sign-in, or production data. #507 must later prove privacy approval, scoped authorization, isolated staging, backend-first deployment and readback, and a separate reviewed source flip before connected use. The future connected feature is not the official roster and performs no photo query or facial recognition.