Saphan StudioDocs
Security

Documented limits

What this release does not protect against, stated plainly so you can plan around it.

Everything below is a limit of the current release. Each is stated so that you can decide what to own yourself. The limits of the three network-and-conversation controls have their own pages: egress control, guardrails and session recording.

Dispatch and the control machine

  • A fully compromised control machine can still dispatch. Every work order needs a passkey approval from a named person. The approval binds the exact order text, is single-use and lasts four hours by default (24 hours at most). The remote machine re-checks the approval, its time window and the signatures on rule updates. But that remote check cannot see revocations or reuse, and it trusts the sending machine's key.
  • Instruction-signature enforcement is off by default. It has three settings — off, warn and enforce. While it is not enforce, an unsigned instruction is admitted and the fact is recorded, so you can measure unsigned traffic before you tighten it. The sending side can also lower the check on remote machines for rule updates.
  • Instructions pointing at files a remote machine cannot have are refused before a remote agent starts. The check reads wording, so unusual phrasing can slip through, and it applies to remote runs only.

Identity, keys and signatures

  • The root key cannot be rotated. Losing it means reinstalling. Keep it offline.
  • Revocation is a signed record, not a feed. The owner can revoke an actor, a delegated key, an identity provider or a person's sign-in, and verifiers refuse revoked keys. There is no external revocation feed (CRL or OCSP).
  • Delegations you create must expire, and there is no maximum. Keep windows short. The identity-signing grant the ceremony writes has no expiry until you revoke it.
  • One signature is enough. Every signed act — decisions, rule sets, gate records — needs one signature. No act requires two people.
  • Gate records are signed by the installation key unless you turn on per-person signing. With it on, owner approvals must carry that person's passkey. Passkeys may be synced across devices unless you require attested hardware keys.
  • Merge commits are unsigned. The engine's merge commits carry text trailers naming the decision, not a cryptographic signature.

Integrity of what is sent and run

  • Transfers are checked against transit corruption, not a lying host. Files sent to a remote machine are hashed on arrival and compared with what was sent; a mismatch stops the run. The hash is computed by the remote machine.
  • Content has no freshness. A matching hash proves the bytes match a value, not that the value is the latest; an older, correctly signed version is not detected as a rollback.
  • The engine's copy on a remote machine is not pinned by digest. On a remote Mac, the system sandbox launcher on that path is not checked either.
  • Agent binaries are hashed and recorded; compared only when you pin a digest (saphan machine set-binary --agent-digest). On ssh machines, --allow-unknown-binary-provenance waives the digest when it cannot be established.
  • A modified engine build warns but runs. An engine built from uncommitted source carries a marker and warns on every run. The build origin of remote agent binaries is not checked.

Machines

  • Retiring a machine refuses new work to it, and it can be re-admitted later. There is no drain state, and retiring does not revoke the machine's certificate.
  • Retirement reaches remote machines with a delay. A retired machine can still admit delegated work for up to 15 minutes per approval, until a healthy control machine delivers the update.
  • A replaced machine certificate stays valid for a grace period, 24 hours by default (trust.rotation_grace). It can be changed but not set to zero.

Confinement

  • Confinement limits writes, not reads. ⚠ On the default local lane (run.harness = legacy) a confined run can read the machine's signing-key directory, other seats' profiles and a metered seat's stored API key; remote runs and the saphan-agent harness hide the signing-key directory, and seat tokens and other runs' captures are hidden only when seat issuance is configured. See Runners.
  • A confined run can write to the shared version-control object store and can move other branches in its own namespace.
  • Confinement can be switched off for the whole installation, handing it to the agent backend. The run record then says that no engine boundary was claimed. A container mode also exists; it is local only and refused for remote runs.
  • Some backends run without an engine boundary on macOS. On macOS, local or remote, the codex, droid and qwen-code backends rely on their vendor's own sandbox, and their run records say they are not governed. On Linux the engine confines codex itself and refuses the run if it cannot prove the boundary. The cursor backend is refused.
  • Remote confinement is enforced by the remote machine. It is composed by the engine and applied there; on remote Macs the behaviour of cloud metadata access is unmeasured.

Outbound connections made by the server itself

  • The authorization server fetches client metadata from a URL the caller chooses. The fetch is limited to public addresses, capped at 256 KiB and rate-limited; its DNS answer is not pinned.
  • The resource server fetches your identity provider's signing keys — over HTTPS only, without redirects or proxies, size- and time-limited, from addresses checked at connection time. An address outside the public internet can be allowed only by a record signed with your root key or a key you delegated identity-signing to, re-checked on every fetch.
  • Cloudflare receives data when sign-up or e-mail is on. Bot-check tokens and recipient e-mail addresses are sent to Cloudflare.

Forge writes

  • With a forge write credential configured, Saphan Studio writes to your forge. After an owner's passkey decision in the console it posts a commit status, files an approval and calls the forge's merge API — on Gitea; writes to GitHub are refused in this release. It does not push branches. Approvals are filed under the account of the approval credential (forge.approval_token_lane); statuses and merges use forge.token_lane. Store that credential as carefully as any deploy key; branch protection remains your forge's decision.

Records you should protect

  • The delegation spend record can be edited by anyone with write access to the server's state directory; it is not tamper-evident.

Not in the threat model

An operator already root on a runner; an actively hostile rather than careless executor; physical access; and prompt injection itself. On the last: nothing here prevents an agent from being persuaded. The controls bound what a persuaded agent can reach, and record what it did.

On this page