Identity and PKI
Which keys exist, who holds each, what each may sign, and how a signature is checked.
Cryptographic inventory
| purpose | mechanism |
|---|---|
| Owner root identity, delegations, governance log, standing rules | Ed25519 |
| A person's approvals (orders, owner acts, sign-in) | passkeys (WebAuthn, P-256) |
| Owner presence through the gateway | a passphrase-sealed owner key |
| Per-run seat keys | short-lived Ed25519 keys whose private half never leaves the process that runs the agent |
| Intermediate CA and machine certificates | X.509, ECDSA P-256 with SHA-256 |
| Record and capture integrity within a workspace | HMAC-SHA-256, 32-byte key |
| Content identity (artifacts, rule documents, transfers) | SHA-256 |
| Key identifiers | SHA-256 over a domain-separation label and the public key, truncated to 12 bytes, hex-encoded, with a class prefix |
| Management interface | Saphan Studio's own OAuth authorization server; outside OpenID Connect issuers are trusted only from a list, signing with RS256 or ES256 |
The accepted algorithm set is closed, and the algorithm name is part of the signed material — so a signature produced under one algorithm cannot be reinterpreted under another. On the instruction path Ed25519 is pinned and an HMAC envelope is refused before any key material is touched: a local process holding the shared workspace HMAC key can sign records, but cannot mint a standing rule or an instruction. An unknown version is refused, never guessed.
The trust root is born on paper
The owner root is an Ed25519 key pair created in a ceremony (saphan ceremony root), whose purpose
is to produce a root whose private half exists only on paper.
- The engine generates the key pair with the platform's secure random generator.
- It renders the private half as a numbered word list over a 2,048-word dictionary built into the binary, eleven bits per word. No printer driver, no external tool, no clipboard, no shell-out to any cryptographic utility.
- It requires proof of capture: a witness reads the words back from the paper and the engine compares them position by position.
- Only on an exact match does it erase the private key and the word list from disk. The public key stays, as proof that a root exists.
A mismatch erases nothing and refuses by name, reporting word counts and positions only, never a word value — so the failure message is safe to paste into a ticket. A read-back pointed at the engine's own word-list file is refused.
Root creation refuses to overwrite an existing root, because re-minting would silently invalidate every
earlier signature. If the machine is lost, saphan ceremony root --restore rebuilds the root from the
paper words on a new machine, checked against the expected key fingerprint.
The ceremony also creates a narrower identity-signing key, granted by the root and kept on disk, which signs machine admissions from then on, so the root itself never needs to come back.
⚠ After the ceremony, compromising the control machine does not compromise the root, because the root is not on it. It does expose the identity-signing key, which can sign identity records until you revoke it. That grant has no expiry.
Intermediate authority
The intermediate CA issues the short-lived leaf certificates machines use. It is a P-256 key with its
own self-signed certificate (saphan ceremony intermediate, valid 365 days by default); it is not
signed by the root.
⚠ Today the intermediate key is a software file readable only by its owner. Hardware custody — an HSM or a secure enclave — is not available in this release.
Actors and delegation
An actor is a named identity in an append-only registry with a status. A delegation is a grant naming the delegate, their key identifier, the scope and the validity window. The signature covers every field, so a grant cannot be widened after signing.
- The scope list is closed: four scopes — order signing, identity signing, seat issuing and owner presence — each allowing exactly one kind of signature. Any other scope is refused; nothing fails open into general authority.
- Order-signing grants carry a not-before and an expiry; the ceremony's identity-signing grant has no expiry. An expiry is a ceiling, never a trigger: nothing gains permission through the passage of time.
- Only the root ratifies standing rules and grants delegate keys. A person's signed order can also authorise one dispatching key to place the follow-up orders it lists, within a budget and a time window; this never covers merges, admissions or trust acts.
- Actor status is checked independently of the grant, so a retired actor is refused even with a valid signature and an unexpired grant.
- The owner can revoke an actor, a delegated key, an identity provider or a person's sign-in. Each revocation is a signed record, and verifiers refuse revoked keys. See Documented limits for what revocation does not cover.
Standing rules and orders
Ratifying rules produces a root-signed manifest listing each document with its SHA-256, carrying a signed serial number that rises with each ratification. Given a document the engine walks the chain: these bytes hash to this ratified entry, in this manifest, signed by this root. ⚠ Automatic refusal of an older manifest is not implemented yet.
Every order a run receives must carry a person's passkey approval: it binds the exact order text, is
single-use, and is valid for four hours by default (24 hours at most). Checking an order's signature
chain back to the root is a separate setting, trust.enforce (off, warn, enforce); only enforce
refuses an order that fails it, and off — the default — admits it and records the fact.
Verification is structural: internally, "this document was verified" is a value that can only be built by walking the chain, so an unverified document cannot be mistaken for a verified one downstream.
Private key handling
| control | detail |
|---|---|
| Permissions | Owner read/write only. Any group or other permission bit is a refusal. |
| Check order | The path is inspected without following symbolic links → it must be a regular file → permission mask → exact key length → only then is the value read. |
| Machine keys | Generated on the machine that will use them; the certificate request is signed there, so no key material leaves it. |
| Transmission | Nothing in the system transmits a private key. SSH identities are recorded as paths, never values. |
| In memory | The workspace secret and seat keys render as fingerprints, so ordinary logging cannot leak them. |