# Forgejo Tier 3 — State Hub `remote_url` and Sweep Playbook Date: 2026-07-04 Workplans: `RAIL-HO-WP-0005-T10`, `RAIL-HO-WP-0005-T11`, `CUST-WP-0054-T04`, `CUST-WP-0054-T05` `no_secret_material_recorded: true` ## Purpose Operational playbook for promoting a **registered** State Hub repo from Gitea (`gitea-remote`) to Forgejo (`forgejo-remote`) without breaking the consistency sweep, DoI checks, or agent attribution. Applies to: - **Tier 2.5** (done on Forgejo, hub `remote_url` may still say Gitea) — run §4 after git promotion. - **Tier 3** (`state-hub`, `issue-core`, production wave) — full §3–§8 with operator approval. Does **not** perform cutover by itself. Gitea remains canonical until tier gates and explicit approval per `RAIL-HO-WP-0005` safety contract. ## Preconditions (gates) | Gate | Tier 2.5 | Tier 3 (`state-hub`) | | --- | --- | --- | | Forgejo repo exists + history mirrored | required | required | | Workstation `origin=forgejo-remote`, `gitea` legacy remote | required | required | | Operator SSH on Forgejo (`tegwick` or team keys) | required | required | | CI smoke green on Forgejo (`.forgejo/workflows/ci-smoke.yaml`) | required | required | | Image workflow if applicable | optional | required for `state-hub` | | Restore drill passed | recommended | required | | Scheduled backups automated | optional | required (disaster-control) | | Operator approval recorded | not required | **required** | | `CUST-WP-0011-T07` freeze/restore for hub primary | n/a | required before hub production | ## Canonical remote conventions State Hub stores the **SSH remote name form** used on the operator workstation, not the HTTPS URL. This matches existing registrations (`gitea-remote:coulomb/.git`). | Role | Remote name | Example | | --- | --- | --- | | Canonical (post-promotion) | `origin` → `forgejo-remote` | `forgejo-remote:coulomb/state-hub.git` | | Rollback mirror | `gitea` → `gitea-remote` | `gitea-remote:coulomb/state-hub.git` | | Hub `remote_url` field | same as `origin` | `forgejo-remote:coulomb/state-hub.git` | HTTPS canonical URL for humans and archive CI: `https://forgejo.coulomb.social/coulomb/.git` SSH config block (workstation `~/.ssh/config`): ```ssh Host forgejo-remote HostName 92.205.62.239 Port 30022 User git IdentityFile ~/.ssh/id_gitea StrictHostKeyChecking accept-new ``` ## Per-repo promotion procedure ### 1. Git forge (workstation) Use `railiance-enablement/tools/promote-repo-to-forgejo.sh` or manually: ```bash cd ~/REPO_SLUG # Create empty repo on Forgejo if needed (org coulomb) git push forgejo-remote:coulomb/REPO_SLUG.git main # first mirror git remote rename origin gitea git remote add origin forgejo-remote:coulomb/REPO_SLUG.git git push -u origin main # Optional: keep Gitea mirror current git push gitea main ``` Add `.forgejo/workflows/ci-smoke.yaml` from `railiance-enablement/workflows/ci-smoke.yaml`. For images, use `container-build-push.yaml` or `container-build-push-multirepo.yaml` (state-hub + hub-core). ### 2. Verify git + SSH ```bash git remote -v # origin forgejo-remote:coulomb/REPO_SLUG.git (fetch/push) # gitea gitea-remote:coulomb/REPO_SLUG.git (fetch/push) ssh forgejo-remote # expect: Hi there, tegwick! git ls-remote origin # reachable ``` ### 3. Patch State Hub registration ```bash API=http://127.0.0.1:8000 SLUG=REPO_SLUG NEW_URL="forgejo-remote:coulomb/${SLUG}.git" curl -s -X PATCH "${API}/repos/${SLUG}" \ -H "Content-Type: application/json" \ -d "{\"remote_url\": \"${NEW_URL}\"}" | python3 -m json.tool # Confirm curl -s "${API}/repos/${SLUG}" | python3 -c \ "import json,sys; r=json.load(sys.stdin); print(r['slug'], r['remote_url'])" ``` Batch helper for tier-2.5 stack (skips slugs not registered in State Hub): ```bash ~/the-custodian/tools/patch-forgejo-remote-urls.sh --tier-25 ``` Applied 2026-07-04 for registered tier-2.5 repos + `key-cape`. `glas-harness` is not in State Hub registration — patch when/if registered. ### 4. Consistency sweep / fix-consistency The sweep (`POST /consistency/sweep/remote-all` or `statehub fix-consistency`) resolves repos by `host_paths[hostname]` or `local_path`, then `git pull --ff-only` from the checkout's **tracking branch** (now Forgejo `origin`). After `remote_url` patch: ```bash cd ~/REPO_SLUG git fetch origin statehub fix-consistency --repo REPO_SLUG # or from state-hub checkout: cd ~/state-hub && make fix-consistency REPO=REPO_SLUG ``` **Attribution:** `consistency_check.py` matches repos by git fingerprint and `remote_url` string. Stale `gitea-remote` in the hub causes mis-attribution when multiple checkouts exist — patch hub **before** relying on sweep writebacks. ### 5. DoI verification ```bash curl -s "http://127.0.0.1:8000/repos/REPO_SLUG/doi" | python3 -m json.tool # C4 Remote URL set → pass # C4 reachability → git ls-remote origin succeeds ``` ## Railiance01 sweep checkout paths (`CUST-WP-0054-T05`) Today sweeps write back from **workstation** `host_paths` (`bnt-lap001`). Wave 2 moves primary checkouts to **railiance01** so triage survives workstation loss. When railiance01 clone tree exists (e.g. `/home/tegwick/` or agreed path): ```bash API=http://127.0.0.1:8000 SLUG=REPO_SLUG HOST=railiance01 # must match socket.gethostname() on that machine PATH_ON_HOST=/home/tegwick/REPO_SLUG curl -s -X POST "${API}/repos/${SLUG}/paths" \ -H "Content-Type: application/json" \ -d "{\"host\": \"${HOST}\", \"path\": \"${PATH_ON_HOST}\"}" | python3 -m json.tool ``` Clone on railiance01 **from Forgejo**: ```bash git clone forgejo-remote:coulomb/REPO_SLUG.git /home/tegwick/REPO_SLUG # or HTTPS with deploy key / token ``` Sweep on railiance01 then uses `host_paths[railiance01]`; workstation path remains in map for dev but is no longer the production writeback source. ## Tier 3 — `state-hub` specifics Before promoting `state-hub`: 1. **Multi-repo image workflow** — copy `railiance-enablement/workflows/container-build-push-multirepo.yaml` to `state-hub/.forgejo/workflows/image.yaml`; set `IMAGE_NAME=coulomb/state-hub`, `EXTRA_REPOS="coulomb/hub-core@hub_core_src"`. 2. **Prove image pull** on railiance01 (`crictl pull` or deployment dry-run). 3. **Hub primary cutover** — follow `CUST-WP-0011-T07` (freeze → restore → rewire); independent of forge remote but same maintenance window may apply. 4. **ArgoCD / GitOps** — repoint repository URLs to Forgejo (Wave 5 in `docs/coulombcore-drain-placement-plan.md`). 5. **Record approval** — `POST /decisions/` or workplan note with operator id. `state-hub` promotion order: ``` mirror git → Forgejo remotes → CI image workflow green → PATCH remote_url → fix-consistency → (later) railiance01 host_paths → CUST-WP-0011-T07 cutover ``` ## Gitea read-only policy (post-cutover per repo) After Forgejo is canonical **for that repo** and hub `remote_url` is patched: 1. Do **not** delete the Gitea repo (safety contract). 2. Org/repo setting: disable push for non-admin users, or maintenance flag. 3. Workstation: stop pushing to `gitea` except intentional rollback mirror. 4. Document rollback: `git push gitea main` from last known-good Forgejo SHA. Org-wide Gitea read-only is an operator action (T11); per-repo is enough for staged ladder. ## Rollback | Step | Action | | --- | --- | | Git canonical | `git remote rename origin forgejo; git remote rename gitea origin` | | Hub | `PATCH /repos/{slug}` → `gitea-remote:coulomb/.git` | | Sweep | `statehub fix-consistency` on reverted checkout | | Gitea | Re-enable push if read-only was set | ## Verification checklist - [ ] `GET /repos/{slug}` → `remote_url` is `forgejo-remote:coulomb/.git` - [ ] Workstation `git remote -v` → `origin` is Forgejo - [ ] `ssh forgejo-remote` → operator user (not only `forgejo_admin`) - [ ] `statehub fix-consistency --repo ` → PASS - [ ] Forgejo Actions `ci-smoke` → success (tier 1+) - [ ] Gitea copy exists and matches SHA (optional `git push gitea` mirror) - [ ] Tier 3 only: image workflow + pull smoke + operator approval on record ## References - `docs/forgejo-repo-migration-pilot-glas-harness.md` (tiers 1–2.5) - `railiance-enablement/docs/forgejo-actions-workflow-templates.md` - `railiance-enablement/tools/promote-repo-to-forgejo.sh` - `state-hub/docs/consistency-sweep-runbook.md` - `state-hub/policies/repo-doi.md` (C4 remote URL) - `docs/coulombcore-drain-placement-plan.md` (Wave 1–2) - `disaster-control/history/2026-07-04-forgejo-backup-strategy-assessment.md`