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
12 changes: 7 additions & 5 deletions GITHUB_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Every issue inherits `AGENTS.md` and the definition of done in `IMPLEMENTATION_P
| 2 | CI-001 | Repair test gates and secure the deployment pipeline | P0 | L | partial: #103 baseline + #124 hosted Jest; #126 SPA gate and remaining gates open | SAFETY-001 |
| 3 | SUPPLY-001 | Remove vulnerable dependency chains and stage SDK/build upgrades | P0 | L | partial: SUPPLY-001A tracked by #127; remaining upgrade/scanning slices open | CI-001 baseline |
| 4 | SEC-001 | Replace the Firestore admin catch-all with resource-specific rules | P0 | M | source merged #123; Firebase live unproven | — |
| 5 | CONFIG-001 | Fail closed on server environment and commerce configuration | P0 | M | CONFIG-001A tracked in [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149); CONFIG-001B proposed; not deployed | CI-001A recommended |
| 5 | CONFIG-001 | Fail closed on server environment and commerce configuration | P0 | M | CONFIG-001A merged in #149/PR #150; CONFIG-001B1 tracked in [#151](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/151); B2 blocked; not deployed | CI-001A recommended |
| 6 | AUTH-001 | Require verified email for member and privileged claims | P0 | M | partial: #98 merged; backend live unproven; parent open | SEC-001 recommended |
| 7 | AUTH-002 | Replace the legacy static-key membership synchronization endpoint | P0 | M | ready | AUTH-001 |
| 8 | OAUTH-001 | Make Strava token lifecycle server-only, transactional, and auditable | P0 | M | represented by live [#88](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/88); proposed/unclaimed | SAFETY-001/#99; SEC-001 and AUTH-003 for A; staged deferral/cutover with ABUSE-001A for C |
Expand Down Expand Up @@ -274,14 +274,16 @@ Do not compensate by moving sensitive data into a different admin-readable docum
## CONFIG-001 — Fail closed on server environment and commerce configuration

**Labels:** `priority:P0`, `type:security`, `type:reliability`, `area:firebase`, `area:stripe`, `size:M`
**Status:** CONFIG-001A source/tests are tracked in live issue [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149), which remains the status source for review and merge evidence. CONFIG-001B remains proposed. No Firebase/Stripe configuration or live behavior is proven by source.
**Status:** CONFIG-001A source/tests merged through [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) / PR #150 as `2c62b8dd`; Firebase and Stripe configuration remain unproven. CONFIG-001B1 source enforcement is tracked in [#151](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/151). CONFIG-001B2 remains blocked on a safe operator path, command/provider-object recovery, and protected deployment.
**Depends on:** CI-001A recommended

### Problem

Several Functions default a missing `SITE_ORIGIN` to the production domain; until this pass the webhook accepted a missing expected Stripe mode. `ENVIRONMENT_NAME` and `COMMERCE_ENABLED` are documented but not validated or enforced. A missing/mistyped deployment variable can therefore create unsafe URLs, mix live/test behavior, or leave no operational kill switch.
The assessment found production-origin fallbacks, a webhook mode default, and an unenforced `COMMERCE_ENABLED`. CONFIG-001A removes the origin/mode/key fail-open source behavior. CONFIG-001B1 adds the read-only command-enforcement layer, but an audited no-deploy operator control and recovery drill still do not exist. Missing/mistyped deployment or runtime controls must never enable commerce.

CONFIG-001A [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) owns only the typed invocation-time environment/origin/expected-mode boundary, server-key mode compatibility on the four key-bound Functions, removal of production-origin fallbacks, and fixed no-side-effect tests. Webhook and mail remain free of the Stripe API key. CONFIG-001B separately owns `COMMERCE_ENABLED`, global/per-domain switches, refund/incident continuity, and switch-race tests; do not combine the children.
CONFIG-001A [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) owns only the typed invocation-time environment/origin/expected-mode boundary, server-key mode compatibility on the four key-bound Functions, removal of production-origin fallbacks, and fixed no-side-effect tests. Webhook and mail remain free of the Stripe API key.

CONFIG-001B is split because source enforcement is not the same as an operable emergency procedure. B1 [#151](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/151) owns the exact deploy ceiling, fresh server-only runtime admission read, current command matrix, browser-deny proof, and honest in-flight boundary. B2 owns the protected audited writer, operator drill, existing Stripe-object recovery, deployment, and provider proof. Do not claim B1 alone is an officer-usable kill switch.

### Scope

Expand Down Expand Up @@ -310,7 +312,7 @@ CONFIG-001A [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) ow

### Agent handoff

Implement as CONFIG-001A/001B from `GITHUB_ISSUE_SLICES.md`. Never print or pattern-match an entire secret; use provider-safe key-mode metadata/prefix only where documented. External parameter/Secret Manager changes need an authorized owner.
Implement only the live CONFIG child claimed in `GITHUB_ISSUE_SLICES.md`. Never print or pattern-match an entire secret; use provider-safe key-mode metadata/prefix only where documented. External parameters, runtime-control mutation, Secret Manager, deployment, and provider changes need their authorized owner issue.

---

Expand Down
5 changes: 3 additions & 2 deletions GITHUB_ISSUE_SLICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ An agent-ready child has one observable outcome, one trust boundary, explicit de

| ID | Status/dependency | Bounded deliverable | Close evidence |
| --- | --- | --- | --- |
| CONFIG-001A | source/tests tracked in live [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149); CI-001A; not deployed/configured | Add typed server configuration requiring environment name, exact hosted/loopback origin, explicit Stripe mode, and key-bound environment/key compatibility; remove production fallbacks while keeping webhook/mail least-privilege. | Missing/malformed/local-live tests fail after auth/signature but before rate-limit/Firestore/Stripe/mail/ledger work; merge and provider evidence remain separate. |
| CONFIG-001B | proposed; CONFIG-001A | Implement a server-owned global/per-domain commerce kill switch checked by every Session/refund/catalog command while webhook/reconciliation remain active. | Disabled-command tests prove no external call; webhook/reconcile tests still pass. |
| CONFIG-001A | source/tests merged through [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) / PR #150 as `2c62b8dd`; not deployed/configured | Add typed server configuration requiring environment name, exact hosted/loopback origin, explicit Stripe mode, and key-bound environment/key compatibility; remove production fallbacks while keeping webhook/mail least-privilege. | Missing/malformed/local-live tests fail after auth/signature but before rate-limit/Firestore/Stripe/mail/ledger work; merge and provider evidence remain separate. |
| CONFIG-001B1 | source/tests tracked in live [#151](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/151); CONFIG-001A; not deployed/configured | Require an exact deploy-time ceiling; false denies new commerce before Firestore. Commands passing it and every refund use a fresh server-only global/domain/resource admission read. Keep webhook/mail independent; missing controls and resource flags mean disabled. | Paid/free/volunteer, merchandise, comp, late-registration, and refund allow/deny tests prove fixed failures and no command side effect; Rules tests deny every browser role; continuity and race-boundary tests pass. |
| CONFIG-001B2 | blocked owner/operator; B1, AUTH-003/ADMIN-001, PAY-002/PAY-004/PAY-005, CI-001 | Add a recent-auth capability-protected writer, append-only audit, active-provider-object recovery, protected deployment, and private disable/restore drill. | Named owners safely disable and restore synthetic staging commerce without code changes; in-flight and existing Stripe objects are reconciled; private provider/Firebase evidence is recorded. |
| PROMO-001 | source/tests complete in [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102); provider inventory and deployment remain owner action; PAY-001A for future enablement | Disable promotion entry and automatic tax in both Session creators; require a complete Stripe breakdown; quarantine unknown or nonzero discount, tax, or shipping adjustments. | Exact Session payload assertions and signed zero/partial/full/malformed/failed/expired adjustment tests pass; pre-change Session/provider inventory and live verification remain open. |
| PAY-001A | ready; CI-001A | Add shared strict-object, bounds/depth, integer-cents/currency/date/URL/email primitives, safe errors, and redacted logging. | Prototype/deep/Unicode/money/unknown-key tests prove no external call. |
| PAY-001B | proposed; PAY-001A | Apply race/volunteer/free schemas, validate server event custom fields/options, and persist price/waiver snapshots. | Positive/hostile dynamic-field and snapshot tests. |
Expand Down
2 changes: 1 addition & 1 deletion IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ The first tranche deliberately combines one urgent safety repair with the most c
2. **PAY-003 — webhook event inbox and safe reducer:** PAY-003A [#101](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/101) source merged through [PR #140](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/140) as `87bd1210575882cdd06214bcb98ed38ce00f71c2`, adding event replay protection, async-aware payment confirmation, expected total/currency validation, direct metadata lookup, terminal-state guards, and lifecycle tests. Firebase and Stripe live behavior remain unproven; PAY-003B/C remain open.
3. **SEC-001 — Firestore trust boundaries:** #100 merged through [PR #123](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/123) at `a7fc301e85b0aeabe396e771faea21d3fc8e7b2b`. Source and 295 Rules tests remove recursive browser-admin authority. Firebase deployment explicitly skipped, so deployed/live Rules remain unproven under #105.
4. **PROMO-001 — fail-safe monetary policy:** [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102) disables promotion entry and automatic tax in both current creators, adds exact payload tests, and quarantines unknown or nonzero discount/tax/shipping adjustments on success, failure, and expiry. The private pre-change Session/provider inventory, protected deployment, provider readback, and live verification remain owner actions; no discount, tax, or shipping feature is authorized.
5. **Identity/config next:** AUTH-001A [#98](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/98) merged at `ce22c110`, proving source/tests for unverified-target rejection at the existing role-grant endpoints. Its Firebase deployment skipped, so backend-live status is unproven and the remaining AUTH-001 parent work stays open. CONFIG-001A is tracked in live child [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) for the typed environment/origin/Stripe-mode source boundary; CONFIG-001B retains the later commerce stop-switch matrix. Follow the live issues and do not duplicate either #98 or #149.
5. **Identity/config next:** AUTH-001A [#98](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/98) merged at `ce22c110`, proving source/tests for unverified-target rejection at the existing role-grant endpoints. Its Firebase deployment skipped, so backend-live status is unproven and the remaining AUTH-001 parent work stays open. CONFIG-001A merged through [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) / PR #150 as `2c62b8dd`, but remains undeployed. CONFIG-001B1 is tracked in [#151](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/151) for fail-closed read-only admission; B2 retains the protected audited operator control, provider-object recovery, and drill. Follow the live issues and do not duplicate these slices.
6. **Then:** PAY-001/PAY-002 so checkout creation, refunds, and every later handler share strict schemas and idempotent state.
7. **Then:** RACE-001 and MERCH-001 transactional reservations.

Expand Down
33 changes: 24 additions & 9 deletions OPERATIONS_RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Complete this table in the private operations system before live payments. Do no

| Area | Primary role | Backup role | Required access |
| --- | --- | --- | --- |
| Incident commander | Platform lead | Club president/designee | Checkout kill switch, status communication |
| Incident commander | Platform lead | Club president/designee | Commerce containment coordination and status communication; officer switch not available yet |
| Stripe account and payouts | Treasurer | Finance backup | Stripe finance/admin with MFA |
| Refunds and disputes | Finance admin | Treasurer | Stripe refunds/disputes; scoped MPRC finance capability |
| Race operations | Race director | Registrar backup | Event/roster capability; no payout settings |
Expand All @@ -30,15 +30,30 @@ Review access quarterly, when an officer changes, and after every incident. Remo

## 2. Emergency controls

Before launch, implement and test a checkout kill switch that does not require a code deployment. Recommended controls:
### Commerce pause — B1 source only, operator control NOT AVAILABLE YET

- `commerceEnabled=false` globally in server-controlled configuration.
- Per-event `checkoutEnabled=false` and per-product/variant non-sellable status.
- Server functions check the switch before reserving capacity/inventory or calling Stripe.
- The public site displays a neutral maintenance message and support path.
- Existing paid records and webhook/reconciliation processing continue while new Sessions are disabled.
CONFIG-001B1 [#151](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/151) adds the read-only server enforcement layer. It does not deploy that layer, create its runtime record, or provide a safe way for an officer to change it.

Changing only the UI is not a kill switch. Existing Checkout Sessions may remain payable; incident procedures must also expire active Sessions when required.
```mermaid
flowchart LR
Command["New signup, shop, comp, late add, or refund"] --> Typed["Check typed deploy settings"]
Typed --> Runtime["Read server-only runtime control fresh"]
Runtime --> Resource["Check event or product when required"]
Resource --> Decision{"This command admitted?"}
Decision -- "No" --> Stop["Stop before command side effects"]
Decision -- "Yes" --> Existing["Continue existing validation and command logic"]
Webhook["Signed Stripe webhook"] --> Continue["Continue payment evidence processing"]
```

Text alternative: new commerce and incident refunds use separate server decisions; a pause stops newly denied commands, while signed webhook processing continues without reading the pause control.

The exact server-only `systemConfig/commerce` version-1 record contains a safe integer revision plus four booleans: global new commerce, race registration, merchandise checkout, and incident refunds. New race work also requires `events/{id}.checkoutEnabled=true`. New shop work also requires `products/{id}.checkoutEnabled=true`. Missing/malformed documents or missing resource fields mean disabled. Browser visitors, members, and browser admins cannot read/write this control or set those resource fields.

`COMMERCE_ENABLED` is a deploy-time ceiling. It is not the no-deploy emergency switch. Keep it false during rollout. A false ceiling denies new commerce before a Firestore read. New-commerce requests that pass the ceiling, and every refund request, read the runtime record fresh without caching.

Changing only the UI is never a kill switch. A request admitted immediately before a pause can finish, and an existing Stripe Session or Payment Link can remain payable. The current local Cancel action does not expire Stripe. PAY-002/PAY-004 must add command drain and provider-object expiry. No reconciliation job exists yet; PAY-005/#106 own it.

CONFIG-001B2 must add a capability-protected, recent-auth, audited operator path and a private staging disable/restore drill. Until then, officers must use the stop-and-escalate steps below. Do not edit Firebase data, environment values, or Stripe objects from this guide.

## 3. Environment inventory

Expand Down Expand Up @@ -114,7 +129,7 @@ flowchart TD

Text alternative: after identity, signature, or mail-eligibility checks, invalid server settings stop the invocation before any business, mail, event-ledger, or Stripe side effect.

The webhook and confirmation-mail triggers validate environment, origin, and expected mode without receiving the Stripe API key. Only the two checkout creators and two admin action Functions bind and validate that key. `COMMERCE_ENABLED` and global/per-domain stop-switch behavior remain **NOT AVAILABLE YET** under CONFIG-001B.
The webhook and confirmation-mail triggers validate environment, origin, and expected mode without receiving the Stripe API key. Only the two checkout creators and two admin action Functions bind and validate that key. CONFIG-001B1 requires exact `COMMERCE_ENABLED` only on those four command handlers and keeps webhook/mail independent. Its runtime enforcement remains source-only; the CONFIG-001B2 operator control and drill are **NOT AVAILABLE YET**.

Source tests do not prove Firebase parameters, Secret Manager bindings, Stripe account mode, or production behavior. Before deployment, record only parameter names in public evidence. Store values and provider identifiers in the approved private continuity record. Use a protected staging release with made-up data; never discover configuration by trying a real payment or email.

Expand Down
Loading
Loading