railiance-infra/docs/s1-handoff.md
codex 40e295e3bd
All checks were successful
CI Smoke / source-contract (push) Successful in 8s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Make S1 handoff read-only by default
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
2026-08-23 12:41:23 +02:00

1.9 KiB

S1 Handoff Gate

make s1-handoff is the operator-facing gate from host substrate (S1) to the cluster layer (S2). It validates inventory and baseline contracts, requires a clean checkout, runs the applicable Goss profile for every selected host, and emits a metadata-only JSON receipt under reports/.

A passing receipt pins:

  • source revision and inventory digest
  • every selected host and its baseline profile
  • observed time and 24-hour default freshness boundary
  • per-host exit status
  • SHA-256 digests of the resulting TAP evidence

Any failed host fails the aggregate. A passing receipt without host evidence is invalid. make s1-handoff-dry-run validates local inputs but records status: not-run; it cannot authorize S2 handoff.

The current profiles are:

  • Railiance01: ufw-managed
  • CoulombCore: external-firewall, which verifies the declared iptables INPUT default-drop replacement control

The gate is read-only on managed hosts. Before connecting, scripts/handoff_contract.py proves that its playbook contains only remote stat, assert, and the fixed Goss validation command, and pins the Goss assertion commands themselves to a reviewed read-only set. It renders each selected profile locally and fails if the installed /etc/goss/baseline.yaml digest differs, instead of refreshing it implicitly. TAP and the aggregate receipt are written only beneath the controller's ignored reports/ path.

If a surface is stale, the operator must review it and separately approve make verify-refresh-host; that change cannot be smuggled through a handoff run. The handoff command does not run Terraform, change provider resources, rotate credentials, converge a role, write remote files, or change services.

Receipts validate with:

python3 scripts/s1_receipt.py reports/s1-handoff-*.json

S2 should accept only status: pass receipts whose source and inventory match the intended handoff and whose fresh_until has not elapsed.