forge: stop the Garage mirror job from turning every forge deploy red #36
No reviewers
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
key-store/key.store!36
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/garage-replicate-hetzner-only"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
deploy-forge-hostruns report FAILURE (switch-to-configurationrc=4) even though the forge lands on the new generation. Run 17013 (2026-08-16) shows the mechanism end to end:key-store-garage-replicate.serviceexits 1 by design after the Hetzner leg because the GCS archival leg can't run:/run/agenix/gcpServiceAccountexists but is 0 bytes.secrets/gcp/service-account.agehas had an empty payload since the lossy rekey in4f53e0f2(2026-01-19) — the forge is a recipient and agenix decrypts it fine; the script's-stest then reports it "missing". Founder decision 2026-07-25: Hetzner-only for now.${self}, so the unit is "modified" on every deploy. 17013 landed while the 05:28 hourly pass was still copying, so s-t-c SIGTERMed the in-flight rclone copy, started it again inline, blocked activation for the full 3.5-minute pass, and then reported the designed exit 1 as a failed unit → rc=4.(Pre-existing failed units do not cause rc=4 — s-t-c calls
ResetFailedbeforedaemon-reload; only units that fail inside the switch window count. So the failure needs the deploy to overlap a mirror pass, or any other timer job that fails during the window.)What
nixos/keystore-forge.nix: declare the decision asgarageGcsArchiveEnabled = false→KEYSTORE_GCS_ARCHIVE_OPTIONAL=1on the unit. Hetzner leg unchanged; a non-empty credential in the secret re-enables the GCS leg on the next tick with no config change; flip the flag once provisioned so a missing credential fails the unit again.nixos/keystore-forge.nix:restartIfChanged = falseon the unit — a timer-driven batch job should never be pulled into the activation path.Scripts/forge-blob-replicate.sh: in optional mode, log the single-mirror state at journal WARNING priority (<4>prefix whenJOURNAL_STREAMis set) every run and exit 0. Default mode unchanged (still exits 1). Documented the env var.secrets/README.md: status note + the KSE-0182 direction (WIF external-account file, not a new service-account key).Verification (on the forge, no activation)
nixos-rebuild dry-activate --flake .#keystore-forgeon main+this patch: builds in 78 s (cached), rc=0.Environment="KEYSTORE_GCS_ARCHIVE_OPTIONAL=1"andX-RestartIfChanged=false; ExecStart resolves to the patched script.switch-to-configuration dry-activatenow printswould NOT stop the following changed units: key-store-garage-replicate.service(previously: stop + inline start).<4>prefix; optional + non-empty credential → GCS leg runs).Not done here (needs a decision / separate work)
.agefiles insecrets/also carry empty payloads from the same4f53e0f2rekey (e.g.identity/forge-ssh,stalwart/dkim/staging-2025,roles/jmap-bearer,nexmail/encryption-key,grafana/planetscale-*,finances/*,records/*). Detected by size = 102 + 110·recipients. Worth an audit.redmine-dispatcher-smoke.servicefails every 2 h run (mail leg, known); it can also poison a deploy if the switch overlaps its ~1m42s run.🤖 Generated with Claude Code
36a3ab6218to69f8599e41