Skip to content

feat(core): route hub-relayed requests to the correct local peer via toDevice - #185

Merged
Mearman merged 2 commits into
mainfrom
feat/hub-todevice-routing
Sep 17, 2026
Merged

Mearman merged 2 commits into
mainfrom
feat/hub-todevice-routing

Conversation

@Mearman

@Mearman Mearman commented Sep 17, 2026

Copy link
Copy Markdown
Member

Closes #184

Summary

Bumps wire-mesh-core to 1.48.2, which carries both wire-mesh#170 (IncomingManageRequest.toDevice) and wire-mesh#175 (ExaDev/wire-mesh#175, relay-hub actually forwarding to-device onward to the receiving peer, a gap found while implementing this issue -- see that PR for detail).

In hub-session.ts's consume(): when an inbound hub-relayed request carries a toDevice naming a different device than this gateway's own, it's now forwarded on to that device's own local mesh session (sendToLocalPeer, extracted to hub-forwarding.ts) instead of being dispatched unconditionally against the gateway's own state. Falls back to the existing behaviour when toDevice is absent or names the gateway itself, so a sender on an older wire-mesh-core keeps working.

The forwarded command carries the request's already-verified true sender in a new "on-behalf-of" params field (the same open-params-tail convention room.send's "streaming-behavior" already uses). room-router.ts's resolveHandle reads it back out on the receiving side and substitutes it for the request's dispatch identity, so a DM's own participant check or a capability token's bearer check still authenticates against the real remote origin rather than the gateway -- without this, the forwarded request would be attributed to the gateway itself, breaking every subsequent operation (a DM acceptRoomJoin keyed by the wrong requester, a room.send token bearer mismatch). Only honoured when the session's peer identity matches this side's own recorded coordinator device (WireMeshTransport.coordinatorDeviceHex), checked by identity rather than by which call established the session, since mesh formation's own reciprocal connectToPeer can reach a coordinator over a second, independently-accepted connection to that same device.

Also extracts readvertiseGossip into gossip-directory.ts, under this repo's max-lines cap once the above pushed wire-mesh-transport.ts over it.

Test plan

  • New integration test (hub-todevice-routing.integration.test.ts): a three-party scenario (gateway a1, non-gateway local peer a2, remote hub peer b1) proves a DM addressed to a2 lands in a2's own state (not a1's), with the true sender (b1) preserved through the forward, end to end including delivered message content.
  • pnpm exec vitest run -- 1037 passing
  • pnpm exec tsc --noEmit -- clean
  • pnpm exec eslint . -- clean (3 pre-existing, unrelated warnings)

wire-mesh#170 fixed the session layer's relay-pairing tracking and
added IncomingManageRequest.toDevice, read from each relay-data
frame's own field; wire-mesh#175 (following, once implementation here
surfaced the gap) made relay-hub actually forward that field onward to
the receiving peer, which toDevice disambiguation depends on entirely.
…toDevice

Every inbound hub-relayed manage-request used to dispatch unconditionally
against the gateway's own local state, even when it named a different
device (toDevice) that this gateway also fronts as an ordinary local
peer -- the known limitation hub-session.ts's own consume() doc comment
flagged. It now forwards such a request on to that peer's own local
mesh session (WireMeshTransport.sendToLocalPeer, extracted to
hub-forwarding.ts) instead, falling back to the existing gateway-local
dispatch when toDevice is absent or names the gateway itself, so a
sender still on an older wire-mesh-core (never stamping toDevice)
keeps working exactly as before.

The forwarded command carries the request's already-verified true
sender in a new "on-behalf-of" params field, and room-router.ts's own
resolveHandle substitutes it for the request's dispatch identity once
it reaches the local peer, so a capability token or a DM's own
participant check still authenticates against the real remote origin
rather than this gateway. resolveHandle only honours that field when
the session's peer identity matches this side's own recorded
coordinator device (WireMeshTransport.coordinatorDeviceHex) -- checked
by identity rather than by which call established the session, since
mesh formation's own reciprocal connectToPeer can reach a coordinator
over a second, independently-accepted connection to that identical
device.

Also extracts readvertiseGossip into gossip-directory.ts, under this
repo's max-lines cap once the above pushed wire-mesh-transport.ts over
it.
@Mearman
Mearman marked this pull request as ready for review September 17, 2026 20:19
@Mearman
Mearman merged commit 4d64d05 into main Sep 17, 2026
6 checks passed
@Mearman
Mearman deleted the feat/hub-todevice-routing branch September 17, 2026 20:19
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-17T20:28:03.388127Z 0cedfb5 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 3.14.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Route inbound hub requests to the correct local peer via toDevice

1 participant