RPF-WP-0015-T02: contained-login only, single silent attended window

Harness used the retracted public OpenBao URL and a token file; lane used a
standing token file. Both now run only inside the contained ops-warden login
(tunnel, helper token), and t02-attended-window.py runs project, exercise and
cleanup under one login without printing (warden fails closed on child output).

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 680842@bnt-lap001
Assistant-Session: 54eb9e8e-7d6b-4f0c-b7a6-a6db62075883
This commit is contained in:
codex 2026-10-14 14:23:46 +02:00
parent badb91c71a
commit 311aa86eef
10 changed files with 773 additions and 109 deletions

View file

@ -2,7 +2,7 @@
# Custodian Brief — railiance-platform
**Domain:** financials
**Last synced:** 2026-10-11 21:24 UTC
**Last synced:** 2026-10-14 10:58 UTC
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
## Active Workstreams
@ -25,14 +25,14 @@ Progress: 4/6 done | workplan_id: `b2c25a01-4a80-55c1-90cf-8538000f7e0e`
- ! T06 — Publish the Railiance/OpenBao custody handoff `3b9748c4`
*(wait: Owner work first: the OpenBao owner confirms mount/path/fields and writer/reader authority and publishes the non-secret routing receipt; the handoff enters the attended window only when a command exists.)*
### Onboard rapp-policy-nexus to the ArgoCD production lane
Progress: 3/5 done | workplan_id: `ec41a4bd-df18-5b07-9b63-ccb80d9f001c`
### Admit the Playwright pilot and establish scoped consumer group access
Progress: 4/6 done | workplan_id: `31933d08-c877-5fda-aff9-6220c3705c92`
**Open tasks:**
- ! Adopt policy-nexus (live, founder go-ahead) `8638fa73`
*(wait: Merge approved by D2; runs once T03 diff and root automation (RPF-WP-0044-T07) are proven.)*
- ! Prepare the Application and the dry-run evidence `2afb1aac`
*(wait: Steps 1 and 3 done 2026-10-11 (docs/evidence/2026-10-11-policy-nexus-adoption-readonly-precheck.json, no stop condition). Step 2 render diff needs the railiance-apps values path from RAPP-POLICY-NEXUS-WP-0002-T02; agents can run it read-only via the k3s tunnel.)*
- ! Connect platform identities and prove managed consumer operation `a82762eb`
*(wait: User admitted all identified platform humans and coding agents; their supported Kubernetes authentication/group-mapping route remains unimplemented.)*
- ! Obtain Custodian coordination of self-service consumer delivery `ef6c21bc`
*(wait: Await Custodian coordination acceptance, accountable coordinator and source task links for the existing cross-repository delivery.)*
### ArgoCD phase B: adopt the four existing Applications on railiance01
Progress: 5/8 done | workplan_id: `98140775-3b9a-5cf9-9af6-722502d487dc`
@ -44,11 +44,21 @@ Progress: 5/8 done | workplan_id: `98140775-3b9a-5cf9-9af6-722502d487dc`
- ! Turn the root back to the production lane `844ff115`
*(wait: Restore prune/selfHeal once T03-T06 have automated proof; no further founder go-ahead (D2).)*
### Onboard rapp-policy-nexus to the ArgoCD production lane
Progress: 3/5 done | workplan_id: `ec41a4bd-df18-5b07-9b63-ccb80d9f001c`
**Open tasks:**
- ! Adopt policy-nexus (live, founder go-ahead) `8638fa73`
*(wait: Merge approved by D2; runs once T03 diff and root automation (RPF-WP-0044-T07) are proven.)*
- ! Prepare the Application and the dry-run evidence `2afb1aac`
*(wait: Steps 1 and 3 done 2026-10-11 (docs/evidence/2026-10-11-policy-nexus-adoption-readonly-precheck.json, no stop condition). Step 2 render diff needs the railiance-apps values path from RAPP-POLICY-NEXUS-WP-0002-T02; agents can run it read-only via the k3s tunnel.)*
### Close Forgejo primary backup coverage on Scaleway
Progress: 3/4 done | workplan_id: `7beec1a7-aa82-5a36-9a66-6b60008a2455`
**Open tasks:**
- · Establish primary full-archive delivery and application recovery `a4807df0`
- ! Establish primary full-archive delivery and application recovery `a4807df0`
*(wait: Durable scheduled caller, verified inventory and quota/retention gates remain open; September 15 prohibits expiration and scheduled secondary cutover.)*
### Close S3 service assurance and ownership gaps
Progress: 4/7 done | workplan_id: `ca639c3d-3a87-5fa4-ad13-6f2e014b0c84`

View file

