railiance-infra/workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md
codex c8cb1c8edf
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Add reproducible S1 handoff workplan
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
2026-08-23 11:21:45 +02:00

8.9 KiB

id type title domain repo status owner topic_slug created updated related
RAIL-HO-WP-0011 workplan Make the S1 declaration reproducible and the handoff verifiably green financials railiance-infra ready codex railiance 2026-08-23 2026-08-23
RAIL-HO-WP-0002
RAIL-HO-WP-0009

RAIL-HO-WP-0011 — reproducible S1 declaration and handoff

Goal

Close the highest-leverage gaps identified in history/2026-08-23-scope-against-intent.md: distinguish adopted resources from provider-managed resources, make the host baseline contract executable, produce a green per-host handoff gate, and make every declared secret input fail closed. Then add the evidence and rotation mechanics needed for an auditable provisioning path.

This workplan improves the source and validation path. It does not authorize a Terraform apply or destroy, a live firewall change, a credential rotation, or any other live-host mutation. Those actions require their normal reviewed plan and operator approval.

Delivery Order

The critical path is T01 → T02 and T03 → T04 → T05. T06 can proceed independently. T07 depends on T02 and T05. T08 depends on T06.

T01 — Define and validate adopted-versus-managed inventory

id: RAIL-HO-WP-0011-T01
status: todo
priority: high

Define one explicit inventory contract for common host identity and provider-specific lifecycle fields. At minimum it must distinguish:

  • provider (hosteurope, hetzner, or a documented extension value)
  • lifecycle mode (adopted versus provider-managed)
  • connection identity used by Ansible
  • provider-specific provisioning fields used by Terraform

Add a side-effect-free validator and fixtures for the two current adopted Host Europe hosts, a valid provisionable Hetzner host, and invalid mixed records. Preserve stable host names and connection behavior.

Done when: the current inventory validates as adopted Host Europe resources, a complete Hetzner fixture validates, incomplete or contradictory records fail with actionable errors, and no provider command is needed to run the validation.

T02 — Make Hetzner planning select only managed Hetzner resources

id: RAIL-HO-WP-0011-T02
status: todo
priority: high

Update the Terraform and helper path to consume the T01 contract and exclude adopted or non-Hetzner records. Audit any existing Terraform state before changing resource addresses or selection logic. Correct provisioning docs and Make targets so they name the provider-token source actually used.

Add an isolated plan test or equivalent deterministic validation that proves the current Host Europe records cannot become Hetzner creates or destroys.

Done when: current adopted records pass through the inventory-to-Terraform boundary without missing-field errors or managed resources, a provisionable Hetzner fixture produces the expected resource shape, and a reviewed plan shows no unintended create, replace, or destroy. Do not apply the plan in this task.

T03 — Model host-specific baseline profiles and reconcile declared state

id: RAIL-HO-WP-0011-T03
status: todo
priority: high

Replace the permanent CoulombCore expected failure with an explicit baseline profile or declared exception. Reconcile spec/server-baseline.yaml, Ansible, and the intended live posture for packages, users/sudo, SSH, firewall, fail2ban, SOPS tooling, swap, and resource limits.

Exceptions must say which control replaces the default, why it is accepted, and who owns removing it. An unmanaged control must not silently count as verified.

Done when: both current hosts resolve to complete declared profiles, the UFW-managed and externally-filtered postures have explicit assertions, and the spec no longer claims properties that convergence neither establishes nor deliberately delegates.

T04 — Add automated baseline contract-parity tests

id: RAIL-HO-WP-0011-T04
status: todo
priority: high

Add repository tests that fail when the human baseline, convergence roles, and Goss assertions diverge on governed properties. Prefer a single machine-readable model or generated artifacts where that reduces duplicated declarations; otherwise implement explicit parity checks with clear failure messages.

Cover the profiles introduced by T03 and run the tests in Forgejo CI without requiring host access or secrets.

Done when: a deliberate mismatch in a governed package, SSH setting, firewall rule, service, or user property fails locally and in CI, while all declared profiles pass from a clean checkout.

T05 — Provide a fresh green S1 handoff gate

id: RAIL-HO-WP-0011-T05
status: todo
priority: high

Provide one operator-facing, non-ambiguous command that checks inventory and contract validity, runs the applicable profile for every selected host, collects evidence, and exits non-zero if any required control is failed, unknown, stale, or skipped without an accepted declaration.

The receipt must identify the source revision, inventory digest, host/profile, check time, and result without including credentials or sensitive host output. Define freshness and the exact interface S2 can consume.

Done when: both current hosts can return green against their declared profiles in an attended verification run, a failing or stale host makes the aggregate gate fail, and the evidence is sufficient for an S2 handoff without interpreting an expected-red exception.

T06 — Repair and enforce the secret-source contract

id: RAIL-HO-WP-0011-T06
status: todo
priority: high

Remove, relocate, or SOPS-encrypt the plaintext inventory/group_vars/secrets.sops.yaml placeholder. Inventory every path that documentation or automation describes as secret-bearing, extend the pre-commit/CI check to those paths, and make playbooks fail clearly when an expected encrypted input is absent or malformed.

Align README, provisioning, convergence, Make targets, and playbooks on the actual Hetzner token source and on the fact that the host role installs tools but does not distribute an age private key.

Done when: plaintext fixtures at any declared secret path are rejected, the committed tree contains no file falsely presented as encrypted input, and the documented controller and host secret flows match the executable paths.

T07 — Emit non-secret provisioning and handoff receipts

id: RAIL-HO-WP-0011-T07
status: todo
priority: medium

Define a versioned receipt linking inventory and source revisions, provider plan/apply identity, provider resource ID, cloud-init completion, Ansible convergence, and the T05 handoff result. Separate plan, apply, and verification events so a plan receipt cannot be mistaken for proof of a live change.

Exercise the format with a dry-run or synthetic fixture first. A live provider apply remains separately approved and is not required merely to prove schema and redaction behavior.

Done when: the receipt schema validates, sensitive fields and provider tokens cannot be serialized, incomplete phases fail closed, and a synthetic end-to-end example is consumable without prose interpretation.

T08 — Automate bounded SOPS recipient rotation

id: RAIL-HO-WP-0011-T08
status: todo
priority: medium

Build a dry-run-first rotation workflow for the repository's SOPS files. It must inventory affected files and recipients, detect recipient drift, require a reviewed change set, verify decryption through an approved non-printing check, and emit a metadata-only receipt. Never print a decrypted value or private age key.

Live recipient removal or credential replacement requires explicit operator approval after the dry run. Scheduling may be proposed only after rollback and recovery-key custody are documented.

Done when: CI can detect recipient drift without secrets, an attended dry run identifies the exact files and before/after recipient set, rollback is documented, and a sample receipt proves verification without exposing values.

Acceptance

  • Adopted Host Europe records and provider-managed Hetzner records cannot be confused by validation or Terraform selection.
  • The declared baseline, Ansible convergence, and Goss verification have an automated parity contract.
  • Every current host can produce a fresh green result against an explicit profile, and the aggregate handoff fails closed.
  • Every documented secret-bearing path is encrypted or deliberately absent and protected by local and CI checks.
  • Provisioning/handoff and rotation paths emit metadata-only receipts with reviewed redaction behavior.
  • No live provider, firewall, credential, or host mutation occurs without a separately reviewed plan and the required operator approval.

Completion Evidence

Record the final inventory schema and examples, Terraform plan safety result, baseline parity tests, CI run, attended all-host gate receipt, secret-path negative tests, provisioning receipt fixture, and SOPS rotation dry-run receipt here before marking the workplan finished.