Skip to content

net: our peer id outlives a signaling drop (deployed net-drop: joiner came back a viewer) - #478

Merged
AlexZ005 merged 6 commits into
feat/52-intfrom
fix/net-drop-heal
Oct 26, 2026
Merged

AlexZ005 merged 6 commits into
feat/52-intfrom
fix/net-drop-heal

Conversation

@AlexZ005

Copy link
Copy Markdown
Collaborator

Lane net-drop-1. Base feat/52-int because it already contains v1.44.0. feat/70 and feat/52 do not, so a PR into either would show every 1.43/1.44 commit. Retarget to feat/70 if 52-int leaves this for 70-int (see QUESTIONS-net-drop-1 Q2).

The bug

On deployed builds (production 1.43.2 and 1.44.0, every preview), net-drop was red: after a network drop healed, B's edits never reached A. On a dev server it was green.

The wire trace showed the healed link was fine: one conn, messages delivered both ways. B's creations never left B. After the heal, B's rolesInfo said myRole: viewer while A still had B as editor, so gateCreationBroadcast kept every B edit local.

Why:

  • peerjs Peer.disconnect() runs on every drop of the signaling socket. It sets peer.id = null until the socket re-registers.
  • The cloud roles plugin reads its own id from getPeers().peer.id. During the drop that id was '', so it saw only one known peer, decided it was alone, and wiped its role map and its record of the host it joined.
  • After the heal, B defaulted to viewer. A's open-peer set never changed across the heal, so A never re-sent the map.
  • A dev server loads no plugin, which is the whole local-vs-deployed difference. Any signaling-only blip demotes a joiner the same way, for example a restart of the signaling box.

Core read null in the same seconds too: own avatar, own locks shown as foreign, shared-library owner, chat, atscene.

The fix

peerDrop.keepPeerId gives the one Peer core builds an own id getter: the live id, else the id it was created with. peerjs's _id, _lastServerId, open and disconnected are untouched. reconnectDial now checks peer.disconnected itself, which keeps its old no-dial-while-off-signaling behaviour.

Tests (counterfactual)

  • Unit: keepPeerId against the real peerjs Peer. Unwrapped, the id reads null after disconnect(). Wrapped, it keeps the id and reconnect() re-registers it.
  • net-drop, own id: each page must read its own peer id throughout the drop. Red on v1.44.0 (A read null at 4.7 s, B at 5.3 s).
  • net-drop, roles: roles must be unchanged by the heal (with a roles plugin loaded). Measured with the PRODUCTION plugin:
    • dev server: v1.44.0 had 4 B→A reds with B as viewer; the fix is 22/0.
    • built app (serve-build) with the plugin served same-origin: v1.44.0 is red, with B ending as admin/viewer; the fix is 4/4 at 25/0.
  • net-drop shim: the cut now fires the 1006 close a dropped socket fires. A graceful ws.close() waited 3 s+ under offline emulation on built code, and that was the QUIET "signaling down at the cut" premise that was red on production.
  • net-drop trace: a wire and app trace now prints on a miss, or with NETDROP_TRACE=1.

Green on this branch

  • net-drop 23/0 (dev)
  • net-relink 9/0
  • dialback-sync 7/0
  • fix-live 46/0
  • net-reconnect 13/0
  • fast-channel-loss 32/0
  • vitest 2772/0
  • svelte-check ratchet 287/39
  • build, check:tokens/storage/zindex

Owed: net-drop 3/3 on a Pages preview. This box has no cloud deploy env, so I couldn't deploy one.

Known, pre-existing: at load average 20 or more, a local heal dial can stall and tear the session down. It happens on v1.44.0 too.

Changelog (Fixes): Someone who joined your room no longer comes back from a network or signaling blip as View-only. Before this fix, their edits stayed on their own screen while you still saw them as an Editor.

🤖 Generated with Claude Code

AlexZ005 and others added 6 commits October 26, 2026 09:35
…ny drop

The suite's shim now records, per page: every RTCPeerConnection's real ICE /
connection state, every data channel's open/close with sent / delivered / held
counts (a peerjs channel's label is its conn id), the selected ICE pair's
candidate types, each DataConnection's sends and 'data' events by message type,
and what the send / receive / role gates answer about the other peer. Printed
with NETDROP_TRACE=1, or when B's edit never reaches A. A new premise checks that
B's edit reaches A before any drop (the lock alone is a protocol-floor message).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
peerjs Peer.disconnect() - every drop of the signaling socket - sets peer.id to
null until the socket re-registers. The cloud roles plugin reads its own id from
getPeers().peer.id, so for the length of a drop it counted itself alone, wiped its
role map and the host it joined; after the heal a joiner read itself as a VIEWER
(the host still had it as an editor and, its open set unchanged, never re-sent the
map), and core's creation gate kept every edit local. Only deployed builds load
the plugin, which is why net-drop was green on a dev server and red on production
1.43.2 / 1.44.0 and every preview.

peerDrop.keepPeerId gives the Peer an own id getter: the live id, else the id it
was created with. peerjs bookkeeping (_id, _lastServerId, open, disconnected) is
untouched. reconnectDial keeps its old no-dial-while-disconnected behaviour.

net-drop: own id readable at the cut (red on v1.44.0), roles unchanged by the heal
(with a plugin). Unit: keepPeerId against the real peerjs Peer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…7/39)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…h E2E_CLOUD_PLUGIN for the deployed condition

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… conn already closed

A graceful ws.close() waits for the server's close frame, which offline emulation
held for 3 s+ on a built app: B's socket sat CLOSING with peer.open still true, so
the QUIET premise 'signaling down at the cut' was red on production and on any
build, and a 3 s cut window let A's conn close before HARD reached it (script
error). The shim now hands peerjs the 1006 close a dropped socket fires.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s too (room labels, recent toasts)

A single sample at the cut raced peerjs's own retries, which put the id back
between failed reconnects (v1.44.0 read null at 4.7 s / 5.3 s when sampled).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AlexZ005
AlexZ005 merged commit 1065f8b into feat/52-int Oct 26, 2026
9 checks passed
AlexZ005 added a commit that referenced this pull request Oct 26, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AlexZ005
AlexZ005 deleted the fix/net-drop-heal branch October 26, 2026 08:47
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