GENERATED VIEW · PRIVATE ARCHIVE · DO NOT SERVE
RobCo Industries · Archive Museum

deploy_branch_model.md



MEMORY

memory/project-C--Dev--RobCo--RobCo-UOS/deploy_branch_model.md

sha256 b168b96e8812fce7 · 3597 bytes · original held in the private archive

--- name: deploy-branch-model description: "Branch model + staging — work on dev, main is release-only; Cloudflare staging and its setup gotchas" metadata: node_type: memory type: project originSessionId: 31cdf095-f3aa-4bf2-9933-aa1072ac47b1 --- As of 2026-06-29 the repo uses a two-branch deploy model, replacing the old "every push to main deploys live": - **`dev`** = working/unreleased branch. ALL day-to-day work goes here. Cloudflare Pages builds `dev` → a private staging site (`robco-uos-dev.pages.dev`), gated by Cloudflare Access to the owner's Google login only. Different origin = fully isolated PWA storage / service worker / cache from prod, which is the whole point of the split. - **`main`** = release-only. Merged from `dev` only when cutting a release (with the `APP_VERSION` bump). GitHub Pages production (`zerckzzyhd.github.io/Robco-UOS/`) deploys from main. **Default working branch is `dev` — start new work there, not `main`.** Every gate and protocol applies identically on `dev`. This is codified in CLAUDE.md + `rules/`; read the current protocol text there rather than trusting a number quoted here. **Release gating:** production deploy is triggered by an actual version release, not by a bare push to `main` — the deploy workflow is `workflow_call`-only (plus manual `workflow_dispatch` for rollback), invoked by the release workflow only when a new version tag was just created from an `APP_VERSION` bump. Defense-in-depth is deliberate: the deploy job keeps its own success + `main` guard, and `main` is branch-protected. See the workflow files and `CHANGELOG.md` + git log for current state. *Correction 2026-07-20: a production version claim was removed from this file. `APP_VERSION` in source is the only answer to "what is prod on".* **Adding a NEW serving origin to this app requires THREE Google allowlist edits, not just one:** (1) Firebase Auth → Authorized domains; (2) the Firebase **browser API key**'s HTTP-referrer restriction in Google Cloud Console → Credentials — missing this gives `auth/requests-from-referer-…-are-blocked` and **silently breaks ALL auth**; (3) the App Check **reCAPTCHA v3** key's domain list — only matters when App Check is enforced rather than monitoring-only. Forgetting (2) is the one that costs an afternoon. **Cloudflare Access setup gotcha:** the self-hosted-app Public-hostname form has separate Subdomain + Domain fields. Putting the project name in Subdomain AND the full hostname in Domain doubled the destination (`robco-uos-dev.robco-uos-dev.pages.dev`) and the gate silently didn't intercept. Fix = leave **Subdomain EMPTY**, Domain = the full hostname. Access login method is **Google**, chosen for biometric/passkey sign-in: a dedicated Google OAuth client was created in the `nv-overlord` Google Cloud project, added as a Cloudflare Zero Trust IdP, with the app set to Google-only + "Apply instant authentication" ON so it redirects straight to Google. One-time PIN is no longer used. The allow policy is a single-email allowlist — the address is `<owner email>`; the real value lives in the Cloudflare Access policy itself and is not recorded here. **Branch-protection incident:** a force-reset of `origin/main` was blocked by branch protection (`allow_force_pushes:false`). It was completed by temporarily toggling the setting via API and restoring it immediately afterward. If you ever need this again: verify protection is fully restored before finishing, and confirm any commits you're about to strand are already ancestors of `origin/dev`. Related: [[project-state]].
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).