@ -526,41 +526,60 @@ audit-database-lease-approval-check: ## Validate an approved T02 receipt without
python3 scripts/audit-core-database-lease-recovery.py validate-approval \
--approval "$(AUDIT_DATABASE_LEASE_APPROVAL)"
audit-database-lease-recovery-exercise: ## Run the separately approved attended T02 exercise
@test -n "$(AUDIT_DATABASE_LEASE_APPROVAL)" || { echo 'set AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json' >&2; exit 2; }
@test -n "$(AUDIT_DATABASE_LEASE_DRIVER)" || { echo 'set AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver' >&2; exit 2; }
python3 scripts/audit-core-database-lease-recovery.py exercise \
--approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" \
--load-driver "$(AUDIT_DATABASE_LEASE_DRIVER)" \
ATTENDED_EXEC = python3 scripts/openbao-attended-exec.py --
AUDIT_T02_NEED_APPROVAL = @test -n "$(AUDIT_DATABASE_LEASE_APPROVAL)" || { echo 'set AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json' >&2; exit 2; }
AUDIT_T02_NEED_DRIVER = @test -n "$(AUDIT_DATABASE_LEASE_DRIVER)" || { echo 'set AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver' >&2; exit 2; }
AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM = @test -n "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)" || { echo 'set AUDIT_SYNTHETIC_SENDER_CONFIRM to the confirm string printed by preflight' >&2; exit 2; }
AUDIT_T02_NEED_BEARER = @test -n "$(AUDIT_T02_TOKEN_FILE)" || { echo 'set AUDIT_T02_TOKEN_FILE=/abs/path/outside/git (0700 dir)' >&2; exit 2; }
audit-t02-attended-window: ## One contained login: project, port-forward, T02 exercise, evidence, cleanup
$(AUDIT_T02_NEED_APPROVAL)
$(AUDIT_T02_NEED_DRIVER)
$(AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
$(AUDIT_T02_NEED_BEARER)
@test -n "$(AUDIT_T02_EVIDENCE_DIR)" || { echo 'set AUDIT_T02_EVIDENCE_DIR=/abs/dir/outside/git (0700)' >&2; exit 2; }
$(ATTENDED_EXEC) scripts/t02-attended-window.py \
--approval "$(abspath $(AUDIT_DATABASE_LEASE_APPROVAL))" \
--load-driver "$(abspath $(AUDIT_DATABASE_LEASE_DRIVER))" \
--bearer-file "$(AUDIT_T02_TOKEN_FILE)" --evidence-dir "$(AUDIT_T02_EVIDENCE_DIR)" \
--confirm "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)" $(if $(AUDIT_T02_PORT),--port $(AUDIT_T02_PORT))
audit-database-lease-recovery-exercise: ## Exercise step alone, inside its own contained login (prefer audit-t02-attended-window)
$(AUDIT_T02_NEED_APPROVAL)
$(AUDIT_T02_NEED_DRIVER)
@test -n "$(AUDIT_T02_RESULT_FILE)" || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >&2; exit 2; }
$(ATTENDED_EXEC) scripts/audit-core-database-lease-recovery.py exercise --contained --result-file "$(AUDIT_T02_RESULT_FILE)" \
--approval "$(abspath $(AUDIT_DATABASE_LEASE_APPROVAL))" \
--load-driver "$(abspath $(AUDIT_DATABASE_LEASE_DRIVER))" \
--confirm RAILIANCE-WP-0024-T02:attended
AUDIT_SYNTHETIC_SENDER_LANE = python3 scripts/audit_core_synthetic_sender_lane.py
AUDIT_SYNTHETIC_SENDER_NEED_APPROVAL = @test -n "$(AUDIT_DATABASE_LEASE_APPROVAL)" || { echo 'set AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json' >&2; exit 2; }
AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM = @test -n "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)" || { echo 'set AUDIT_SYNTHETIC_SENDER_CONFIRM to the confirm string printed by preflight' >&2; exit 2; }
audit-synthetic-sender-test: ## Unit tests for the synthetic sender lane and T02 window (no network)
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_audit_core_synthetic_sender_lane tests.test_t02_attended_window
audit-synthetic-sender-test: ## Unit tests for the synthetic sender lane (no network)
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_audit_core_synthetic_sender_lane
audit-synthetic-sender-preflight: ## Read-only gate for the T02 synthetic sender (no registry values read)
$(AUDIT_SYNTHETIC_SENDER_NEED_APPROVAL)
$(AUDIT_SYNTHETIC_SENDER_LANE) preflight --approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" \
audit-synthetic-sender-preflight: ## Read-only T02 sender gate; cluster checks only (registry metadata needs --contained)
$(AUDIT_T02_NEED_APPROVAL)
python3 scripts/audit_core_synthetic_sender_lane.py preflight --approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" \
$(if $(AUDIT_T02_TOKEN_FILE),--bearer-file "$(AUDIT_T02_TOKEN_FILE)") $(AUDIT_SYNTHETIC_SENDER_PREFLIGHT_ARGS)
audit-synthetic-sender-project: ## Attended: add the one T02 identity, write bearer file, restart audit-core
$(AUDIT_SYNTHETIC_SENDER_NEED_APPROVAL)
audit-synthetic-sender-project: ## Projection step alone, inside its own contained login (prefer audit-t02-attended-window)
$(AUDIT_T02_NEED_APPROVAL)
$(AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
@test -n "$(AUDIT_T02_TOKEN_FILE)" || { echo 'set AUDIT_T02_TOKEN_FILE=/abs/path/outside/git (0700 dir)' >&2; exit 2; }
$(AUDIT_SYNTHETIC_SENDER_LANE) project --approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" \
$(AUDIT_T02_NEED_BEARER)
@test -n "$(AUDIT_T02_RESULT_FILE)" || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >&2; exit 2; }
$(ATTENDED_EXEC) scripts/audit_core_synthetic_sender_lane.py project --contained --result-file "$(AUDIT_T02_RESULT_FILE)" \
--approval "$(abspath $(AUDIT_DATABASE_LEASE_APPROVAL))" \
--bearer-file "$(AUDIT_T02_TOKEN_FILE)" --confirm "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)"
audit-synthetic-sender-cleanup: ## Attended, idempotent: remove the T02 identity after evidence, restart audit-core
$(AUDIT_SYNTHETIC_SENDER_NEED_APPROVAL)
audit-synthetic-sender-cleanup: ## Idempotent cleanup inside a contained login (evidence or abort reason required)
$(AUDIT_T02_NEED_APPROVAL)
$(AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
@test -n "$(AUDIT_T02_EVIDENCE)$(AUDIT_SYNTHETIC_SENDER_ABORT_REASON)" || { echo 'set AUDIT_T02_EVIDENCE=/path/to/exercise-result.json or AUDIT_SYNTHETIC_SENDER_ABORT_REASON=...' >&2; exit 2; }
$(AUDIT_SYNTHETIC_SENDER_LANE) cleanup --approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" \
@test -n "$(AUDIT_T02_RESULT_FILE)" || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >&2; exit 2; }
$(ATTENDED_EXEC) scripts/audit_core_synthetic_sender_lane.py cleanup --contained --result-file "$(AUDIT_T02_RESULT_FILE)" \
--approval "$(abspath $(AUDIT_DATABASE_LEASE_APPROVAL))" \
--confirm "$(AUDIT_SYNTHETIC_SENDER_CONFIRM)" \
$(if $(AUDIT_T02_TOKEN_FILE),--bearer-file "$(AUDIT_T02_TOKEN_FILE)") \
$(if $(AUDIT_T02_EVIDENCE),--exercise-evidence "$(AUDIT_T02_EVIDENCE)") \
$(if $(AUDIT_T02_EVIDENCE),--exercise-evidence "$(abspath $(AUDIT_T02_EVIDENCE))") \
$(if $(AUDIT_SYNTHETIC_SENDER_ABORT_REASON),--abort-reason "$(AUDIT_SYNTHETIC_SENDER_ABORT_REASON)")
railiance01-reboot-preflight: ## Read-only gate for the coordinated single-node reboot
@ -589,7 +608,7 @@ wp0024-t02-driver-register: ## Register committed audit-core driver source witho
@python3 scripts/wp0024-t02-driver-candidate.py register \
--repo "$(abspath ../audit-core)" --driver "$(DRIVER)" --reviewer "$(REVIEWER)"
.PHONY: audit-synthetic-sender-test audit-synthetic-sender-preflight audit-synthetic-sender-project audit-synthetic-sender-cleanup audit-whitehat-e2-preflight audit-whitehat-e2-project audit-whitehat-e2-cleanup audit-database-lease-recovery-preflight audit-database-lease-approval-check audit-database-lease-recovery-exercise railiance01-reboot-preflight wp0024-owner-show wp0024-owner-verify wp0024-owner-status wp0024-t02-driver-show wp0024-t02-driver-status wp0024-t02-driver-register apps-pg-isolation-probe
.PHONY: audit-t02-attended-window audit-synthetic-sender-test audit-synthetic-sender-preflight audit-synthetic-sender-project audit-synthetic-sender-cleanup audit-whitehat-e2-preflight audit-whitehat-e2-project audit-whitehat-e2-cleanup audit-database-lease-recovery-preflight audit-database-lease-approval-check audit-database-lease-recovery-exercise railiance01-reboot-preflight wp0024-owner-show wp0024-owner-verify wp0024-owner-status wp0024-t02-driver-show wp0024-t02-driver-status wp0024-t02-driver-register apps-pg-isolation-probe
.PHONY: db-deploy db-status db-shell db-logs forgejo-db-deploy forgejo-db-status forgejo-db-shell apps-pg-deploy apps-pg-status apps-pg-shell apps-pg-logs net-kingdom-pg-inter-hub-networkpolicy-deploy pg-deploy pg-status pg-pgpool-check valkey-deploy valkey-status openbao-repo openbao-dry-run openbao-overlay-apply openbao-verify-login-overlay openbao-deploy openbao-status openbao-verify openbao-verify-post-unseal openbao-configure-initial openbao-configure-ssh openbao-verify-ssh openbao-verify-authenticated openbao-configure-external-secrets-issue-core openbao-configure-external-secrets-activity-core openbao-configure-external-secrets-forgejo openbao-validate-restore-evidence openbao-validate-emergency-evidence credential-grants-validate credential-change-applier-dry-run credential-change-applier-apply-plan credential-change-applier-apply credential-change-runbook credential-change-record-evidence credential-change-lifecycle-plan credential-change-lifecycle-event credential-change-import-inventory agent-high-risk-boundary-check openbao-credential-change-appliers-dry-run openbao-configure-credential-change-appliers openbao-token-grants-dry-run openbao-configure-token-grants openbao-verify-token-grants-dry-run openbao-verify-token-grants openbao-verify-token-grants-smoke credential-helper-dry-run credential-tests credential-exec-ops-warden-smoke argocd-bootstrap-dry-run argocd-bootstrap-deploy argocd-repo-apply argocd-status backup forgejo-backup forgejo-backup-dry-run forgejo-backup-status forgejo-package-prune forgejo-package-prune-dry-run reef-deployables apps-pg-evidence consumption-preflight help
##@ S3 service assurance (read-only)

View file

@ -85,46 +85,86 @@ driver as `AUDIT_T02_SOURCE`/`AUDIT_T02_TENANT`; the confirm string is
`platform/workloads/audit-core/senders` → ExternalSecret `audit-core-senders`
→ env `AUDIT_CORE_SENDERS`, so both projection and cleanup restart
`deploy/audit-core`. The restarts must stay outside the restart-free proof:
project → restart → T02 exercise → evidence → cleanup → restart.
1. `make audit-synthetic-sender-preflight` — read-only; reads registry metadata
only (add `AUDIT_SYNTHETIC_SENDER_PREFLIGHT_ARGS=--inspect-registry` for an
attended in-memory conflict check).
2. `make audit-synthetic-sender-project` — only within the first 3 minutes of
the window and with ≥10 minutes of identity lifetime left. Writes the
bearer to a new mode-`0600` file (`AUDIT_T02_TOKEN_FILE`, absolute, in an
operator-owned `0700` directory outside every Git work tree; never stdout),
adds the one identity, forces `audit-core-senders` sync and runs
`rollout restart deploy/audit-core`, then waits for one Ready restarted pod
and prints its UID. Any failure removes the identity (restarting again) and
shreds the bearer file.
3. The T02 exercise (`make audit-database-lease-recovery-exercise`). Its
baseline must capture the post-projection pod UID; never project while the
exercise is running.
4. Record the exercise's JSON result (it is the evidence file).
5. `make audit-synthetic-sender-cleanup AUDIT_T02_EVIDENCE=<result.json>` —
refuses unless that evidence names this `approval_id`, or an explicit
`AUDIT_SYNTHETIC_SENDER_ABORT_REASON` is given for an aborted run. Removes
only the exact named identity (refusing if the name carries a different
scope), restarts audit-core only if something was removed, shreds the
bearer file, and confirms 1/1 Ready. Re-running is a no-op.
**Contained OpenBao access only.** Every live command (lane `project` and
`cleanup`, harness `exercise`) requires `--contained` and refuses unless it runs
as the child of `scripts/openbao-attended-exec.py`, i.e. inside `warden access
openbao-platform-admin-login --exec`: `WARDEN_ROUTING_CATALOG` must be set,
`BAO_ADDR` must be the operator tunnel `http://127.0.0.1:18200`, and
`BAO_TOKEN`/`VAULT_TOKEN`/`OPENBAO_TOKEN` must be absent; `bao` authenticates
through Warden's isolated token helper. There is no token-file path any more:
the harness's former default `https://bao.coulomb.social` was retracted on
2026-09-15 (RMASTER-WP-0020-T09), and a standing 0600 platform-admin token file
is outside the accepted custody path. Outside the login, the lane's `preflight`
runs the cluster checks only; `--contained` adds registry metadata and
`--inspect-registry` (attended, in-memory) adds the conflict check.
**One login per window.** `scripts/t02-attended-window.py` is the single reviewed
child of the contained login, so the ≤15-minute window needs one MFA login:
```bash
make audit-t02-attended-window \
AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json \
AUDIT_DATABASE_LEASE_DRIVER=/path/to/audit-core/scripts/t02_synthetic_load_driver.py \
AUDIT_SYNTHETIC_SENDER_CONFIRM=audit-core-synthetic-sender:<approval_id>:attended \
AUDIT_T02_TOKEN_FILE=/abs/0700-dir/t02.bearer \
AUDIT_T02_EVIDENCE_DIR=/abs/0700-dir/evidence
```
It runs, in order:
1. lane `project` — only within the first 3 minutes of the window and with
≥10 minutes of identity lifetime left. Writes the bearer to a new mode-`0600`
file (absolute, operator-owned `0700` directory outside every Git work tree;
never stdout), adds the one identity, forces `audit-core-senders` sync, runs
`rollout restart deploy/audit-core`, and waits for one Ready restarted pod.
Any failure removes the identity (restarting again) and shreds the file.
2. port-forward — the lane and harness reach the cluster only via
`ssh railiance01 kubectl`, so the window uses the same path: `ssh -L
127.0.0.1:<port>:127.0.0.1:<port> railiance01 timeout 900 kubectl -n
audit-core port-forward --address 127.0.0.1 deploy/audit-core <port>:8080`
(default port 18081, `AUDIT_T02_PORT` to override). No local kubeconfig is
needed; the remote forward is bounded by `timeout` even if SSH is cut.
audit-core's NetworkPolicies admit only named namespaces, which is why the
operator-side driver needs the forward.
3. harness `exercise --contained` with the registered driver; the driver env is
derived from the receipt: `AUDIT_T02_BASE_URL=http://127.0.0.1:<port>`,
`AUDIT_T02_TOKEN_FILE`, `AUDIT_T02_SOURCE`/`AUDIT_T02_TENANT` from
`synthetic_sender`, `AUDIT_T02_FIXTURE_ID=t02-<approval_id>`,
`AUDIT_T02_OCCURRED_AT=window.start`. Its baseline captures the
post-projection pod UID.
4. evidence — the exercise JSON is written `0600` as
`<evidence-dir>/<approval_id>-exercise.json`; a value-safe window summary is
written as `<approval_id>-window.json` on success and on abort.
5. lane `cleanup` with that evidence — refuses unless it names this
`approval_id`. Removes only the exact named identity (refusing if the name
carries a different scope), restarts audit-core only if something was
removed, shreds the bearer file, and confirms 1/1 Ready.
The port-forward is stopped before cleanup and on every exit. Any failure after
projection was attempted (including a failed `project`, whose registry write may
already have reached OpenBao) runs `cleanup --abort-reason` before the window
script exits non-zero. Children's stdout is captured and only their value-safe
JSON is re-emitted.
The single-step targets (`audit-synthetic-sender-project`,
`audit-database-lease-recovery-exercise`, `audit-synthetic-sender-cleanup`)
remain for the abort path; each costs its own contained login. A missed cleanup
is run as `make audit-synthetic-sender-cleanup
AUDIT_SYNTHETIC_SENDER_ABORT_REASON=...` and is a no-op if nothing remains.
If cleanup is missed, audit-core's `expires_at` enforcement makes the identity
inert at window end; the attended cleanup is still required to remove it.
Projection and cleanup each restart audit-core once (Recreate, no surge), so
production senders see two short retryable gaps per window; announce them with
the window.
The driver runs operator-side. audit-core's NetworkPolicies admit only named
namespaces, so reach the pod with `kubectl -n audit-core port-forward
deploy/audit-core <local-port>:8080` and set `AUDIT_T02_BASE_URL` to that local
URL; record the base URL and the driver's source/tenant (which must equal the
receipt's `synthetic_sender` block) in the window notes. Projection and cleanup
each restart audit-core once (Recreate, no surge), so production senders see two
short retryable gaps per window; announce them with the window.
If `project` fails after the registry write may already have reached OpenBao (for
example a transport error on the write itself), treat the identity as possibly
present: run `cleanup --abort-reason "<reason>"` before leaving the window.
The lane's `--token-file` is the attended operator token for the registry write;
it must come from the contained ops-warden attended login accepted under RPF-WP-0017
(isolated 0600 helper state, removed afterwards), not from a long-lived token file.
The contained login discards child output and fails closed (exit 12, token
revoked) on any byte the child prints. The window script therefore prints
nothing: its summary, exercise evidence or `window-error-*.json` land only in the
evidence directory. Single-step targets require `AUDIT_T02_RESULT_FILE` so the
lane/harness write their JSON there instead of stdout.
## Attended sequence
@ -165,13 +205,11 @@ bodies and clear all transient JSON from memory or mode-`0600` temporary files.
(which restarts audit-core after the evidence is captured). The replacement remains managed by ESO
and expires through its normal database role TTL.
Only inside the approved window, with the receipt and reviewed driver:
```bash
make audit-database-lease-recovery-exercise \
AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json \
AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver
```
Only inside the approved window, with the receipt and reviewed driver, run the
whole window through `make audit-t02-attended-window` (see the synthetic sender
section above); the exercise step alone is
`make audit-database-lease-recovery-exercise`, which also runs inside the
contained login.
The exact confirmation string is embedded in the target. The harness rechecks
the Secret/pod/lease immediately before revocation, requires at least five

View file

@ -25,10 +25,15 @@ Runbook: `docs/audit-core-database-lease-recovery.md`.
`RAILIANCE-WP-0024-T02-SYNTHETIC-01` with driver revision
`sha256:941ba251…` (audit-core `8c8bcf4`), and the `synthetic_sender` block.
3. Announce two short audit-core restart gaps (projection and cleanup).
4. In the window: attended ops-warden login → `make audit-synthetic-sender-project`
→ port-forward audit-core → `make audit-database-lease-recovery-exercise` →
record evidence → `make audit-synthetic-sender-cleanup`.
5. Abort: stop load, `cleanup --abort-reason`, never restart the pod by hand.
4. In the window: one contained ops-warden login runs the whole sequence —
`make audit-t02-attended-window` (approval receipt, driver path, confirm
string, bearer file and evidence dir outside Git). Through
`scripts/openbao-attended-exec.py` it runs lane project (restart #1) →
ssh-bounded port-forward → contained T02 exercise → 0600 evidence JSON →
lane cleanup with that evidence (restart #2). No token file is used.
5. Abort: the window script stops the port-forward and runs `cleanup --abort-reason`
itself; if it could not, run `make audit-synthetic-sender-cleanup
AUDIT_SYNTHETIC_SENDER_ABORT_REASON=...` (one more login). Never restart the pod by hand.
## T03 — coordinated railiance01 reboot

View file

@ -34,7 +34,8 @@
"docs/railiance01-coordinated-reboot.md",
"scripts/audit-core-recovery-preflight.py",
"scripts/audit-core-database-lease-recovery.py",
"scripts/audit_core_synthetic_sender_lane.py"
"scripts/audit_core_synthetic_sender_lane.py",
"scripts/t02-attended-window.py"
],
"assertions": [
"The database-lease exercise uses only synthetic load and proves health 200, readiness and requests fail retryably with 503, then recover without changing the audit-core pod UID or restart count.",
@ -57,7 +58,8 @@
"docs/railiance01-coordinated-reboot.md",
"scripts/audit-core-recovery-preflight.py",
"scripts/audit-core-database-lease-recovery.py",
"docs/audit-core-database-lease-approval.example.json"
"docs/audit-core-database-lease-approval.example.json",
"scripts/t02-attended-window.py"
],
"assertions": [
"Lease selection aborts unless exactly one live handle exists below database/creds/audit-core-runtime and its issue time coheres with the ExternalSecret refresh.",
@ -122,7 +124,8 @@
"scripts/audit-core-database-lease-recovery.py",
"docs/audit-core-database-lease-approval.example.json",
"docs/openbao-reboot-snapshot-receipt.example.json",
"scripts/audit_core_synthetic_sender_lane.py"
"scripts/audit_core_synthetic_sender_lane.py",
"scripts/t02-attended-window.py"
],
"assertions": [
"T02 remains gated on a named window, abort operator, exact owner receipts and a revision-pinned synthetic-load driver; repository access alone cannot revoke a lease.",
@ -139,11 +142,12 @@
},
"artifact_sha256": {
"docs/audit-core-database-lease-approval.example.json": "87e57e3290074508c6517938acf8c7fc1a75d977b8ff630f247a4ff5d2ee45c6",
"docs/audit-core-database-lease-recovery.md": "3544abde993505c1d84a5cfc882ce1619ca2e115b1cb74fdb1262a42984ee0be",
"docs/audit-core-database-lease-recovery.md": "ae9e6b0d6cf60514cfabbd1fd0a9818d33dbbac9291a94539a4e049492c48f21",
"docs/openbao-reboot-snapshot-receipt.example.json": "b95903a3e1aacb607eecc3f0440f08e62480d09da47bd7ab96c3eb8814f2eaee",
"docs/railiance01-coordinated-reboot.md": "6fd6301f808449ad6519764d994b05aee41a96be879b917ee1d18d8a65d5f6fd",
"scripts/audit-core-database-lease-recovery.py": "1de02f13dcd4e87e65de1c97ae14567f2b8e4a51aa0423e3fca78a224cf374b3",
"scripts/audit-core-database-lease-recovery.py": "3066718d06ce29b98fa8ee513bd967e0f48c47db2d66b5083c591f5f1c2bddd6",
"scripts/audit-core-recovery-preflight.py": "05e89ae9d0b9a636b67d9a8d0c203ac746c97208e32061cb23f1ca785eda8e5c",
"scripts/audit_core_synthetic_sender_lane.py": "e71b3b3d56472a1cc91fc093d327a72116d89d07c1676f44bc24a8da36154bd4"
"scripts/audit_core_synthetic_sender_lane.py": "608145f43b5adea110c4c73a2f567370fcfb603ef73ca8230775de9bf7687fa6",
"scripts/t02-attended-window.py": "1faca370d14de9a55a3f04e55f6046105b5d3a606a4d79dadbe72c495ab75077"
}
}

View file

@ -14,7 +14,6 @@ import hashlib
import importlib.util
import json
import os
import stat
import subprocess
import sys
import time
@ -122,14 +121,41 @@ def safe_run(
return completed
# Operator OpenBao access is the SSH tunnel; the public endpoint was retracted
# 2026-09-15 (RMASTER-WP-0020-T09).
TUNNEL_ADDR = "http://127.0.0.1:18200"
CONTAINMENT_MARKER = "WARDEN_ROUTING_CATALOG"
TOKEN_ENV = ("BAO_TOKEN", "VAULT_TOKEN", "OPENBAO_TOKEN")
def contained_bao_env(environ: dict[str, str] | None = None) -> dict[str, str]:
"""Environment for ``bao`` inside the contained attended login only.
``scripts/openbao-attended-exec.py`` runs ``warden access
openbao-platform-admin-login --exec`` with BAO_ADDR pinned to the tunnel,
no token variable, and the Warden routing marker; ``bao`` then
authenticates through Warden's isolated token helper. Anything else --
a token file, a token variable, or another address -- is refused.
"""
env = dict(os.environ if environ is None else environ)
if not env.get(CONTAINMENT_MARKER):
raise ProcedureError(
"live OpenBao access requires the contained attended login "
"(scripts/openbao-attended-exec.py)"
)
if env.get("BAO_ADDR", TUNNEL_ADDR) != TUNNEL_ADDR:
raise ProcedureError(f"BAO_ADDR must be the operator tunnel {TUNNEL_ADDR}")
if any(env.get(name) for name in TOKEN_ENV):
raise ProcedureError("token variables must be unset inside the contained login")
for name in TOKEN_ENV:
env.pop(name, None)
env["BAO_ADDR"] = TUNNEL_ADDR
return env
class Authority:
def __init__(self, token_file: Path) -> None:
if not token_file.is_file() or stat.S_IMODE(token_file.stat().st_mode) != 0o600:
raise ProcedureError("OpenBao token file must exist with mode 0600")
token = token_file.read_text(encoding="utf-8").splitlines()[0].strip()
if not token:
raise ProcedureError("OpenBao token file is empty")
self.env = dict(os.environ, BAO_ADDR="https://bao.coulomb.social", BAO_TOKEN=token)
def __init__(self) -> None:
self.env = contained_bao_env()
def bao(self, args: list[str], *, label: str) -> str:
return safe_run(["bao", *args], label=label, env=self.env).stdout.strip()
@ -333,7 +359,7 @@ def exercise(args: argparse.Namespace) -> dict[str, Any]:
if args.confirm != CONFIRM:
raise ProcedureError(f"live exercise requires --confirm {CONFIRM}")
remote = PREFLIGHT.Remote(args.remote)
authority = Authority(args.token_file)
authority = Authority()
driver = LoadDriver(
args.load_driver,
approval["synthetic_load"]["contract_id"],
@ -450,18 +476,30 @@ def exercise(args: argparse.Namespace) -> dict[str, Any]:
}
def main() -> int:
def emit_result(result: dict, result_file: "Path | None") -> None:
"""Under the contained login the child must stay silent: write, never print."""
if result_file is None:
print(json.dumps(result, indent=2, sort_keys=True))
return
fd = os.open(result_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(result, handle, indent=2, sort_keys=True)
handle.write("\n")
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument("command", choices=["validate-approval", "exercise"])
parser.add_argument("--approval", type=Path, required=True)
parser.add_argument("--remote", default="railiance01")
parser.add_argument(
"--token-file", type=Path,
default=Path.home() / ".local/openbao/platform-admin.token",
"--contained", action="store_true",
help="required for exercise: run inside the contained attended OpenBao login",
)
parser.add_argument("--load-driver", type=Path)
parser.add_argument("--confirm")
args = parser.parse_args()
parser.add_argument("--result-file", type=Path, help="write the JSON result here (0600, new) and print nothing")
args = parser.parse_args(argv)
try:
if args.command == "validate-approval":
approval = load_approval(args.approval, datetime.now(UTC), require_open_window=False)
@ -472,13 +510,18 @@ def main() -> int:
"secret_values_observed": False,
}
else:
if not args.contained:
raise ProcedureError("live exercise requires --contained (scripts/openbao-attended-exec.py)")
if args.load_driver is None:
raise ProcedureError("live exercise requires --load-driver")
result = exercise(args)
except (OSError, IndexError, ProcedureError, PREFLIGHT.PreflightError) as exc:
print(f"database lease recovery failed: {exc}", file=sys.stderr)
if args.result_file is None:
print(f"database lease recovery failed: {exc}", file=sys.stderr)
else:
emit_result({"status": "failed", "error": str(exc), "secret_values_observed": False}, args.result_file)
return 1
print(json.dumps(result, indent=2, sort_keys=True))
emit_result(result, args.result_file)
return 0

54
scripts/audit_core_synthetic_sender_lane.py Normal file → Executable file
View file

@ -262,6 +262,24 @@ def settled_state(remote: Any, *, previous_uid: str | None, timeout: float = 90)
time.sleep(3)
class ContainedOperator(CREDS.Operator):
"""The -02 registry/kubectl helpers, authenticated only by the contained login.
No token file and no token variable: ``bao`` runs with the tunnel address
and Warden's isolated token helper (see ``LEASE.contained_bao_env``).
"""
def __init__(self, remote: str) -> None: # noqa: D107 - deliberately no super().__init__
self.remote = remote
self.bao_env = LEASE.contained_bao_env()
def contained_operator(args: argparse.Namespace) -> Any:
if not args.contained:
raise LaneError(f"{args.command} requires --contained (scripts/openbao-attended-exec.py)")
return ContainedOperator(args.remote)
def registry_metadata(operator: Any) -> dict[str, Any]:
raw = operator.bao(["kv", "metadata", "get", "-format=json", REGISTRY_PATH], label="read sender registry metadata").stdout
try:
@ -303,9 +321,16 @@ def preflight(args: argparse.Namespace) -> dict[str, Any]:
if args.offline:
result["live_checks"] = "skipped"
return result
if args.inspect_registry and not args.contained:
raise LaneError("--inspect-registry requires --contained")
remote = PREFLIGHT.Remote(args.remote)
result["audit_core"] = cluster_state(remote)
operator = CREDS.Operator(args.remote, args.token_file)
result["automated_checks_passed"] = result["audit_core"]["automated_checks_passed"]
if not args.contained:
# Outside the attended login there is no OpenBao credential at all.
result["sender_registry_metadata"] = "skipped-not-contained"
return result
operator = contained_operator(args)
result["sender_registry_metadata"] = registry_metadata(operator)
if args.inspect_registry:
# Attended only: values are held in memory and never emitted.
@ -316,7 +341,6 @@ def preflight(args: argparse.Namespace) -> dict[str, Any]:
)
result["registry_conflicts"] = registry_conflicts(registry, spec)
registry.clear()
result["automated_checks_passed"] = result["audit_core"]["automated_checks_passed"]
return result
@ -334,6 +358,7 @@ def project(args: argparse.Namespace) -> dict[str, Any]:
approval, spec = load_receipt(args.approval, now, require_open_window=True)
if args.confirm != confirm_token(approval):
raise LaneError(f"project requires --confirm {confirm_token(approval)}")
operator = contained_operator(args)
assert_projection_time(approval, spec, now)
if args.bearer_file is None:
raise LaneError("project requires --bearer-file outside Git")
@ -343,7 +368,6 @@ def project(args: argparse.Namespace) -> dict[str, Any]:
if not before["automated_checks_passed"]:
raise LaneError("audit-core recovery preflight is not green; refusing to project")
LEASE.attest_chain_state(remote)
operator = CREDS.Operator(args.remote, args.token_file)
registry = operator.registry()
conflicts = registry_conflicts(registry, spec)
if conflicts:
@ -384,8 +408,8 @@ def cleanup(args: argparse.Namespace) -> dict[str, Any]:
approval, spec = load_receipt(args.approval, utc_now(), require_open_window=False)
if args.confirm != confirm_token(approval):
raise LaneError(f"cleanup requires --confirm {confirm_token(approval)}")
operator = contained_operator(args)
gate = assert_evidence(args.exercise_evidence, args.abort_reason, approval)
operator = CREDS.Operator(args.remote, args.token_file)
removed = _cleanup_registry(operator, spec)
remaining = any(
isinstance(entry, dict) and entry.get("name") == spec["name"] for entry in operator.registry()
@ -412,26 +436,42 @@ def cleanup(args: argparse.Namespace) -> dict[str, Any]:
}
def emit_result(result: dict, result_file: "Path | None") -> None:
"""Under the contained login the child must stay silent: write, never print."""
if result_file is None:
print(json.dumps(result, indent=2, sort_keys=True))
return
fd = os.open(result_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(result, handle, indent=2, sort_keys=True)
handle.write("\n")
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("command", choices=["preflight", "project", "cleanup"])
parser.add_argument("--approval", type=Path, required=True, help="approved T02 window receipt")
parser.add_argument("--bearer-file", type=Path, help="absolute path outside Git (mode-0700 directory)")
parser.add_argument("--remote", default="railiance01")
parser.add_argument("--token-file", type=Path, default=Path.home() / ".local/openbao/platform-admin.token")
parser.add_argument("--contained", action="store_true",
help="run inside the contained attended OpenBao login (required for project/cleanup)")
parser.add_argument("--confirm")
parser.add_argument("--offline", action="store_true", help="preflight: validate receipt/path only")
parser.add_argument("--inspect-registry", action="store_true", help="preflight: attended registry conflict check")
parser.add_argument("--exercise-evidence", type=Path, help="cleanup: recorded T02 exercise result JSON")
parser.add_argument("--abort-reason", help="cleanup: abort path when no exercise evidence exists")
parser.add_argument("--result-file", type=Path, help="write the JSON result here (0600, new) and print nothing")
args = parser.parse_args(argv)
try:
handler = {"preflight": preflight, "project": project, "cleanup": cleanup}[args.command]
result = handler(args)
except ERRORS as exc:
print(f"synthetic sender lane failed: {exc}", file=sys.stderr)
if args.result_file is None:
print(f"synthetic sender lane failed: {exc}", file=sys.stderr)
else:
emit_result({"status": "failed", "error": str(exc), "secret_values_observed": False}, args.result_file)
return 1
print(json.dumps(result, indent=2, sort_keys=True))
emit_result(result, args.result_file)
return 0

274
scripts/t02-attended-window.py Executable file
View file

@ -0,0 +1,274 @@
#!/usr/bin/env python3
"""One contained attended window for RAILIANCE-WP-0024-T02.
Run only as the single child of the contained attended login, so the whole
window costs one MFA login:
python3 scripts/openbao-attended-exec.py -- scripts/t02-attended-window.py ...
Order (each step a reviewed command; this file adds no OpenBao call of its own):
1. lane ``project`` -- one temporary sender, bearer to a 0600 file, audit-core restart
2. port-forward -- ``ssh -L`` to ``kubectl -n audit-core port-forward`` on the remote
3. harness ``exercise`` with the registered load driver
4. evidence -- exercise JSON written 0600 outside Git
5. lane ``cleanup`` -- with that evidence (restart #2)
Any failure after projection was attempted runs ``cleanup --abort-reason``.
The port-forward is always stopped. Children's stdout is captured and only
their value-safe JSON is re-emitted; nothing here reads the bearer.
"""
from __future__ import annotations
import argparse
import importlib.util
import json
import os
import re
import subprocess
import sys
import time
import urllib.error
import urllib.request
from datetime import UTC, datetime
from pathlib import Path
from typing import Any, Callable
ROOT = Path(__file__).resolve().parents[1]
LANE_SCRIPT = ROOT / "scripts" / "audit_core_synthetic_sender_lane.py"
HARNESS_SCRIPT = ROOT / "scripts" / "audit-core-database-lease-recovery.py"
HARNESS_CONFIRM = "RAILIANCE-WP-0024-T02:attended"
REMOTE_PF_TIMEOUT_SECONDS = 15 * 60
CONTAINER_PORT = 8080
class WindowError(RuntimeError):
pass
def _load_lane() -> Any:
spec = importlib.util.spec_from_file_location("audit_core_synthetic_sender_lane", LANE_SCRIPT)
if not spec or not spec.loader:
raise WindowError("cannot load synthetic sender lane")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
LANE = _load_lane()
def stamp(value: datetime) -> str:
return value.astimezone(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
def run_json(command: list[str], *, label: str, env: dict[str, str] | None = None) -> dict[str, Any]:
"""Run a value-safe reviewed child; capture both streams (warden fails on any output)."""
completed = subprocess.run(
command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, check=False
)
if completed.returncode != 0:
# Reviewed lane/harness errors are fixed value-safe strings; keep one bounded line.
reason = (completed.stderr.strip().splitlines() or [""])[-1][:200]
raise WindowError(f"{label} failed (exit {completed.returncode}): {reason}")
try:
payload = json.loads(completed.stdout)
except json.JSONDecodeError as exc:
raise WindowError(f"{label} returned invalid JSON") from exc
if not isinstance(payload, dict) or payload.get("secret_values_observed") is not False:
raise WindowError(f"{label} did not attest value safety")
return payload
def private_dir(path: Path) -> Path:
"""An operator-owned 0700 directory outside every Git work tree."""
path = path.resolve()
LANE.check_bearer_path(path / "probe", must_exist=False)
return path
def write_private_json(path: Path, document: dict[str, Any]) -> None:
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(document, handle, indent=2, sort_keys=True)
handle.write("\n")
def slug(value: str) -> str:
cleaned = re.sub(r"[^a-z0-9-]+", "-", value.lower()).strip("-")
if not cleaned:
raise WindowError("approval id cannot form a fixture id")
return cleaned[:48]
def driver_env(spec: dict[str, Any], approval: dict[str, Any], bearer: Path, port: int) -> dict[str, str]:
env = dict(os.environ)
env.update({
"AUDIT_T02_BASE_URL": f"http://127.0.0.1:{port}",
"AUDIT_T02_TOKEN_FILE": str(bearer),
"AUDIT_T02_SOURCE": spec["source"],
"AUDIT_T02_TENANT": spec["tenant"],
"AUDIT_T02_FIXTURE_ID": f"t02-{slug(approval['approval_id'])}",
"AUDIT_T02_OCCURRED_AT": approval["window"]["start"],
})
return env
class PortForward:
"""``ssh -L`` to a remote, time-bounded ``kubectl port-forward`` on loopback.
The lane and harness reach the cluster only through ``ssh <remote> kubectl``;
the port-forward uses the same path so the window needs no local kubeconfig.
"""
def __init__(self, remote: str, port: int) -> None:
self.remote = remote
self.port = port
self.process: subprocess.Popen[bytes] | None = None
def command(self) -> list[str]:
remote_cmd = (
f"timeout {REMOTE_PF_TIMEOUT_SECONDS} kubectl -n audit-core port-forward "
f"--address 127.0.0.1 deploy/audit-core {self.port}:{CONTAINER_PORT}"
)
return [
"ssh", "-o", "BatchMode=yes", "-o", "ExitOnForwardFailure=yes",
"-L", f"127.0.0.1:{self.port}:127.0.0.1:{self.port}",
self.remote, remote_cmd,
]
def start(self, *, timeout: float = 30) -> None:
self.process = subprocess.Popen(
self.command(), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
if self.process.poll() is not None:
raise WindowError("audit-core port-forward exited early")
# A local ssh listener accepts before the remote forward is ready, so
# require a real /healthz answer through the whole chain.
try:
with urllib.request.urlopen(f"http://127.0.0.1:{self.port}/healthz", timeout=2) as response:
if response.status == 200:
return
except (OSError, urllib.error.URLError):
pass
time.sleep(1)
raise WindowError("audit-core port-forward did not become reachable")
def stop(self) -> None:
if self.process is None or self.process.poll() is not None:
return
self.process.terminate()
try:
self.process.wait(timeout=10)
except subprocess.TimeoutExpired:
self.process.kill()
self.process.wait(timeout=5)
def window(args: argparse.Namespace, *, runner: Callable[..., dict[str, Any]] = run_json,
forward_factory: Callable[[str, int], Any] = PortForward) -> dict[str, Any]:
if not args.bearer_file.is_absolute() or not args.evidence_dir.is_absolute():
raise WindowError("--bearer-file and --evidence-dir must be absolute")
LANE.LEASE.contained_bao_env() # refuse before any step outside the contained login
approval, spec = LANE.load_receipt(args.approval, datetime.now(UTC), require_open_window=True)
if args.confirm != LANE.confirm_token(approval):
raise WindowError(f"window requires --confirm {LANE.confirm_token(approval)}")
evidence_dir = private_dir(args.evidence_dir)
base = f"{slug(approval['approval_id'])}"
exercise_path = evidence_dir / f"{base}-exercise.json"
summary_path = evidence_dir / f"{base}-window.json"
for path in (exercise_path, summary_path):
if path.exists():
raise WindowError(f"evidence file already exists: {path.name}")
lane = [sys.executable, str(LANE_SCRIPT)]
common = ["--approval", str(args.approval), "--remote", args.remote, "--contained",
"--confirm", args.confirm]
summary: dict[str, Any] = {
"procedure": "t02-attended-window",
"approval_id": approval["approval_id"],
"identity": LANE.identity_view(spec),
"started_at": stamp(datetime.now(UTC)),
"steps": [],
"secret_values_observed": False,
}
projection_attempted = False
forward = forward_factory(args.remote, args.port)
try:
projection_attempted = True
summary["project"] = runner(
[*lane, "project", *common, "--bearer-file", str(args.bearer_file)], label="lane project")
summary["steps"].append("project")
forward.start()
summary["steps"].append("port-forward")
exercise = runner(
[sys.executable, str(HARNESS_SCRIPT), "exercise", "--approval", str(args.approval),
"--remote", args.remote, "--contained", "--load-driver", str(args.load_driver),
"--confirm", HARNESS_CONFIRM],
label="T02 exercise",
env=driver_env(spec, approval, args.bearer_file, args.port),
)
summary["steps"].append("exercise")
write_private_json(exercise_path, exercise)
summary["exercise_evidence"] = exercise_path.name
summary["steps"].append("evidence")
forward.stop()
summary["cleanup"] = runner(
[*lane, "cleanup", *common, "--bearer-file", str(args.bearer_file),
"--exercise-evidence", str(exercise_path)], label="lane cleanup")
summary["steps"].append("cleanup")
summary["outcome"] = "completed"
except BaseException as exc:
forward.stop()
summary["outcome"] = "aborted"
summary["abort_reason"] = str(exc)[:200] if isinstance(exc, WindowError) else type(exc).__name__
if projection_attempted and "cleanup" not in summary["steps"]:
try:
summary["cleanup"] = runner(
[*lane, "cleanup", *common, "--bearer-file", str(args.bearer_file),
"--abort-reason", f"t02 window aborted: {summary['abort_reason']}"],
label="lane abort cleanup")
summary["steps"].append("abort-cleanup")
except BaseException as cleanup_exc: # keep the first failure; flag cleanup loudly
summary["abort_cleanup_failed"] = str(cleanup_exc)[:200]
summary["finished_at"] = stamp(datetime.now(UTC))
write_private_json(summary_path, summary)
raise
finally:
forward.stop()
summary["finished_at"] = stamp(datetime.now(UTC))
write_private_json(summary_path, summary)
return summary
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--approval", type=Path, required=True)
parser.add_argument("--load-driver", type=Path, required=True)
parser.add_argument("--bearer-file", type=Path, required=True)
parser.add_argument("--evidence-dir", type=Path, required=True)
parser.add_argument("--confirm", required=True)
parser.add_argument("--remote", default="railiance01")
parser.add_argument("--port", type=int, default=18081)
args = parser.parse_args(argv)
# The contained login discards child output and fails closed on any byte,
# so this script never prints; its outcome lives only in the evidence dir.
try:
window(args)
except (OSError, KeyError, WindowError, LANE.LaneError, LANE.LEASE.ProcedureError) as exc:
try:
LANE.emit_result(
{"status": "failed", "error": str(exc), "secret_values_observed": False},
args.evidence_dir / f"window-error-{int(time.time())}.json",
)
except OSError:
pass
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -200,15 +200,15 @@ class SyntheticSenderLaneTests(unittest.TestCase):
bearer = self.dir / "t02.bearer"
operator = FakeOperator([{"name": "user-engine", "tokens": ["p"], "sources": ["user-engine"], "tenants": ["*"]}])
args = argparse.Namespace(
approval=approval_path, confirm=lane.confirm_token(approval), bearer_file=bearer,
remote="railiance01", token_file=self.dir / "unused",
command="project", approval=approval_path, confirm=lane.confirm_token(approval),
bearer_file=bearer, remote="railiance01", contained=True,
)
green = {"automated_checks_passed": True, "ready": "1/1", "pod_uid": "uid-1"}
restart = mock.Mock(side_effect=[lane.CREDS.ProcedureError("rollout failed"), None])
with mock.patch.object(lane.PREFLIGHT, "Remote"), \
mock.patch.object(lane, "cluster_state", return_value=green), \
mock.patch.object(lane.LEASE, "attest_chain_state"), \
mock.patch.object(lane.CREDS, "Operator", return_value=operator), \
mock.patch.object(lane, "contained_operator", return_value=operator), \
mock.patch.object(lane.CREDS, "force_sender_sync_and_restart", restart):
with self.assertRaises(lane.CREDS.ProcedureError):
lane.project(args)
@ -226,6 +226,45 @@ class SyntheticSenderLaneTests(unittest.TestCase):
self.assertIn("--confirm audit-core-synthetic-sender:t02-window-1:attended", stderr.getvalue())
self.assertEqual("", stdout.getvalue())
def test_live_commands_refuse_outside_contained_login(self) -> None:
approval_path = self.write_receipt(receipt(self.now))
approval = json.loads(approval_path.read_text())
base = dict(approval=approval_path, confirm=lane.confirm_token(approval),
bearer_file=self.dir / "t02.bearer", remote="railiance01",
exercise_evidence=None, abort_reason="test")
for command in ("project", "cleanup"):
with self.subTest(command=command):
with self.assertRaisesRegex(lane.LaneError, "--contained"):
getattr(lane, command)(argparse.Namespace(command=command, contained=False, **base))
contained = {"WARDEN_ROUTING_CATALOG": "/catalog.yaml", "BAO_ADDR": lane.LEASE.TUNNEL_ADDR}
refusals = [
{},
{**contained, "WARDEN_ROUTING_CATALOG": ""},
{**contained, "BAO_ADDR": "https://bao.coulomb.social"},
{**contained, "BAO_TOKEN": "x"},
{**contained, "VAULT_TOKEN": "x"},
]
for env in refusals:
with self.subTest(env=sorted(env)):
with mock.patch.dict(os.environ, env, clear=True):
with self.assertRaises(lane.LEASE.ProcedureError):
lane.ContainedOperator("railiance01")
with mock.patch.dict(os.environ, contained, clear=True):
operator = lane.ContainedOperator("railiance01")
self.assertEqual(lane.LEASE.TUNNEL_ADDR, operator.bao_env["BAO_ADDR"])
self.assertNotIn("BAO_TOKEN", operator.bao_env)
def test_harness_exercise_requires_contained(self) -> None:
stderr = io.StringIO()
with redirect_stderr(stderr):
code = lane.LEASE.main(["exercise", "--approval", str(self.write_receipt(receipt(self.now))),
"--load-driver", "/bin/true", "--confirm", lane.LEASE.CONFIRM])
self.assertEqual(1, code)
self.assertIn("--contained", stderr.getvalue())
with mock.patch.dict(os.environ, {"BAO_ADDR": lane.LEASE.TUNNEL_ADDR}, clear=True):
with self.assertRaises(lane.LEASE.ProcedureError):
lane.LEASE.Authority()
def test_offline_preflight_is_value_safe(self) -> None:
stdout = io.StringIO()
with redirect_stdout(stdout):

View file

@ -0,0 +1,192 @@
from __future__ import annotations
import argparse
import importlib.util
import json
import os
import tempfile
import unittest
from datetime import UTC, datetime
from pathlib import Path
from unittest import mock
ROOT = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location("t02_attended_window", ROOT / "scripts" / "t02-attended-window.py")
assert SPEC and SPEC.loader
window = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(window)
TEST_SPEC = importlib.util.spec_from_file_location(
"test_audit_core_synthetic_sender_lane", ROOT / "tests" / "test_audit_core_synthetic_sender_lane.py")
assert TEST_SPEC and TEST_SPEC.loader
lane_tests = importlib.util.module_from_spec(TEST_SPEC)
TEST_SPEC.loader.exec_module(lane_tests)
CONTAINED = {"WARDEN_ROUTING_CATALOG": "/catalog.yaml", "BAO_ADDR": "http://127.0.0.1:18200", "PATH": os.environ.get("PATH", "")}
class FakeForward:
def __init__(self, log: list[str], fail: bool = False) -> None:
self.log, self.fail = log, fail
def start(self) -> None:
self.log.append("port-forward-start")
if self.fail:
raise window.WindowError("audit-core port-forward exited early")
def stop(self) -> None:
self.log.append("port-forward-stop")
class Runner:
"""Records the step of each reviewed child; optionally fails one step."""
def __init__(self, log: list[str], fail_on: str | None = None) -> None:
self.log, self.fail_on, self.envs = log, fail_on, {}
def __call__(self, command: list[str], *, label: str, env: dict | None = None) -> dict:
step = "exercise" if "exercise" in command else command[2]
if step == "cleanup" and "--abort-reason" in command:
step = "abort-cleanup"
self.log.append(step)
self.envs[step] = env
assert "--contained" in command
if step == self.fail_on:
raise window.WindowError(f"{label} failed (exit 1)")
if step == "exercise":
return {"procedure": window.LANE.LEASE.PROCEDURE, "approval_id": "t02-window-1",
"completed_at": "2026-10-14T00:00:00Z", "secret_values_observed": False}
return {"secret_values_observed": False}
class AttendedWindowTests(unittest.TestCase):
def setUp(self) -> None:
self.temp = tempfile.TemporaryDirectory()
self.dir = Path(self.temp.name)
os.chmod(self.dir, 0o700)
self.now = datetime.now(UTC)
document = lane_tests.receipt(self.now)
self.approval = self.dir / "receipt.json"
self.approval.write_text(json.dumps(document), encoding="utf-8")
self.evidence = self.dir / "evidence"
self.evidence.mkdir(mode=0o700)
self.args = argparse.Namespace(
approval=self.approval, load_driver=Path("/opt/driver"), bearer_file=self.dir / "t02.bearer",
evidence_dir=self.evidence, confirm=window.LANE.confirm_token(document),
remote="railiance01", port=18081,
)
def tearDown(self) -> None:
self.temp.cleanup()
def run_window(self, runner: Runner, forward: FakeForward) -> dict:
with mock.patch.dict(os.environ, CONTAINED, clear=True):
return window.window(self.args, runner=runner, forward_factory=lambda remote, port: forward)
def test_main_never_prints_and_records_failure_in_evidence_dir(self) -> None:
import contextlib
import io
out, err = io.StringIO(), io.StringIO()
with tempfile.TemporaryDirectory() as tmp, contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
evidence = Path(tmp)
with mock.patch.object(window, "window", side_effect=window.WindowError("project failed (exit 1): x")):
code = window.main(
["--approval", "/a", "--load-driver", "/d", "--bearer-file", "/b",
"--evidence-dir", str(evidence), "--confirm", "c"]
)
records = list(evidence.glob("window-error-*.json"))
self.assertEqual(1, code)
self.assertEqual(1, len(records))
self.assertEqual(0o600, records[0].stat().st_mode & 0o777)
self.assertEqual("failed", json.loads(records[0].read_text())["status"])
with mock.patch.object(window, "window", return_value={"status": "ok"}):
self.assertEqual(0, window.main(
["--approval", "/a", "--load-driver", "/d", "--bearer-file", "/b",
"--evidence-dir", str(evidence), "--confirm", "c"]
))
self.assertEqual("", out.getvalue())
self.assertEqual("", err.getvalue())
def test_result_file_is_new_private_and_suppresses_stdout(self) -> None:
import contextlib
import io
out = io.StringIO()
with tempfile.TemporaryDirectory() as tmp, contextlib.redirect_stdout(out):
target = Path(tmp) / "r.json"
window.LANE.emit_result({"secret_values_observed": False}, target)
self.assertEqual(0o600, target.stat().st_mode & 0o777)
with self.assertRaises(FileExistsError):
window.LANE.emit_result({"secret_values_observed": False}, target)
self.assertEqual("", out.getvalue())
def test_happy_path_order_and_evidence(self) -> None:
log: list[str] = []
runner = Runner(log)
summary = self.run_window(runner, FakeForward(log))
self.assertEqual(
["project", "port-forward-start", "exercise", "port-forward-stop", "cleanup", "port-forward-stop"], log)
self.assertEqual("completed", summary["outcome"])
exercise = self.evidence / "t02-window-1-exercise.json"
self.assertEqual(0o600, exercise.stat().st_mode & 0o777)
self.assertTrue((self.evidence / "t02-window-1-window.json").exists())
env = runner.envs["exercise"]
self.assertEqual("http://127.0.0.1:18081", env["AUDIT_T02_BASE_URL"])
self.assertEqual(str(self.args.bearer_file), env["AUDIT_T02_TOKEN_FILE"])
self.assertEqual("railiance-t02-synthetic", env["AUDIT_T02_SOURCE"])
self.assertEqual("tenant:trial:t02-synthetic-1", env["AUDIT_T02_TENANT"])
def test_failures_after_projection_run_abort_cleanup(self) -> None:
for fail_on in ("project", "exercise", "cleanup"):
with self.subTest(fail_on=fail_on):
for path in self.evidence.iterdir():
path.unlink()
log: list[str] = []
with self.assertRaises(window.WindowError):
self.run_window(Runner(log, fail_on), FakeForward(log))
self.assertEqual("abort-cleanup", log[-2] if log[-1] == "port-forward-stop" else log[-1])
self.assertIn("port-forward-stop", log)
summary = json.loads((self.evidence / "t02-window-1-window.json").read_text())
self.assertEqual("aborted", summary["outcome"])
self.assertFalse(summary["secret_values_observed"])
def test_port_forward_failure_aborts_without_exercise(self) -> None:
log: list[str] = []
with self.assertRaises(window.WindowError):
self.run_window(Runner(log), FakeForward(log, fail=True))
self.assertNotIn("exercise", log)
self.assertIn("abort-cleanup", log)
def test_refuses_outside_contained_login_before_any_step(self) -> None:
log: list[str] = []
with mock.patch.dict(os.environ, {"BAO_TOKEN": "x", **CONTAINED}, clear=True):
with self.assertRaises(window.LANE.LEASE.ProcedureError):
window.window(self.args, runner=Runner(log), forward_factory=lambda r, p: FakeForward(log))
with mock.patch.dict(os.environ, {"PATH": "/usr/bin"}, clear=True):
with self.assertRaises(window.LANE.LEASE.ProcedureError):
window.window(self.args, runner=Runner(log), forward_factory=lambda r, p: FakeForward(log))
self.assertEqual([], log)
def test_wrong_confirm_and_git_evidence_dir_refused_before_any_step(self) -> None:
log: list[str] = []
self.args.confirm = "wrong"
with self.assertRaises(window.WindowError):
self.run_window(Runner(log), FakeForward(log))
self.args.confirm = window.LANE.confirm_token(json.loads(self.approval.read_text()))
self.args.evidence_dir = ROOT / "docs"
with self.assertRaises(window.LANE.LaneError):
self.run_window(Runner(log), FakeForward(log))
self.assertEqual([], log)
def test_port_forward_uses_ssh_remote_loopback_and_bounded_kubectl(self) -> None:
command = window.PortForward("railiance01", 18081).command()
self.assertIn("ExitOnForwardFailure=yes", command)
self.assertIn("127.0.0.1:18081:127.0.0.1:18081", command)
self.assertTrue(command[-1].startswith("timeout 900 kubectl -n audit-core port-forward --address 127.0.0.1"))
self.assertTrue(command[-1].endswith("deploy/audit-core 18081:8080"))
if __name__ == "__main__":
unittest.main()