Revision: 1 Last modified: 2026-07-22T00:00:00Z
Idempotent, fully-dynamic Firebase project setup for helix_terminator (or
any consuming project — the script is decoupled/project-agnostic per
Constitution §11.4.28). It uses the real Firebase CLI to:
- Select-or-create the target Firebase project.
- Select-or-create Android / iOS / Web apps under that project (only when the operator supplies a package name / bundle id / app name — this script never invents one, per §11.4.6 no-guessing).
- Download each configured app's client SDK config
(
google-services.json,GoogleService-Info.plist, or a Web config JSON) into a gitignored secrets directory. - Generate the FCM (Firebase Cloud Messaging) HTTP v1 service-account JSON
that
services/notification-service/internal/delivery/push.goreads for real push delivery — when a scriptable path exists (see "FCM service-account key" below for the honest limitation).
This script is the regeneration mechanism (§11.4.77) for every
credential/config file it produces — nothing under
scripts/firebase/secrets/ is meant to be hand-crafted or committed;
everything there is reproducible by re-running this script.
- Firebase CLI (
npm install -g firebase-tools), authenticated (firebase login, orFIREBASE_TOKENset fromfirebase login:cifor non-interactive environments). jq(JSON parsing of Firebase CLI--jsonoutput).- Optional:
gcloud(Google Cloud SDK), authenticated (gcloud auth login) — only required for the automated FCM service-account key generation step. Its absence does not block the rest of the script; it produces an honest SKIP with exact remediation instructions (see below).
# Select the existing project (or fail with guidance if it doesn't exist):
bash scripts/firebase/firebase_setup.sh
# Create the project if it doesn't exist yet:
FIREBASE_ALLOW_CREATE_PROJECT=1 bash scripts/firebase/firebase_setup.sh
# Also register an Android app and download its config:
FIREBASE_ALLOW_CREATE_PROJECT=1 \
FIREBASE_ALLOW_CREATE_APPS=1 \
FIREBASE_ANDROID_PACKAGE=com.helixdevelopment.helix_terminator \
bash scripts/firebase/firebase_setup.sh| Variable | Default | Purpose |
|---|---|---|
FIREBASE_PROJECT_ID |
repo dirname, lowercased, _→- (e.g. helix-terminator) |
Target Firebase/GCP project id. |
FIREBASE_PROJECT_DISPLAY_NAME |
same as FIREBASE_PROJECT_ID |
Display name if the project is created. |
FIREBASE_ALLOW_CREATE_PROJECT |
unset (0) |
Set 1 to allow creating the project when absent. |
FIREBASE_ALLOW_CREATE_APPS |
unset (0) |
Set 1 to allow creating platform apps when absent. |
FIREBASE_ANDROID_PACKAGE |
unset | Android applicationId. Registers/selects an Android app when set. |
FIREBASE_IOS_BUNDLE_ID |
unset | iOS bundle id. Registers/selects an iOS app when set. |
FIREBASE_WEB_APP_NAME |
unset | Web app display name. Registers/selects a Web app when set. |
FIREBASE_SETUP_OUTPUT_DIR |
scripts/firebase/secrets/ |
Where every secret/config file is written. Verified gitignored before any write. |
FIREBASE_SERVICE_ACCOUNT_KEY_PATH |
<output-dir>/fcm-service-account.json |
Override the FCM service-account JSON output path. |
FIREBASE_TOKEN |
unset | Non-interactive CLI auth token from firebase login:ci. Never read/printed/stored by this script — the Firebase CLI itself consumes it. |
Safe by default. Without the *_ALLOW_CREATE_* flags, the script only
selects existing resources and SKIPs with exact guidance when something
is missing — it never surprise-mutates a real Google Cloud account on a
bare invocation.
All under FIREBASE_SETUP_OUTPUT_DIR (default scripts/firebase/secrets/,
gitignored, directory mode 700, files mode 600):
fcm-service-account.json— FCM HTTP v1 service-account key. Pointservices/notification-service'sFCM_SERVICE_ACCOUNT_JSONenv var at this path to enable real push delivery.<platform>-<identifier>.json/.plist— client SDK config per registered app.
The Firebase CLI has no command to generate or download a
service-account key (verified 2026-07-22 against firebase --help's full
command list — apps:* covers client SDK config only, login:ci issues a
CI auth token, not a service-account credential). There are exactly two
genuine paths to obtain one:
gcloud(automated by this script when present):gcloud iam service-accounts keys createagainst the project's auto-provisionedfirebase-adminsdk-*service account — the same account Firebase's Console "Generate New Private Key" button targets. Firebase grants this account Cloud Messaging send permission (cloudmessaging.messages.create) automatically; the script does not create a new IAM principal or role binding, only an additional key for an account that already has the permission.- Firebase Console (manual, UI-only, not scriptable): Project
Settings → Service accounts tab → Generate New Private Key. Console
URL:
https://console.firebase.google.com/project/<PROJECT_ID>/settings/serviceaccounts/adminsdk.
When gcloud is absent, the script prints both remediation paths verbatim
(with the exact Console URL and exact target file path) and exits with a
skipped_no_gcloud summary line — an honest SKIP (Constitution §11.4.3),
never a fabricated success.
- stdout/stderr separation is load-bearing. The Firebase CLI writes its
human-readable progress spinner to stderr even when
--jsonis passed. The script's first live run (2026-07-22) initially merged2>&1into the captured auth-check variable, which corrupted the JSON payload and made every downstreamjqparse fail even though the underlyingfirebase projects:listcall had actually succeeded. Fixed by capturing stdout and stderr into separate variables/files — this regression is now called out explicitly in the script's own comments so it is never reintroduced. - Gitignore preflight. Before writing anything, the script creates a
probe file under the output directory and verifies
git check-ignorereports it as ignored — refusing to proceed (rather than writing a credential into a trackable path) if the directory is not covered by.gitignore. - Idempotency. Re-running with the same inputs is a no-op for every already-satisfied step (existing project selected, existing apps selected, existing service-account key left untouched) — verified by two consecutive live runs during this script's initial validation.
- App identifiers are never guessed. With no
FIREBASE_ANDROID_PACKAGE/FIREBASE_IOS_BUNDLE_ID/FIREBASE_WEB_APP_NAMEset, the corresponding platform step SKIPs cleanly — inventing a placeholder package/bundle id would create a semi-permanent, hard-to-reverse App-Store-facing identifier without operator confirmation (Constitution §11.4.6 / §11.4.101).
services/notification-service/internal/delivery/push.go— the real FCM HTTP v1 + APNs-via-FCM delivery client that consumes this script'sfcm-service-account.jsonoutput viaFCM_SERVICE_ACCOUNT_JSON.scripts/rotate-secrets.sh— general secret-rotation entry point (this script's service-account key is one of the secret classes it should eventually cover — tracked as phase-2 work, seedocs/CONTINUATION.md).
2026-07-22 — live-run verified twice against the real, authenticated
Firebase CLI (project helix-terminator selected on both runs; FCM
service-account step honestly SKIPPED with gcloud absent from the host).