Document State Hub PATCH procedure, sweep implications, railiance01 host_paths, state-hub image specifics, and rollback. Include batch patch helper; applied tier-2.5 remote_url updates in hub DB.
238 lines
8.4 KiB
Markdown
238 lines
8.4 KiB
Markdown
# 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/<slug>.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/<slug>.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/<slug>` 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/<slug>.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/<slug>.git`
|
||
- [ ] Workstation `git remote -v` → `origin` is Forgejo
|
||
- [ ] `ssh forgejo-remote` → operator user (not only `forgejo_admin`)
|
||
- [ ] `statehub fix-consistency --repo <slug>` → 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` |