Saphan StudioDocs
Audit and compliance

Checks you can run yourself

The verifications that do not depend on trusting the control plane, and the two ceremonies a human performs by hand.

A trail you have to take on trust is a story. This page lists the checks that convert parts of the story into things you established yourself, and it is deliberately short — a long list of checks nobody runs is worse than a short list somebody does.

Run these against a live deployment, not against a demonstration. Three of the four are things your own team performs; none of them requires our involvement.

Four checks that do not require trusting us

CheckWhat it establishes when it passes
Verify the governance history against your root public key aloneThat every admission, delegation, seat and capability grant in the log is what it says it is — established without trusting the control plane that produced it. This is the check to run first, because everything else in the record is read in the light of who was allowed to act.
Walk the chain for a standing instructionThat the bytes of the document agents worked under hash to its ratified entry, in a manifest whose own signature validates against your root, at or above the serial you expect. This is how the agent worked under the rules we approved stops being an assertion.
Reproduce a confinement claim from a run recordThat the boundary the record claims for a run is the boundary that tool and that policy actually produce. The row names the enforcing tool and the directories that stayed writable — run the same tool with the same policy and try to write outside them. A claim your team can reproduce is worth more in a report than a claim we make.
Re-resolve a binary's identity on the runner and compareThat what executed on that machine is what the record says executed. The value in the record was resolved on the machine itself rather than on the control plane, so this comparison is a real one rather than a copy of a copy.

On the third check, one caution. A matching digest establishes that bytes match a value. It does not establish that they are the current bytes — a genuine older binary with a genuine matching digest passes. What this does not prove states that limit and what follows from it.

Signals worth monitoring between audits

These are the readings that change what you should do next. Everything else in the record is for answering questions after the fact.

SignalWhy it matters
A non-empty roster gapNames in the gate log that the actor guard would refuse today. Expected right after a roster change and unexpected otherwise — each one is a person who will meet a refusal at their next decision.
A roster gap that could not be measuredThe gap is unknown, not empty. No conclusion about who would be refused may be drawn from it. Repair the store and read it again.
A rising count of one refusal classRefusals name their rule and reason. A class that starts climbing is usually a change in your estate — a machine that lost a facility it used to have, for instance — rather than a change in the software.
Host key rotation eventsExpected on machines that regenerate keys at boot, and worth a question anywhere else. Rotation is permitted only where policy allows it and is always recorded.
Capability staleness reasonsA lease expiring is routine. Drift in a machine's own manifest, or a probe that failed, is not — those mean the machine changed underneath you.
Admissions of unsigned instructionsRecorded even while enforcement of instruction signatures is switched off. This is the number to read before you tighten that setting, not after.
Gate decisions that fail re-verification at mergeA tampering signal. Treat it as one — not as a defect report.

Ceremony — reading the roster gap before somebody is refused

This is a human act, performed at a terminal, and it is the one most often skipped until it fails at an awkward moment. Actor status is enforced on every gate decision — the name recorded as having decided must be enrolled and active in the signed registry or the write is refused, with no warning tier and no grace period.

  1. Read the gap.

    saphan actor list --workspace <path>

    Add --json for a machine-readable report.

  2. Read the status word before you read any rows. It tells you whether the gate log was measured at all. A log that was measured gives you an authoritative list; a workspace never written to says so; a log that exists and could not be tallied is unmeasured, and is not an empty gap. In that third case, repair the store and start again — no conclusion about who would be refused is available from it.

  3. Read the marked lines. Every marked name would be refused at its next decision, and the reason is printed beside it. Two of those reasons are traps worth knowing in advance. A workspace with no registry at all is not consent — it is an absence, and it refuses. And a reason that says the binary and the store disagree about their schema version means the question was never asked; treat it as unmeasured, never as a pass.

  4. Decide per name, not in bulk. A name that has stopped deciding needs no registry entry at all, and leaving it in the gap is a legitimate outcome — the decisions it already wrote keep their verification either way. Only a name that must keep deciding needs the ceremony in the next step.

  5. Enrol the names that must keep deciding. This is an owner act, performed on the machine that holds the root key.

    saphan identity enroll --workspace <path> --kind actor --subject "<name>" --key-id <id>

    The refusal an unenrolled name meets at the gate prints this same command with the name already filled in.

  6. Read it back. The same command as step 1. The name you enrolled should now be unmarked.

