Expand description
Jekt sender signing/verification — three schemes, one signed-material format:
- Host-tier: HMAC-SHA256, one shared key per agent (
sign_jekt/verify_jekt). A 1:1 sender-to-local-srv relationship, so a symmetric key is fine — the srv instance is the sole verifier. - WAN, reagent only: Ed25519 against one pinned service keypair
(
verify_reagent_jekt). Multi-party (any receiving instance must verify without being able to forge), hence asymmetric. - LAN, any agent: Ed25519, one keypair per agent
(
sign_lan_jekt/verify_lan_jekt). Same multi-party reasoning as WAN, but per-sender instead of one pinned key — see docs/specs/SPEC_JEKT_LAN_TIER_SIGNING_2026_08_15.md.
Shared between agentmux-mcp (the SendMessage tool signs an outgoing
jekt with the calling agent’s own key, read from its AGENTMUX_JEKT_KEY/
AGENTMUX_LAN_KEY process env vars) and agentmux-srv (the reactive
handler verifies an incoming jekt’s claimed signature against the
claimed sender’s key, looked up server-side by agent_id — see
crates/srv/src/backend/storage/agent_jekt_keys.rs and
agent_lan_keys.rs). Living here, not in either binary, is what
guarantees the two can never independently drift on the signed-material
format.
See docs/specs/SPEC_JEKT_TRUST_LAYER_COMPLETION_2026_08_13.md §2.2,
docs/specs/SPEC_JEKT_SECURITY_AND_VISIBILITY_2026_07_01.md §5.3 (the
original spec’s never-built “Phase 5” this module implements), and
docs/specs/SPEC_JEKT_LAN_TIER_SIGNING_2026_08_15.md.
The signed material binds sender, target, message id, and timestamp together (not just msgid+payload) so a valid signature can’t be replayed against a different target or reattributed to a different claimed sender.
Structs§
- WanCarried
- The signed tuple as carried through the relay (§2.1). All of it is
sender-supplied;
key_fpis a lookup hint and is not signed. - WanKey
Record - One entry in the per-account key directory (§2.2): an agent key,
certified by the instance that minted it. Public keys are standard
base64 of the raw 32 bytes;
cert_sigis standard base64 Ed25519. - WanRevocation
- An instance revocation (§2.2), signed by the old instance key so only the owner of that key can retire it. Sticky: nothing un-revokes an id.
Enums§
- WanCheck
Failure - Why a carried signature did not verify. Each maps to the §2.3 verdict the
caller records; the ones that are
Nonethere are “couldn’t check”, never a forgery signal.
Constants§
- WAN_
INSTANCE_ ID_ LEN - Length of an instance id: 128 bits as unpadded base32.
- WAN_
KEY_ FP_ LEN - Length of a key fingerprint: 256 bits as unpadded base64url.
- WAN_
SIG_ MAX_ AGE_ SECS - How old a carried
wan_ts_secsmay be: the relay’s own 1800 s delivery TTL plus 300 s of slack (§2.4). A shorter window would reject messages the relay is still legitimately holding. - WAN_
SIG_ MAX_ FUTURE_ SKEW_ SECS - How far in the future a carried
wan_ts_secsmay be — desktop clock skew between sender and receiver (§2.4).
Functions§
- check_
wan_ envelope - The checks that need no directory record, in the §2.3 order: the
envelope binding, then freshness.
row_source_agentis the cloud row’ssource_agent;polled_agent_idis the agent the subscriber is delivering to — not a row field, so a cloud can’t move a message signed for one agent into another’s queue. - decode_
key - Decode a base64-encoded signing key (e.g. from the
AGENTMUX_JEKT_KEYprocess env var). ReturnsNoneon malformed input rather than erroring — callers should treat that the same as “no key available” (sign nothing, deliver unsigned rather than fail the send). Exposed here soagentmux-mcpdoesn’t need its ownbase64dependency just for this. - generate_
lan_ keypair - Generate a fresh Ed25519 keypair for a newly-registered agent’s LAN
signing key. Returns
(public_key_bytes, private_key_seed_bytes), both 32 bytes raw — callers base64-encode for storage/transport (mirrors therandom_seed_bytes+BASE64.encodesplit in the srv crate’sagent_lan_keys.rs). - generate_
wan_ keypair - Generate an agent’s WAN keypair from 32 bytes of seed entropy, returning
(public_key, private_key). - is_
reagent_ trusted_ signing_ key - The one
key_idwhose matching private key is believed secret today.reagent-v1-devstays registered inreagent_public_key(soverify_reagent_jektstill checks its signatures), but its private half is documented above as exposed since it was generated, so a signature under it proves nothing about the sender. Receivers record a reagent signature as verified only throughverify_trusted_reagent_jekt, which requires this key; any other key yieldsreagent_verified = Some(false)— an active verification failure, forced sensitive. - is_
valid_ wan_ host_ hint - Whether
host_hintis safe to render:[a-z0-9.-]{1,48}(§2.6). The hint is sender-chosen; anything else renders as?. - sign_
channel_ jekt - Sign a cross-channel jekt with the sending agent’s own LAN private key
(
AGENTMUX_LAN_KEY) — called client-side inagentmux-mcp, never server-side, exactly likesign_lan_jekt. - sign_
channel_ jekt_ v2 - Sign a cross-channel jekt as
source_uid, with that UID’s own LAN private key.Nonefor a key that isn’t 32 bytes, like the other signers. - sign_
jekt - Sign a jekt with the sender’s key. Returns a base64-encoded HMAC-SHA256
tag.
keyof any length is accepted (HMAC’s own key-hashing handles that) butagent_jekt_keys::agent_jekt_key_ensurealways mints 32 bytes. - sign_
lan_ jekt - Sign a jekt with the sending agent’s own LAN private key (read from its
AGENTMUX_LAN_KEYprocess env var, mirroringsign_jekt’sAGENTMUX_JEKT_KEY— called client-side, insideagentmux-mcp, never server-side).private_keymust be exactly 32 bytes (the seedgenerate_lan_keypairproduced) — returnsNoneon any other length rather than panicking, same “missing/malformed key must never block sending” policysign_jekt’s callers already rely on. - sign_
lan_ jekt_ v2 - Sign a LAN jekt as
source_uid, with that UID’s own LAN private key.Nonefor a key that isn’t 32 bytes, like the other signers. - sign_
wan_ jekt - Sign a jekt with the sending agent’s own WAN private key (read from its
AGENTMUX_WAN_KEYprocess env var, client-side insideagentmux-mcp, never server-side — same placement assign_lan_jekt). - verify_
channel_ jekt - Verify a claimed cross-channel signature against the claimed sender’s
published public key (read server-side from the host-global shared registry
entry —
AgentEntry::jekt_public_key). - verify_
channel_ jekt_ v2 - Verify a v2 cross-channel signature against the public key published for
source_uid.false, never a panic, for anything malformed. - verify_
jekt - Verify a claimed signature against the sender’s stored key. Constant-time
comparison via
Mac::verify_slice(not a manual==), so this can’t leak timing information about how much of the tag matched. - verify_
lan_ jekt - Verify a claimed LAN signature against the claimed sender’s public key
(looked up server-side by
source_agent, over the LAN peer-lookup RPC — seeLanDiscovery::find_agent_lan_pubkey). Returnsfalse(never panics) for a malformed public key, malformed base64, a malformed/wrong-length signature, or a signature that simply doesn’t verify — same “treat all of these identically as not verified” policyverify_reagent_jektalready documents. - verify_
lan_ jekt_ v2 - Verify a LAN v2 signature against the UID key the answering peer gave.
false, never a panic, for anything malformed. - verify_
reagent_ jekt - Verify a WAN-tier jekt’s claimed
reagent_sigagainst the pinned public key forkey_id. Returnsfalse(never panics) for an unknownkey_id, malformed base64, a malformed/wrong-length signature, or a signature that simply doesn’t verify — callers should treat all of these identically: “not verified,” renderingSIG=invalidrather thanSIG=verifiedin the marker (seewrap_jekt_message’s doc comment). - verify_
trusted_ reagent_ jekt - The verification result a receiver records as
reagent_verified: the signature checks out AND its key is the trusted production key. A signature under any other registered key is not a verified sender. - verify_
wan_ against_ record - The checks against a directory record, in the §2.3 order: the chain, the
record matching what was carried, then the message signature. Run after
check_wan_envelopepasses and a record was found. Replay is the caller’s (it needs state). - verify_
wan_ jekt - Verify a claimed WAN signature against the claimed sender’s published public key.
- wan_
instance_ display_ label - The label a human sees for a verified instance:
<host_hint>~<id8>(§2.6). The suffix comes from the id the chain proved, never from the sender’s own label, so two instances claiming the same hostname still render differently. - wan_
instance_ id - An instance’s id: the first 128 bits of
SHA-256(instance public key), lowercase unpadded base32 — 26 chars of[a-z2-7](§2.2). - wan_
key_ fingerprint - An agent key’s fingerprint:
SHA-256(agent public key), unpadded base64url — 43 chars. Carried aswan_key_fp, an unsigned lookup hint (§2.1): a wrong value finds a key the signature won’t verify under. - wan_
sig_ is_ fresh - Whether a carried
wan_ts_secsis inside the freshness window (§2.4). Both bounds inclusive. Outside it the verdict isNone(stale), notSome(false): a stale signature grants nothing that dropping it wouldn’t.