Expand description
One shared server per checkout: the lease that decides who it is. The first
majordomus mcp (or serve) to create state/mcp/server.json under the checkout’s
local half owns the server and publishes its URL there; every later process reads the
file, checks that the server answers for this root, and attaches to it. A lease whose
server does not answer is stale, and the next process takes it over; so is a file that
is not a lease document, an empty one, or one whose owner never published a URL. A
server that is slow to answer while its process is alive is busy, not stale: the
election waits for it for BUSY_GRACE before it takes anything over. The file lives
under .ai/local/ (never tracked, by the layer’s contract), and it is removed when the
server stops — including when SIGTERM, SIGINT or SIGHUP asks it to — but only by
the process that still holds it: every removal and every rewrite re-reads the file under
the lock beside it (server.lock) and acts only on a lease that carries the actor’s own
token (take_over_with says why it is a lock).
Per checkout, and this file said “per repository” until ADR 0044: a linked worktree
is a checkout, so it has a manifest, a root, a lease and a server of its own, and on
2026-09-11 this repository had seven of them at once. What is repository-wide is the
board, which peers.list gathers over these very leases; the election is unchanged.
Structs§
- Executable
Identity - The identity of the file an executable was started from: where it is, and the mtime and size of the file at that path. A server outlives its own binary — a rebuild replaces the file under a process that keeps serving the code it loaded hours ago — and nothing about the process itself says so. This is what makes that visible.
- Lease
- The lease this process holds. Dropping it removes the file (when the file is still this process’s), so a failed start never leaves a stale lease behind.
- Lease
Document - The lease file, typed: what the process serving a checkout wrote down about itself.
Every reader of the file — the election, a command asking which server is running, the
environment snapshot, the server’s own status — reads it through
LeaseFile::read, so that a field added here is added once and a file that is not a lease is refused in one place. A key this version does not know is ignored, so a lease written by a newer executable still reads; a key it needs that an older lease lacks takes its default. - Timings
- The timings above are the defaults. What a process actually judges a lease contest by is
declared in
.ai/repo/policy.yaml’sserver:block and read once, here.
Enums§
- Lease
File - What a lease file holds, read once. Every state of the file is a variant, so that a reader decides what to do about each rather than learning it from an error.
- Role
- What the election decided for this process.
- Version
Verdict - How the version a server published in its lease stands to this executable’s.
Constants§
- BIND_
GRACE - How long a lease without a URL may be (the owner is still binding) before it counts as abandoned.
- BUSY_
GRACE - How long a live server that does not answer its probe is waited for before its lease is taken over.
- IDLE
- How long a server started for a checkout no client owns — by
serve ensureor by a shell entering the checkout — outlives its last peer before it stops by itself:crate::cli::DEFAULT_IDLE_SECONDS.serve --idleon a command line overrides whatever is declared (I2701). - JOIN_
TIMEOUT - How long a process keeps trying to acquire or join the lease before it gives up and says so: the bind grace with a margin for the probes. The caller then serves its client alone.
- JUDGED_
STALE_ ENV - The environment variable a server is started with when the process that started it had
already waited on the lease it found and judged it stale; its value is that lease’s
token. The new server’s election then takes exactly that lease over without spending the
same patience on it again. Any other lease, including one that changed in between, still
gets
probe_patiently’s wait. - LEASEHOLDER_
KEY - The key the index answers
was_lost’s converse under: whether the server answering still holds the lease of the checkout it serves. Named once, read byprobeand written by the index route, so the two cannot drift apart. - LEASE_
PATH - The lease file, relative to the checkout-local half (
.ai/local/). - PROBE_
TIMEOUT - How long the probe of a published URL waits.
- READ_
DEADLINE - How long the shared server lets a connection go without a request read in full on it
before it closes the connection: waiting for a request head, kept alive between requests,
or reading a body. A client that sends one byte a second would otherwise hold a request
thread forever, and enough of them would leave the probe above nobody to answer it (I2156).
Generous, because a person’s client on a slow link is a client too; far below anything
that would let a handful of slow sockets outlast a
BUSY_GRACEcontest many times over. - SCHEMA
- The lease file’s
schema.
Functions§
- declare_
timings - Declare the timings for this process from a repository’s policy. The first call decides;
later ones are ignored, which is what makes
timingsanswer the same thing all the way through one contest. - elect
- Decide whether this process serves the repository or attaches to the process that does.
- executable_
identity - What the running executable is, for the lease to record: the path it was started from and the identity of the file at that path. A server outlives its own binary — a rebuild replaces the file under a process that keeps serving the code it loaded hours ago — and nothing about the process itself says so. This is what makes that visible.
- file_
age - How long ago the file was last written; zero when that cannot be read, so that a file whose age is unknown is never taken for an old one.
- held
- The lease this process holds and has published:
Nonein a process that serves nothing. - is_own
- Is
docthe lease this very process holds, published and not lost? - lease_
file - Where the lease of the checkout at
rootlives, given the repository-relative path of its local half (.ai/local). The one composition of that path; every reader of a lease that is not this process’s own repository — another checkout’s, in the server’s status or the environment snapshot — goes through here. - lease_
path - Where the lease of a repository lives.
- lost
- This process’s lease was taken over by another process: from now on nothing this process
does on its way out touches the file, which is somebody else’s, and it stops claiming to be the
checkout’s server —
heldanswersNoneandwas_lostanswerstruefrom here on. It serves the peers it has and ends with them; what it must not do is take on new ones, or answer a stranger’s probe as though it were still the one. - lost_
for - How long ago this process lost the checkout’s lease, when it did.
- probe
- Does a Majordomus server answer at
urlfor the repository atroot, as that repository’s server? - probe_
patiently probe, with patience for an owner that is alive but slow.- probe_
reply probe, keeping what the server answered: its index document when it answers as the leaseholder of this checkout,Noneotherwise. One decision, so a caller that also wants the surfaces the server lists makes neither a second request nor a second judgement.- serving
- Which server is serving this repository right now, if any.
- serving_
replaced_ code - Why this process is serving code that is no longer on disk, or
Nonewhile the file it was started from is still the file at that path. The same judgement the lease reader makes from outside (ExecutableIdentity::replaced), made by the process about itself, so that the endpoint a client asks for readiness can say so. - started_
as - The executable this process was started from, as it stood on disk when first asked.
- timings
- What this process judges a lease contest by. The declaration when one was read, the constants otherwise.
- version_
verdict - Decide how
served, the version in a lease, stands tomine. - was_
lost - Did this process hold the checkout’s lease and lose it?