Skip to content

fix(python): declare off1 address to relay (Internet transport parity with Swift) - #404

Open
kivtxs wants to merge 1 commit into
mainfrom
fix/python-internet-declare-address
Open

fix(python): declare off1 address to relay (Internet transport parity with Swift)#404
kivtxs wants to merge 1 commit into
mainfrom
fix/python-internet-declare-address

Conversation

@kivtxs

@kivtxs kivtxs commented Aug 21, 2026

Copy link
Copy Markdown
Member

Problem

The Python Internet transport (bindings/python/offline_protocol_sdk/internet_manager.py) authenticates the WebSocket connection to the relay ({"type":"Authenticate","token":…}) but never answers the relay's address-routing challenge. As a result a Python node's off1… address is never bound on the relay, so the relay cannot route messages addressed to it. Addressed sends and Service Discovery over the Internet transport therefore never reach a Python node.

The Swift transport already implements this handshake (bindings/react-native/ios/AddressDeclarationPolicy.swift + InternetManager.swift), so this was a Python/Swift parity gap, not a protocol change.

Repro

Bring up relay-server (it advertises the address_routing_v1 capability and mints an address_challenge in its Authenticated frame). Connect a Python node via the Internet transport and connect an RN/Swift node. The Swift node logs Binding address off1… to user …; the Python node authenticates but its address is never bound — confirmed in the relay's connection_manager logs.

Fix

On Authenticated, when the relay advertises the address_routing_v1 capability and includes an address_challenge, sign the domain-separated proof and reply with DeclareAddress:

payload = "offline-relay-addr-v1" || u32be(len(account.utf8)) || account.utf8 || challenge
DeclareAddress { address, public_key(b64), signature = sign(payload) }

This matches the relay's address_binding::address_proof_payload and the Swift AddressDeclarationPolicy byte-for-byte (domain, big-endian length prefix, standard-padded base64). The relay's AddressDeclared / AddressDeclarationRefused responses are now logged.

Why it's safe (additive, non-destructive)

  • No-op unless invited: runs only when the relay advertises address_routing_v1 and sends a non-empty 32-byte challenge and a username is present. Relays without the capability (or that omit the challenge) see byte-identical behavior to today.
  • Never breaks auth: the entire new path is wrapped; any failure emits a diagnostic and leaves the connection authenticated (just unrouted, exactly as before).
  • One file, backward-compatible signatures: the new _handle_authenticated / _safe_handle_authenticated params are optional with defaults.

Validation

Ran a Python node against relay-server with this fix. The relay now logs on connect:

Registering user: <node> (conn N)
Binding address off1q9eqy0ww55qxm8ve0jv8gxpxknay7fkj9veg0swe to user <node> (conn N)

i.e. the node's off1 address binds and becomes routable, matching Swift-node behavior. Without the fix, the Binding address … line never appears for a Python node.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

The Python Internet transport authenticated the WebSocket connection but never
answered the relay's address-routing challenge, so a Python node's off1 address
was never bound on the relay. The relay could therefore not route messages
addressed to that node, which breaks addressed sends and service discovery over
the Internet transport. The Swift transport (AddressDeclarationPolicy +
InternetManager) already performs this handshake, so this was a Python/Swift
parity gap.

On `Authenticated`, when the relay advertises the `address_routing_v1`
capability and includes an `address_challenge`, sign the domain-separated proof
("offline-relay-addr-v1" || u32be(len(account)) || account || challenge) with the
node identity key and reply with `DeclareAddress`, matching the Swift
implementation and the relay's `address_binding::address_proof_payload`. Also log
the relay's `AddressDeclared` / `AddressDeclarationRefused` responses.

Additive and non-destructive: relays that do not advertise the capability or
omit the challenge see no change, and any failure in the new path is caught and
leaves the authenticated session intact (unrouted, as before).

Validated against relay-server: the node's off1 address now binds on connect.
@kivtxs
kivtxs force-pushed the fix/python-internet-declare-address branch from 2eeca96 to e9a90d4 Compare August 21, 2026 20:05
@kivtxs

kivtxs commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

I have read the CLA Document and I hereby sign the CLA

github-actions Bot added a commit that referenced this pull request Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant