maintainer
Deploy to Firebase
The codebase is complete and emulator-verified. Bringing it up against real infrastructure is a
one-time provisioning pass. There are two ways to push it — a manual one-shot from your machine,
and a keyless CI pipeline on every push to main.
A deploy workflow already exists: .github/workflows/deploy.yml.
It deploys Hosting + Functions + rules via Workload Identity Federation (no stored key) and rolls
the Cloud Run feed-engine. It's gated by a repo variable until you've provisioned the GCP side — the
runway below walks you through arming it.
Two paths
manual one-shot, from your machine
scripts/deploy.sh — containerised, builds web + functions and pushes Hosting,
Functions, and rules. Best for a first deploy, a hotfix, or a project without WIF yet.
automated keyless CI
deploy.yml — on push to main (and workflow_dispatch).
Authenticates via WIF, deploys the Firebase surfaces, and builds + deploys the Cloud Run feed-engine.
Manual one-shot deploy
The hands-on counterpart to the CI pipeline. Everything runs in a node:26 container — the host only
needs Docker. It builds the web app and functions, then runs firebase deploy.
-
Pick your auth
Non-interactive (CI token) or interactive login inside the container:
bash · auth options# option A — token (non-interactive) export FIREBASE_TOKEN="$(firebase login:ci)" # option B — leave it unset; the script runs `firebase login` in-container -
Deploy
Point it at your project id. It builds, then deploys Hosting + Functions + Firestore/RTDB rules.
bashFIREBASE_PROJECT=your-project-id scripts/deploy.sh -
Narrow the scope (optional)
Deploy just one surface — e.g. a Hosting-only push for a UI change:
bashCANCRI_DEPLOY_ONLY=hosting FIREBASE_PROJECT=your-project-id scripts/deploy.sh
The manual script deploys the Firebase surfaces (Hosting, Functions, rules). It does not build the Cloud Run feed-engine image — that's the always-on price source. Without it the terminal authenticates and onboards, but no live ticks flow. Roll the feed-engine via the CI pipeline or the runway.
Keyless CI deploy
On push to main, deploy.yml authenticates to GCP with a short-lived OIDC token
(no JSON key in the repo), then:
- builds the web app + functions;
firebase-tools deploy --only hosting,functions,firestore:rules,database;- builds + pushes the feed-engine image and
gcloud run deploys it (always-on, single instance, internal ingress).
The split — firebase-tools for the Firebase surfaces, gcloud for the
long-running Cloud Run deploy — keeps the container build off the firebase-tools ADC timeout path.
The workflow is gated by vars.CANCRI_DEPLOY_ENABLED == 'true'. It stays inert until you've set up
WIF + the deployer service account and flip that variable on — exactly the runway below.
Going-live runway
None of this is needed to run locally (scripts/quickstart.sh). It's the
one-time provisioning to arm the CI deploy. The canonical checklist is
SETUP.md §4; the shape of it:
1 · Firebase project irreversible region
Create a Blaze-plan project
Spark blocks the outbound egress to ls-tc.de / Yahoo / Vertex, and Cloud Run / Scheduler / Secret Manager need Blaze.
Firestore + RTDB in
europe-west1ADR-0001. Firestore's location is permanent — get it right before any data exists. RTDB is offered only in
us-central1,europe-west1,asia-southeast1— noteurope-west3.Enable Auth providers
Email/Password + Google; configure the consent screen and authorized domains. Decide your invite-allowlist policy — this is an access-gated product.
2 · Gemini via Vertex AI no API key
- Enable the Vertex AI API; grant the Functions runtime SA
roles/aiplatform.user. - Confirm
gemini-3.5-flashavailability/quota ineurope-west1. - Set
CANCRI_USE_VERTEX=truefor the deployed functions. There is no Gemini API key — access is via IAM.
3 · Workload Identity Federation keyless deploy
deploy.yml authenticates via WIF — no stored JSON key. One-time setup:
WIF pool + provider
With an attribute-condition pinning
assertion.repository == 't11z/cancri'.A dedicated deployer service account
Granted
roles/iam.workloadIdentityUserfor your repo'sprincipalSet, plus least-privilege deploy roles (firebasehosting.admin,cloudfunctions.admin,run.admin,artifactregistry.writer, …).An Artifact Registry Docker repo
Named
cancriin your region.Enable the APIs
Cloud Run, Cloud Build, Artifact Registry, Secret Manager, Cloud Scheduler.
-
Set repo Variables, then arm it
Set
GCP_PROJECT_ID,GCP_REGION(defaulteurope-west1),WIF_PROVIDER,DEPLOYER_SA— and finally:arm the CI deploygh variable set CANCRI_DEPLOY_ENABLED --body true
4 · The remaining live fills flagged in code
- Secret Manager entries for the L&S handshake config (
LS_cid, magic/idle/polling params, origin) — never in the repo. - The first L&S capture (the self-heal Job, during trading hours) fills
packages/ls-protocol/protocol.config.v1with the real handshake bytes / frame offsets — exactly what the replay gate verifies. - A Playwright Dockerfile for
services/selfheal, thengcloud run jobs deploy. - A logo provider behind
resolveLogo's fetcher (monogram-only until then). - Branch-protect
mainto require the replay-regression check + ≥1 human review for self-heal PRs.
Deploy the docs site
This documentation site is hand-authored static HTML/CSS in site/ — no build step. It ships to
GitHub Pages via its own workflow,
.github/workflows/pages.yml,
on any push to main that touches site/.
-
Turn Pages on (one time)
Repo Settings → Pages → Build and deployment → Source = “GitHub Actions.”
-
Push a change under
site/The workflow uploads the folder and deploys it. The site lives at
https://t11z.github.io/cancri/— all links are relative, so it works under the/cancri/subpath. -
Preview locally (optional)
Any static server works, e.g.:
bash · preview the docscd site && python3 -m http.server 8080 # → http://localhost:8080