saphan actor
Signing actors: the workspace key identity and the actor registry
saphan actor
Signing actors: the workspace key identity and the actor registry
Subcommands
| Subcommand | What it does |
|---|---|
saphan actor init | Mint this workspace's signing key at .saphan/secret (O_EXCL, never re-mints) |
saphan actor list | List enrolled actors and this workspace's key-id — never the secret value |
saphan actor init
Mint this workspace's signing key at .saphan/secret (O_EXCL, never re-mints)
Mints this workspace's 32-byte signing secret at <workspace>/.saphan/secret,
mode 0600, and prints the resulting wsk- key-id. This is the FIRST-MINT
ceremony: the owner-class verbs (saphan gate, saphan merge) sign every
decision with this key and refuse a session that has none.
O_EXCL, and there is no --force: an existing secret is REFUSED (workspace-key-exists), never clobbered. A re-mint would derive a NEW key-id, and every gate row this workspace has already signed would then verify against a key that no longer exists. The refusal fires on any existing secret file — including a loose-permissioned or a malformed one — so a broken secret is repaired by restoring or removing it deliberately, never by a verb that silently mints over it.
The secret's VALUE is never printed, logged, or returned on any surface (§1.6): only its key-id (a truncated one-way fingerprint), its path and its mode are ever shown.
Minting is not enrolment. This verb writes NO registry row — same as
trust init, whose registry act is the separate trust delegate. To let
this key's decisions pass the actor guard, enrol its key-id afterwards:
saphan identity enroll --kind actor --subject <name> --key-id <the wsk- id>
Run it in the workspace that owns the record: an unresolved workspace is refused before anything is written, so a mint can never land in an arbitrary CWD. See docs/Security §"First mint: giving a workspace its signing key".
saphan actor list
List enrolled actors and this workspace's key-id — never the secret value
Read the append-only actor registry (bridge/registry/ACTORS.md) and this workspace's signing key-id, and print one row per actor (actor · key-id · status · added-by · ts), marking the row that matches this workspace's key. The workspace secret is loaded ONLY for its key-id (a fingerprint) — its VALUE is never read into output, logged, or printed (§1.6). This verb is a READ: it never mints the secret. A workspace with no secret reports its absence and omits the key-id line and the ThisWorkspace marker — run "saphan actor init" to mint one before running owner-class verbs.
A secret with loose permissions (group/other-reachable) is REFUSED with a chmod remediation and its value is not loaded. The registry is READ only: enrolling or retiring an actor is a human-gated edit to ACTORS.md by convention, never an engine verb (order SIGN-02).
It also reports the ROSTER GAP: every actor name this workspace's gate log already records, how many fleet_gate rows each has written, when each was last seen, and what the actor guard would say about that name on its next gate write. Names the guard would refuse are marked. Each name is resolved through the same enrolment question the write path asks, so this report cannot disagree with the gate.
The gap section carries its own status word — read | gate-log-absent | gate-log-unreadable — and an empty gap means "nobody would be refused" ONLY under 'read'. 'gate-log-unreadable' is NOT an empty gap: it means the log exists and could not be tallied, so who would be refused is unknown.
This verb writes nothing and refuses nothing on account of a gap. See docs/Security §12, "Reading the roster gap before a gate decision is refused", for the operator runbook.