Expand description
File-based cross-instance agent registry.
Each AgentMux instance writes agent registrations to
{data_dir}/agents/{agent_id}.json. When a local inject fails with
“agent not found”, the inject handler looks up this registry and
HTTP-forwards the request to the owning instance.
Lifecycle:
- Register: write file (on HTTP register endpoint + shell auto-register)
- Unregister: delete file (on HTTP unregister endpoint + process exit)
- Cleanup: TTL-based removal of stale files at startup
Structs§
- Agent
Entry - One entry per registered agent in the shared data dir.
Constants§
- FORWARD_
FAILURE_ 🔒GRACE_ MS - Grace period during which a forward failure against a live-process entry
is presumed transient rather than proof the specific agent is gone — see
should_evict_on_forward_failure.pub(crate)somuxspect_handlers’sverify-senderroute can reuse the same real, already-tested staleness judgment for shared-registry entries instead of inventing its own threshold (see that module’s own doc comment for why).
Statics§
- JEKT_
PUBLIC_ 🔒KEY_ RESOLVER - LOCAL_
AUTH_ 🔒KEY - Process-wide auth key for the local AgentMux instance.
- REGISTRY_
OP_ 🔒LOCK - Serializes this process’s own read-compare-remove sequences
(
remove_if_nonce/remove_shared_if_nonce) against its own writes. Both the racing writer (a fallback respawn’s re-registration) and the racing remover (the dying spawn’s exit-handler) live in this same agentmux-srv process — each per-agent registry file has exactly one writing instance — so a process-local mutex is sufficient to make the compare-and-remove atomic; no file locking needed. Plain removes/writes for other controller types don’t need the guard but take it anyway for uniformity (it’s uncontended). - UID_
KEY_ 🔒RESOLVER
Functions§
- agent_
path 🔒 - agents_
dir 🔒 - cleanup_
stale - Remove stale entries at startup.
- cleanup_
stale_ shared - Startup sweep over the host-global registry: remove any entry whose
updated_atis pastmax_age_msOR whosepidis no longer alive on this host (§4.4) — the dead-Agent3case from the spec’s problem statement, where per-channel TTL cleanup never reached an entry belonging to a channel that hadn’t restarted. - init_
jekt_ public_ key_ resolver - Install the process’s agent-public-key resolver. Idempotent — first call
wins, same contract as
init_local_auth_key. - init_
local_ auth_ key - Initialise the process’s local auth key. Idempotent — first call wins.
- init_
uid_ key_ resolver - Install the process’s block → (UID, UID public key) resolver. First call wins. Until it is called, entries are written without a UID.
- jekt_
public_ 🔒key_ for - The agent’s published public key, or empty if unavailable.
- list_
all_ shared - List every entry currently in the host-global shared registry, across
every agent name and channel. Used by the
/agentmux/discoveryendpoint to populatehost.cross_channel[](§4.5 of the cross-channel delivery spec) — unlikelookup_all_shared, which targets one known agent name, this enumerates the whole directory. - local_
auth_ 🔒key - local_
channel_ id - This process’s own channel id —
AGENTMUX_CHANNEL, default"stable". - local_
host_ label - This machine’s host label — the
hostnamehalf of an agent’s WAN instance identity, paired withlocal_channel_idto identify which AgentMux instance under an account owns a given agent’s WAN keypair. - lookup
- Look up an agent entry. Returns None if not found or file is malformed.
- lookup_
all_ shared - Return every live candidate for
agent_idacross all channels, freshest-first (§4.3: Tier 2b prefers the freshest candidate). Does NOT filter by staleness/pid-liveness — callers needing that should usecleanup_stale_shared(startup sweep) plus the existing evict-on-forward-failure pattern already used by Tier 2a/3 inserver/reactive.rs, matching how this codebase already handles registry staleness elsewhere rather than re-validating on every read. - lookup_
shared_ by_ uid - Every channel’s shared entry that published a key for
uid(identity M4d-6). Scans the whole registry: entries are filed by name, and one UID live in several channels has an entry, and a key, in each. - pid_
alive 🔒 - True if a process with this PID is currently running on this host. Meaningful here specifically because the shared registry is always same-host by construction (a local file only readable by local processes) — unlike a LAN/cloud registry, PID-liveness is an authoritative staleness signal, not just a heuristic.
- read_
entry_ 🔒file - Best-effort read of one (agent, channel) entry file. Missing,
unreadable, or malformed files are treated as absent — same
graceful-degradation posture as the per-channel
lookup. - read_
entry_ 🔒files_ in_ dir - Read every valid
.jsonentry file directly insidedir(one level, not recursive) — used for bothlookup_all_shared(one agent’s per-channel dir) andlist_all_shared(one agent-name dir at a time). - remove
- Remove an agent entry from the shared registry.
- remove_
if_ nonce - Remove an agent entry only if it was written by the spawn with
expected_nonce— compare-and-remove for persistent-controller exit-handlers (issue #2363; nonce is process-wide unique, so the guard holds across controller replacement too — codex P1 on PR #2500). An entry with no recorded nonce (0) is never removed by this variant: leaving a stale file to the TTL sweep (cleanup_stale) is strictly safer than deleting a live registration. Atomic w.r.t. this process’s own writes viaREGISTRY_OP_LOCK. - remove_
shared - Remove this channel’s entry for
agent_idfrom the shared registry. Best-effort removes the now-possibly-empty agent directory too (a concurrent writer recreating it a moment later is harmless — the next write just re-creates the directory viacreate_dir_all). - remove_
shared_ from_ env - Convenience wrapper over
remove_shared— seewrite_shared_from_env. - remove_
shared_ from_ env_ if_ nonce - Convenience wrapper over
remove_shared_if_nonce— seewrite_shared_from_env. - remove_
shared_ if_ nonce remove_sharedonly if this channel’s entry was written by the spawn withexpected_nonce— seeremove_if_nonce(issue #2363). Cross-channel note: only this channel’s own file is read and removed, matchingwrite_shared’s single-writer contract, so the process-localREGISTRY_OP_LOCKstill suffices.- sanitize_
path_ 🔒component - Lowercase + sanitize a single path component (agent name or channel
id) to alphanumeric/dash/underscore only, preventing path traversal
and matching
ReactiveHandler’s lowercase key convention. - shared_
agent_ 🔒dir - Directory holding one file per channel currently registering
agent_idin the host-global shared registry. - shared_
channel_ 🔒path - Path to one (agent, channel) pair’s entry file.
- should_
evict_ on_ forward_ failure - Should
server/reactive.rs’s evict-on-forward-failure sites remove this entry after a failed forward (success:falseor a connection error)? - uid_
key_ 🔒for_ block - write
- Write (create or update) an agent entry in the shared registry.
- write_
entry_ 🔒file - Write one (agent, channel) entry file, atomically (temp file + rename) so a concurrent reader never observes a partially-written file. Mode 0600 on Unix at open time — same auth_key exposure boundary as the per-channel registry (§5 of the cross-channel spec: same-user trust boundary, not cross-user).
- write_
shared - Write (create or update) this channel’s entry for
agent_idin the host-global shared registry. Writes only this channel’s own file (shared_channel_path) — never touches another channel’s file, so concurrent writers for different channels of the same agent name cannot race or clobber each other. - write_
shared_ from_ env - Convenience wrapper over
write_shared: resolves the shared dir and this process’s channel itself, no-op if the shared root can’t be resolved. Exists so call sites outsideserver/reactive.rs’s explicit register/unregister handlers (PTY shell auto-register, persistent stream-json controller auto-register — both call the per-channelwrite/removedirectly, not through the HTTP handler) don’t each repeat the same resolve-dir-then-read-env dance. - write_
shared_ from_ env_ with_ nonce - Convenience wrapper over
write_shared_with_nonce— seewrite_shared_from_env. - write_
shared_ with_ nonce write_shared, recording the registering persistent-controller spawn’s registration nonce — seeAgentEntry::registration_nonce/ issue #2363.- write_
with_ nonce - [
write], recording the registering persistent-controller spawn’s registration nonce — seeAgentEntry::registration_nonce/ issue #2363.
Type Aliases§
- Pubkey
Resolver 🔒 - Resolves an agent’s published Ed25519 public key for
AgentEntry::jekt_public_key. - UidKey
Resolver 🔒 - Resolves a registered block’s agent UID and that UID’s public key, for
AgentEntry::uid/AgentEntry::uid_public_key(identity M4d-5). By block, because the block is what every registration path has. A load, never a mint, likejekt_public_key_for: M4d-2 mints the key at spawn.