17 KiB
homelab → konairius/homelab, master → main — Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Move the Gitea repo k.renner/homelab into the konairius org and rename its default branch master → main, with no lost Flux sync, pipeline, cron, secret or review.
Architecture: Two separate cut-overs, each with its own window, prep PR and rollback: Cut-over 1 moves the repo (path changes, branch stays master); Cut-over 2 renames the branch (path stays). Everything that can land beforehand lands beforehand; each cut-over's own PR is merged inside its window. The pattern is the one proven on vsftpd, notes, renovate-config, authentik-samba-bridge and homeassistant on 2026-10-18.
Tech Stack: Gitea 1.27 (API, transfer, repoRenameBranch), Woodpecker v3.19.0 (move API, crons, org secrets), Flux (GitRepository flux-system, ImageUpdateAutomation homelab), secret-rotator, OpenBao.
Spec: this conversation's decisions, recorded in the wiki raw notes of 2026-10-18 (konairius migration series). No separate spec doc.
Global Constraints
- All repos live in
konairius(exception:k.renner/turm1388stays). - The default branch of every repo is
main. - Gitea old paths redirect (301) for reads; Gitea API writes through an old path fail (wiki on
k.renner/notes, 2026-10-18). Every writer must address the new path right after the transfer. - A transferred repo keeps Woodpecker's old org link until
POST /api/repos/{id}/move?to=<owner>/<repo>(homelab: Woodpecker repo id1). Org secrets follow the org link. - Gitea's
RenameBranchrenames, in one transaction: the branch, the default branch, a protection rule named exactlymaster, and the base/head of unmerged PRs (models/git/branch.go). It does not keep amasterref; anything fetchingmasterfails afterwards. - Renaming the default branch needs repo admin (owner or site admin), not
claude-bot. - Gitea's transfer removes collaborators who are members of the new org (their access then comes only from their konairius team) and keeps non-members (
services/repository/transfer.go). woodpecker repo repaircopies the forge's default branch into Woodpecker (repairRepo→Repo.UpdatesetsBranch).- Checked live 2026-10-18: Woodpecker repo id
1; cron idsalert-monitor4,ci-claude-refresh7,daily-security-scan2,issue-fix5,update-audit3,weekly-update1,wiki-maintenance8; thek.rennerorg holds onlyclaude_code_oauth_tokenandreview_bot_token, every other pipeline secret is a repo secret of homelab. - Gitea API:
PATCH /repos/{owner}/{repo}/branches/{branch}with{"name": "<new>"}(repoRenameBranch), present on our Gitea. - Each service reads only its own OpenBao path; no cross-service key sharing.
- Never force-push; PRs for every repo change; never
2>/dev/null.
Review Focus
- Flux fetch across the transfer. Flux's GitRepository fetches
k.renner/homelab.gituntil the URL change lands. Documented behaviour says this works: source-controller v1.9.5 uses go-git v5.19.1 (itsgo.mod), whoseRedirectPolicy"mirror[s] Git's http.followRedirects config" with the zero valueinitial(pkg.go.dev); git documentsinitialas following the redirect on the first request and using "the redirected URL as the base for the follow-up requests" (git-config,http.followRedirects). Gitea's 301 stays on the same host and scheme, so fetch and the IUA push both reachkonairius/homelab. Fallback only, should that fail: Task 3 has a declared break-glass (suspend theflux-systemKustomization so it cannot re-apply the old URL, patch the GitRepository URL to the value the PR sets, resume once the source is Ready on the merge commit), checked byflux get source git flux-system. - Write access lost in the transfer. Any writer that is a konairius org member loses its collaborator grant and keeps only its team permission (
fluxpushes for image automation; claude-bot, issue-fix-bot, security-bot, review-bot, argus-bot, renovate-bot write PRs/reviews). Expect: Task 3 Step 0 snapshot of collaborators and themasterprotection, compared after Step 1 (memberships confirmed fine by the owner). - Image automation push between merge and rename (Cut-over 2). The IUA pushes to
master; after the Task 5 PR it expectsmain. Expect: IUA suspended for the whole window and resumed only after Flux reportsmain. - Org secrets after the Woodpecker move. Homelab pipelines use the org secrets
claude_code_oauth_tokenandreview_bot_token; after the move they resolve inkonairius. Expect: both exist inkonairiusbefore Cut-over 1 (Task 1 verification). - Crons on a vanished branch. All 7 crons run on
master. Expect: each switched tomainin place (woodpecker repo cron update --branch main) inside the Cut-over 2 window, verified bycron ls. - Rotations that verify against the old path. flux-git (
GITEA_REQUIRE_PUSH=true, verify repok.renner/homelab) would fail every quarter. Expect: verify repo switched in Task 2 and a manual rotator run in Task 3.
Task 1: Org secrets in konairius before the move
Files:
- Modify:
kubernetes/infrastructure/openbao/rotation/claude-oauth.yaml(add a second containersync-woodpecker-konairius, same env withWOODPECKER_ORG=konairius) - Modify:
kubernetes/infrastructure/openbao/rotation/review-bot.yaml(same: a second sync container forkonairius,WOODPECKER_SECRET_NAME=review_bot_token, eventscron,manual,pull_request)
Interfaces: Produces the konairius org secrets claude_code_oauth_token and review_bot_token that Task 3 relies on. The k.renner containers stay until Task 4.
- Step 1: Add the containers. Both CronJobs reuse their existing OpenBao role (it only reads OpenBao and the woodpecker-bot bootstrap; no policy change).
- Step 2:
kubectl kustomize kubernetes/infrastructure/openbao/rotationrenders. - Step 3: PR, merge,
flux reconcile kustomization infrastructure. - Step 4: Run both once:
kubectl create job -n openbao --from=cronjob/claude-oauth-secret-sync claude-oauth-konairiusand--from=cronjob/review-bot-secret-rotate review-bot-konairius. Expected logs:updated|created woodpecker org secret konairius/<name>. Note: the review-bot run also executes its init containersrotate-mintandrotate-mint-mcp, i.e. mints and reaps both review-bot PATs off schedule. Keeping the two newest makes one extra run safe; afterwards checkkubectl get externalsecret -n review-bot review-bot-secretsis SecretSynced before anything else rotates. - Step 5:
woodpecker org secret ls --organization konairiuslistsclaude_code_oauth_token,review_bot_token,registry_bot_token;woodpecker org secret ls --organization k.rennerstill lists onlyclaude_code_oauth_tokenandreview_bot_token(nothing else to carry over).
Task 2: PR "follow homelab to konairius/homelab" (prepared, merged in Task 3)
Files (all FUNCTIONAL k.renner/homelab references, 2026-10-18 inventory):
-
kubernetes/clusters/homelab/flux-system/gotk-sync.yaml:14— GitRepository url →https://gitea.renner.uno/konairius/homelab.git -
kubernetes/infrastructure/openbao/rotation/flux-git.yaml:81—GITEA_VERIFY_REPO→konairius/homelab(and the comment at :84) -
kubernetes/apps/review-bot/deployment.yaml:38—REPOSentry →konairius/homelab -
.woodpecker/alert-monitor.yml:133,241,ci-claude-refresh.yml:177,179,200,issue-fix.yml:85,166,190,199,pr-review.yml:46,security-scan.yml:150,160,update-audit.yml:114— API, clone and push URLs -
Header comments with
--repo k.renner/homelab/pipeline deploy k.renner/homelabin the same files ande2e.yml:10 -
review-bot/go.mod:1→module gitea.renner.uno/konairius/homelab/review-bot(no Go file imports it) and the test fixtures inreview-bot/*_test.go(consistency only) -
ci/images/heating-can/Containerfile:66— OCI source label -
Step 1: Apply the replacements; leave
docs/,PROPOSAL*andMIGRATION*history as is. -
Step 2:
git grep -n 'k.renner/homelab' -- ':!docs'returns only historical files (PROPOSAL*,MIGRATION*). -
Step 3:
go test -C review-bot ./...passes;kubectl kustomize kubernetes/clusters/homelaband the touched app directories render. -
Step 4: Open the PR titled "(merge inside Cut-over 1 only)"; do not merge.
Task 3: Cut-over 1 — transfer (window)
Preconditions: Task 1 verified; Task 2 PR green; no deploy pipeline running (woodpecker pipeline ls k.renner/homelab --limit 5); record flux get source git flux-system revision.
- Step 0 (claude-bot): snapshot
tea api --login claude-bot /repos/k.renner/homelab/collaborators(2026-10-19: security-bot, claude-bot, alert-monitor-bot, issue-fix-bot, update-audit-bot, review-bot, flux, renovate-bot, hermes-bot) and/repos/k.renner/homelab/branches/master(protected: true,required_approvals: 1). konairius memberships confirmed fine by the owner; whitelists need admin and are not snapshotted. - Step 1 (owner):
tea api --login renner.uno -X POST /repos/k.renner/homelab/transfer -f new_owner=konairius. Expectcurl -sI https://gitea.renner.uno/k.renner/homelab→ 301. - Step 2 (claude-bot): Woodpecker move:
POST $WOODPECKER_SERVER/api/repos/1/move?to=konairius/homelab→ 204;woodpecker repo lsshowskonairius/homelab. - Step 3 (owner): merge the Task 2 PR.
- Step 1a (claude-bot): repeat the Step 0 reads against
konairius/homelaband compare; a missing writer is re-added by the owner before continuing. - Step 4:
flux reconcile source git flux-system. If it fails to fetch: break-glass —flux suspend kustomization flux-system(else it re-applies the last good artifact with the old URL), thenkubectl -n flux-system patch gitrepository flux-system --type merge -p '{"spec":{"url":"https://gitea.renner.uno/konairius/homelab.git"}}'(the value the PR sets), wait for the source to be Ready on the Task 2 merge commit, thenflux resume kustomization flux-system. Expect: revision = Task 2 merge commit, Ready. - Step 5:
flux reconcile image update homelabafter the next ImagePolicy change, or check the last IUA push reacheskonairius/homelab(commit by Flux Image Automation on master). - Step 6: Run flux-git rotator once (
kubectl create job -n openbao --from=cronjob/flux-git-secret-rotate flux-git-move); log showspull+push verifiedagainstkonairius/homelab. - Step 6a: Expected side effects of the Task 2 merge, not failures:
build-ci-imagesrebuilds the review-bot image (go.mod, tests) and heating-can (label), and image automation rolls out the new review-bot tag;ci-claude-refreshruns (its own file is in its path filter) and may open a ci-claude bump PR. - Step 7: Verify writers: a PR on konairius/homelab gets a review-bot review;
woodpecker pipeline ls konairius/homelabshows the merge's push pipeline green; an agent cron (alert-monitor, every 15 min) succeeds;security-scan/issue-fixopen PRs against the new path at their next run. - Step 8: Check the GitHub push mirror (
konairius/Homelabon GitHub, restore-test source) still syncs (Gitea repo Settings → Mirror, last sync after the transfer).
Rollback: owner transfers back to k.renner, Woodpecker move back to k.renner/homelab, revert the Task 2 PR.
Task 4: After Cut-over 1
- Remove the
k.rennersync containers from Task 1 (nothing in thek.rennerWoodpecker org uses them any more; turm1388 uses repo secrets only). PR, reconcile. - Workstation: homelab clone remote →
https://gitea.renner.uno/konairius/homelab.git;~/source/CLAUDE.md:13row →konairius/homelab. - Wiki deposit of the observed Cut-over 1 results.
Task 5: PR "default branch main" (prepared, merged in Task 6)
Files (all FUNCTIONAL master references):
-
.woodpecker/build-ci-images.yml:39,ci-claude-refresh.yml:71,deploy-claude-workstation.yml:4,deploy-edge-mqtt-bridge.yml:4,deploy-infrastructure.yml:7,deploy-netbird-client.yml:4,deploy-netbird-proxy.yml:4,deploy-netbird-server.yml:4,security-scan.yml:72—branch: main -
.woodpecker/ci-claude-refresh.yml:211,issue-fix.yml:202,security-scan.yml:155— PRbase: "main" -
kubernetes/clusters/homelab/flux-system/gotk-sync.yaml:11—ref.branch: main -
kubernetes/infrastructure/image-automation/update-automation.yaml:26,36— checkout and push branchmain -
kubernetes/restore-test/flux-root.yaml:16,kubernetes/vmtest-job.yaml:90—main -
kubernetes/bootstrap.sh:107defaultmain,:1478-1479guard onmain,:102example -
Not the prompts:
review-bot/prompt.mdis baked into the review-bot image, whichbuild-ci-imagesonly rebuilds on a push to the branch itswhen:names. In this PR that filter flips tomainwhile the PR merges intomaster, so the prompt edit would not ship. It moves to Task 6 Step 9. -
Step 1: Apply;
git grep -n -w master -- ':!docs' ':!ansible'returns only historical/runbook text (handled in Task 7), the two prompts, and unrelated words (VRRP, Open3E, Multus"master"). -
Step 2:
bash -n kubernetes/bootstrap.sh;kubectl kustomize kubernetes/clusters/homelabrenders. -
Step 3: Open the PR titled "(merge inside Cut-over 2 only)"; do not merge.
Task 6: Cut-over 2 — rename master → main (window)
Preconditions: Cut-over 1 done; Task 5 PR green; no deploy pipeline running.
- Step 1:
flux suspend image update homelab. - Step 2 (owner): merge the Task 5 PR into
master. Flux now points atmain, which does not exist yet; the cluster keeps its last applied state. - Step 3 (owner, immediately):
tea api --login renner.uno -X PATCH /repos/konairius/homelab/branches/master -f name=main. Expect: default branchmain, protection rule now namedmain(GET /repos/konairius/homelab/branches/main→protected: true, required_approvals: 1), open PRs based onmain. - Step 4: the live GitRepository still has
ref.branch: masterand cannot fetch the commit that changes it (couldn't find remote ref "refs/heads/master").flux suspend kustomization flux-system,kubectl -n flux-system patch gitrepository flux-system --type merge -p '{"spec":{"ref":{"branch":"main"}}}',flux resume kustomization flux-system→ Ready onmain;flux get kustomizationsall Ready. - Step 5: Crons: for each of
alert-monitor(4),ci-claude-refresh(7),daily-security-scan(2),issue-fix(5),update-audit(3),weekly-update(1),uptime-kuma-token-renew(10):PATCH $WOODPECKER_SERVER/api/repos/1/cron/<id>with{"branch":"main"}(woodpecker-cli 3.18.0repo cron updatereturns 404 against server 3.19.0). Deletewiki-maintenance(8): no pipeline filters on it (git grep wiki-maintenance -- .woodpeckeris empty), it starts nothing.cron lsshows seven crons, all onmain. - Step 6:
woodpecker repo repair konairius/homelab;woodpecker repo showshows branchmain. - Step 7:
flux resume image update homelab; next IUA commit lands onmain. - Step 8: Verify: a small PR into
maingets reviewed and its merge triggers the push pipelines; the deploy pipelines'when: branch: mainfire. - Step 9: PR into
mainupdatingreview-bot/prompt.md:16andci/agents/pr-review/mistral-prompt.md:16(all repos usemain). Its merge is a push tomaintouchingreview-bot/**, sobuild-ci-imagesrebuilds the review-bot image; image automation rolls it out; review-bot's log shows the Mistral agents re-synced (new prompt hash). This PR doubles as the Step 8 test PR. - Step 10 (owner, GitHub): the push mirror
konairius/Homelabon GitHub now carriesmain. GitHub refuses to delete a repo's default branch, so first set its default branch tomain(repo Settings → Branches), then deletemasterthere.kubernetes/restore-test/flux-root.yamlalready points atmain(Task 5).
Rollback: PATCH .../branches/main -f name=master, revert the Task 5 PR, crons back to master (re-add wiki-maintenance only if wanted), resume IUA.
Task 7: Doc and memory sweep
- One PR replacing remaining
k.renner/homelabandmasterin runbooks and READMEs:kubernetes/RUNBOOK-cold-rebuild.md(incl.flux bootstrap --branch=masterat :282),RUNBOOK-home-assistant-image-rollback.md,kubernetes/infrastructure/image-automation/README.md,README.md, pipeline comments,ci/agents/pr-review/run.sh. Leave dated plans/specs andMIGRATION*history unchanged. - Update the two memory notes that assume
master(feedback_image_bumps_via_flux_automation.md, vicanverify-repo-freshness-before-review.md). - Local clones:
git branch -m master main && git fetch origin && git branch -u origin/main main(owner's workstation). - Wiki deposit of Cut-over 2 results.