Found while verifying ADR 0004 against the code for #365. Pre-existing, unrelated to that PR.
What happens
bindings/python/offline_protocol_sdk/internet_manager.py:616-635 injects relay group answers using prefixes that do not exist in the reserved prefix registry:
__GRP_CREATED__
__GRP_INVITE__
__GRP_MSG__
The real names are in crates/offline-protocol/src/protocol/prefixes.rs and are mirrored by hand into Swift (RelayAnswerPrefixes.swift) and Kotlin (RelayAnswerPrefixes.kt) with literal-pinning tests in each. Python has no copy and no pin.
The same injection also supplies an attributed sender_id.
Why it fails silently
This is precisely the hazard ADR 0004 describes. A frame under an unregistered prefix is not in the relay-answer exemption set, so it is treated as ordinary control traffic and refused as unsigned. The refusal is correct, and it is invisible: the Python caller sees a successful inject and no error, and the answer simply never takes effect.
Additionally, because the exemption is narrowed to internet-arrival frames carrying no transport peer identity (security.rs:463-472), attaching a sender_id would disqualify these frames even under the correct prefix names.
Fix
- Correct the three prefixes to the registry names.
- Drop the attributed
sender_id from the injection, matching what the Swift and Kotlin bridges do.
- Add a Python copy of the relay-answer prefix list plus a literal-pinning test, so it fails loudly the next time the registry moves. Contract C5 in
docs/bridges/README.md covers the other two languages; Python is the gap.
Note
Worth checking whether these Python paths are exercised at all — if the desktop bindings have never driven relay group answers end to end, that would explain how the names drifted without anyone noticing, and it is an argument for the pinning test regardless of whether the paths are used today.
Found while verifying ADR 0004 against the code for #365. Pre-existing, unrelated to that PR.
What happens
bindings/python/offline_protocol_sdk/internet_manager.py:616-635injects relay group answers using prefixes that do not exist in the reserved prefix registry:__GRP_CREATED____GRP_INVITE____GRP_MSG__The real names are in
crates/offline-protocol/src/protocol/prefixes.rsand are mirrored by hand into Swift (RelayAnswerPrefixes.swift) and Kotlin (RelayAnswerPrefixes.kt) with literal-pinning tests in each. Python has no copy and no pin.The same injection also supplies an attributed
sender_id.Why it fails silently
This is precisely the hazard ADR 0004 describes. A frame under an unregistered prefix is not in the relay-answer exemption set, so it is treated as ordinary control traffic and refused as unsigned. The refusal is correct, and it is invisible: the Python caller sees a successful inject and no error, and the answer simply never takes effect.
Additionally, because the exemption is narrowed to internet-arrival frames carrying no transport peer identity (
security.rs:463-472), attaching asender_idwould disqualify these frames even under the correct prefix names.Fix
sender_idfrom the injection, matching what the Swift and Kotlin bridges do.docs/bridges/README.mdcovers the other two languages; Python is the gap.Note
Worth checking whether these Python paths are exercised at all — if the desktop bindings have never driven relay group answers end to end, that would explain how the names drifted without anyone noticing, and it is an argument for the pinning test regardless of whether the paths are used today.