Summary
While inventorying the LXMF fields[16] (0x10) wire format across LXMF clients for a community-driven Reticulum/LXMF wire-format spec, I noticed that MeshChatX and Columba (torlando-tech/columba) agree on tap-back reactions but disagree on reply-to threading. This issue is about the reply-to divergence — the reactions side is in great shape on the MeshChatX implementation.
cc @markqvist as the LXMF protocol owner. None of this is a MeshChatX bug per se — upstream LXMF doesn't currently allocate any of 0x10, 0x30, or 0x31, so both clients are working within unallocated bytes. The question is whether the two clients want to converge before upstream blesses (or rejects) an allocation.
What works today — reactions
MeshChatX's src/backend/lxmf_utils.py:11 defines LXMF_APP_EXTENSIONS_FIELD = 16, and lxmf_utils.py:30 explicitly cites "Columba app_extensions" as the interop target. Send and parse both use the same shape:
fields[16] = {
"reaction_to": "<target_message_id_hex>",
"emoji": "<unicode>",
"sender": "<source_identity_hash_hex>",
}
This matches Columba's send shape exactly, so MeshChatX → Columba reactions should land correctly. (There may be an open question about Columba's parse path — filed separately on torlando-tech/columba to confirm.) MeshChatX-to-MeshChatX is clean.
What conflicts — reply-to threading
MeshChatX uses two dedicated fields (meshchat.py:16697):
lxmf_message.fields[0x30] = bytes.fromhex(reply_to_hash) # raw bytes hash
lxmf_message.fields[0x31] = reply_quoted_content.encode("utf-8") # optional quoted text
Columba uses an overload of fields[16] (NativeMessageSender.kt):
fields[16] = {"reply_to": "<target_message_id_hex>"}
A Columba reply (fields[16] = {reply_to}) will not be detected as a reply by MeshChatX (which reads fields[0x30]), and vice versa. Once upstream LXMF allocates any of 0x10, 0x30, or 0x31 canonically, one of the two clients will be at odds with the canonical shape.
Tradeoffs as I see them
- MeshChatX's split approach uses two bytes but cleanly separates reply-target-hash from quote-text, and uses raw 32-byte hash (smaller than 64-char hex on the wire).
- Columba's overload approach uses one byte but couples reply-to to the same field as reactions, which is awkward semantically — and overloaded with
fields[16] = {reaction_to, ...} requires the parser to dispatch on inner-dict keys.
If MeshChatX's split is the intended long-term shape, it'd be worth either getting upstream LXMF to allocate FIELD_REPLY_HASH = 0x30 / FIELD_REPLY_QUOTE = 0x31 (or whatever bytes upstream prefers), or coordinating with Columba so both clients use the same approach. The reverse direction (move MeshChatX onto Columba's fields[16] overload) is also a valid option.
I don't have a strong opinion on which approach should win — happy to support either consensus.
Cross-references
Source citations
- MeshChatX reaction field constant:
src/backend/lxmf_utils.py:11
- MeshChatX reaction docstring referencing Columba interop:
src/backend/lxmf_utils.py:30
- MeshChatX reaction send:
meshchat.py:16805
- MeshChatX reaction parse:
src/backend/lxmf_utils.py:352-358
- MeshChatX reaction tests:
tests/backend/test_lxmf_reactions.py:18
- MeshChatX reply send:
meshchat.py:16697
- Columba reaction send:
reticulum/src/main/java/network/columba/app/reticulum/protocol/NativeReticulumProtocol.kt ~2230-2245
- Columba reaction parse (possible mismatched shape):
app/src/main/java/network/columba/app/ui/model/MessageMapper.kt ~90-160
- Columba reply send:
reticulum/src/main/java/network/columba/app/reticulum/protocol/NativeMessageSender.kt
Summary
While inventorying the LXMF
fields[16](0x10) wire format across LXMF clients for a community-driven Reticulum/LXMF wire-format spec, I noticed that MeshChatX and Columba (torlando-tech/columba) agree on tap-back reactions but disagree on reply-to threading. This issue is about the reply-to divergence — the reactions side is in great shape on the MeshChatX implementation.cc @markqvist as the LXMF protocol owner. None of this is a MeshChatX bug per se — upstream LXMF doesn't currently allocate any of
0x10,0x30, or0x31, so both clients are working within unallocated bytes. The question is whether the two clients want to converge before upstream blesses (or rejects) an allocation.What works today — reactions
MeshChatX's
src/backend/lxmf_utils.py:11definesLXMF_APP_EXTENSIONS_FIELD = 16, andlxmf_utils.py:30explicitly cites "Columba app_extensions" as the interop target. Send and parse both use the same shape:This matches Columba's send shape exactly, so MeshChatX → Columba reactions should land correctly. (There may be an open question about Columba's parse path — filed separately on
torlando-tech/columbato confirm.) MeshChatX-to-MeshChatX is clean.What conflicts — reply-to threading
MeshChatX uses two dedicated fields (
meshchat.py:16697):Columba uses an overload of
fields[16](NativeMessageSender.kt):A Columba reply (
fields[16] = {reply_to}) will not be detected as a reply by MeshChatX (which readsfields[0x30]), and vice versa. Once upstream LXMF allocates any of0x10,0x30, or0x31canonically, one of the two clients will be at odds with the canonical shape.Tradeoffs as I see them
fields[16] = {reaction_to, ...}requires the parser to dispatch on inner-dict keys.If MeshChatX's split is the intended long-term shape, it'd be worth either getting upstream LXMF to allocate
FIELD_REPLY_HASH = 0x30/FIELD_REPLY_QUOTE = 0x31(or whatever bytes upstream prefers), or coordinating with Columba so both clients use the same approach. The reverse direction (move MeshChatX onto Columba'sfields[16]overload) is also a valid option.I don't have a strong opinion on which approach should win — happy to support either consensus.
Cross-references
FIELD_REACTION = 0x10patch for upstream LXMF (parked on a fork becausemarkqvist/LXMFhas interactions locked to collaborators): https://github.com/thatSFguy/LXMF/tree/field-reaction-0x10Source citations
src/backend/lxmf_utils.py:11src/backend/lxmf_utils.py:30meshchat.py:16805src/backend/lxmf_utils.py:352-358tests/backend/test_lxmf_reactions.py:18meshchat.py:16697reticulum/src/main/java/network/columba/app/reticulum/protocol/NativeReticulumProtocol.kt~2230-2245app/src/main/java/network/columba/app/ui/model/MessageMapper.kt~90-160reticulum/src/main/java/network/columba/app/reticulum/protocol/NativeMessageSender.kt