Repository navigation
Conversation
wasm/web/nettest.mjs puts pages in a room behind links of their own and measures what the network does to the jam: each page's clock offset against the relay's true clock, how early every command arrives and how many land late, commands lost on the mesh and keys left sounding, when a pair falls back to the relay, a late joiner's catch-up, a blackholed page's rejoin, seat, edit and mesh, and a page whose CPU is slowed and whose tab is hidden. Five scenarios (clock, mesh, join, blackhole, throttle), with round trip, jitter, loss and the uplink's share per page, a summary on stdout and everything as JSON. scripts/nettest-ns.sh is the network: an unprivileged user and network namespace with a bridge, and a namespace per page on a veth, so netem on the two ends of each veth is that page's downlink and uplink. Nothing routes out, so the mesh has to open on host candidates, and does. Each page is a Chromium of its own in its namespace, launched without Playwright's switches and attached to with noDefaults: Playwright's focus emulation keeps a tab visible whatever is put in front of it. Its debugging port and its site stay on its own loopback, so nothing the harness asks crosses the impaired link. Real time and namespaces, so a tool rather than a ctest gate.
The hub is a PID namespace of its own, with the script its first process: when it exits, however the run ended, the kernel kills every browser, site, forwarder and namespace holder left in it, and unshare's --kill-child takes the script with it if unshare itself is killed. A node that died of an uncaught error or a SIGKILL used to leave all of them running, and the namespaces with them. The traps and the cleanup they ran are gone with the need. A debugging port's forwarder that cannot listen says so rather than taking the run down with an uncaught 'error'.
A run killed its processes and removed the directory they wrote to at once, and a Chromium still writing its profile could make the removal fail and the run exit 1 for nothing. Each is killed outright and waited for, and the removal retries what is left behind by a browser's own children.
A command that never reached a page was counted with one the page dropped on arrival: a key behind a later one of its note, or a command of a run since replaced, or one that came before the page had a synth. A page that says what it dropped, and why (window.jam.dropped), has those counted by reason beside what was lost. The page's late count took in every direct key, which is stamped with when it was played and is always late by the time it arrives: the figure is now of stamped commands only, from the page's own list. A failed poll of a page was swallowed, and showed as commands lost or keys stuck; each is counted, and the summary says how many. A key played ahead is let go a bar after the hand lets go it, and the Stop came sooner than that: the last release was stamped past it, never applied, and the key showed as held at the end of a run with no key stuck. The Stop now waits, a while, for every key to be let go, and the keys still lit after it are named as such.
A key played ahead sounds a bar after its press as well as its release, so the keyboard was still dark when the run looked at it, and the Stop could still land between a last key's stamped press and its stamped release: the key sounded, its release fell past the Stop, and it was left lit with no key stuck. The Stop now waits for the transport to pass the last release this page stamped.
This was referenced Oct 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
wasm/web/nettest.mjs puts pages in a room behind links of their own and
measures what the network does to the jam: each page's clock offset
against the relay's true clock, how early every command arrives and how
many land late, commands lost on the mesh and keys left sounding, when a
pair falls back to the relay, a late joiner's catch-up, a blackholed
page's rejoin, seat, edit and mesh, and a page whose CPU is slowed and
whose tab is hidden. Five scenarios (clock, mesh, join, blackhole,
throttle), with round trip, jitter, loss and the uplink's share per page,
a summary on stdout and everything as JSON.
scripts/nettest-ns.sh is the network: an unprivileged user and network
namespace with a bridge, and a namespace per page on a veth, so netem on
the two ends of each veth is that page's downlink and uplink. Nothing
routes out, so the mesh has to open on host candidates, and does.
Each page is a Chromium of its own in its namespace, launched without
Playwright's switches and attached to with noDefaults: Playwright's focus
emulation keeps a tab visible whatever is put in front of it. Its
debugging port and its site stay on its own loopback, so nothing the
harness asks crosses the impaired link.
Real time and namespaces, so a tool rather than a ctest gate.