pub struct InjectionRequest {Show 35 fields
pub target_agent: String,
pub message: String,
pub source_agent: Option<String>,
pub request_id: Option<String>,
pub priority: Option<String>,
pub jekt_tier: Option<JektTier>,
pub delivery_tier: Option<String>,
pub forward_hops: u8,
pub ts_secs: Option<i64>,
pub jekt_sig: Option<String>,
pub sig_verified: Option<bool>,
pub audit_source_uid: String,
pub lan_claimed_uid: String,
pub held_sent_at_ms: Option<i64>,
pub reagent_sig: Option<String>,
pub reagent_key_id: Option<String>,
pub reagent_msg_id: Option<String>,
pub reagent_ts_secs: Option<i64>,
pub reagent_verified: Option<bool>,
pub lan_sig: Option<String>,
pub lan_verified: Option<bool>,
pub source_channel: Option<String>,
pub channel_sig: Option<String>,
pub source_uid: Option<String>,
pub channel_sig_v2: Option<String>,
pub lan_sig_v2: Option<String>,
pub wan_sig: Option<String>,
pub wan_source_host: Option<String>,
pub channel_verified: Option<bool>,
pub wan_verified: Option<bool>,
pub wan_instance: Option<WanInstanceInfo>,
pub wan_reason: Option<String>,
pub wan_detail: Option<String>,
pub is_transcript_request: bool,
pub transcript_request_escalate_forced: bool,
}Expand description
Request to inject a message into an agent’s terminal.
Fields§
§target_agent: String§message: String§source_agent: Option<String>§request_id: Option<String>§priority: Option<String>§jekt_tier: Option<JektTier>Sensitivity tier declared by the sender. When absent, defaults to Coord.
The handler may escalate to Sensitive based on keyword scanning.
delivery_tier: Option<String>Delivery tier of this jekt (host/lan/wan) — used in the marker.
forward_hops: u8Number of cross-instance HTTP forwards this request has already gone
through (Tier 2a/2b/3 each increment before forwarding). Bounds a
pathological cycle where two channels each hold a stale-but-
PID-alive shared-registry entry pointing at the other for the same
agent name — without this, such a cycle would forward back and
forth indefinitely, hanging the original request (reagent P1 on
PR #2350). #[serde(default)] so older callers (muxbus client,
any request that predates this field) default to 0, unaffected.
ts_secs: Option<i64>Unix seconds this jekt was signed at (part of the signed material,
not just a display timestamp) — see agentmux_common::jekt_sign and
jekt_sig below. None for unsigned/legacy requests.
jekt_sig: Option<String>Base64 HMAC-SHA256 signature over (request_id, source_agent,
target_agent, ts_secs, message), produced by the sender’s own
AGENTMUX_JEKT_KEY. Verified against the claimed source_agent’s
stored key (SPEC_JEKT_TRUST_LAYER_COMPLETION_2026_08_13.md §2.2);
absent, unverifiable, or mismatched all mean “unverified” — the
message still delivers, but downgraded to TRUST=unverified and
escalated to TIER=sensitive, never silently trusted.
sig_verified: Option<bool>Server-computed verification outcome — #[serde(skip_deserializing)]
means an attacker-supplied JSON body can NEVER set this field, even
by including it; only handle_reactive_inject
(server/reactive.rs, which has AppState::mstore) may set it,
after independently looking up the claimed source_agent’s stored
key and verifying jekt_sig against it, before handing the request
to Handler::inject_message (which has no Store access “by
design” — see record_supervisor_decision’s doc comment for why).
None = not checked (network-tier messages are already
unconditionally sensitive regardless of signature; or the claimed
source_agent has never had a key minted, so there’s nothing to
verify against — see the rollout-safety note in
handle_reactive_inject, this deliberately does NOT escalate a
caller — e.g. the Slack/Discord/Telegram/WhatsApp bridges, or an
agent not yet respawned since this feature shipped — that was never
capable of signing in the first place). Some(false) is the real
signal: this source_agent DOES have a key, and either no signature
was given or it didn’t match.
audit_source_uid: StringIdentity M4c-2d (SPEC_AGENT_IDENTITY_CARRIED_NOT_DERIVED_2026_09_23.md
§6.5.9): the sender’s UID, from the request’s Caller — its
X-Agent-Token — set by the srv handler that received it, and copied
onto the audit entries this delivery writes. #[serde(skip)] both
ways: a body can never set it, and it never rides a forwarding hop —
distinct from the source_uid wire field §6.5.3 reserves for the MCP
to set and sign (M4d). Empty = Unattributed.
lan_claimed_uid: StringIdentity M4d-6: the UID a LAN v2 signature proved under the answering
peer’s pinned key (server/reactive/lan_v2.rs). Claimed by that LAN
path only, never verified attribution; #[serde(skip)] like
audit_source_uid. Empty = none.
held_sent_at_ms: Option<i64>When a held message was originally accepted (ms), set only by the
held-jekt replay (SPEC_DURABLE_JEKT_DELIVERY_2026_09_24.md §2.4):
the delivered header shows this as TS plus HELD_FOR, so a late
message never reads as a current one. #[serde(skip)] both ways.
reagent_sig: Option<String>Base64 Ed25519 signature over the same signed material as jekt_sig
(request_id, source_agent, target_agent, ts_secs, message), produced
by an AgentMux-operated WAN-tier service sender (currently only the
GitHub review-notification consumer, “reagent”) using a private key
held only by the cloud service. Distinct from
jekt_sig (host-tier HMAC, symmetric, one key per local instance) —
see agentmux_common::jekt_sign’s module doc for why WAN needs an
asymmetric scheme instead. None for any non-reagent WAN sender or
any host/lan-tier request — this field is meaningless off the WAN
tier and verification is skipped entirely when absent.
reagent_key_id: Option<String>Which pinned public key reagent_sig claims to be signed under —
see agentmux_common::jekt_sign::reagent_public_key for the
rotation rationale. A legitimate sender always sends all four
signing fields (reagent_sig, reagent_key_id, reagent_msg_id,
reagent_ts_secs) together, so None here alongside a present
reagent_sig is treated the same as no signature having been
attempted at all (reagent_verified resolves to None, not
Some(false)) — see cloud_subscriber::sync_agent_reactive’s match
on all four fields. This does not weaken verification: dropping this
field can only ever lose a legitimate SIG=verified/possible tier
relaxation, never grant one — see reagent_verified’s doc comment
below for what this field, once present, is now also used for
besides rendering SIG=.
Whichever caller builds the InjectionRequest that ultimately
reaches Handler::inject_message MUST carry this field through from
wherever it originated (e.g. cloud_subscriber::sync_agent_reactive
copying it from the Injection it received) — handler.rs’s tier-
relaxation gate checks THIS field, not just whether reagent_sig
verified, so dropping it silently disables relaxation on that
delivery path even when reagent_verified is correctly Some(true)
(reagentx P0 on PR #2576 — exactly this omission on the WS path).
reagent_msg_id: Option<String>Message id that was part of reagent_sig’s signed material — the
same id the cloud relay gives each pending injection it hands
this client. Distinct from request_id above, which is
this request’s own (possibly re-derived, e.g. across a cross-instance
forward) identifier — reagent_msg_id must stay exactly what was
signed for verification to succeed regardless of how request_id
is set on this particular hop.
reagent_ts_secs: Option<i64>Unix seconds reagent_sig was signed at — part of the signed
material, checked for anti-replay against a max-age window (the WS
path’s cloud_subscriber::REAGENT_SIG_MAX_AGE_SECS, or the HTTP
path’s own constant of the same name in server/reactive.rs), same
purpose as ts_secs/JEKT_SIG_MAX_AGE_SECS for host-tier jekt_sig.
reagent_verified: Option<bool>Server-computed verification outcome for reagent_sig — same
#[serde(skip_deserializing)] guarantee as sig_verified: no
attacker-supplied JSON body can set this directly. Set by whichever
caller actually received this WAN injection and independently
verified reagent_sig against the pinned key —
cloud_subscriber::sync_agent_reactive for the desktop app’s WS
delivery path, or server/reactive.rs::verify_reagent_signature for
the HTTP /agentmux/reactive/inject path used by standalone pollers
like @agentmuxai/muxbus-client (reagentx P1 on PR #41 — that path
used to receive the four reagent_* fields over HTTP with nothing on
the receiving end able to verify them at all). Some(true) renders
SIG=verified in the marker; Some(false) (a reagent_sig was present but didn’t
verify) renders SIG=invalid — a real red flag, same spirit as
host-tier’s TRUST=unverified. None (no reagent_sig attempted)
renders no SIG= field at all, keeping ordinary WAN traffic
unchanged.
Never affects TRUST — WAN jekts stay unconditionally
TRUST=network-claimed regardless of this field; verification
answers “who is this really from,” not “did this cross a network
boundary.”
DOES affect TIER. As of
docs/specs/SPEC_JEKT_SENSITIVE_TIER_NARROWING_2026_08_15.md
(superseding SPEC_JEKT_REAGENT_TRUST_RELAXATION_2026_08_14.md’s
narrower version of this same claim), handler.rs’s tier-escalation
block only forces TIER=sensitive by delivery tier when this is
Some(false) — a reagent_sig was present but did NOT
cryptographically verify, i.e. an active forgery attempt, or verified
only under a key other than the trusted production key (see
agentmux_common::jekt_sign::verify_trusted_reagent_jekt). None (no
signature attempted) falls through to the declared tier (default
coord), same as any other self-declared sender. Declared-sensitive and
keyword-match escalation still apply unconditionally on top of all
of the above.
lan_sig: Option<String>Base64 Ed25519 signature over the same signed material as jekt_sig/
reagent_sig, produced by the claimed source_agent’s own LAN
signing key (AGENTMUX_LAN_KEY, read client-side in agentmux-mcp —
see agentmux_common::jekt_sign::sign_lan_jekt). Meaningless off the
LAN tier, same scoping as reagent_sig being WAN-only. See
docs/specs/SPEC_JEKT_LAN_TIER_SIGNING_2026_08_15.md §2.3/§2.4.
lan_verified: Option<bool>Server-computed verification outcome for lan_sig —
#[serde(skip_deserializing)] guarantee identical to sig_verified/
reagent_verified: no attacker-supplied JSON body can set this
directly. Set by handle_reactive_inject after looking up the
claimed source_agent’s LAN public key (a LAN round-trip via
LanDiscovery::find_agent_lan_pubkey — unlike host-tier’s
synchronous local lookup, this one is async) and independently
verifying lan_sig against it.
None — no lan_sig attempted, OR the claimed sender has no LAN
public key this instance could find (never minted one, or genuinely
unreachable on the LAN right now) — nothing to check against, same
“don’t escalate a caller that was never capable of signing”
semantics sig_verified’s None case documents. Some(true) renders
TRUST=lan-verified in the marker; Some(false) (a lan_sig was
present, a public key WAS found, but it didn’t verify — someone tried
to forge a specific agent’s identity) is a real red flag, same
spirit as host-tier’s TRUST=unverified and WAN’s SIG=invalid —
forces TIER=sensitive unconditionally
(docs/specs/SPEC_JEKT_LAN_TIER_SIGNING_2026_08_15.md §2.4/§2.5).
source_channel: Option<String>The sending instance’s channel id, as declared by the sender and bound
into channel_sig’s signed material
(SPEC_JEKT_CROSS_CHANNEL_TRUST_2026_09_02.md §D5). Client-supplied,
like source_agent — a wrong value simply fails verification, it
grants nothing. Carried through same-host forwards unchanged.
channel_sig: Option<String>Base64 Ed25519 signature for the cross-channel tier (same machine,
different AgentMux instance), produced by the claimed source_agent’s
own LAN keypair over a domain-separated payload that also binds
source_channel (agentmux_common::jekt_sign::sign_channel_jekt).
Meaningless off the channel tier, same scoping as lan_sig.
source_uid: Option<String>Identity M4d-6 (SPEC_AGENT_IDENTITY_CARRIED_NOT_DERIVED_2026_09_23.md
§6.5.10): the sending agent’s UID, as its MCP fetched it with its
UID-keyed key (M4d-3). Bound into channel_sig_v2; a forwarding srv
never sets or changes it. Absent when the sender has no UID key.
channel_sig_v2: Option<String>Base64 Ed25519 signature, made only with the sender’s UID-keyed LAN
key, over the cross-channel material plus source_uid
(agentmux_common::jekt_sign::sign_channel_jekt_v2). Verified by UID
against the uid_public_key an instance published for it (M4d-5), so
it proves which agent sent the jekt, not just which name. The v1
channel_sig is still sent beside it.
lan_sig_v2: Option<String>Identity M4d-6: base64 Ed25519 signature, made only with the sender’s
UID-keyed LAN key, over the LAN material plus source_uid
(agentmux_common::jekt_sign::sign_lan_jekt_v2). A receiver checks it
only after the v1 lan_sig verified, and records the UID as claimed by
that LAN path, never as verified attribution.
wan_sig: Option<String>Base64 Ed25519 signature for the general agent-to-agent WAN tier,
produced by the claimed source_agent’s own WAN keypair
(db_agent_wan_keys — a different key from the LAN one) over a
domain-separated payload
(agentmux_common::jekt_sign::sign_wan_jekt).
Carried but NOT yet verified — there is deliberately no
wan_verified companion field yet. Verification has two
prerequisites that do not exist: resolving (sender_account, source_agent) to exactly one published key waits on relay-side work
tracked in the private cloud repo, and the
signed msgid/ts_secs must survive the cloud round trip, which
today they do not — cloud_subscriber replaces request_id with the
cloud’s own injection id and leaves ts_secs unset. Adding a
verification-outcome field before either exists would read as wired
when it is not, so it lands with the verifier instead.
Signing ships first on purpose: an agent only receives its key at spawn, so minting now is what makes verification apply to a real population of agents later rather than almost none.
wan_source_host: Option<String>The sending machine’s host label. With source_channel, identifies
which AgentMux instance under the sending account owns the keypair
wan_sig was produced with — the same account can legitimately run one agent name on
several machines and several channels, each with its own database and
so its own key. Client-supplied like source_channel and bound into
the signed material, so a wrong value fails verification rather than
selecting a key it shouldn’t.
channel_verified: Option<bool>Server-computed verification outcome for channel_sig —
#[serde(skip_deserializing)], the same guarantee as sig_verified/
lan_verified: no attacker-supplied JSON body can set this. Set by
verify_cross_channel_signature (server/reactive.rs) after resolving
the claimed source_agent’s published public key from the host-global
shared registry (AgentEntry::jekt_public_key, §D1) — a local file
read, so unlike lan_verified it’s synchronous.
None — no entry for the claimed sender in the shared registry (a
bridge, or an agent that has never registered), an entry with an
empty jekt_public_key (written before Phase A shipped — “cannot
check,” never “failed the check,” spec §6), or the sender is a
same-instance agent (the HMAC path owns those, §D2 step 2).
Some(true) renders TRUST=channel-verified and joins the
verified-sender list for the ESCALATE=none relaxation. Some(false)
— an entry with a key WAS found and the signature was missing, stale,
or wrong — is the §D3 red flag, not yet escalated: Phase B
(this) only ever moves a message from self-declared to
channel-verified; Phase C turns Some(false) into forced
TIER=sensitive once published keys have propagated (spec §10).
wan_verified: Option<bool>W3-S (SPEC_WAN_JEKT_VERIFICATION_2026_09_24.md §2.3): the verdict on
a same-account agent’s WAN signature, computed only by the cloud
subscriber (muxbus::wan_verify) — never settable by an HTTP caller
(skip_deserializing), so the HTTP entry point’s self-declared
delivery_tier: "wan" always leaves it None.
Some(true) renders TRUST=wan-verified with wan_instance, and
joins the verified-sender set for an approved or new instance
(same-account installs, spec §2.6 as amended 2026-09-26); a revoked
one is treated as Some(false). Some(false) — the envelope, the
certificate chain, the record match, the signature or the replay check
actively failed — is forced TIER=sensitive. None covers every
“couldn’t check” case: unsigned, another account, stale, no record,
directory unreachable.
wan_instance: Option<WanInstanceInfo>The verified sending instance, set with wan_verified == Some(true).
wan_reason: Option<String>Why wan_verified isn’t Some(true), when there’s something to say
(wan_sig_stale, wan_sig_replay, wan_key_unavailable,
wan_cert_invalid, …) — audit only, never rendered.
wan_detail: Option<String>The cause behind wan_reason, when there is one to read (e.g.
directory answered 403 Forbidden) — audit only, never rendered.
is_transcript_request: boolServer-computed: this jekt’s message is a transcript_request
payload (agentmux_common::transcript_request::parse_transcript_request)
— muxspect Phase B/C’s LAN/WAN conversation-visibility protocol.
#[serde(skip_deserializing)], same guarantee as sig_verified
etc.: no attacker-supplied JSON body can force or suppress this —
resolve_transcript_request_tier_fields always recomputes it by
re-parsing message itself, ignoring anything the client claims.
Called from both delivery paths that reach Handler:
handle_reactive_inject for host/cross-channel/LAN, and
muxbus::cloud_subscriber::sync_agent_reactive for WAN. Forces TIER=sensitive
unconditionally when true, regardless of trust
(SPEC_JEKT_TRANSCRIPT_REQUEST_TIER_RULES_2026_08_22.md rule 1) — a
content-disclosure request isn’t caught by the existing credential/
destructive-keyword scan at all.
transcript_request_escalate_forced: boolServer-computed: when is_transcript_request is also true, whether
the RESPONDING agent’s (target_agent’s) own conversation_visibility
setting is ask, or trusted_peers with a requester not on that
agent’s own allowlist — i.e. a case that genuinely needs a human
decision, per SPEC_JEKT_TRANSCRIPT_REQUEST_TIER_RULES_2026_08_22.md
rule 2. When true, ESCALATE=required is NOT relaxed to none
even for a cryptographically verified sender (the one named
exception to the 2026-08-17 relaxation) — unlike every other
TIER=sensitive case, where a verified sender’s identity answers
the only open question. Meaningless (always false) when
is_transcript_request is false. Resolved by
resolve_transcript_request_tier_fields (has Store access, unlike
Handler::inject_message, which has none “by design”) — called from
both delivery paths that reach Handler: handle_reactive_inject
for host/cross-channel/LAN, and
muxbus::cloud_subscriber::sync_agent_reactive for WAN — same
“resolve in the caller with Store access, pass the outcome through”
pattern sig_verified/lan_verified already establish.
Implementations§
Source§impl InjectionRequest
impl InjectionRequest
Sourcepub fn is_signed(&self) -> bool
pub fn is_signed(&self) -> bool
Whether a sender signature rides on this request. Each binds the
target as signed, so a signed request’s target must reach every hop
unchanged; the sender’s MCP already sends the route name
(agentmux-mcp signed_jekt).
Sourcepub fn route_unsigned_target(&mut self)
pub fn route_unsigned_target(&mut self)
An unsigned request’s target as its route name, so a display-name
target (“Loap #3”) routes as “Loap-3” on every tier, not only the
local one. A signed one is left as it is (see is_signed).
Trait Implementations§
Source§impl Clone for InjectionRequest
impl Clone for InjectionRequest
Source§fn clone(&self) -> InjectionRequest
fn clone(&self) -> InjectionRequest
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for InjectionRequest
impl Debug for InjectionRequest
Source§impl Default for InjectionRequest
impl Default for InjectionRequest
Source§fn default() -> InjectionRequest
fn default() -> InjectionRequest
Source§impl<'de> Deserialize<'de> for InjectionRequest
impl<'de> Deserialize<'de> for InjectionRequest
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for InjectionRequest
impl RefUnwindSafe for InjectionRequest
impl Send for InjectionRequest
impl Sync for InjectionRequest
impl Unpin for InjectionRequest
impl UnsafeUnpin for InjectionRequest
impl UnwindSafe for InjectionRequest
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more