railiance-infra/workplans/RAIL-HO-WP-0015-s1-execution-attribution-receipt-owner-answer.md
codex babae80c82
All checks were successful
CI Smoke / source-contract (push) Successful in 14s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
RAIL-HO-WP-0015-T01: owner answer for the S1 execution-attribution receipt (NK-WP-0040-T02)
No S1 runner exists: operator runs ansible-playbook through make converge.
Records the future converge-wrapper emitter, the proposed audit-core sender
name, per-field fillability, and open items. Emission not implemented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 308701@bnt-lap001
Assistant-Session: 5548e447-444c-4dcc-bed1-05c16eebfe56
2026-10-28 09:56:57 +01:00

7.9 KiB

id type title domain repo status flavor owner topic_slug created updated related_workplans
RAIL-HO-WP-0015 workplan S1 playbook runner: owner answer for the execution-attribution receipt financials railiance-infra active decision railiance-infra railiance 2026-10-28 2026-10-28
NK-WP-0040
RPF-WP-0067
AUDIT-WP-0014
RAIL-HO-WP-0013

Owner record for the net-kingdom request (State Hub message 848ef70a-992e-4834-8d8a-a5ba3c67f96f, NK-WP-0040-T02) to name the component that emits the "Execution Attribution Receipt (proposed)" of net-kingdom/canon/standards/playbook-capability-contract_v0.2.md (lines 292-337) for the reference declaration capabilities/playbooks/railiance-infra.bootstrap-host.yaml. Answered from source only. Emission is not implemented here.

T01 — Answer the four owner questions from source

id: RAIL-HO-WP-0015-T01
status: done
priority: high

(1) What runs S1 playbooks today. There is no runner component. S1 playbooks are run by a human operator calling ansible-playbook directly through Make targets:

  • The declaration names invocation: make converge for ansible/playbooks/bootstrap.yaml (capabilities/playbooks/railiance-infra.bootstrap-host.yaml:16-19).
  • make converge is cd ansible && ansible-playbook $(PLAY) $(ANSIBLE_USER_FLAG) with PLAY := ansible/playbooks/bootstrap.yaml (Makefile:284, Makefile:361-362); converge-host / converge-tags / ansible-bootstrap are variants (Makefile:193-194, Makefile:364-370). SSH_USER is optional; the remote user otherwise comes from inventory (Makefile:285-288).
  • The documented live path is the operator on the host: ssh tegwick@92.205.62.239 '... ansible-playbook playbooks/bootstrap.yaml -c local --become -l Railiance01' (Makefile:234, printed by make deploy-stack).
  • ansible/ansible.cfg:7 uses stdout_callback = default; there is no callback plugin, wrapper or service that records a run.

Started by: the founder/operator (tegwick) from a shell, on the workstation or on Railiance01. Not a cluster workload, CI job or agent service. The only existing receipt-producing wrapper is scripts/s1_handoff.py (make s1-handoff, Makefile:84-85), and it wraps the read-only verify.yaml, not the converge playbook (scripts/s1_handoff.py:133-139). It writes a local JSON file under reports/ (scripts/s1_handoff.py:153-159) and sends nothing to audit-core.

Owner decision: the emitter will be a railiance-infra converge wrapper modelled on scripts/s1_handoff.py (Python, invoked by make converge in place of the bare ansible-playbook call). It does not exist yet.

(2) Emission at every terminal outcome keyed by run_id. Not today. make converge emits nothing. s1_handoff.py emits only on its normal path: it returns 2 without a receipt on a dirty checkout (scripts/s1_handoff.py:119-123) or a missing ansible-playbook (:129-131), and an inventory/spec validation exception exits without a receipt (:111-117). Owner commitment for the future wrapper: one run_id (UUID4, as s1_handoff.py:63) generated before anything else, and a receipt emitted from a finally path for every terminal outcome: succeeded, failed (non-zero ansible exit), refused (pre-flight refusal, e.g. dirty tree) and interrupted (signal/exception). A receipt that cannot be delivered is kept locally under reports/ and retried with the same run_id as Idempotency-Key. This is a commitment, gated on the receipt schema (NK-WP-0040-T02) and the sender credential below.

