#!/usr/bin/env python3 """repoint.py mirror|drill|restore|status โ€” point a box (scratch 9202; copied from audits/burndown-3-2026-10-17/tools/ for the D39 security lane, 2026-10-23) at the drill catalog WITHOUT a credential (R-992). WHY A LOCAL MIRROR. `09` ยง6.5's route needs a catalog credential on the box: the drill repo is private (R-992). This session could not mint a READ-ONLY one โ€” the Gitea credential on DooPlex is itself a token, and Gitea answers 403 to a token asking to list or create tokens (measured 2026-10-14) โ€” and the night of 2026-10-06 put the admin WRITE token into the box's controller.yaml, which this task forbids. The cache-staging workaround of 2026-10-12 does not hold for a test that lasts days: every sync (and every controller start) re-applies the live catalog over the staged templates and over the installed apps' compose files (Syncer.copyTemplates, no deployed check). So the drill catalog is MIRRORED onto the box: a bare git repository inside the controller's own data volume, and `git.repo_url: file:///opt/docker/felhom-controller/drill-catalog.git`. The controller's sync runs exactly as for a remote (clone/fetch/reset, the R-615 re-clone on a changed URL); no secret is on the box. DooPlex updates the mirror from the drill repo with a git bundle (`mirror`). The drill repo on Gitea stays the record. SC=~/.felhom-tester1 TARGET=tester-1 python3 repoint.py mirror # (re)load the mirror from the drill repo main ... repoint.py drill # save controller.yaml (once), set repo_url to the mirror, restart, sync, rescan ... repoint.py restore # put the saved controller.yaml back (byte-identical), restart, sync, rescan ... repoint.py status """ import os, subprocess, sys, time sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts") import box_walk as w DRILL = "/mnt/5_hdd/felhom.eu/drill/app-catalog-drill" VOL = "/var/lib/docker/volumes/felhom-controller-data/_data" # guest path of the controller's data volume CVOL = "/opt/docker/felhom-controller" # the same, inside the controller container SAVE = f"{VOL}/controller.yaml.pre-seclane1023" MIRROR_URL = f"file://{CVOL}/drill-catalog.git" def status(): print(w.guest(f"""grep -nE 'repo_url|sync_interval' {VOL}/controller.yaml; ls -l {SAVE} 2>&1 | tail -1 docker exec felhom-controller sh -c 'git -C {CVOL}/drill-catalog.git log -1 --format="mirror main %h %s" main 2>&1; git -C {CVOL}/data/catalog-cache log -1 --format="cache HEAD %h %s" 2>&1; git -C {CVOL}/data/catalog-cache remote get-url origin'""")) def mirror(): b = "/tmp/drill-seclane1023.bundle" subprocess.run(["git", "-C", DRILL, "bundle", "create", b, "main"], check=True, capture_output=True) head = subprocess.run(["git", "-C", DRILL, "rev-parse", "--short", "main"], capture_output=True, text=True).stdout.strip() ssh = w.ssh_args() # bundle -> PVE host -> guest -> controller volume; the bare repo is made and fetched BY the controller's own user subprocess.run(["scp", "-q", "-o", "StrictHostKeyChecking=accept-new"] + (["-J", w._T["jump"]] if w._T["jump"] else []) + [b, f"{w._T['ssh']}:{b}"], check=True) subprocess.run(ssh + [f"pct push {w.GUEST} {b} {VOL}/drill-seclane1023.bundle && rm -f {b}"], check=True) print(w.guest(f"""set -e docker exec felhom-controller sh -c 'test -d {CVOL}/drill-catalog.git || git init -q --bare -b main {CVOL}/drill-catalog.git git -C {CVOL}/drill-catalog.git fetch -q {CVOL}/drill-seclane1023.bundle +main:main && rm -f {CVOL}/drill-seclane1023.bundle git -C {CVOL}/drill-catalog.git log -1 --format="mirror main %h %s" main'""")) print("drill repo main:", head) os.remove(b) def restart_sync(): w.guest("docker restart felhom-controller >/dev/null") for _ in range(60): time.sleep(3) try: w.login() break except SystemExit: continue w.sync_rescan() time.sleep(3) print(w.guest("docker logs --since 3m felhom-controller 2>&1 | grep -E '\\[sync\\]' | grep -v DEBUG | tail -8")) def drill(): print(w.guest(f"""set -e test -f {SAVE} || cp -p {VOL}/controller.yaml {SAVE} sed -i -E 's#^(\\s+repo_url: ).*$#\\1{MIRROR_URL}#' {VOL}/controller.yaml grep -n 'repo_url' {VOL}/controller.yaml; diff {SAVE} {VOL}/controller.yaml || true""")) restart_sync() status() def restore(): print(w.guest(f"""set -e cp -p {SAVE} {VOL}/controller.yaml cmp {SAVE} {VOL}/controller.yaml && echo RESTORED-IDENTICAL; grep -n repo_url {VOL}/controller.yaml""")) restart_sync() status() {"mirror": mirror, "drill": drill, "restore": restore, "status": status}[sys.argv[1]]()