Verify a change yourself
Check one change on your own machine, with no Saphan store and no network, from three things you already trust and one folder someone hands you.
You can check that every document of one change was written by a key that chains to your organisation's owner root — on a machine that has no Saphan store and no network.
⚠ Two parts of this are not yet shipped as products. The folder is produced by a library call, not by a saphan command, and saphan-verify is not in the release packages. Until both ship, the owner's side needs engineering help to export a change, and you need the verifier built from source by someone you trust. What the check proves, once you have both, is below.
What you bring
| You need | Where it comes from | Why it must not come from the folder |
|---|---|---|
| The owner-root public key | The owner, in person or over a channel you trust. It is not a secret. | A folder that names its own root can name any root. |
| The deployment id | The owner or the console | It says which organisation's record you are reading. |
| The change id (the stream name) | The person who asked you to check | It says which change the folder claims to be about. |
| The bundle folder | The person who exported it | — |
The steps
-
The owner's side exports the change. The export is one folder: the revocation chain and epoch history up to the newest checkpoint, the signed identity rows, the log segment and the change's documents. ⚠ There is no
saphancommand that writes this folder in this release; it is produced by a library call. -
Copy the folder to your machine by any means — a USB stick, a file share, an attachment. Nothing in it is secret, and nothing about the copy has to be trusted.
-
Get the verifier.
saphan-verifyis a single program with no other part of Saphan behind it. ⚠ It is not part of the release packages, so it is built from source; compare its checksum with one the owner gave you. It opens only the folder you name; a file in the folder that links out of it is treated as missing. -
Run it, with absolute paths:
--roottakes the 64 hex digits of the public key, the same prefixed withed25519:, or a file holding either. -
Read the answer. It says either
change verified as of checkpoint <id>ornot verified: <which items>. The exit status is0only for the first,1when anything is not verified, and2when the command could not run.
What "verified as of checkpoint X" means
- Every document the bundle lists has an author key that chains to the root you gave, and no revocation that checkpoint X commits to disowns that key at the point it signed.
- It is a limit, not a courtesy. What the record did after X — a key re-granted, withdrawn or retired — is not covered. A newer checkpoint covers it; ask the owner for a newer export. It does not mean the key is still good today.
- The list of documents and the change id are the folder's own, and are not signed. The answer does not show that no required document was left out, and a fork is visible only if the bundle carries it. Ask the owner which documents the change must have, and note that the checkpoint is chosen by whoever exported the folder.
- A seat's validity window is not judged offline; the server enforces it when the seat acts.
- A machine certificate, where a document needs one, is carried in the folder and is not judged by this check.
- In a Solo deployment the answer carries
solo: seat-signature-only — actual execution and review are not proven. - Lines beginning
remark:list what the command cannot tell you. Read them; they are part of the answer. - The verifier states that each listed document's author key chains to your root. It does not, on the strength of that document, state that the executor's seat and the reviewer's seat differ. Compare the two author keys yourself if separation of duties is your question.
When it refuses
| What you see | What it means | What to do |
|---|---|---|
cannot-verify: no-checkpoint-covers-item | No person-signed checkpoint covers that document yet (a merge gate is covered only by a later checkpoint). | Ask the owner to sign a newer checkpoint and export again. |
cannot-verify: identity-row-bytes-missing | The folder leaves out a signed row the command needs to be sure no key was disowned. | Ask for a complete export; do not accept "it is probably fine". |
cannot-verify: log-segment-missing | The exported part of the log starts too late. | Ask for a complete export. |
cannot-verify: person-assertion-missing | A checkpoint the folder carries has no stored person assertion. | Ask the exporter to start the bundle after that link. |
cannot-verify: machine certificate not provided | The document needs a machine co-signature and the folder has none. | Ask for the certificate to be included. |
refused: revocation-set-incomplete | A revocation the checkpoint commits to is not in the folder, or a signed revocation is not committed. | Treat the bundle as damaged or tampered; ask the owner. |
refused: deployment-binding-mismatch | Something in the folder belongs to another deployment. | Check the deployment id you gave; otherwise ask the owner. |
fork or historical | The record has two assertions for one checkpoint, or the document predates the epochs. | Neither is a pass; ask the owner. |
| exit status 2 | The command could not run (a missing input or an unreadable folder). | The message names the missing input. |
Nothing the command prints turns cannot-verify, historical, fork or promise-not-applicable into a pass.
Where this sits
This is the check behind the statement on what a third party can check, and its limits are collected on the limits of the signed record. The list of checks that need no trust in the machine that produced the record is on checks you can run yourself.