From 17d1c52ecaf1646a08a622b0bc2deb5d2fe95f91 Mon Sep 17 00:00:00 2001 From: SAU-384 implementer Date: Sat, 31 Oct 2026 13:35:12 +0000 Subject: [PATCH] Record read-only launcher inspection trust prerequisite and caller inventory Card: SAU-384 --- docs/SAU-384-rehearsal.md | 2 +- docs/SAU-384-resume-1372.md | 32 ++++++++++++++++++++++++++++++++ docs/SAU-384-runbook.md | 6 +++--- 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 docs/SAU-384-resume-1372.md diff --git a/docs/SAU-384-rehearsal.md b/docs/SAU-384-rehearsal.md index ac3f7ac..e2b204e 100644 --- a/docs/SAU-384-rehearsal.md +++ b/docs/SAU-384-rehearsal.md @@ -10,7 +10,7 @@ Authority: [/docs/design/plugin-handover/design.md](/docs/design/plugin-handover 2. Resolved rollback choice: **unsubscribing works; merely re-keying the registration does not**. An actual pre-best-effort session component, reading a registration written by the target component, ignores `best_effort` and sends history to its new ID. Rehearsal: unsubscribe = one barrier callback and no gateway-shaped replay; re-key = barrier plus historical replay. The test used the actual old manager and a native callback sink, not browser SSE. The runbook now makes unsubscribe mandatory, using the card's authorized option. Do not claim an ID rename is safe. 3. **Closed in run 1369:** armed/exit-race delay-gate tests passed on real core with actual target WASM; replacement orchestrator readiness/one-owner/all-watch arming passed; actual old-manager/gateway rollback without re-registration reproduced unsubscribe safety and ID-only re-key replay. See [resume evidence](SAU-384-resume-1369.md). The delay/observation hooks were only in a disposable host copy, not the selected host artifact or committed code. -Use the card's explicit **unsubscribe** rollback option; an ID rename is not a safe alternative. The remaining blocker is the actual external host launcher/native installation manifest. Read-only SSH credential request **055995a51608c57dd25b323bea731fcb** is pending; it grants no stop/restart permission. +Use the card's explicit **unsubscribe** rollback option; an ID rename is not a safe alternative. The remaining blocker is the actual external host launcher/native installation manifest. Read-only SSH credential request **055995a51608c57dd25b323bea731fcb** is approved, but connection was refused before authentication for an unknown Citadel host. Mediator must add the observed public host key, then resume inspection; workers may not change live config. See [run 1372](SAU-384-resume-1372.md). No new approval request or live stop/restart permission is needed/granted. ## What was built and exercised diff --git a/docs/SAU-384-resume-1372.md b/docs/SAU-384-resume-1372.md new file mode 100644 index 0000000..3256983 --- /dev/null +++ b/docs/SAU-384-resume-1372.md @@ -0,0 +1,32 @@ +# SAU-384 resume — run 1372 + +No live operation is authorized or performed. The existing rehearsal and target bundle are unchanged. + +## Read-only launcher inspection: trust prerequisite + +The existing card-duration SSH grant was approved; no new credential request was filed. `ssh.exec` to `citadel.vpn.surma.technology`, login `surma`, with the read-only command `uname -m; ps -eo pid,ppid,user,args | grep -E '([h]erdr|[s]ausage)'` was refused **before authentication/command execution** because the host is unknown. The refusal reported: + +```text +SHA256:Fj3jS6DuV9SAo4PVQl/hTLI9SZPqWDwEd7E5hemfFkE +citadel.vpn.surma.technology ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB8gP37Pnb3EyyQnkR2VdeJNzGU3OAuyJc3E3a8XpDaV +``` + +The SSH skill permits installing this observed key without another person approval, but this card explicitly forbids worker live-configuration changes. Therefore the **mediator** must add the observed line to `/config/known_hosts`, preserving existing entries, then resume this worker. Surma does nothing. No private key was requested or fetched. `ssh.hosts_status` confirmed there is no already-trusted Citadel alias. The preceding run already tried the preferred read-only systemd-web route: it cannot identify the Herdr panel/native installation boundary. Do not substitute Nexus credentials, forward another host's SSH agent, bypass host checking, or treat this as permission to stop anything. + +After trust is installed: reuse the attached grant, inspect launcher/process parents/Herdr config read-only; pin actual world path, architecture, executable/runtime closure and event/completion stop/start interface; then build/rehearse that specific outer host swap in a disposable box. No live stop, restart, config change or deployment is part of the resume. + +## Caller-store inspection (not a frozen live audit) + +`evidence/resume-1372-caller-inventory.json` records a read-only observation via `cron.list`, `board.hook_list`, boot-file inspection and current-main schema inspection. All eight cron jobs currently have `next_run:null`; six are historical `session.send` jobs with the legacy inner `detach:true`. Convert their executable JSON fields once with the already-rehearsed cron procedure, preserving SQL IDs, schedule/outcome fields and prompt bytes. References to old RPCs *inside their prompt strings* are history/instructions, not nested executable configuration; do not rewrite or replay them. + +Five board hooks were observed. Only `mediator-done` calls `session.send`; its `{session,text}` payload already fits the target contract without legacy detach/notify/interrupt fields. Target board supplies a fresh per-attempt input ID and follow-up kind. No rewrite is needed for that observed hook. Other hooks target mail/orchestrator and must remain intact. + +The frozen operator audit must additionally include board `invariant_target.data` (an arbitrary RPC template without a read-only target-get API), hooks and historical `hook_attempts`, board subscriptions/batches, room workflow/action records, orchestrator run/skill-review/host-job records and target session event registrations if present. Distinguish resumable target inputs from old diagnostic snapshots and never infer safe replay from a stored call-looking string. Current schemas are pointers, not substitutes for predecessor decoding or the actual frozen bytes. This session does **not** claim that final audit complete. + +## Evidence/checks + +- SSH refusal/public trust metadata: `evidence/resume-1372-ssh-trust.json`. No secret/JWT is copied to artifacts. +- Existing box `tc-asa3yr` was inspected, not restarted. Executed controllers remain absent, including Python cache contents. Inspection log: `/logs/box/2026-10-31/tc-asa3yr-exec-803562803.stderr`, lines 1–40; the initial directory-list command returned 2 only because optional guessed directories did not exist, next inspection succeeded. +- Host worktree fetched origin and merged local `main`: already current at documentation commit `8bf2a3c661ead774689e508ed45f609b40f60dd1`. Initial merge used `target` instead of the typed `rev` argument and was rejected without changes; corrected call succeeded. +- JSON/caller/trust/manifest assertions and controller/cache-absence checks passed: `/logs/box/2026-10-31/tc-asa3yr-exec-666665822.stderr`. All 20 retained target file sizes/SHA-256 matched the updated manifest: `/logs/box/2026-10-31/tc-asa3yr-exec-3598104138.stderr`. No executable test/controller was written for these inline checks. +- No Rust/plugin behavior changed or rebuilt. Run 1369's forced-recompile workspace pass and builds 1600–1603 remain the relevant gates. Independent plugin recovery follow-ups remain independent. diff --git a/docs/SAU-384-runbook.md b/docs/SAU-384-runbook.md index 5ae596b..434342f 100644 --- a/docs/SAU-384-runbook.md +++ b/docs/SAU-384-runbook.md @@ -1,6 +1,6 @@ # One-off handover operator runbook — blocked draft -Read [rehearsal/results](SAU-384-rehearsal.md) and [resume verification](SAU-384-resume-1369.md), the [authority](/docs/design/plugin-handover/design.md) and [approval gates](/docs/symphony/artifacts/SAU-356/plan.md). This is **not** authorization to execute. Workers must not perform these live steps. Mediator/orchestrator alone lands and executes. +Read [rehearsal/results](SAU-384-rehearsal.md), [resume verification](SAU-384-resume-1369.md) and [inspection prerequisite](SAU-384-resume-1372.md), the [authority](/docs/design/plugin-handover/design.md) and [approval gates](/docs/symphony/artifacts/SAU-356/plan.md). This is **not** authorization to execute. Workers must not perform these live steps. Mediator/orchestrator alone lands and executes. ## 0. Finish the installation manifest (no live mutation) @@ -10,7 +10,7 @@ Bundle: `/docs/symphony/artifacts/SAU-384/target-artifacts.tar.gz`. - Verify bundle hash against `bundle-sha256.txt`, then every extracted target file against `manifest.json`. This bundle contains target binaries/static assets and the one-file SDK patch, **not** a migration executable. - Record the actual native service owner/launcher, its synchronous stop/start commands, automatic host restart mechanism, runtime closure and native architecture. Manifest fields are deliberately null until inspected. The bundled host is a local **aarch64-linux debug-profile, stripped** build of `e09c4f9f`; it has not been established as the right live-host installation package. Stop here on a mismatch; build/rehearse the right artifact rather than substituting an untested binary. - Pin all old plugin hashes, full boot bytes, current SDK source hashes, caller configuration, and `/proc/*/info.json` inventory. Record active agent/tool processes and any external effects whose result is uncertain. Do not infer settlement from the last assistant text. -- Inventory executable caller payloads in cron jobs, boot and other saved callback/action/configuration stores using their predecessor schemas. History text mentioning an old RPC is not an executable payload. The tested converter covers `jobs.data` where `method == session.send`; it does not claim a full live caller-store audit. Unknown executable references, `session.work`, `session.cancel`, ambiguous `interrupt:true`, invalid text or unexplained notify destinations stop preflight for explicit resolution. +- Inventory executable caller payloads in cron jobs, boot and other saved callback/action/configuration stores using their predecessor schemas. History text mentioning an old RPC is not an executable payload. The tested converter covers `jobs.data` where `method == session.send`; it does not claim a full live caller-store audit. Run 1372 observed six historical legacy session cron jobs (all jobs have `next_run:null`) and one already-target-valid board session hook. See `evidence/resume-1372-caller-inventory.json`; the frozen audit must also cover board `invariant_target.data`, hook attempts/subscription batches, room/orchestrator workflow records and any session event registrations. Diagnostic call snapshots and RPC names inside prompt strings must not be rewritten/replayed as executable configuration. Unknown executable references, `session.work`, `session.cancel`, ambiguous `interrupt:true`, invalid text or unexplained notify destinations stop preflight for explicit resolution. - Source references for predecessor formats: session `f9122585`, `src/main.rs` Paths (`pending.jsonl`, `cancel`) and `src/queue.rs`; cron `ba75c102`; target cron README's one-off conversion procedure. Active old component hashes are separately captured; do not equate a source reference with its compiled active binary without checking. - Confirm the frontend artifact is approved commit `3d76026749a5d21722033bbfe8743ac368946b0f`; keep `publish:hold` until step 5. Do not publish a worktree snapshot or release it early. - Rollback uses the card's explicit **unsubscribe** option. ID-only re-key is **unsafe**, proven again with the actual old manager and gateway in run 1369. Prevent the gateway from re-registering `web-chat` against an old session binary; never call `web.start` in that state. A different proposed “re-key” needs its own defined semantics/rehearsal. @@ -23,7 +23,7 @@ Run the one-off controller **outside the world and outside the service/process g Close the actual ingress and stop the **external host/service owner** synchronously using the pinned command. Await its shutdown/process-exit completion, not a loop over `/proc`, endpoint retry, sleep, or a set of stop requests. If an outer launcher would respawn the host, deliberately stop that owner too; do not edit boot config while agents can still run. `core.restart`/exit-75 is a restart request, not stop-all. The real core's `Host.stop` suppresses service restart admission and stops all trees in parallel. Design budgets are grace 5 seconds and separate EOF-stop 5 seconds, not arbitrary new timeouts. -The rehearsal's native external controller awaited `Host.stop`, asserted its process table was empty, then awaited host exit. The live equivalent needs the actual launcher identity from step 0. **That identity is currently missing; do not invent a unit name.** Source config has `services.sausage.enable = false` and describes a Herdr panel; read-only systemd-web inspection corroborates a user Herdr socket, not a system `sausage.service`. Credential request `055995a51608c57dd25b323bea731fcb` awaits approval for read-only inspection to pin the actual panel/launcher. It is not permission to stop or change it. +The rehearsal's native external controller awaited `Host.stop`, asserted its process table was empty, then awaited host exit. The live equivalent needs the actual launcher identity from step 0. **That identity is currently missing; do not invent a unit name.** Source config has `services.sausage.enable = false` and describes a Herdr panel; read-only systemd-web inspection corroborates a user Herdr socket, not a system `sausage.service`. Credential request `055995a51608c57dd25b323bea731fcb` is approved for read-only inspection, but SSH refused the unknown Citadel host before authentication. Mediator must install the observed public host-key line from `evidence/resume-1372-ssh-trust.json` in `/config/known_hosts` and resume inspection; worker configuration changes remain forbidden. No additional person approval is needed for that trust entry under the SSH skill. It is not permission to stop or change the launcher. ## 2. Back up frozen state and plan changes