(3) audit-core sender identity. Proposed sender name: railiance-infra-s1-runner (envelope source = that name, per the pattern in railiance-platform/workplans/RPF-WP-0067-*.md:49-67). No sender exists (RPF-WP-0067:66-67); the credential is a CCR to railiance-platform, not done here. Open, needs founder/railiance-platform: the documented delivery is a per-sender bearer projected by ESO into a Kubernetes Secret. The S1 runner runs in an operator shell, not a pod, so that projection does not reach it. How an operator-run process gets that bearer (for example a short-lived fetch from OpenBao by the operator, or sending through an in-cluster relay) is not decided in source.

(4) Per-field fillability today (for a wrapper built from current source; "no" fields must be recorded as unknown, or omitted where the contract says so, never inferred):

Field Truthful today? Basis / gap
initiator.iss / initiator.sub No Operator starts the run from a shell; no IAM Profile token is presented or checked anywhere on the path (Makefile:361-362, :234).
delegation No The runner cannot tell whether an agent invoked make for the operator. The contract allows "absent" only when there was no delegation (playbook-capability-contract_v0.2.md:315), which the runner cannot assert. Needs an unknown value in the schema (NK).
executing_workload No No workload binding: the run is a shell process, not a Tenancy Posture workload.
runtime_principal Partly The local OS user and the remote SSH user / become root can be read at run time. These are OS accounts, not IAM principals. A warden SSH cert principal, if used, is not visible to the wrapper.
tenant No inventory/servers.yaml has no tenant field. S1 acts for the platform; the value to record needs an NK convention.
environment No inventory/servers.yaml has no environment field (hosts carry baseline_profile only, :9, :15).
run_id Yes UUID4 per run (pattern scripts/s1_handoff.py:63).
source_commit Yes git rev-parse HEAD (Makefile:9, s1_handoff.py:119), but only truthful with the clean-tree refusal (s1_handoff.py:120-123); make converge has no such check today.
artifact_digest No No built artifact. A digest of the playbook + roles tree and inventory_sha256 (s1_handoff.py:68) is possible, but the Galaxy collections are unpinned (ansible/requirements.yaml:2-4), so the executed code is not fully pinned. What the digest covers for an Ansible tree needs an NK definition.
target Yes Inventory host names from --limit / inventory plus inventory SHA-256 (s1_handoff.py:38-46, :68-70).
action Yes Playbook path + tags/limit, e.g. ansible/playbooks/bootstrap.yaml / converge.
decision_refs No make converge has no approval gate. The gated targets use approval tokens (APPROVE_VERIFY_REFRESH, Makefile:352-356; tf-apply "exact approval", :101), not decision record ids.
time_interval.start / .end Yes Controller UTC wall clock around the ansible-playbook call (s1_handoff.py:34-35, :125).
time_interval.clock_source Partly Can name "controller system clock"; its sync state is not verified by the runner.
time_interval.bound No (omit) No bound established: railiance_clock_enabled: false by default (ansible/roles/railiance_clock/defaults/main.yaml:3) and RAIL-HO-WP-0013 is blocked. The max_error_ns default (:16) is a policy target, not a measured bound.

Side observation (not investigated further): bootstrap.yaml:14 names a swapfile role that is neither under ansible/roles/ nor in ansible/requirements.yaml, so make converge as declared may not run cleanly. Should be checked before any wrapper work.

T02 — Build the converge wrapper and emit receipts

id: RAIL-HO-WP-0015-T02
status: wait
priority: medium

Waits on: the receipt schema and validator in net-kingdom canon/schemas/ (NK-WP-0040-T02), including an unknown value for delegation and definitions for tenant, environment and artifact_digest for S1; the railiance-infra-s1-runner sender in the audit-core registry plus a decided bearer delivery path for an operator-run process (railiance-platform CCR, founder decision). Wrapper scope: replace the bare ansible-playbook call in make converge* with the wrapper. Do not start before these are settled.