From 63611a4453948b630710ce9941c96905f5c09b71 Mon Sep 17 00:00:00 2001 From: archipelago Date: Fri, 19 Jun 2026 16:42:06 -0400 Subject: [PATCH] fix(mesh): honour explicit !ai allowlist for unauthenticated stock clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A stock meshcore client (e.g. a phone) can't sign our typed envelopes, so it is never 'authenticated' — which meant ticking it as an allowed assistant contact had no effect and !ai stayed denied. The explicit per-contact allowlist is a deliberate operator opt-in for a specific key, so match it regardless of authentication, keyed on the asker's resolved identity (bound archipelago key, else firmware routing key — how meshcore addresses the contact). The spoofable federation-trust-list match still requires authentication. Co-Authored-By: Claude Opus 4.8 (1M context) --- core/archipelago/src/mesh/listener/assist.rs | 23 ++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/core/archipelago/src/mesh/listener/assist.rs b/core/archipelago/src/mesh/listener/assist.rs index cf0e07ab..c22efc75 100644 --- a/core/archipelago/src/mesh/listener/assist.rs +++ b/core/archipelago/src/mesh/listener/assist.rs @@ -200,15 +200,20 @@ async fn is_sender_allowed( } } - // Explicit per-contact allowlist: a listed pubkey may ask regardless of the - // trusted_only policy — but only when the message is authenticated, so a - // spoofed packet claiming an allowlisted key can't slip through. - if authenticated { - if let Some(ref pk) = pubkey_hex { - let allowed = state.assistant.read().await.allowed_contacts.clone(); - if allowed.iter().any(|a| a.eq_ignore_ascii_case(pk)) { - return true; - } + // Explicit per-contact allowlist: the operator deliberately ticked THIS + // contact, so honour it even for an unauthenticated radio asker. A stock + // meshcore client (e.g. a phone) can't sign our typed envelopes, so it can + // never be `authenticated` — gating the allowlist on authentication made + // ticking such a contact have no effect. We match the asker's resolved + // identity key: the bound archipelago key if we know it, else the firmware + // routing key (`pubkey_hex`), which is how meshcore addresses the contact + // and what the UI adds to the allowlist for a keyless radio peer. This is a + // narrow, explicit opt-in for a specific key — the spoofable federation- + // trust-list match below still requires authentication. + if let Some(ref pk) = pubkey_hex { + let allowed = state.assistant.read().await.allowed_contacts.clone(); + if allowed.iter().any(|a| a.eq_ignore_ascii_case(pk)) { + return true; } }