Firestore rules and indexes are in source control but never deployed #13

Open
opened 2026-08-19 22:06:55 -04:00 by logan · 6 comments
Owner

infra/firestore/firestore.rules and firestore.indexes.json landed during the tenancy pass but have not been deployed — deployment is a manual step documented inside the files themselves.

Until that happens, the rules actually enforcing browser direct-reads are whatever is live in the Firebase console, not what is in the repo. The repository and production are silently out of sync on a security boundary, which is the worst kind of drift: reviewing the repo gives false assurance.

Also unverified: whether Firestore genuinely requires the composite index for org_id == combined with array_contains that was included defensively.

`infra/firestore/firestore.rules` and `firestore.indexes.json` landed during the tenancy pass but have not been deployed — deployment is a manual step documented inside the files themselves. Until that happens, the rules actually enforcing browser direct-reads are whatever is live in the Firebase console, not what is in the repo. The repository and production are silently out of sync on a security boundary, which is the worst kind of drift: reviewing the repo gives false assurance. Also unverified: whether Firestore genuinely requires the composite index for `org_id ==` combined with `array_contains` that was included defensively.
Author
Owner

Confirmed live in prod (drb.cusano.net, git_sha 861ea41, 2026-08-23 02:07 EDT) — both halves of this are still undeployed and now cause visible, misleading UI failures:

Indexes. useIncidents fails on every load of /incidents:

useIncidents: FirebaseError: The query requires an index. ...collectionGroups/incidents/indexes/... org_id, started_at

That index IS declared in infra/firestore/firestore.indexes.json (incidents: org_id ASC + started_at ASC). It has never been pushed. The consequence is worse than a blank page: app/incidents/page.tsx renders the empty state "No incidents recorded yet / Incidents appear automatically once calls start correlating." while the real cause is a failed query. The page lies about the state of the system. Same shape is waiting for calls (org_id+started_at, org_id+incident_ids) and both alert_events indexes.

Rules. useUnacknowledgedAlerts: FirebaseError: Missing or insufficient permissions. fires from the root layout on every page load, so every route in the app logs an error. firestore.rules:98 does grant allow read: if docInMyOrg() on alert_events — so this is the rules file not being deployed, or docInMyOrg() failing because live alert_events docs predate org_id.

Deploy command is in the header comment of the indexes file: firebase deploy --only firestore:indexes --project discord-radio-bot-461301 (plus firestore:rules).

Secondary fix worth doing at the same time: the hooks swallow the error into an empty array, so useIncidents/useCalls should surface error to the page and the page should render an error state instead of "nothing recorded yet".

Confirmed live in prod (drb.cusano.net, git_sha 861ea41, 2026-08-23 02:07 EDT) — both halves of this are still undeployed and now cause visible, misleading UI failures: **Indexes.** `useIncidents` fails on every load of `/incidents`: ``` useIncidents: FirebaseError: The query requires an index. ...collectionGroups/incidents/indexes/... org_id, started_at ``` That index IS declared in `infra/firestore/firestore.indexes.json` (incidents: org_id ASC + started_at ASC). It has never been pushed. The consequence is worse than a blank page: `app/incidents/page.tsx` renders the empty state **"No incidents recorded yet / Incidents appear automatically once calls start correlating."** while the real cause is a failed query. The page lies about the state of the system. Same shape is waiting for `calls` (org_id+started_at, org_id+incident_ids) and both `alert_events` indexes. **Rules.** `useUnacknowledgedAlerts: FirebaseError: Missing or insufficient permissions.` fires from the root layout on *every* page load, so every route in the app logs an error. `firestore.rules:98` does grant `allow read: if docInMyOrg()` on `alert_events` — so this is the rules file not being deployed, or `docInMyOrg()` failing because live `alert_events` docs predate `org_id`. Deploy command is in the header comment of the indexes file: `firebase deploy --only firestore:indexes --project discord-radio-bot-461301` (plus `firestore:rules`). Secondary fix worth doing at the same time: the hooks swallow the error into an empty array, so `useIncidents`/`useCalls` should surface `error` to the page and the page should render an error state instead of "nothing recorded yet".
Author
Owner

Promoted to a hard revenue gate by board minutes #42 (2026-08-23) - this is Gate B condition B1.

