Saphan StudioDocs
Getting started

Acts that need the owner present

The verbs that refuse unless the owner is present, and a numbered runbook for each: what to run, what success prints, and what to do when one refuses.

Some acts change who the fleet trusts: which machines it may use, which host keys it accepts, who may sign, and which laws bind. Each of these is an owner act. The owner is a person, not a role an agent can hold, and a verb that writes this kind of record refuses unless that person is present when it runs. A refusal writes nothing.

Which acts

ActWhat it changes
saphan gate (owner-class gates: stop1, stop2, merge, promotion) and saphan gate batchrecords a human gate decision
saphan merge, saphan accept, saphan landlands a stream
saphan teardown, saphan teardown batchrecords the teardown decision and removes the worktree
saphan claim --release, saphan claim --release-torn-downends another actor's hold on a stream
saphan workspace migrate --applyre-signs remote machines' rows when it moves their worktrees
saphan machine admit, saphan machine renewadmits a machine to the fleet, or renews its admission
saphan machine set-* (set-account, set-artifact-root, set-binary, set-container-runtime, set-price, set-repo-dir, set-slots, set-worktrees-root)changes how an admitted machine is used
saphan machine retirewithdraws a machine from the roster
saphan machine pinseeds the fleet's host-key pin store
saphan machine unpinremoves a machine's host-key pin
saphan trust initmints the root key
saphan trust delegateroot-signs a delegation
saphan laws ratifyroot-signs the laws manifest
saphan identity importseeds the signed identity store
saphan identity revokerevokes an actor, binding or issuer

The list is not complete: a few rarer acts (among them saphan trust owner-key init, saphan identity import-machines with admissions, saphan store mark and the console device verbs) ask the same way, and the command reference says so on each. This page gives a runbook for the last eight rows.

Not on the list. These need no owner at the keyboard, so an install can run them unattended: saphan init, saphan bridge init, saphan machine csr, saphan identity enroll and saphan machine seat.

How the prompt waits. The act prints a one-time six-character code on your terminal — the terminal itself, not the command's output — and asks you to type it back. The prompt is printed once. Enter with no code asks again, and a wrong code prints one line and asks again. Only Ctrl-C, closing the terminal, or 5 wrong codes cancel it, and a cancel writes nothing. The same code is valid for the whole act. See Reading the CLI's output.

