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
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 |
|
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 convergeforansible/playbooks/bootstrap.yaml(capabilities/playbooks/railiance-infra.bootstrap-host.yaml:16-19). make convergeiscd ansible && ansible-playbook $(PLAY) $(ANSIBLE_USER_FLAG)withPLAY := ansible/playbooks/bootstrap.yaml(Makefile:284,Makefile:361-362);converge-host/converge-tags/ansible-bootstrapare variants (Makefile:193-194,Makefile:364-370).SSH_USERis 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 bymake deploy-stack). ansible/ansible.cfg:7usesstdout_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.