The board ruled that no card is charged, no invoice is issued and no Founding Operator slot is sold until this issue is closed. It was the CISO's #1 finding (#39): Server/infra/firestore/firestore.rules is deny-by-default and correctly scoped on resource.data.org_id == request.auth.token.org_id in source, but undeployed - so the live tenant boundary is unknown, not known-good. Blast radius if the console still holds permissive rules: every signed-in user of every org reading every other org's calls, incidents, transcripts and geocoded addresses.

To close, per the minutes: deploy firestore.rules and reconcile firestore.indexes.json (see #33), then paste the deployed ruleset ID into this issue. The ruleset ID is the verification artefact - "I ran the deploy" is not, because this exact process has already drifted silently once (#33).

Owner: the owner (WSL/console - not reachable from the authoring machine). Date: not set; this is one of the items blocking first revenue.

**Promoted to a hard revenue gate by board minutes #42 (2026-08-23) - this is Gate B condition B1.** The board ruled that **no card is charged, no invoice is issued and no Founding Operator slot is sold** until this issue is closed. It was the CISO's #1 finding (#39): `Server/infra/firestore/firestore.rules` is deny-by-default and correctly scoped on `resource.data.org_id == request.auth.token.org_id` in source, but undeployed - so the live tenant boundary is *unknown*, not known-good. Blast radius if the console still holds permissive rules: every signed-in user of every org reading every other org's calls, incidents, transcripts and geocoded addresses. **To close, per the minutes:** deploy `firestore.rules` **and** reconcile `firestore.indexes.json` (see #33), then **paste the deployed ruleset ID into this issue**. The ruleset ID is the verification artefact - "I ran the deploy" is not, because this exact process has already drifted silently once (#33). Owner: **the owner** (WSL/console - not reachable from the authoring machine). Date: not set; this is one of the items blocking first revenue.
Author
Owner

Verified 2026-08-23: rules do NOT auto-deploy on push to main.

Server/infra/firestore/firestore.rules (plus firebase.json, firestore.indexes.json) exist in source, but .gitea/workflows/deploy.yml has no firebase deploy / firebase-tools / firestore:rules step. The only Firestore reference in the whole workflow is NEXT_PUBLIC_FIRESTORE_DATABASE as a frontend build-arg (line 65). Grep across all *.md, *.yml, *.sh and Makefiles in Server/ returns zero matches for firebase deploy or firestore:rules — there is no script, make target, or documented procedure that deploys them anywhere.

So the deployed ruleset is whatever was last hand-set in the Firebase console, and nothing in the repo will ever change that. Writing the rules file did not close this issue; it only made the gap versionable.

Two things follow:

  1. Prod tenant boundary remains unknown/unverified from source. Console check required.
  2. This issue is not just "deploy the rules once" — it needs a deploy path, or it silently regresses the next time the rules change.

Raised at board minutes #42 (Gate B). Board context: the owner reasonably assumed "everything deploys on push to main"; that assumption holds for the three container images and not for Firestore rules.

**Verified 2026-08-23: rules do NOT auto-deploy on push to main.** `Server/infra/firestore/firestore.rules` (plus `firebase.json`, `firestore.indexes.json`) exist in source, but `.gitea/workflows/deploy.yml` has **no** `firebase deploy` / `firebase-tools` / `firestore:rules` step. The only Firestore reference in the whole workflow is `NEXT_PUBLIC_FIRESTORE_DATABASE` as a frontend build-arg (line 65). Grep across all `*.md`, `*.yml`, `*.sh` and Makefiles in `Server/` returns zero matches for `firebase deploy` or `firestore:rules` — there is no script, make target, or documented procedure that deploys them anywhere. So the deployed ruleset is whatever was last hand-set in the Firebase console, and nothing in the repo will ever change that. Writing the rules file did not close this issue; it only made the gap versionable. Two things follow: 1. Prod tenant boundary remains **unknown/unverified** from source. Console check required. 2. This issue is not just "deploy the rules once" — it needs a deploy path, or it silently regresses the next time the rules change. Raised at board minutes #42 (Gate B). Board context: the owner reasonably assumed "everything deploys on push to main"; that assumption holds for the three container images and not for Firestore rules.
Author
Owner

Pulled forward by board minutes #62 (2026-08-24) as the single highest-leverage owner action this week, ahead of everything except the discovery conversations. Due 2026-08-31.

CTO (#60 rec. 10) and CISO (#61 finding 4) converged on this independently. infra/firestore/firestore.rules is deny-by-default, well-formed and committed - and has never been deployed. The frontend reads Firestore directly from the browser, so for reads the live console ruleset is the access control, not auth.py. What is enforcing it right now is whatever was hand-set in the console: unknown, unversioned, and unreviewed - while the unattended runner operates against production twice daily.

This is the load-bearing blocker on Gate B tenant isolation, and it costs the owner minutes:

firebase deploy --only firestore:rules,firestore:indexes --project discord-radio-bot-461301

#51 (a CI step so it can never drift again) follows this and is engineering work, not owner work.

**Pulled forward by board minutes #62 (2026-08-24) as the single highest-leverage owner action this week, ahead of everything except the discovery conversations. Due 2026-08-31.** CTO (#60 rec. 10) and CISO (#61 finding 4) converged on this independently. `infra/firestore/firestore.rules` is deny-by-default, well-formed and committed - and has never been deployed. The frontend reads Firestore **directly** from the browser, so for reads the live console ruleset *is* the access control, not `auth.py`. What is enforcing it right now is whatever was hand-set in the console: unknown, unversioned, and unreviewed - while the unattended runner operates against production twice daily. This is the load-bearing blocker on Gate B tenant isolation, and it costs the owner minutes: ``` firebase deploy --only firestore:rules,firestore:indexes --project discord-radio-bot-461301 ``` #51 (a CI step so it can never drift again) follows this and is engineering work, not owner work.
Author
Owner

Deployed and verified 2026-08-25.

Ran from Server/infra/firestore/:

firebase deploy --only firestore:rules,firestore:indexes --project discord-radio-bot-461301

Output confirms the correct database target, which is the failure mode firebase.json warns about:

  • firestore: deployed indexes in firestore.indexes.json successfully for c2-server database
  • firestore: released rules firestore.rules to cloud.firestore
  • Deploy complete!

The "database": "c2-server" pin in firebase.json did its job — without it the CLI deploys to (default), reports success, and changes nothing the app can see.

Note for the record: the CLI found 1 index in the live project that is not in firestore.indexes.json and it was deleted during the deploy (prompt answered Yes). That was live-vs-repo drift, now reconciled in the repo's favour. If a query starts reporting "index required" against /databases/c2-server/, this is the first thing to check.

This was named in board minutes #62 as the sole remaining blocker on tenant isolation, due 2026-08-31 — closed six days early. Closing.

Deployed and verified 2026-08-25. Ran from `Server/infra/firestore/`: firebase deploy --only firestore:rules,firestore:indexes --project discord-radio-bot-461301 Output confirms the correct database target, which is the failure mode `firebase.json` warns about: - `firestore: deployed indexes in firestore.indexes.json successfully for c2-server database` - `firestore: released rules firestore.rules to cloud.firestore` - `Deploy complete!` The `"database": "c2-server"` pin in `firebase.json` did its job — without it the CLI deploys to `(default)`, reports success, and changes nothing the app can see. Note for the record: the CLI found **1 index in the live project that is not in `firestore.indexes.json`** and it was deleted during the deploy (prompt answered Yes). That was live-vs-repo drift, now reconciled in the repo's favour. If a query starts reporting "index required" against `/databases/c2-server/`, this is the first thing to check. This was named in board minutes #62 as the sole remaining blocker on tenant isolation, due 2026-08-31 — closed six days early. Closing.
logan closed this issue 2026-08-25 00:04:48 -04:00
logan reopened this issue 2026-09-13 17:38:40 -04:00
Author
Owner

Reopened per board minutes 2026-09-13 CIO draft (server-26#144): PR #124's "Closes #N" auto-closed this, but the CI firebase deploy step (.gitea/workflows/deploy.yml:131-137) gates on command -v firebase and the VM has no firebase-tools installed -- the step silently no-ops on every deploy. Firestore rules/indexes are NOT actually being applied in prod. See DEFERRED.md row 54. Fix: install firebase-tools on the VM, confirm next deploy log shows a real firebase deploy run, not the WARNING line.

Reopened per board minutes 2026-09-13 CIO draft (server-26#144): PR #124's "Closes #N" auto-closed this, but the CI firebase deploy step (`.gitea/workflows/deploy.yml:131-137`) gates on `command -v firebase` and the VM has no firebase-tools installed -- the step silently no-ops on every deploy. Firestore rules/indexes are NOT actually being applied in prod. See DEFERRED.md row 54. Fix: install firebase-tools on the VM, confirm next deploy log shows a real `firebase deploy` run, not the WARNING line.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#13