Where it refuses. With no controlling terminal (a script, a CI job, an agent's process) the act is refused and nothing is written. Inside the console's browser terminal it is refused too: there the owner's hand is the passkey prompt the console offers for that act.

⚠ What the code proves. It stops an agent that would otherwise perform an owner act simply because nothing refused it. It does not stop a hostile process running as the same user, which can open a terminal of its own. Treat the refusal text as the authority: it says what was measured, what was refused and what to do next. The eight verbs with a runbook below have no dry-run form, so there is no rehearsal: read the runbook before running the real line.

The pattern every runbook follows

  1. The owner runs the line at their own terminal, on the machine the act belongs to.
  2. Read what it prints. Success names the record it wrote. Anything else is a refusal.
  3. If it refuses, read the first sentence. It says what was measured and what to do next. An agent that meets such a refusal stops and hands the line to the owner.

saphan machine retire

This act needs the owner present. It marks an admitted machine as withdrawn from the roster. The row stays: retirement is a mark, never a delete, and saphan run --machine refuses to place work on a retired machine.

  1. Free the machine first. Move any seated identity to another machine with saphan machine seat, and wait for any live run on it to end.
  2. The owner runs saphan machine retire --machine <id> --retired-by <owner> --workspace <path>.
  3. Success marks the machine retired. saphan machine list still shows it, marked retired.

When it refuses. For a missing owner, the refusal names the act and says the owner must run it. Other refusals are about the machine and are unchanged: machine-not-admitted (check the id), machine-already-retired (nothing to do), machine-retire-seated (the refusal names the seated identities; move them), machine-retire-live-run (the refusal names the run; wait for it).

saphan machine pin

This act needs the owner present. It copies a host-key trust decision you already made, from your own known-hosts file into the fleet's pin store, for a machine admitted earlier.

  1. Connect to the machine once yourself with ssh, so its key lands in your own known-hosts file.
  2. The owner runs saphan machine pin --machine <id> --workspace <path>, or --all for every ssh machine.
  3. Success reports each host as seeded or already-pinned. --all also reports not-found, revoked and failed per host, and exits non-zero if any host failed.

When it refuses. For a missing owner, run it again as the owner. For a machine, the refusal names its own class: machine-pin-operator-entry-missing (do step 1), machine-pin-revoked (a revoked key is never adopted), machine-pin-not-ssh, machine-pin-ssh-host-missing (record the host with machine renew --ssh-host), machine-not-admitted.

saphan machine unpin

This act needs the owner present. It removes the pin for one machine and adopts nothing. After it runs the host has no pin, so the next contact is first contact again.

  1. The owner runs saphan machine unpin --machine <id> --workspace <path>.
  2. Success reports the pin removed. A host with no pin is reported, not refused, and the verb exits 0.
  3. To trust the host again, run saphan machine pin (see above) as a separate act.

When it refuses. For a missing owner, run it again as the owner. Otherwise the refusal is machine-not-admitted, machine-pin-not-ssh or machine-pin-ssh-host-missing, as for machine pin.

saphan trust init

This act needs the owner present. It mints the root key at ~/.saphan/trust/root.key. Everything the root signs later depends on it, and it is never re-minted.

  1. Choose the machine that will hold the root key. Every later root-signed act runs there.
  2. The owner runs saphan trust init.
  3. Success prints the apk- pin id. The private key is never printed. Record the pin id.
  4. Check the result with saphan trust doctor.

When it refuses. For a missing owner, run it again as the owner. If a root key already exists the verb refuses and does not overwrite it, because a re-mint would invalidate every signature the old root made. Do not delete the key to get past this; restore it, or stop and ask.

saphan trust delegate

This act needs the owner present. It root-signs a delegation that lets another actor sign orders for named streams between two times, and records it in the signed identity store. Two more powerful scopes exist (--scope seat-issuing and --scope owner-presence); they only preview what they would sign until you add --yes.

  1. Have the delegate's actor name, apk- key id and base64 public key.

  2. Choose the streams (--stream <slug>, repeatable, or *) and the --nbf and --exp times. ⚠ Leaving --stream out grants every stream.

  3. The owner runs, on the machine holding the root key:

    saphan trust delegate --delegate-actor <name> --delegate-key-id <apk-id> --delegate-pub <base64> --stream <slug> --nbf <rfc3339> --exp <rfc3339> --workspace <path>
  4. Success records the grant and regenerates the delegations section of the actor roster file.

When it refuses. For a missing owner, run it again as the owner. A missing or unreadable root key means this is not the machine that holds it.

saphan laws ratify

This act needs the owner present. It root-signs a new laws manifest over the exact bytes of the documents you name. A document changed afterwards no longer matches the manifest.

  1. Finalise the law documents. Ratify the bytes you intend to bind.
  2. The owner runs saphan laws ratify <path>... --note "<why>" --workspace <path>.
  3. Success writes the manifest with the next serial number. saphan laws cat reads it back.

When it refuses. For a missing owner, run it again as the owner. A missing or unreadable root key means this is not the machine that holds it.

saphan identity import

This act needs the owner present. It reads the roster files once and writes one root-signed record per row, all or nothing. It runs once per store.

  1. Run it on the machine holding ~/.saphan/trust/root.key.
  2. Check the roster files have at least one actor row. An empty roster is refused.
  3. The owner runs saphan identity import --workspace <path>.
  4. Success regenerates the roster files as a read-only audit projection.

When it refuses. For a missing owner, run it again as the owner. A store that already holds identity records is refused, by design: import never merges. To add or change a record afterwards, use saphan identity enroll and saphan identity revoke.

saphan identity revoke

This act needs the owner present. It appends a root-signed revoke record for an actor, binding or issuer. Nothing is deleted: a revoked actor reads as retired and a revoked binding or issuer as absent.

  1. Run it on the machine holding ~/.saphan/trust/root.key.
  2. The owner runs saphan identity revoke --kind <actor|binding|issuer> --subject <name> --workspace <path>.
  3. Success appends the record and regenerates the roster files.

When it refuses. For a missing owner, run it again as the owner. A subject with no live record is refused, so check the spelling with saphan actor list.

On this page