Saphan StudioDocs
Troubleshooting

Identity and actors

Refusals about who is acting, the keys that prove it, and the ceremonies that mint them.

21 classes. 7 explained, 14 named only.

Decisions in this product are signed. That means most refusals in this family are about one of three things: a name that is not the enrolled name, a key that is absent or already there, or a ceremony that will not run twice.

Almost every refusal here is deliberately not self-correcting. The product will not guess which identity you meant, because the guess would be right almost every time — and being right almost every time is what makes a silent correction dangerous rather than safe.

The name is one fold away from an enrolled one

Class: actor-near-miss.

What it means. The name you passed differs from an enrolled, active name only in case or whitespace. Nothing was written.

What to do. The refusal prints the canonical spelling in quotes, so a trailing space is visible, and it prints the exact --actor argument to re-run with. Use it.

Do not run the enrolment ceremony. Enrolling the misspelling would mint a second registry row for one person. This class exists as its own class precisely so that you are not sent to enrol.

Why the product will not just fix it for you. The actor name is signed content. A silent normalisation would write a name you never typed into bytes your key then attests to.

The name is unknown to the registry

Class: actor-not-enrolled.

What it means. The name matched no enrolled, active actor. Two cases produce it that are worth telling apart before you act, because in both of them the remedy the refusal suggests is the wrong one:

  • A look-alike character. A name written with a Cyrillic character that is visually identical to a Latin one is not treated as a near miss — the fold is case and whitespace only, on purpose. The refusal will invite you to run the enrolment ceremony. ⛔ Do not run it. Nothing was substituted and nothing was written, so no signature is at risk; what is wrong is only the advice, and following it would mint a second registry row for one person. Check the roster with saphan actor list and correct the name at the keyboard.
  • A retired actor. A name one fold away from a retired row is actor-not-enrolled too, and the refusal will again invite you to enrol. ⛔ Do not enrol someone the workspace retired on purpose.

⚠ In both cases the refusal is correct and its advice is not. This is known.

Why the comparison is not widened to look-alike characters. A confusable fold that fires wrongly merges two identities inside signed content — silently, and irreversibly, because the distinct spellings were the only evidence the two were ever different people. The narrow fold's worst case is a misleading sentence in a refusal a human reads.

The workspace has no signing key

Class: owner-key-absent.

What it means. Owner-class decisions are signed by the workspace key. This session has none, so the decision was refused and nothing was written. A brand-new workspace has none.

What to do — and note that every step is a human act.

  1. Check what is there. saphan actor list --workspace <path> reports the key-id if a key exists, or says the workspace key is absent. ⚠ This verb is a read: it never mints. A read that minted would hand every keyless session a fresh self-authorising key as the price of looking.
  2. Mint the key. saphan actor init --workspace <path>. It writes the secret readable only by you, writes a cover beside it so the value can never be committed, and prints the derived key-id. The value itself is never printed, logged, or returned on any surface.
  3. Enrol the key-id. Minting is not enrolment. Until an actor row names this identity the gate still refuses, under a different class naming the roster rather than the key. Enrolment is an owner-root ceremony, run on the machine holding the root key.
  4. Confirm. saphan actor list now shows the key-id and marks the roster row that matches it.

⚠ If you are running against the wrong workspace, the fix is not to mint a second key — re-run against the workspace that owns the record.

A key is already at the secret path

Class: workspace-key-exists.

What it means. saphan actor init found a file already at the secret path and refused rather than clobber it. That is all this refusal proves. It fires on any existing file — including a dangling symlink or a directory — because it asks whether the file is there, never whether it is good. ⚠ So it does not prove the workspace has a usable key.

There is no force flag, for the same reason root creation has none: a re-mint derives a new key-id, and every decision this workspace has already signed would verify against a key that no longer exists.

What to do. Ask saphan actor list. It reports the key-id, and a refusal from it means the file is not a key. Then:

