Skip to main content

Module jekt_sign

Module jekt_sign 

Source
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_fp is a lookup hint and is not signed.
WanKeyRecord
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_sig is 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§

WanCheckFailure
Why a carried signature did not verify. Each maps to the §2.3 verdict the caller records; the ones that are None there 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_secs may 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_secs may 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_agent is the cloud row’s source_agent; polled_agent_id is 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_KEY process env var). Returns None on 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 so agentmux-mcp doesn’t need its own base64 dependency 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 the random_seed_bytes + BASE64.encode split in the srv crate’s agent_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_id whose matching private key is believed secret today. reagent-v1-dev stays registered in reagent_public_key (so verify_reagent_jekt still 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 through verify_trusted_reagent_jekt, which requires this key; any other key yields reagent_verified = Some(false) — an active verification failure, forced sensitive.
is_valid_wan_host_hint
Whether host_hint is 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 in agentmux-mcp, never server-side, exactly like sign_lan_jekt.
sign_channel_jekt_v2
Sign a cross-channel jekt as source_uid, with that UID’s own LAN private key. None for 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. key of any length is accepted (HMAC’s own key-hashing handles that) but agent_jekt_keys::agent_jekt_key_ensure always mints 32 bytes.
sign_lan_jekt
Sign a jekt with the sending agent’s own LAN private key (read from its AGENTMUX_LAN_KEY process env var, mirroring sign_jekt’s AGENTMUX_JEKT_KEY — called client-side, inside agentmux-mcp, never server-side). private_key must be exactly 32 bytes (the seed generate_lan_keypair produced) — returns None on any other length rather than panicking, same “missing/malformed key must never block sending” policy sign_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. None for 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_KEY process env var, client-side inside agentmux-mcp, never server-side — same placement as sign_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 — see LanDiscovery::find_agent_lan_pubkey). Returns false (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” policy verify_reagent_jekt already 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_sig against the pinned public key for key_id. Returns false (never panics) for an unknown key_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,” rendering SIG=invalid rather than SIG=verified in the marker (see wrap_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_envelope passes 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 as wan_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_secs is inside the freshness window (§2.4). Both bounds inclusive. Outside it the verdict is None (stale), not Some(false): a stale signature grants nothing that dropping it wouldn’t.