---
description: Firebase (modular SDK) conventions
globs: ["src/**/*.{ts,tsx}","firestore.rules","functions/**/*.ts"]
alwaysApply: false
---

# Firebase

- Use the modular SDK only: `import { getFirestore, doc, getDoc } from 'firebase/firestore'`. No `firebase/compat` imports.
- Initialize once in `src/lib/firebase.ts` (`initializeApp` guarded by `getApps().length`), export `auth`, `db`, `storage`.
- Firestore data model: design around queries. Keep documents small, use subcollections for unbounded lists, and denormalize what a screen reads together.
- Use typed converters (`withConverter`) for each collection.
- Writes that must be consistent use `runTransaction` or `writeBatch`. Timestamps with `serverTimestamp()`.
- Every query must be allowed by `firestore.rules`. Update the rules in the same change as the feature; rules check `request.auth.uid` and validate field types and sizes.
- Composite indexes go in `firestore.indexes.json`, not just the console.
- Listeners (`onSnapshot`) return an unsubscribe; call it on unmount.
- Privileged logic (payments, roles, counters) runs in Cloud Functions (v2 API, `firebase-functions/v2`) with the Admin SDK, never on the client.
- The web config is public; real secrets go in Functions secrets (`defineSecret`).

## Don't
- Don't ship `allow read, write: if true` rules, even temporarily.

## Done when
Rules tests pass against the emulator: `firebase emulators:exec --only firestore "npm test"`.
