fix(mesh): DM-via-channel tunnel + disable presence spam
Meshcore direct unicast silently drops between our two Archy nodes (firmware reports flood sends with resp_code=6 but nothing arrives). Wrap DMs as channel-1 broadcasts with a [0xD1][dest_prefix(6)][inner] header; receivers filter by prefix and dispatch the inner payload through the existing typed/base64/chunk ladder. Shrink chunk body to 125B so the wrapper still fits the 160B LoRa budget. Auto-heal routing: CMD_RESET_PATH (0x0D) any type-1 contact with path_len=0 on refresh so floods take over. send_text now returns the firmware's flood/direct mode flag for diagnostics. Disable the 120s presence heartbeat broadcaster — its CBOR payload was being re-echoed as plaintext by the shared repeater, spamming every visible node with garbled "Archy-…: av�…fstatusfonline…" messages on channel 0. mesh.broadcast-presence RPC stays registered but no longer transmits. Re-enable only once presence moves off the shared broadcast path. Also: MeshState.cmd_tx behind RwLock so stop()→start() cycles don't fail with "command channel already consumed"; MeshService.send_cmd helper; drop_message_by_id for control envelopes that shouldn't appear as Sent bubbles; self_advert_name reflected into MeshStatus after set; path_len/flags parsed out of RESP_CONTACT. Frontend: unified inbox merges mesh peers with federation nodes by DID/pubkey/name; hide presence/read_receipt/edit/channel_invite/ contact_card from chat stream; publicChannel index → 1 to match the new DM-via-channel routing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
bdacc06a2b
commit
d514e0e5e4
@@ -24,6 +24,12 @@ pub const CMD_SET_DEVICE_TIME: u8 = 0x06;
|
||||
pub const CMD_SEND_SELF_ADVERT: u8 = 0x07;
|
||||
pub const CMD_SET_ADVERT_NAME: u8 = 0x08;
|
||||
pub const CMD_SYNC_NEXT_MESSAGE: u8 = 0x0A;
|
||||
/// CMD_RESET_PATH (0x0D): Tell the firmware to drop the stored route for
|
||||
/// a contact and fall back to flood routing (out_path_len = 0xFF). Used to
|
||||
/// unstick direct messages to contacts whose `path_len=0` means "no route
|
||||
/// known" — without this, the firmware silently drops outbound TXT_MSG
|
||||
/// frames to such contacts.
|
||||
pub const CMD_RESET_PATH: u8 = 0x0D;
|
||||
pub const CMD_SET_RADIO_PARAMS: u8 = 0x0B;
|
||||
pub const CMD_SET_RADIO_TX_POWER: u8 = 0x0C;
|
||||
pub const CMD_SET_TUNING_PARAMS: u8 = 0x15;
|
||||
@@ -72,6 +78,16 @@ pub const ERR_ILLEGAL_ARG: u8 = 0x06;
|
||||
/// Maximum payload size for a single LoRa message.
|
||||
pub const MAX_MESSAGE_LEN: usize = 160;
|
||||
|
||||
/// Marker byte for "direct message wrapped as channel broadcast". Our
|
||||
/// meshcore devices can hear each other's channel broadcasts (via
|
||||
/// repeater flooding) but direct unicast frames don't reach between
|
||||
/// archipelago nodes — so we emulate DMs by sending them on the shared
|
||||
/// channel with a recipient pubkey-prefix header. Format:
|
||||
/// `[DM_VIA_CHANNEL_MARKER][dest_pubkey_prefix(6B)][inner_payload…]`
|
||||
/// The inner payload is whatever we would have sent directly — a typed
|
||||
/// envelope, a chunked MC frame, or plain text.
|
||||
pub const DM_VIA_CHANNEL_MARKER: u8 = 0xD1;
|
||||
|
||||
/// Minimum frame size: marker (1) + length (2) + command/response (1) = 4 bytes.
|
||||
const MIN_FRAME_SIZE: usize = 4;
|
||||
|
||||
@@ -224,6 +240,15 @@ pub fn build_get_contacts() -> Vec<u8> {
|
||||
encode_frame(&[CMD_GET_CONTACTS])
|
||||
}
|
||||
|
||||
/// CMD_RESET_PATH (0x0D): `[0x0D][pub_key:32]`. Clears the stored route
|
||||
/// for a contact so subsequent sends route via flood instead of being
|
||||
/// silently dropped.
|
||||
pub fn build_reset_path(pubkey: &[u8; 32]) -> Vec<u8> {
|
||||
let mut data = vec![CMD_RESET_PATH];
|
||||
data.extend_from_slice(pubkey);
|
||||
encode_frame(&data)
|
||||
}
|
||||
|
||||
/// CMD_SYNC_NEXT_MESSAGE (0x0A): Retrieve the next queued message.
|
||||
pub fn build_sync_next_message() -> Vec<u8> {
|
||||
encode_frame(&[CMD_SYNC_NEXT_MESSAGE])
|
||||
@@ -294,6 +319,8 @@ pub struct ParsedContact {
|
||||
pub advert_name: String,
|
||||
pub last_advert: u32,
|
||||
pub contact_type: u8,
|
||||
pub path_len: u8,
|
||||
pub flags: u8,
|
||||
}
|
||||
|
||||
/// Parse RESP_CONTACT (0x03) response.
|
||||
@@ -305,7 +332,8 @@ pub fn parse_contact(data: &[u8]) -> Result<ParsedContact> {
|
||||
|
||||
let public_key_hex = hex::encode(&data[0..32]);
|
||||
let contact_type = data[32];
|
||||
// flags at data[33], path_len at data[34]
|
||||
let flags = if data.len() > 33 { data[33] } else { 0 };
|
||||
let path_len = if data.len() > 34 { data[34] } else { 0 };
|
||||
// path at data[35..99] (64 bytes)
|
||||
// name at data[99..131] (32 bytes)
|
||||
let name_start = 99.min(data.len());
|
||||
@@ -330,6 +358,8 @@ pub fn parse_contact(data: &[u8]) -> Result<ParsedContact> {
|
||||
advert_name,
|
||||
last_advert,
|
||||
contact_type,
|
||||
path_len,
|
||||
flags,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user