Add reproducible S1 handoff workplan
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
This commit is contained in:
codex 2026-08-23 11:21:45 +02:00
parent 3734a1c3bc
commit c8cb1c8edf
2 changed files with 243 additions and 0 deletions

View file

@ -14,6 +14,7 @@
| workplan | RAIL-HO-WP-0008 | finished | — | workplans/RAIL-HO-WP-0008-railiance01-resource-and-commercial-evidence.md |
| workplan | RAIL-HO-WP-0009 | finished | — | workplans/RAIL-HO-WP-0009-firewall-declared-state-and-api-exposure.md |
| workplan | RAIL-HO-WP-0010 | finished | — | workplans/RAIL-HO-WP-0010-new-reef-ports-need-a-grant.md |
| workplan | RAIL-HO-WP-0011 | ready | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |
| task | ADHOC-2026-08-22-T01 | done | — | workplans/ADHOC-2026-08-22.md |
| task | ADHOC-2026-08-22-T02 | done | — | workplans/ADHOC-2026-08-22.md |
| task | ADHOC-2026-08-22-T03 | done | — | workplans/ADHOC-2026-08-22.md |
@ -40,3 +41,11 @@
| task | RAIL-HO-WP-0009-T05 | done | — | workplans/RAIL-HO-WP-0009-firewall-declared-state-and-api-exposure.md |
| task | RAIL-HO-WP-0009-T06 | done | — | workplans/RAIL-HO-WP-0009-firewall-declared-state-and-api-exposure.md |
| task | RAIL-HO-WP-0010-T01 | done | — | workplans/RAIL-HO-WP-0010-new-reef-ports-need-a-grant.md |
| task | RAIL-HO-WP-0011-T01 | todo | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |
| task | RAIL-HO-WP-0011-T02 | todo | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |
| task | RAIL-HO-WP-0011-T03 | todo | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |
| task | RAIL-HO-WP-0011-T04 | todo | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |
| task | RAIL-HO-WP-0011-T05 | todo | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |
| task | RAIL-HO-WP-0011-T06 | todo | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |
| task | RAIL-HO-WP-0011-T07 | todo | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |
| task | RAIL-HO-WP-0011-T08 | todo | — | workplans/RAIL-HO-WP-0011-reproducible-s1-declaration-and-handoff.md |

View file

@ -0,0 +1,234 @@
---
id: RAIL-HO-WP-0011
type: workplan
title: "Make the S1 declaration reproducible and the handoff verifiably green"
domain: financials
repo: railiance-infra
status: ready
owner: codex
topic_slug: railiance
created: "2026-08-23"
updated: "2026-08-23"
related:
- 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
```task
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
```task
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
```task
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
```task
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
```task
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
```task
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
```task
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
```task
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.