Expand description
One live instance per agent — the host tier
(docs/specs/SPEC_AGENT_SINGLE_LIVE_INSTANCE_2026_09_24.md, Phase 1).
An agent identity (its UID) may be driven by at most one process on this host. Two layers:
acquire— the binding decision. A persistent pane claims the agent’sLeaseStorelease right before its CLI process is spawned and holds it for that process’s lifetime (HeldAgentLease): renewed on its own task, released on drop, and fenced — a holder that loses the lease (suspended past the TTL and reclaimed) is told throughon_lostso it stops driving, andHeldAgentLease::verifylets it refuse a turn before starting one.check_before_spawn— the early, read-only check the async spawn paths run before they write anything shared on the agent’s behalf (spec I9: in the 2026-09-25 incident the second instance broke the first by rebinding its account, before any spawn). It reads the lease and, for instances too old to take one, asks every other live AgentMux instance on this host whether it is running this UID.
Structs§
- Held
Agent Lease - A lease held for the lifetime of one CLI process. Dropping it stops the renewal task and releases the lease.
- Holder
Endpoint - Where the instance currently running an agent can be reached.
- Legacy
Holder - A live instance on this host that is running the same agent UID but took no lease (it predates Phase 1) — found by asking it.
- Lost
Reason 🔒 - The first loss message, kept for every later refusal.
- Peer
Holding - A LAN peer’s answer to
GET /agentmux/agent/holding?uid=…. - Record
Fence - The writer-side fence on the agent’s shared record (spec §4.3, Phase 3).
Constants§
- HANDOVER_
HOLD - How long, after this srv hands an agent over to another instance, it refuses to claim that agent again on its own. Covers the gap between the holder’s process exiting and the requester claiming, in which a jekt delivered here would otherwise respawn the agent and win it straight back. A takeover the user starts from this srv clears it.
- LAN_
QUERY_ 🔒TIMEOUT - How long one LAN peer gets to answer.
- LAN_
RECHECK_ 🔒TICKS - Renewals between LAN re-checks: 6 × RENEW_INTERVAL_MS = 30 s.
- LAN_
SKEW_ BUDGET_ MS - Start times closer than this are “simultaneous”: clocks on two hosts are not compared finer than this; the hostname decides instead (§4.4).
- REFUSAL_
MARKERS - Wording every single-live-instance refusal contains (lower-cased), so the failure classifier and callers can recognise one without a type.
- RELEASE_
REQUEST_ 🔒TIMEOUT - How long a release request may take to be answered (see
request_release).
Statics§
- HANDOVER_
HOLDS 🔒 - One entry per agent this srv handed over: until when, and to whom (for the refusal text). A single map, so an expired hold goes away whole (ReAgent P2 on #3742).
- HELD_
BY_ 🔒NAME - The held leases of this process by agent name (lower-cased) — the key the muxbus relay and its pending queue use — so the WAN side can fence the local holder when the relay says another instance holds the agent.
- LAN_
DISCOVERY 🔒 - PROBE_
CLIENT 🔒
Functions§
- acquire
- Claim
uidfor this process’sblock_id, and keep it renewed until the returned handle is dropped.on_lostruns (once, off the caller’s thread) if the lease is found to belong to someone else — the holder must then stop driving the agent. - check_
before_ spawn - The early, read-only admission check (see module doc).
Erris the user-facing refusal; callers must run it before any shared write. - claimant_
info - What this instance records about itself in a lease it takes.
- clear_
handover_ hold - Drop any handover hold on
uid— the user is taking it back. - denied_
message - The refusal a user sees when
agentis already running elsewhere. - endpoint_
for_ channel - The running instance on this computer in
channel, from the host-global shared registry — how Take over reaches a holder it knows by channel (from the local lease file, or from the relay’s holder record). - fence_
agent_ named - The relay says another instance holds
agent_name(WAN tier): fence this process’s holder of that agent, if any — record the loss (the pre-turn fence refuses its next turn) and stop its CLI process.truewhen a local holder was fenced. - fenced_
zone zoneunlessfencesays this process may no longer write the agent’s shared record.- find_
uid_ 🔒block - The block of the first registration in
regscarryinguid. - handover_
hold_ 🔒refusal Some(refusal)whileuidis inside a handover hold on this srv. An expired hold is removed here.- hold_
after_ handover - Record that this srv just handed
uidover (seeHANDOVER_HOLD). - is_
admission_ refusal - Is
messagea single-live-instance refusal (any of this module’s user-facing refusal texts)? - is_
this_ 🔒host - lan_
denied_ message - The refusal when a LAN peer runs the agent. Carries the same marker as
the host refusal, so it classifies as
LiveElsewhere. - lan_
holders - Every reachable LAN peer on another host that says it holds
uid. Unreachable or older peers (no route) are skipped: not evidence either way (D2, fail open). Answers from this host are dropped: another AgentMux instance here also shows up as a LAN peer, but it reads this host’s own lease store, so it would report this process’s own lease back — the host tier already governs this host. - lan_
lost_ 🔒message - lan_
place 🔒 - lease_
store_ for - The lease store under a shared agent registry root,
Noneif there is no registry or its lease directory can’t be opened (logged). - local_
holding - What this host answers to a LAN peer asking about
uid: held when any instance on this host holds a live lease for it. - locate_
holder - Find the other instance on this host that runs
uid: the lease holder (its channel’s live srv, from the shared registry) or — for an instance that takes no lease — the compat probe’s hit.Nonewhen nobody else runs it, or the holder’s srv can’t be found. - lost_
message 🔒 - peer_
wins - The LAN tie-break (§4.4): does a peer that started the agent at
peer_atonpeer_hostwin over this host’s claim atmine_at? The earlier start wins; starts within the skew budget are simultaneous and the lower hostname wins. Deterministic and symmetric: exactly one side wins for any pair of distinct hosts. - probe_
other_ instances - Ask every other live AgentMux instance on this host (from the
host-global cross-channel registry) whether it has
uidregistered. Matched by UID, never by name (display names are not unique — Codex P2 on #3730). An instance too old to report UIDs, or one that doesn’t answer, is not evidence either way and is skipped (logged). - register_
by_ 🔒name - registered_
uid_ 🔒blocks GET <local_url>/agentmux/reactive/agentsand return the block of the first registration whoseuidequalsuid.- request_
release - Ask the holder’s srv to stop running
uidso this instance can take it (POST /agentmux/agent/release).Erris user-facing. - set_
lan_ discovery - Called once at bootstrap: gives admission the LAN peer list. Without it (tests, LAN discovery off) the LAN tier is skipped.
- spawn_
renewal 🔒 - this_
host_ 🔒label - This host’s label, as a LAN peer reports it and as the tie-break compares.
- unregister_
by_ 🔒name - wait_
until_ free - Wait until no other instance holds
uid(lease and compat probe both clear), polling.Erraftertimeout. - wan_
denied_ message - The refusal when the relay says another instance runs the agent. Carries
the host refusal’s marker, so it classifies as
LiveElsewhere. - wan_
lost_ 🔒message - wan_
place 🔒 - Where the relay says the holder is: “on another computer (computer X, …)”, or “on this computer (channel Y, vZ)” for another instance on this host (charlie, 2026-09-26: a same-host holder was called “another computer”).