Skip to main content

Module identity

Module identity 

Source
Expand description

Agent identity at the MCP boundary, jekt ids and outgoing jekt / UI-automation signatures. Split out of main.rs unchanged (SPEC_LARGE_FILE_MODULE_ANALYSIS_2026_09_30.md §4.2).

Structs§

OutgoingJektSignatures 🔒
Build the id/timestamp/host-signature/LAN-signature quadruple for an outgoing jekt (SPEC_JEKT_TRUST_LAYER_COMPLETION_2026_08_13.md §2.2, SPEC_JEKT_LAN_TIER_SIGNING_2026_08_15.md §2.3). Reads this process’s OWN AGENTMUX_JEKT_KEY/AGENTMUX_LAN_KEY — injected at spawn alongside AGENTMUX_AGENT_ID, into this agent’s env only, never any other agent’s — and signs over the same (msgid, source_agent, target_agent, ts_secs, message) material both schemes share.

Statics§

JEKT_MSGID_COUNTER 🔒
Process-wide counter for generate_jekt_msgid — millis-timestamp alone isn’t guaranteed unique if two jekts are sent in the same millisecond.

Functions§

agent_slug 🔒
The calling agent’s slug (its AGENTMUX_AGENT_ID), injected by AgentMux into this MCP server’s trusted environment. MuxAPI identity/preset/memory REST endpoints stamp their agent_id from this — the agent’s own model output cannot reach those endpoints (no auth key in the PTY) nor override this value, so the slug cannot be forged. See SPEC_AGENT_APP_API_MCP_BINDINGS_2026_06_28.md §5.
build_block_to_agent_map 🔒
FleetBroadcast’s block_id -> agent-name resolution (REPORT_CROSS_INSTANCE_CONTROL_ROBUSTNESS_AUDIT_2026_08_22.md): a /agentmux/discovery response’s host.addressable AND host.cross_channel sections both carry a block_id — addressable under agent_id/block_id, cross_channel (a different channel on this same host) under name/block_id. A block_id is unique across channels on one host, so both are folded into one map, not kept separate. lan/wan entries carry no block_id at all (they’re not local blocks) — a target for those tiers is never in this map, and the caller falls back to treating it as a literal agent name instead (see the FleetBroadcast handler).
generate_jekt_msgid 🔒
A unique-enough message id for a signed jekt: no uuid dependency needed (this crate doesn’t otherwise pull one in) — timestamp + this process’s own pid + a monotonic counter is unique enough for its one purpose (a value both this signer and the receiving srv agree to include in the signed material, so a signature can’t be replayed under a different id).
interpret_resolve_response 🔒
The response half of resolve_agent_name_at_boundary, separated so the status handling is testable without a server.
resolve_agent_name_at_boundary 🔒
Identity M3 (spec §5): resolve a typed agent name at the boundary — the only place a name is turned into a UID — before a tool stores it. Ok(Some(uid)) when exactly one identified agent matches; Ok(None) when none does, the match has no UID, or the srv predates the endpoint (the tool then sends the name and the server records the miss). Err in two cases, both of which fail the tool call: the name is ambiguous — the error lists the candidates so the model can retry by uid (§5.2) — or the resolver could not answer (a store fault, any other non-2xx, an unreadable or unexpected response), where sending the bare name would skip the ambiguity check. See interpret_resolve_response.
sign_outgoing_jekt 🔒
sign_ui_automation_auth 🔒
Build the identity proof every /api/v1/ui/* request carries — an HMAC-SHA256 signature over this agent’s own agent_id, using this agent’s own AGENTMUX_JEKT_KEY (the same per-agent key sign_outgoing_jekt above uses for jekt messages; reused rather than inventing a parallel credential system). Unlike jekt signing, a missing key here is a hard error, not a silent “send unsigned” — srv has no unverified fallback path for UI automation (see crates/srv/src/server/ui_handlers.rs’s module doc comment), so a tool call with no key would just 401 anyway; failing fast with a clear “respawn to get a key” message is more useful than a confusing round trip.
signed_jekt 🔒
The request for a jekt to target_agent, signed: every send path builds it here. The target is its route name first (agent_route_name: a fork’s “Loap #3” is “Loap-3”), because the signatures bind the target: every hop routes by the route name, and normalizing a signed request at a hop would break its signature. A blank target is left blank.