Repository navigation
CONFIG-001B1 — Enforce server-only runtime commerce admission controls #151
Description
Officer impact: Merged source will stop new race, shop, complimentary, and late-registration commerce when a server-only control is missing, invalid, or off. Signed Stripe events and confirmation mail remain independent. The officer-operated emergency switch is NOT AVAILABLE YET until CONFIG-001B2 supplies a protected audited control path and staged drill.
Officer documentation: Update docs/officers/EMERGENCY_AND_RECOVERY.md and the matching release guidance with a short diagram, stop conditions, proof, undo, and escalation. Do not give officers Firebase Console, terminal, secret, or production-record editing steps.
Deployment evidence: None at issue creation. Closure must separate source/tests, merge, Firebase Rules/Functions deployment, runtime-control creation, Stripe/provider state, website publication, and observed behavior. No production payment, email, member data, provider change, or secret is authorized.
Parent, dependency, and split
Focused CONFIG-001B1 child of the catalogued CONFIG-001 design. CONFIG-001A #149 merged through PR #150 as 2c62b8d and supplies typed environment/origin/mode/key configuration. B1 adds read-only admission enforcement only.
CONFIG-001B2 remains a separate blocked owner/operator outcome for a recent-auth, capability-protected, audited no-deploy writer, private switch drill, staging/provider proof, and continuity procedure. PAY-002/PAY-004 own stronger command leases and retirement of already-open Stripe Sessions or Payment Links. PAY-005/#106 own refund redesign and real reconciliation. #105/#133/#136 own protected deployment authority and plans.
Problem
A deploy-time environment value alone is not the promised emergency switch because changing Firebase Function environment configuration requires a Function deployment. Current race, merchandise, complimentary, late-registration, Payment Link, and refund commands do not consult one server-only runtime policy. A UI-only pause can be bypassed, while a careless global stop could disable signed webhooks and make reconciliation worse.
Atomic outcome
Add one fail-closed, non-cached admission kernel with two layers:
- Require exact COMMERCE_ENABLED=true or false on current key-bound commerce commands as a deployment-time ceiling for new commerce.
- A false deployment ceiling denies new commerce before Firestore. Commands that pass the ceiling, and every refund command, strongly read the server-only Firestore document at systemConfig/commerce. No browser role can read or write it.
The exact version-1 runtime document is:
- schemaVersion: integer 1
- revision: safe integer at least 1
- newCommerceEnabled: boolean
- raceRegistrationEnabled: boolean
- merchandiseCheckoutEnabled: boolean
- incidentRefundsEnabled: boolean
Missing documents, missing or extra fields, wrong types, unknown versions, and unsafe revisions fail closed with fixed nonsecret messages. Never cache the document. Never log or return its supplied values. Missing event/product flags mean disabled.
Command matrix
| Command | Required admission | Continues when new commerce is off? |
|---|---|---|
| Race participant, free participant, or volunteer signup | deploy ceiling + runtime global + race domain + event checkoutEnabled true | No |
| Merchandise checkout | deploy ceiling + runtime global + merchandise domain + product checkoutEnabled true | No |
| Admin mark_comp | same race admission | No |
| Admin add_late_registration, free or paid | same race admission | No |
| Lazy Stripe Product, Price, Payment Link, or Session creation | inherits its enclosing admitted command | No |
| Registration or order refund | valid typed config + runtime incidentRefundsEnabled | Yes, independent of new-commerce ceiling |
| Note, substitute, fulfillment, tracking, or local cancel | unchanged existing behavior | Yes; do not claim local cancel expires Stripe |
| Signed webhook, confirmation mail, reads/exports, stale cleanup | no runtime-control dependency | Yes |
Unknown future command classes deny by default. Membership dues #114 must adopt an explicit future domain and cannot silently inherit race or merchandise permission.
Ordering and race contract
- App Check runs first. Admin authentication runs second.
- CONFIG-001A base/key configuration and exact COMMERCE_ENABLED parsing run next.
- Only the minimum identifier/action shape needed to select the control is inspected before admission.
- A disabled command stops before rate-limit writes, business-record writes, mail, audit, or Stripe calls.
- The successful fresh admission read is the linearization point. A request admitted on revision N may finish if revision N+1 disables commerce afterward. Later requests must see the committed disabled state.
- No cache is allowed. Tests must control a switch change at the admission boundary and prove deny-before-provider behavior.
- Existing/open Stripe Sessions and Payment Links may remain payable. B1 must state this plainly and must not promise drain, expiry, idempotency, or reconciliation that does not exist.
Initial source boundary
- New functions/commerceControl.js and focused tests.
- functions/serverConfig.js and its test for exact deploy-ceiling parsing without affecting webhook/mail base configuration.
- Minimal guarded hunks in createCheckoutSession.js, createMerchCheckout.js, adminRegistrationAction.js, and adminOrderAction.js.
- Focused current tests plus one entry-point matrix test if needed.
- Direct Firestore Rules allow/deny tests proving every browser role is denied runtime control and cannot mutate event/product checkout flags. Add an explicit deny rule only if the current default-deny proof is not durable.
- functions/.env.example.
- Minimum authoritative root status/design/security/runbook docs and docs/officers/EMERGENCY_AND_RECOVERY.md.
Post a scope amendment before editing any other runtime, client, workflow, package/lock, provider, webhook runtime, schema, or migration file.
Acceptance criteria
- COMMERCE_ENABLED accepts exact true/false only when the command boundary requires it; missing, padded, malformed, or supplied unsafe values fail generically.
- The versioned runtime document parser is pure/table-tested and returns a frozen allow/deny snapshot without supplied document data in errors or logs.
- Missing/malformed runtime control and every disabled global/domain/resource combination stop the correct command with zero rate-limit, business-write, audit, mail, or Stripe side effects.
- Paid/free/volunteer race, merchandise, comp, and free/paid late-registration paths are all covered.
- Refunds are denied when incidentRefundsEnabled is false and remain admitted when it is true even if new commerce is off; existing refund correctness is not redesigned.
- Signed webhook lifecycle and qualifying confirmation-mail tests pass while every commerce flag is false or the runtime control is absent.
- Unknown command classes deny; commands that pass the new-commerce ceiling and every refund read the runtime control fresh; a controlled revision change proves the documented linearization boundary.
- Anonymous, member, and browser-admin clients cannot read/write systemConfig/commerce or set event/product checkout flags.
- Existing PROMO-001 — Disable unmodeled Stripe price adjustments #102 promotion/tax/shipping guards and PAY-003A — Harden Stripe webhook ingress and monotonic event reduction #101/PAY-003A: harden Stripe webhook event processing #140 webhook protections remain unchanged.
- Node 20 focused/full Functions, frontend Jest/SPA, Rules, build, diff/link/sensitive-pattern checks pass.
- Officer guidance uses plain language and marks the protected operator path NOT AVAILABLE YET.
Explicit exclusions
- A browser/admin UI, writer callable, Firebase Console officer steps, runtime-control mutation, audit history, provider settings, protected release-plan changes, deployment, or production drill.
- Webhook runtime changes, a new reconciler, checkout/refund idempotency, command journals, capacity/inventory, open-Session or Payment-Link expiry, refund-policy redesign, dues, or catalog editor work.
- Production data backfill. Missing flags deliberately mean disabled. Any later enablement requires private inventory and a protected staged rollout.
Rollout and rollback
Deploy only through a future reviewed immutable plan with COMMERCE_ENABLED=false and the runtime document/all resource flags false. Prove disabled behavior in an isolated staging project. Enable synthetic resources before domains/global/ceiling, with the deployment ceiling last. Emergency disable uses the runtime global; already-admitted commands and existing provider objects require separate recovery. Roll back by disabling the runtime global and safely rolling forward; never revert to ungated code while the ceiling is true.
Claim protocol
Assigned and claimed by /root on a unique codex branch from exact current main. Keep this one enforcement outcome.
Activity
daliu commentedon Jul 13, 2026
CLAIMED by /root at 2026-07-13T12:19:49Z; branch codex/issue-151-commerce-admission; base 2c62b8d. No other source issue is active in this worktree. CONFIG-001B2/operator control remains unclaimed and excluded.
daliu commentedon Jul 13, 2026
Acceptance wording corrected after independent review: a false COMMERCE_ENABLED ceiling deliberately denies new commerce before any Firestore read. New-commerce commands that pass that ceiling, and every refund command, read the runtime control fresh without caching. Runtime and tests are unchanged; this aligns the live issue and authoritative docs with the fail-closed implementation.
daliu commentedon Jul 13, 2026
Scope amendment before PR: include one minimal hunk in functions/stripeHelpers.js. The module caches the Stripe client, so CONFIG-001B1 must re-parse and enforce the exact COMMERCE_ENABLED ceiling on every key-bound command invocation even when that client already exists. This does not change Stripe payloads, webhook/mail runtime, dependencies, or provider configuration. Focused server-config/entry-point tests cover the cached-client path.
daliu commentedon Jul 13, 2026
EXTRACTION FINISHED / REVIEW READY — 2026-07-13T12:40:20Z
Base: 2c62b8d
Exact staged diff SHA-256: c20abb89cfa667cd691f93d26a3cc19e6b9c77b4a1fcb05ce5503aaa9077efd9
Independent review: two exact-hash reviews APPROVED with no findings.
Verification:
Officer impact: Source now fails closed for new race/shop/comp/late commands when the typed ceiling or server-only admission controls are absent/off. Incident refunds use an independent switch. Signed webhook processing and confirmation mail remain independent.
Officer documentation: docs/officers/EMERGENCY_AND_RECOVERY.md and docs/officers/PUBLISH_AND_CHECK.md updated. The officer-operated switch remains NOT AVAILABLE YET.
Deployment evidence: Source/tests only. No website publication, Firebase Rules/Functions deployment, runtime document, Stripe/provider setting, production payment, email, or live behavior was changed or verified.
daliu commentedon Jul 13, 2026
SOURCE COMPLETE / MERGED — 2026-07-13T12:45:34Z
PR #152 merged as 7c7a41c from exact reviewed source c4d0912. PR CI 29250794633 and exact post-merge CI 29250935019 passed Frontend lint/build, Functions lint/tests, and Java-backed Firestore Rules tests. The Netlify PR preview was ready.
Officer impact: Server source now contains the fail-closed race/shop/comp/late/refund admission boundary. The protected officer switch remains NOT AVAILABLE YET.
Officer documentation: docs/officers/EMERGENCY_AND_RECOVERY.md and docs/officers/PUBLISH_AND_CHECK.md.
Deployment evidence: No Firebase Rules or Functions deployment, systemConfig/commerce document, resource checkout flag, Stripe/provider configuration, website production publication, production payment/email, or live behavior was performed or verified. The only protected release plan is profile-recovery; it does not contain #151 Functions/Rules. Production and staging environments also lack the proven public configuration/cloud authority required by #133/#136. Triggering the unrelated plan would be misleading and unsafe.
CONFIG-001B2 plus #105/#133/#136 retain protected control, staged deployment, provider readback, and drill. PAY-002/PAY-004/PAY-005 retain in-flight/open-Session recovery and reconciliation.