what actor list or a gate sayswhat to do
the workspace key is absentMint it with saphan actor init.
the workspace secret has loose permissionsRestrict the file's mode to owner-only. ⛔ Do not re-mint: the key is fine, its mode is not.
the workspace secret is malformedRestore the file from wherever this workspace's secret is kept. Only if it is genuinely lost: remove it and mint a new one — accepting that every signature the old secret wrote stops verifying.

A root already exists on this host

Class: ceremony-root-exists.

What it means. The root ceremony was re-run on a host that already has a root. There is no force flag. Re-minting would silently invalidate every prior signature.

What to do. If you meant to mint a root, you are on the wrong host or the root you are looking for is already here. Establishing a new root is re-founding the fleet's trust, not a re-run — treat it as such.

There is no root rotation. Losing the root paper means you can never sign a new delegation or admit a new machine under the existing chain. Store it as you would a vault key, and consider geographically separated copies with equal physical protection.

The read-back was pointed at the ceremony's own word list

Class: ceremony-readback-self-referential.

What it means. The root ceremony requires a witness to read the recorded words back, and the engine compares them position by position. Only on an exact match is the key material erased from disk. The read-back was supplied from a file, and that file was the ceremony's own word list — which proves nothing.

What to do. A file read-back exists for a witness on a separate machine. Point it at what the witness wrote, not at what the ceremony produced. ⚠ Accepting the air-gap banner non-interactively is allowed and never skips proof-of-capture; this refusal is that proof working.

The product refuses to start at all

Class: custody-binary-unsafe.

What it means. The program checks the custody of its own binary before it does anything. If that check refuses, nothing ran: no command, no workspace resolution, no write.

What to do — the refusal is self-diagnosing, so read it before you type anything.

  1. Read the whole refusal. It lists every broken clause, not the first one, so you fix the misconfiguration in one pass rather than re-running to discover the next problem. Each clause carries a literal, runnable fix command.
  2. Check the two identity lines against each other — the line naming the binary's owner and the line naming who you are running as. If the owner is neither the system nor you, the binary belongs to a third account: that is exactly the misconfiguration this check exists to catch, and copying a permissions command will not fix it. ⛔ Decide which account should hold the control plane before you change anything. This is a human decision, and the product deliberately does not make it for you.
  3. Run the printed fix commands, in the order printed. They change ownership, group and mode, and nothing else. Whether a line needs elevation is computed for you from who owns the thing being changed and who you are — not from the kind of command.

Named, not yet explained

These classes are real, deliberate and named. This section does not yet carry their resolution. If you are holding one, the name below is what it indicates, and asking us is what will get it written down.

classwhat its name indicates
actor-registry-absentThe workspace has no signed actor registry at all. ⚠ Worth knowing: an absent registry is not consent — it is not read as "everyone is allowed".
actor-registry-unreadableA registry exists and could not be read. ⚠ Treat as unknown, never as empty.
actor-registry-unwiredNothing connected the check to a registry store, so this build cannot say who may sign. The roster report describes the same condition as a wiring fault rather than a workspace one, and says explicitly: ⛔ do not enrol anyone over it.
actor-retiredThe named actor is in the registry and has been retired. Recorded decisions made under a retired name stay valid; what a retired name cannot do is write a new one.
ceremony-capture-mismatchThe witness read-back did not match the recorded words position by position.
ceremony-intermediate-existsAn intermediate authority is already present where the ceremony would mint one.
custody-loose-permsSomething the product owns is reachable by users who should not reach it.
custody-malformed-secretA secret file exists and is not a well-formed secret.
no-identityNo identity was resolved for the act at all.
scope-deniedThe identity is known and is not permitted this particular thing.
session-actor-key-absentThe session has no actor key, as distinct from the workspace having no key.
token-unmappedA token was presented that maps to no known identity.
trust-key-existsA trust key is already present where one would be established.
trust-refusedTrust between two parties was not granted.