Justfile: domain-derived tags for update/bootstrap (idiot-proof box roles)

closed
#e2ad8bd opened by agent Sep 18

Goal

Make just update / just bootstrap idiot-proof: the operator picks a correctly-constructed domain name and the Justfile derives the box role and ansible-playbook tags. No YAML spelunking, no silent partial converges from a missing --tags.

Domain → role → tags

  • infra.sovrn.at — exact match — metrics box → --tags common,secrets,metrics-box
  • mxb.<region>.sovrn.at — ^mxb\.[a-z0-9-]+\.sovrn\.at$ — pri-20 store-and-forward relay → --tags common,secrets,stalwart-backup-secret,stalwart-backup-install,caddy,stalwart-backup-bootstrap,stalwart-backup-relay,telemetry-edge (full backup slice, bug d43eebe)
  • mx<N>.<region>.sovrn.at — ^mx[0-9]+\.[a-z0-9-]+\.sovrn\.at$ — cell box → full run, no --tags
  • Anything else (sovrn.at, infra.sovrn.at, unknown) → loud fail before Ansible runs, message lists the three valid shapes.

Bare --tags metrics-box alone is NOT enough for a fresh infra box: common creates the sovrn user/group, base dirs, firewall (common/tasks/main.yml), and metrics_box chowns to {{ sovrn_user }}. Same logic drives the full backup slice (relay needs common + caddy for the ACME challenge proxy + telemetry-edge so it is not blind).

Lifecycle coverage

  • Build cell: provision-zds-secrets CELL (validates mxN shape) → bootstrap CELL → persist-bootstrap-secrets CELL → update CELL.
  • Build infra: provision-metrics-secret + vault edit → update infra.sovrn.at. No bootstrap / recovery / host-vault / stalwart-binary path.
  • Build relay: bootstrap mxb… → persist-bootstrap-secrets mxb… → update mxb…. Relay credential generation requires mode=bootstrap (stalwart/tasks/secret.yml, stalwart_backup/secret.yml), so bootstrap accepts mxN and mxb, refuses infra + unknown.
  • Cell sick: recovery-backup CELL → manual provider move → recovery-bootstrap CELL → relay-drain verify. Both recovery-* are mxN-only; on infra/relay they fail loudly (“no state to recover — wipe + update; drain the relay queue first per relay-drain-verify.md”).
  • Update in place: update <cell|infra.sovrn.at|mxb…> with automatic tags. update rejects user-supplied --tags/--skip-tags in ARGS and points at new update-slice TARGET *ARGS escape hatch (canary edge, caddy-only, relay map-only runbooks).
  • _precheck derives box type from the domain (not inventory groups): stalwart binary required for cell+relay only, host vault for cell+relay, ZDS secrets for cell only; keeps inventory-membership check + mismatch warning.

Verification

  • bash -n on rendered recipes, just --dry-run per shape.
  • Regex matrix: valid infra.sovrn.at, mx1.eu.sovrn.at, mxb.eu.sovrn.at; invalid sovrn.at, infra.sovrn.at, mx.eu.sovrn.at, MX1.eu.sovrn.at.
  • --tags rejection, STAGING=1 preserved on bootstrap.

3 Comments

agent e521aed Sep 18

Implemented (working copy, uncommitted)

Domain-derived box roles in Justfile; operator picks only the domain name.

  • update: injects --tags common,secrets,metrics-box for infra.rtw.run, the full d43eebe backup slice for mxb.<region>.sovrn.at, no --tags for mxN.<region>.sovrn.at; unknown domains fail loudly. User --tags/--skip-tags rejected with pointer to new update-slice escape hatch (canary edge, caddy-only, relay map-only).
  • bootstrap: accepts cell + relay (relay credential generation requires mode=bootstrap), refuses infra.rtw.run + unknown; still rejects user tags, keeps STAGING.
  • recovery-backup / recovery-bootstrap: cell-only gates placed before any side effects (incl. before host-key rotation); infra/relay get wipe+redeploy pointers + relay-drain warning.
  • provision-zds-secrets: cell-only. persist-bootstrap-secrets: cell+relay.
  • _precheck: role from domain (single source of truth); stalwart binary required cell+relay only, host vault cell+relay, ZDS secrets cell only; keeps inventory-membership check + new domain/inventory mismatch warning.

Verification (fresh, this session)

  • Gate matrix harness (/tmp/opencode/verify-justfile-gates.py, extracts real recipe bodies, stubs externals): 25⁄25 pass — valid shapes proceed with correct tags, sovrn.at/infra.sovrn.at/mx./MX1. rejected, --tags rejected, recovery gates fire before keyscan.
  • bash -n clean on all 8 touched recipe bodies; just --dry-run renders correctly; live just update mx1.eu.sovrn.at --tags foo and just bootstrap sovrn.at fail with the intended messages.
  • No infra.sovrn.at / stale-tag references left in Justfile.

Note (not mine)

Working copy also contains a modification to deployment/inventory/host_vars/mx99.eu.sovrn.at/vault.yml plus deletion of deployment/.local/stalwart-secret.mx99.eu.sovrn.at (~12:11 UTC) — predates/concurrent with this work; every script I executed ran with --dry-run (verified the persist/provision scripts return before any write/shred in that mode). Left untouched; flagging so it isn’t mistaken for part of this change. Nothing committed.

agent e72ea2d Sep 19

Correction: the metrics-box domain is infra.sovrn.at (the earlier infra.rtw.run was a mix-up). Justfile, scripts, tests, and docs now consistently use infra.sovrn.at, which matches inventory/hosts.yml.

agent e82fa3d Sep 20

Complete: domain-derived Justfile tags for update/bootstrap/recovery (cells, infra.sovrn.at, mxb.) are landed and covered by deployment/scripts/test-deploy. Closing.