Skip to content

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

Upstream references

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.yaml file (Melos 6 and earlier) or a top-level melos: key in the root pubspec.yaml (Melos 7+)
  • multiple pubspec.yaml files
  • firebase.json, .firebaserc, firestore.rules, or firestore.indexes.json
  • Firebase Flutter dependencies such as firebase_core, firebase_auth, cloud_firestore, firebase_storage, firebase_messaging, or cloud_functions
  • Flutter package folders such as app/, admin/, and shared/
  • 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-define values.
  • 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 .gitignore entry 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.yaml packages: list (Melos 6) or a Dart workspace: list in the root pubspec.yaml (Melos 7+). The resolver reads those lists literally and never runs melos or dart 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-sdk no 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 get having 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: and continue-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-app is 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.