Accepted adr-002 · 2026-09-24

Supervision and Administration Are Clients of the Operation Layer: a Desktop App over the signatio CLI, No Listener in signatiod

Context

With several projects, clients, principals, hats, decisions and chains, an operator cannot see what happened, where things move and whether flows are right by composing signed requests by hand; supervision is used locally, like Longhorn's or a Kubernetes dashboard, and remotely through `ssh -L` or `kubectl port-forward`. signatio already has the shape the models it follows have (DD-eca's `Opus`, ontoref-daemon's `dispatch_op_request`): one operation layer, the dispatch in code/crates/signatiod/src/ops.rs, which the CLI reaches through `POST /v1/op`. redb holds its file exclusively, so no other process reads the store while the daemon runs (measured by the operations and CLI session, backlog bl-007). The agent runs with the operator's uid on the operator's host: a surface on localhost that accepted changes without a proof would be the path "keys stay out of the agent's reach, by mechanism" forbids. What is most worth seeing — a custodian that is down — is what an observer inside signatiod cannot show, since signatiod does not start without its custodian; that case is `signatio-boot service status`'s (wo-signatio-service-status-v2), outside the daemon.

Decision

EVERY SUPERVISION OR ADMINISTRATION SURFACE IS A CLIENT OF THE OPERATION LAYER. It reaches the authority only through the `signatio` binary and `POST /v1/op`, with the proofs the CLI carries — a live client's token, the person's signature for a human act — and it reads each answer only after the CLI verified it against the pinned authority key. No surface reads the store, and no surface can do what the same user could not do with the CLI. What the operation layer cannot show — the pack's units, a custodian down — a surface reads from the launcher, as ADR-004 decides.

THE SURFACE IS A DESKTOP APP OVER THE CLI. A Tauri app: its webview talks to its own Rust process over Tauri's IPC — no port — and each command it offers runs `signatio … --installed <handover>` as a subprocess and reads its JSON. The CLI is not linked as a library: the app has exactly the CLI's reach, by construction. Remote supervision opens `ssh -L` or `kubectl port-forward` to signatiod's TLS port and runs the same CLI through it.

SIGNATIOD OPENS NO OTHER LISTENER. It serves /v1/challenge, /v1/token and /v1/op on its declared `listen`, and its configuration declares no observation or administration socket. The processes that open the store are signatiod and, with the daemon stopped, `signatio bootstrap`.

A PERSON'S ACT IS PREPARED, SIGNED OUTSIDE, AND SENT. The app prepares the statement of an Assign or a rotation, the person signs it outside the app with minisign or an authenticator, and the app submits the result — the way PrepareRotation works. The administrator's client proves itself with SignedChallenge over a protected key, so the person's presence is shown by the proof, not by the network.

THE APP IS ITS OWN MEMBER. It lives in its own member of signatio's constellation, outside code's workspace and outside the pack: the pack stays binaries, schemas and launcher, and no build of signatio carries Tauri or a webview.

WHAT IT SHOWS IS NOT EVIDENCE. The app renders verified answers; its rendering is not signed and proves nothing. It says so on its own surface: in its About and on its Settings page, not on every page — a capture of another page does not carry the notice, and that is accepted.

Constraints

Alternatives considered