Skip to content

fix(core): address concurrent relay pairings by device, not by recency - #174

Merged
Mearman merged 3 commits into
mainfrom
fix/hub-multi-device-relay-pairing
Sep 17, 2026
Merged

Mearman merged 3 commits into
mainfrom
fix/hub-multi-device-relay-pairing

Conversation

@Mearman

@Mearman Mearman commented Sep 17, 2026

Copy link
Copy Markdown
Member

Closes #170

mesh-session.ts tracked at most one active relay pairing per connection in a single relayPeerDevice field, shared across every device that connection was ever paired with. Establishing a new pairing silently overwrote it, so an inbound relay-data request was always attributed to whichever peer paired most recently rather than whoever actually sent it, and a reply was addressed back the same wrong way.

relay-hub.ts has forwarded real per-frame to-device/from-device addressing since #30 (a multiplexed adjacency map, not a single slot), but mesh-session.ts never read or wrote either field on its own side of the wire.

This replaces the single field with a relayPairings map of every pairing the connection currently holds (used only to avoid a redundant relay-connect for an already-paired target). Outbound relay-data is now always explicitly addressed: sendManageRequest stamps to-device with its own targetDevice, and respond() echoes back whatever from-device the original request actually carried. Inbound attribution (fromDevice, and a new toDevice for a caller fronting more than one local device behind one hub connection, e.g. a gateway) is read solely from each relay-data frame's own fields, never guessed from pairing state.

Root cause and fix are entirely within wire-mesh-core; agent-comms is not touched, per the issue.

Adds a deviceC fixture and RED tests proving mesh-session's relay
handling must read to-device/from-device per frame rather than track a
single most-recently-established pairing: concurrent relay pairings
must each attribute an inbound request to its real sender, a response
must address back to that sender even after a different pairing was
established in between, and a previously-paired target must not
trigger a redundant relay-connect after pairing with someone else.
Also updates the existing fromDevice/relay-connect assertions to match
the wire-mesh#30 hub, which always stamps from-device on every
forwarded relay-data frame.
mesh-session tracked at most one active relay pairing in a single
relayPeerDevice field, shared across every device this connection was
ever paired with. Establishing a new pairing silently overwrote it, so
an inbound request was always attributed to whichever peer paired
most recently rather than whoever actually sent it, and a response
was addressed back the same wrong way -- relay-hub.ts has forwarded
real per-frame to-device/from-device addressing since wire-mesh#30,
but mesh-session never read or wrote either field.

Replaces the single field with a relayPairings map of every pairing
this connection currently holds, used only to avoid a redundant
relay-connect for an already-paired target. Outbound relay-data is now
always explicitly addressed: sendManageRequest stamps to-device with
its own targetDevice, and respond() echoes back whatever from-device
the original request actually carried. Inbound attribution (fromDevice
and the new toDevice, for a caller fronting more than one local device
behind one hub connection) is read solely from each relay-data frame's
own fields, never guessed from pairing state.
mesh-session.ts's relay-connect/relay-data handling had grown past the
project's own 800-line file budget once addressing was fixed to read
to-device/from-device per frame instead of a single tracked pairing.
Pulls two genuinely reusable pieces out into their own small modules
rather than trimming comments to fit: relay-pairing.ts owns the "which
devices is this connection already paired with" set (used only to
avoid a redundant relay-connect), and frame-codec.ts's new
wrapRelayData mirrors its own tryDecodeFrame for the outbound side of
the same relay-data envelope. applyManageRequest now takes the
relay-data-frame itself rather than two separately-threaded fields,
since it already carries both to-device and from-device. No behaviour
change.
@Mearman
Mearman marked this pull request as ready for review September 17, 2026 19:07
@Mearman
Mearman merged commit 3b2cb1f into main Sep 17, 2026
7 checks passed
@Mearman
Mearman deleted the fix/hub-multi-device-relay-pairing branch September 17, 2026 19:08
@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-17T19:15:49.891806Z d30dd8a 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.

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.

Hub relay session tracks only one pairing per remote device, blocking multi-device gateway routing

1 participant