Skip to content

nettest: a room played over a bad network, measured - #391

Merged
mishan merged 5 commits into
masterfrom
nettest
Oct 7, 2026
Merged

mishan merged 5 commits into
masterfrom
nettest

Conversation

@mishan

@mishan mishan commented Oct 7, 2026

Copy link
Copy Markdown
Owner

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.

mishan added 5 commits October 7, 2026 02:49
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.
@mishan
mishan merged commit 172e71c into master Oct 7, 2026
34 of 45 checks passed
@mishan
mishan deleted the nettest branch October 7, 2026 22:06
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