Flutter Firebase App¶
Status: Authored
Flutter applications, often with separate user and admin apps over a shared Dart package, backed by Firebase services and deployed through CI.
This stack was added after reviewing ~/dev/heartfull/platform, which uses a Melos
workspace with app, admin, and shared Flutter packages plus Firebase Hosting,
Firestore, Auth, Storage, Messaging, App Check, Node 22 Cloud Functions, Firestore rules,
and test/deploy dashboards.
Full rubric¶
Flutter Firebase App v1 - 30 rules across six pages,
eight of them blocker.
Reference implementation¶
| Score evidence | Value |
|---|---|
| Repository | vibecodeqa/ref-flutter-firebase-app (experimental) |
| VCQA report | A 93/100 |
| Verification | self-reported |
| Assessed commit | d0d1a28 |
| Assessed on | 2026-08-09 |
| CI run | success (2026-08-08) |
| Independent assessment | None published yet. The score above is the reference repo's own claim, not third-party proof. |
| Catalog entry last verified | 2026-08-09 |
Reference template map¶
| Evidence | Where to look | What it proves |
|---|---|---|
| Workspace declaration | melos.yaml |
Every Dart package is declared in one manifest with bootstrap, analyze, and test scripts. |
| Client/server field contract | packages/shared/lib/src/access_policy.dart, packages/shared/test/access_policy_test.dart, packages/functions/src/policy.test.ts |
The server-owned field list is asserted identical in Dart, firestore.rules, and TypeScript; drift fails the build. |
| Environment separation | .firebaserc, firebase.json |
Distinct project aliases and pinned emulator ports, rather than one id compiled into the app. |
| Rules as a tested artifact | firestore.rules, firestore-tests/rules.test.mjs |
An emulator-backed suite proves a non-owner is denied, and that no client - including one holding admin: true - can write the fields the Cloud Function owns. |
| Index parity | firestore.queries.json, scripts/check-firestore-indexes.mjs |
Every declared composite query has a matching index and no index is orphaned - the gap the emulator cannot catch. |
| Trusted server boundary | packages/functions/src/policy.ts, packages/admin/test/no_direct_write_test.dart |
The privileged write lives in a Function, and the client path that would bypass it is proved denied. |
| Claim re-verification | packages/admin/test/admin_gate_test.dart |
The admin gate is checked server-side, not merely hidden in the UI. |
| Secret boundary | SECURITY.md, scripts/check-no-secrets.mjs, scripts/check-bundle-secrets.mjs |
Public client config is committed and documented as public, while tracked files and the built web bundle are both scanned for real credentials. |
| Build evidence | docs/build-evidence.md, scripts/build-evidence.mjs |
Toolchain versions and platform permissions are recorded per build, taken from the toolchain rather than from a workflow input. |
| Deploy gating | .github/workflows/deploy.yml, scripts/check-deploy-shape.mjs |
Six blocking gates, every third-party action pinned by SHA, and a check that asserts the gate set rather than trusting review. |
| Score evidence | docs/vcqa-report.md |
The template carries a tracked VCQA score and states its own gaps. |
What this teaches¶
Choose this stack when a Flutter client talks directly to Firebase and some of what it does must not be up to the client. The interesting part is not calling Firestore from Dart - FlutterFire documents that - it is that the client is not a trust boundary, and Firebase makes it look like one. The SDK runs on the user's device, the config that looks like a credential is public, and the query the client issues is the query the database runs.
Everything this standard judges follows from that. The real boundary is in three places
only: firestore.rules, the custom claim, and the Cloud Function. So rules must be
tested with denials, because a positive-only suite proves the app works and says
nothing about whether anyone else can read the data. Elevation must be a server-verified
claim rather than a field in a document the user can write, which is the commonest
Firebase authorization bug. A privileged write must be forbidden to the client in the
rules, not merely also available as a Function - otherwise the Function is decoration an
attacker declines to use. And the shared field contract must be drift-checked, because
it is enforced in three runtimes that cannot import each other.
The second theme is that evidence in this stack is unusually easy to fake without meaning to: a mobile build that compiles nothing, a green dashboard next to a red gate, and an emulator suite that cannot detect a missing index. Several rules exist only to make those claims say what they actually prove.
Do not choose this stack for generic Flutter widget guidance, for a Firebase backend with no Flutter client, or for a Flutter app whose backend is not Firebase.
Decision matrix¶
| Need | Better fit |
|---|---|
| A Flutter workspace that owns Firestore rules, indexes, and Functions | Flutter Firebase App. |
| A single-package Flutter app on Firebase | This standard; the workspace rules degrade to one package and the trust-boundary rules are unchanged. |
| A Flutter app with a REST/GraphQL backend it does not own | Not this standard. The client is a normal untrusted client; no rules file to judge. |
| A Firebase backend with a web-only or native client | Not this standard, though the RULES, FUNCTIONS, and CLAIM pages transfer intact. |
| A Dart package published to pub.dev | Not this standard; there is no deployable Firebase surface. |
| Firestore rules and Functions owned by a different repo from the client | The owning repo's standard. Rules belong to whoever deploys them. |
| Test and dependency hygiene across the workspace | Testing v1, Security v1, and Dependency Hygiene, which this standard assumes rather than restates. |
Scope¶
- Flutter mobile, web, desktop, or multi-platform apps.
- Flutter workspaces with app/admin/shared packages.
- Firebase-backed apps using Auth, Firestore, Storage, Messaging, Hosting, Functions, or App Check.
- Repos using Melos or a similar Dart/Flutter workspace orchestration layer.
Not in scope¶
- Generic Flutter widget guidance already owned by Flutter docs.
- Firebase-only backend repos with no Flutter client.
- Native iOS/Android apps without Flutter.
- Flutter apps with a non-Firebase backend; those need a different backend composition.
Composes¶
- Dart
- Flutter
- Firebase
- Melos
- GitHub Actions
- Dependency Hygiene
- Optional: Web Accessibility, Web Security, Node
Upstream references¶
- Flutter Documentation
- Effective Dart
- Add Firebase to your Flutter app
- Cloud Functions for Firebase
- Cloud Firestore Security Rules
- Firebase Security Rules unit tests
- Firebase custom claims
- Firebase API keys are public
- Firebase Hosting GitHub integration
- Melos Documentation
- Dart pub workspaces
VCQA-owned rule surface¶
- Flutter app/admin/shared package workspace shape.
- Firebase project, hosting, functions, and Firestore environment separation.
- Firestore rules, indexes, and emulator-backed test gates.
- mobile/web build artifact and platform permission evidence.
- version bump, deploy gate, and test dashboard evidence.
- client config versus server secret boundary.
- Functions-owned trusted mutation boundaries.
- shared model compatibility across app/admin/function boundaries.
Detection signals¶
The workspace signal is layout-independent by design. Melos 7 removed melos.yaml — its
configuration moved under a melos: key in the workspace root pubspec.yaml, and membership
moved to Dart's native workspace: key — so both layouts are detected and neither is the
target version. The resolver reads member packages from whichever of the two is present.
- a Melos workspace, in either layout: a
melos.yamlfile (Melos 6 and earlier) or a top-levelmelos:key in the rootpubspec.yaml(Melos 7+) - multiple
pubspec.yamlfiles firebase.json,.firebaserc,firestore.rules, orfirestore.indexes.json- Firebase Flutter dependencies such as
firebase_core,firebase_auth,cloud_firestore,firebase_storage,firebase_messaging, orcloud_functions - Flutter package folders such as
app/,admin/, andshared/ - CI workflows running
flutter test,flutter analyze, Firebase deploys, rules tests, or platform builds
Combination-born guidelines¶
- Firestore rules and indexes are deployable artifacts and need tests, review, and rollback evidence.
- Firebase client configuration can be public, but service accounts, signing credentials, Admin SDK credentials, VAPID secrets, and deploy tokens must stay in secret stores.
- Test dashboards are useful evidence, but production deploy workflows must still fail when required app, admin, functions, or rules gates fail.
- Shared Dart packages require consumer evidence from every app that imports them.
- Platform builds need explicit evidence for signing, permissions, and environment-specific
--dart-definevalues. - Functions should own privileged writes and trust boundaries that Flutter clients cannot safely enforce.
Rule highlights¶
The full rubric is Flutter Firebase App v1. The
eight blocker rules, which are what "implements this stack shape" means:
- R-ENV-1 - local, staging, and production are distinct Firebase projects, selected explicitly. One project id in source is how a debug build writes production data.
- R-SECRET-1 - no server credential or signing material is committed, proved by a scan
over git-tracked files. A
.gitignoreentry is not evidence. - R-RULES-1 - rules are a versioned, reviewed, deployable artifact, never edited in the console.
- R-RULES-2 - rules are emulator-tested and the tests include denials.
- R-RULES-4 - elevation is a server-verified claim, never a role field the user can write.
- R-FUNCTIONS-1 - privileged mutations are performed by the server and forbidden to the client in the rules.
- R-CLAIM-1 - every privileged handler re-verifies the claim server-side, with a strict
comparison against
true. - R-DEPLOY-1 - production deploys are gated on required, blocking checks; reporting jobs are excluded from the gate set.
The remaining 22 rules cover workspace orchestration and shared-package drift, emulator and build-time environment selection, bundle secret scanning and Secret Manager use, index parity, callable input validation and patch scope, toolchain pinning, permission evidence, promotion order, and deploy credential shape.
Limitations¶
- Workspace membership is read from the manifest, not resolved. Both Melos layouts are
detected and sliced as of #48 — a
melos.yamlpackages:list (Melos 6) or a Dartworkspace:list in the rootpubspec.yaml(Melos 7+). The resolver reads those lists literally and never runsmelosordart pub, so a member reachable only through a transitive workspace resolution is not sliced. Declare members in the workspace root. - A Cloud Functions package resolves to no archetype.
typescript-sdkno longer claims it (#49), but there is no Firebase Functions archetype to claim it instead, so the Functions slice is judged by this recipe and the cross-cutting rubrics only. That is honest rather than wrong, but it is not the same as classifying it. - Scores for this stack depend on the scanning environment. A Dart repo scanned without
a Flutter SDK, and without
flutter pub gethaving resolved its dependencies, reports far worse than the same commit scanned with a toolchain, because the analyzer cannot resolve imports. The reference repo's tracked score has not been independently reproduced for that reason, and is labelled self-reported everywhere it appears. - Rules are judged as tested, not as correct. An emulator suite with denial assertions proves the cases it names. It cannot prove the absence of a path nobody thought to test, and the emulator does not enforce indexes at all - which is why R-INDEX-1 exists as a separate check.
- Committed-secret scanning is pattern-based. Filename and content patterns catch the known credential shapes; a credential in an unusual encoding or an unscanned artifact can pass. History scanning depends on the history being intact.
- Build-shape checks are graded honestly, not strictly. A debug compile is accepted as evidence with its limits recorded. Release signing, R8/shrinking, App Bundle packaging, and store upload are not judged by v1, and no iOS build is required.
- Deploy gating is judged from the workflow graph. VCQA can read
needs:andcontinue-on-error:; it cannot see branch protection or required-check settings configured in the GitHub UI.
Anti-patterns¶
- Treating Firebase client config as secret while leaking real server credentials elsewhere.
- Letting a green dashboard hide a red deploy-blocking test.
- Deploying Firestore rules by hand outside reviewable CI.
- Writing a Cloud Function for a privileged mutation and leaving the client path allowed.
- Deriving admin rights from a field in a document the same user can write.
- Updating shared Dart models without running app/admin consumer tests.
- Hardcoding production project IDs, bundle IDs, VAPID keys, or backend URLs in source.
- Presenting a manifest lint, or a debug compile, as proof of release readiness.
Benefits¶
- HeartFull platform (
~/dev/heartfull/platform) shows this stack in production shape. ref-flutter-firebase-appis the product-neutral template for this composition; its evidence is in Reference implementation above.
Maintenance¶
| Maintenance | Value |
|---|---|
| Latest edition | Flutter Firebase App v1 |
| Pin reports and scans to | /standards/flutter-firebase-app/v1/ |
| Last reviewed | 2026-08 |
| Next review due | 2027-08 |
| Edition targets | flutter 3, dart 3, firebase latest, melos >=6, cloudFunctions nodejs22, githubActions latest |
| Lifecycle | active |
| Errata | none |
| Composes | dart, flutter, firebase, melos, github-actions, dependencies |
Editions are cut on material change, not on a calendar. A review that finds the edition
still correct moves Last reviewed forward without a new edition. Metadata lives in
standards/registry.json.
Independent Assessment¶
No independent assessment of this page has been published yet. The v1 rubric was authored under #43 and has not been reviewed by a second party; the reference implementation's score is self-reported. Both are the next things to close.