Seats: binding an executor profile to a run
What a seat is, the only way to bind one to a run, and why a status read is not proof.
A seat is a row in the machine registry: (identity, machine, slot) plus a
config-dir (for claude-code/codex seats) or a lane (for openai-compat/
agent-loop seats) — exactly one of the two, never both. It binds a tool profile —
CLAUDE_CONFIG_DIR for claude-code, CODEX_HOME for codex — to a machine slot, so
that a named identity always spawns against the same credentials instead of whatever
profile the process happened to inherit. The security model of seats — including the
role that can reserve a seat and the billing class that tags its economics — is
Security §7; the money side is Cost management.
The machine verb family (from saphan machine --help — re-check it rather than trusting
this list, it grows): admit · csr · list · pin · unpin · probe ·
probe-confinement · renew · retire · seat · set-binary · set-max-sessions ·
set-price · set-repo-dir · set-slots · set-worktrees-root · refresh-repo · deliver-branch ·
trust-stamp · unseat. The five that
touch seating directly: admit (register a machine), seat (write the
identity+machine+slot row), set-slots (resize how many identities a machine carries),
unseat (free one slot), retire (withdraw a machine — retirement is a mark on the row,
never a delete, and casting onto a retired machine is refused separately).
The only way to bind a seat to a run
--identity alone, without --machine, is refused — it does not fall back to
anything:
(The sibling case — a bare --slot with no --machine — refuses with the same shape.)
Why this refusal exists
Before the rule, --identity given without --machine was silently discarded: the run
ended ok, exit 0, and landed on whichever profile the process's own environment happened
to carry — usually the operator's own default, never the named seat's. A green run carried
zero information about whether the named seat actually paid for it.
That is the rule this section exists to state: the engine never chooses a profile for the operator. It either binds the seat you named, or it refuses by name. There is no third outcome where it quietly substitutes a default and calls it success.
A status read is not proof a seat is usable
A green machine probe or a vendor auth status reporting "loggedIn": true tells you the
profile directory parses and holds some stored credential. It does not tell you that
credential still works — token expiry is a separate axis neither check reads. Measured on
our own fleet (2026-08-08): a seat whose auth status said "loggedIn": true failed a real
cast minutes later with 401 OAuth access token has expired; the same verb against a
non-existent profile correctly reports false, so it is not blindly returning true — it
simply never checks whether the stored token is still valid.
Two mechanisms respond to this:
- The engine's own pre-spawn check (R23): before casting onto a seat, the engine runs the cheapest legal renewal probe against that seat's profile (a minimal one-word call, capped at $0.02) and refuses the spawn before it starts if the session is dead — with a TTL cache so a spawn a minute later does not pay for a second probe.
- The only full verification is a real spawn:
A recorded run that completes — or fails with a dated auth error rather than a stale
loggedIn: true — is the signal that the seat's credential is live right now.
Limits are per-profile, not per-account
Counter-intuitive, and reported as one measurement, not a law: two config-dir profiles
on the same vendor account can carry separate session budgets. Measured on our own
fleet (2026-08-08, same minute, same account): one profile returned a session-limit refusal
while the other ran normally. Check it on your own fleet rather than trusting either
direction; saphan fleet usage (which probes every seated profile's own /usage reading)
is the verb that answers it per seat.
One run per slot — and how far one slot stretches
A second dispatch onto a slot that already has a live run is refused, never queued:
A slot carries one concurrent run unless its capacity is raised explicitly, per slot, with
machine set-max-sessions — see Security §8 for the database mechanism
that makes this race-proof.
The --mint-device-key trap
machine admit --mint-device-key writes the device private key to
~/.saphan/machine/device.key by default. Admitting a second machine on the same host
with the default path overwrites the first machine's key. Pass an explicit
--device-key <path> for every machine beyond the first on a shared host.