Follow-up to #926 (which converged Columba onto the upstream fields[0x40] reaction shape — thanks!). This is about attribution of reactions that arrive via a re-originating group relay.
Symptom
Columba already emits and parses the canonical fields[0x40] = {0x00: target, 0x01: emoji}, so reactions relayed through a group relay (e.g. reticulum-forwarding-service) render correctly on the right message — but they are all attributed to the relay, not to the person who reacted.
Why
A re-originating relay re-signs each message (and each reaction) as itself, so the carrying source_hash is the relay's. A reaction has no body to carry a [Nick] prefix, so ReactionWireCodec.parseCanonical (which sets sender = sourceHashHex) collapses every relayed reaction onto the relay's identity. Per the LXMF spec this is expected — §5.9.8 says reaction attribution rides on source_hash — and it's only a problem in the relay case.
The convention (app-layer, on reserved fields)
The relay restores attribution by stamping the reactor's source_hash (its lxmf.delivery destination hash — the same value a direct reaction carries, and what contacts are keyed by, per §9.1) into the reserved app-convention custom fields (§5.9.1):
fields[0xFB] FIELD_CUSTOM_TYPE = "originator-identity" # exact UTF-8
fields[0xFC] FIELD_CUSTOM_DATA = <reactor source_hash, raw 16 bytes>
A cooperating client reads the stamp on a 0x40 reaction and attributes by 0xFC instead of the carrying source_hash, falling back to source_hash when the stamp is absent. Purely additive — the 0x40 wire format is untouched and spec-only clients ignore it.
Full convention + rationale + cross-client decode notes: reticulum-forwarding-service/docs/reaction-attribution.md.
Proposed fix
PR #1006 — a ~12-line change in the shared rns-api ReactionWireCodec (covers both backends): read the stamp in parseCanonical and use it as sender when fields[0xFB] == "originator-identity". No serializer change needed (AppDataParser.serializeFieldsToJson already surfaces 251/252).
Happy to adjust to fit Columba's conventions. (Note: I couldn't run ./gradlew :rns-api:test here — no Android toolchain — so please run it before merge.)
Follow-up to #926 (which converged Columba onto the upstream
fields[0x40]reaction shape — thanks!). This is about attribution of reactions that arrive via a re-originating group relay.Symptom
Columba already emits and parses the canonical
fields[0x40] = {0x00: target, 0x01: emoji}, so reactions relayed through a group relay (e.g.reticulum-forwarding-service) render correctly on the right message — but they are all attributed to the relay, not to the person who reacted.Why
A re-originating relay re-signs each message (and each reaction) as itself, so the carrying
source_hashis the relay's. A reaction has no body to carry a[Nick]prefix, soReactionWireCodec.parseCanonical(which setssender = sourceHashHex) collapses every relayed reaction onto the relay's identity. Per the LXMF spec this is expected — §5.9.8 says reaction attribution rides onsource_hash— and it's only a problem in the relay case.The convention (app-layer, on reserved fields)
The relay restores attribution by stamping the reactor's
source_hash(itslxmf.deliverydestination hash — the same value a direct reaction carries, and what contacts are keyed by, per §9.1) into the reserved app-convention custom fields (§5.9.1):A cooperating client reads the stamp on a
0x40reaction and attributes by0xFCinstead of the carryingsource_hash, falling back tosource_hashwhen the stamp is absent. Purely additive — the0x40wire format is untouched and spec-only clients ignore it.Full convention + rationale + cross-client decode notes:
reticulum-forwarding-service/docs/reaction-attribution.md.Proposed fix
PR #1006 — a ~12-line change in the shared
rns-apiReactionWireCodec(covers both backends): read the stamp inparseCanonicaland use it assenderwhenfields[0xFB] == "originator-identity". No serializer change needed (AppDataParser.serializeFieldsToJsonalready surfaces251/252).Happy to adjust to fit Columba's conventions. (Note: I couldn't run
./gradlew :rns-api:testhere — no Android toolchain — so please run it before merge.)