Skip to main content

agentmux_srv/server/reactive/
lan_v2.rs

1//! Identity M4d-6 (`SPEC_AGENT_IDENTITY_CARRIED_NOT_DERIVED_2026_09_23.md`
2//! ยง6.5.10): the LAN v2 signature, checked once the v1 LAN signature verified.
3//! LAN peers are unauthenticated, so a UID it proves is recorded only as
4//! claimed by that LAN path: never verified, never matched to a local row.
5
6use super::{AppState, InjectionRequest};
7
8/// Record `source_uid` as claimed by this LAN path when the answering peer
9/// gave that UID's key for `claimed`, the `(name, uid)` pin matches, and the
10/// v2 signature verifies under it. Only once the v1 check passed, so the
11/// name pin already matched: the UID pin is set only beside it.
12pub(super) fn claim_uid(state: &AppState, req: &mut InjectionRequest, claimed: &str) {
13    if req.lan_verified != Some(true) {
14        return;
15    }
16    let (Some(sig), Some(uid)) = (req.lan_sig_v2.clone(), req.source_uid.clone().filter(|u| !u.is_empty())) else {
17        return;
18    };
19    let Some((answered_uid, key)) = state.lan_discovery.cached_lan_uid_key(claimed) else {
20        return;
21    };
22    let record = crate::backend::agent_resolve::record_uid_fallback;
23    if answered_uid != uid {
24        record("m4d.lan_v2_other_uid");
25        return;
26    }
27    use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
28    let key_b64 = BASE64.encode(&key);
29    match state.mstore.lan_peer_uid_pin_get_or_set(claimed, &uid, &key_b64) {
30        Ok(pinned) if pinned == key_b64 => {}
31        Ok(_) => {
32            record("m4d.lan_v2_pin_mismatch");
33            return;
34        }
35        Err(_) => return,
36    }
37    let verified = agentmux_common::jekt_sign::verify_lan_jekt_v2(
38        &key,
39        req.request_id.as_deref().unwrap_or(""),
40        claimed,
41        &uid,
42        &req.target_agent,
43        req.ts_secs.unwrap_or(0),
44        &req.message,
45        &sig,
46    );
47    if verified {
48        req.lan_claimed_uid = uid;
49    } else {
50        record("m4d.lan_v2_bad_sig");
51    }
52}
53
54#[cfg(test)]
55#[path = "../tests/reactive/lan_v2_tests.rs"]
56mod tests;