actor list writes nothing, refuses nothing and changes no gate's outcome. It reports; the decision is yours.

When the name is nearly right

A name one fold away from an enrolled active actor — differing only in case or in whitespace — is refused on its own named class, nothing is written, and the refusal prints the canonical spelling in quotes so that a trailing space is visible, together with the exact argument to re-run with.

Do not treat that as an invitation to enrol. It is a typo, and enrolling it would create a second registry entry for one person. Correct it at the keyboard.

The engine will not silently correct it for you, and that is the right call rather than a missing feature. The actor field is signed content: a silent normalisation would write a name you never typed into bytes your key then attests to — and it would be right almost every time, which is what would make it dangerous rather than what would make it safe.

Look-alike characters are out of scope and behave differently. A name written with a character from another alphabet that renders identically in every terminal is not a near miss — it is an unknown name, and the refusal it earns 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. Check the roster and fix the name at the keyboard.

Ceremony — explaining a historical spelling with an alias

Use this when the gate log holds an old spelling of a person who still decides under a current one, and you need an aggregate by person to come out right. It is an owner act, performed on the machine that holds the root key.

  1. Confirm it is historical, not a typo being made today. The roster gap shows which names the log records and when each last wrote. A spelling still in active use is corrected at the keyboard, not enrolled.

  2. Enrol the alias.

    saphan identity enroll --workspace <path> --kind alias --subject "<old spelling>" --canonical "<registry handle>" --reason "<why>"

    The subject is the spelling as it appears in the log; the canonical name is the actor it resolves to. An alias that resolves to itself is refused by name.

  3. Read it back.

    saphan gates --workspace <path>

    Affected rows now render the stored spelling with a marker showing the actor it resolves to. The line explaining what the marker means rides every render, so a reader who has never seen one is not left guessing.

  4. Withdraw one that was wrong.

    saphan identity revoke --workspace <path> --kind alias --subject "<old spelling>"

    This appends a new signed entry rather than deleting anything. The alias then reads as absent.

What an alias can and cannot do, because an auditor will ask. It resolves names for aggregation and never a signature — the verification verdict never consults it, so enrolling or withdrawing one can never change whether a row verifies. And it is bounded below a recorded boundary in the log's own history. At or below that boundary an old spelling is history. Above it, an old spelling is a forgery rather than an old spelling, and no alias applies. Because a row being written today is always above the boundary, enrolling an alias can never make a name able to write a new decision.

Hardening this section will not do for you

These are yours to add, and an auditor should expect to see them in your own controls rather than in ours.

  1. Restrict outbound network access per machine, at your own network boundary. This is the highest-value control available to you today and the one the product does not provide by default. Deny by default; allow only what you actually need; force name resolution through a resolver you control, because permitting general name resolution permits data to leave regardless of any address list.
  2. Restrict the access used to reach runners — pin it to the control plane's own source address and to a single permitted command, and disable forwarding of every kind. Where your server offers one option that applies all present and future restrictions, prefer it, because it fails closed as the server gains capabilities.
  3. Never forward an authentication agent to a runner. Anything able to reach that socket can authenticate as you elsewhere.
  4. Keep nothing on a runner beyond that runner's own scope. Assume any secret readable on a machine is readable by every run on it. Machines that are also somebody's personal computer deserve specific thought about what is in that home directory.
  5. Keep delegation windows short, because expiry is the only revocation mechanism that exists.
  6. Switch on enforcement of instruction signatures once your signing practice is established. It is off by default so that adoption is measurable first — read the ledger, then flip it.
  7. Protect the control plane as your highest-value asset. It holds the means to reach every machine, and it is where instruction verification happens.
  8. Store the root material as you would a vault key. There is no root rotation, so losing it means re-founding the fleet's trust. Consider geographically separated copies with equal physical protection.

On this page