Machines: enrollment and authentication
A machine cannot enrol itself; it is admitted by the owner, and every connection to it is authenticated by a pinned host key.
There is no registration endpoint
A machine cannot enrol itself, and there is no registration token that would let anything holding it become a runner.
- On the machine,
saphan machine csrgenerates a P-256 key pair locally and produces a certificate signing request. The private key never leaves the machine. - The owner admits it with
saphan machine admit, which issues a short-lived certificate from the intermediate (24 hours by default). Admission is an owner act: the owner types back a one-time code shown on their terminal. A one-command local path,--mint-device-key, does both steps on the machine being admitted.
Admission checks, each refusing by name:
| check | what it proves |
|---|---|
the request's signature validates (on the --csr path) | the requester holds the private key |
| the certificate is inside its validity window | an expired certificate is reported as expired |
| the certificate's Common Name equals the machine identifier exactly | the cryptographic identity is bound to the name you see |
| transport, SSH host and SSH key are well formed | the record can be connected to |
| a live SSH login with the recorded key succeeds | the machine is reachable as recorded. --force-unverified skips this and marks the record |
Re-running an identical admission reports "unchanged". A retired machine can be admitted again as a new record.
On success a record signed by the root — or by the identity-signing key the root granted at the
ceremony — is appended to the signed identity log. Certificates are renewed with saphan machine renew
(using machine csr --reuse-key on the machine); a superseded certificate stays valid for a grace
period, 24 hours by default (trust.rotation_grace).
What is recorded
Machine identifier, host, transport (local or SSH), key suite, execution slot count, admitting actor, admission time and connection parameters — with any key referenced by path, never by value. Identity and transport are separate: how a machine is reached can change without re-establishing who it is.
Transport and host authentication
Over SSH, the control machine always opens the connection; runners do not dial it or poll it. If you
enable the gateway's mutual-TLS listener (gateway.mtls_listen), machines use their certificates to
fetch from it and to upload evidence. The product opens no network-reachable port on a runner; at
most, a run may carry a forwarded port bound to the runner's loopback address for the length of the
run.
| setting | value | why |
|---|---|---|
| Client | the system SSH binary, not an embedded library | avoids a known class of host-verification defects in language-level SSH clients |
StrictHostKeyChecking | yes, unconditionally, including for machines that accept rotation | the pinned host key authenticates every connection |
BatchMode | yes | no interactive prompt can be what lets a connection through |
ConnectTimeout | 15 seconds by default; configurable from 1 second to 1 hour, installation-wide or per machine | bounded failure |
SendEnv | negated for all variables | no environment is forwarded to a runner |
| Client environment | only PATH and HOME on the run path | the control machine's environment is not inherited by the agent's connection |
| Configuration file | generated from the machine registry, regenerable | your own SSH configuration is never modified |
Host key rotation is handled by policy, not by hand. Some machines legitimately regenerate host keys — some on every boot. The alternative to a policy is a person editing a trust file whenever it happens, which is exactly the habit that makes a genuine key substitution invisible.
Each machine carries a policy from a closed set, validated when configuration loads:
| tier | effect |
|---|---|
refuse (default) | a changed host key aborts the connection |
accept_rotation | the pin may be updated automatically before an agent run; the rotation is printed in the run's output. It does not disable strict checking. |
First contact with an unknown host is never treated as a rotation. At admission or renewal, a host's key is taken from the installation's own known-hosts file, then from yours, and only if neither has it from a live scan of the host. ⚠ That scan applies to every SSH machine in the registry that has no pin — verify each host's key yourself before you admit.
The topology trade, stated plainly
In your favour: a compromised runner holds no credential to the control machine over SSH, cannot reach its peers through it, and has no identity assertion to forge.
Against you: the control machine holds the means to reach every machine, which makes it the asset to protect — and each runner must be reachable from it. Documented limits states what a compromised control machine can and cannot currently be prevented from doing.