dsh-rerun
Embed the Rerun web viewer in the dsh web GUI: browse .rrd
recordings from the workspace root and connect to live rerun --serve streams —
all inside the harness, fully offline.
What it does
- A Rerun button in the sidebar footer opens a full-window overlay.
- Recordings mode lists
.rrd/.rrd.zstfiles under the workspace root
(env override:DSH_RERUN_RECORDINGS_DIR) and renders the selected one. - Live mode connects the viewer to a running Rerun server, e.g. started
withrerun --serve-web(note: the flag is--serve-web, not--serve).
The default URL is shown/edited in the namespaced alias form
rerun+<page-scheme>://<page-host>/dsh-rerun/live— the same origin as
the GUI. Because it is same-origin there is no CORS and no mixed content,
so Live works on any GUI access scheme —http://127.0.0.1:3080,
http://<host>, orhttps://<host>. How the 0.34.1 viewer actually
connects (verified via Network capture):- the URL is only a parse token: the viewer accepts live URLs whose
path is exactly/proxy(host/scheme free; any other path — including
the/dsh-rerun/livealias — fails as "Failed to parse URL"), - the real connection is grpc-web to
<origin>/rerun.sdk_comms.v1alpha1.MessageProxyService/<Method>
(OPTIONS preflight + POST streaming) —/proxyitself is never fetched.
The panel therefore speaks the alias and maps it to the canonical
/proxypath before the viewer sees it (canonicalLiveUrlin
src/client/store.ts); typing the canonical form directly also works.
The host half registers a passthrough to the rerun server
(http://127.0.0.1:9876, env override:DSH_RERUN_LIVE_ORIGIN) at both
/rerun.sdk_comms.v1alpha1.MessageProxyService/*(the actual live stream)
and/proxy//dsh-rerun/live/*(parse-token safety + legacy URLs).
On an https page the one remaining prerequisite is that the browser
trusts the GUI's TLS certificate — fetch has no "proceed anyway" path
(page loads can be bypassed, fetches cannot). Until it is trusted the
panel shows a 证书不受信任 hint. One-time trust for Caddy's internal CA:sudo cp ~/.local/share/caddy/pki/authorities/local/root.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates(or import
root.crtinto the browser's CA store), then restart the
browser.
- the URL is only a parse token: the viewer accepts live URLs whose
How it stays light (lazy load + local cache)
The viewer is a 48 MB WASM binary. This plugin never ships it in the client
bundle and never hits a CDN:
- The host half serves the viewer straight out of the installed
@rerun-io/web-viewernpm package (a plain dependency → package-manager
cache = local disk cache). Seesrc/index.ts→findViewerRoot. - The client half only carries an 8 KB panel UI. The viewer iframe — and
therefore every viewer byte — is fetched only when the overlay opens. - Versioned asset URLs (
/dsh-rerun/viewer/0.34.1/…) are served with
Cache-Control: immutable, so after the one-time 48 MB fetch every re-open
is instant from the browser cache. The wasm response also sets
rerun-final-lengthso the viewer shows a download progress bar.
Version alignment
@rerun-io/web-viewer@0.34.1 matches the local rerun-cli 0.34.1. Keep the
dependency in lockstep with the Rerun SDK/CLI that produces your .rrd files
(the viewer reads files from the previous minor version and speaks the matching
gRPC protocol for live streaming).
Live operations & troubleshooting
Quick start for a live demo with data streaming in real time:
rerun --serve-web # gRPC server on :9876 (+ web viewer on :9090)
python feed-live.py # any rerun SDK: connect_grpc + rr.log in a loop
# or: rerun --serve-web demo-dna.rrd (pre-loaded file)
rerun-sdk 0.36 API changes vs older docs: connect → connect_grpc,
set_time_sequence → set_time(..., sequence=), Scalar → Scalars.
Stream connection hygiene — the /proxy passthrough is a long-lived
stream, and the viewer drops it whenever the overlay closes. The host half
destroys the upstream connection on client close
(res.on('close') → proxyReq.destroy()); without that, every closed viewer
leaks one idle TCP connection into the rerun server. Symptoms of the leak:
the viewer renders an empty light screen and the rerun server log fills with
re_quota_channel: Sender has been blocked for over 5 seconds…. Diagnosis:
ss -tn | grep 9876 shows a client socket with a multi-MB Send-Q/Recv-Q
that never drains. Leaked sockets live in the dsh web process, so they
survive rerun server restarts — clearing them requires a dsh web restart
(which also loads any rebuilt lib/index.js).
Note that the rerun server and the data feeder are external processes, not
owned by the plugin — if Live shows an empty screen, check they are still up
(ss -tln | grep 9876) before anything else.
Host routes
| Route | Purpose |
|---|---|
GET /dsh-rerun/viewer/<version>/ |
iframe entry page |
GET /dsh-rerun/viewer/<version>/index.js, re_viewer.js, re_viewer_bg.wasm |
viewer assets (immutable) |
GET /dsh-rerun/recordings/<rel>.rrd |
workspace recording (traversal-safe) |
GET /dsh-rerun/api/recordings |
{ recordings[], viewerVersion } |
GET /dsh-rerun/api/health |
{ ok, viewerVersion, recordingsRoot } |
Routes mount on the existing web server (ctx.webServer.register), so there is
no second port and no CORS. Same-origin iframe → no CORS shenanigans.
Build
pnpm install --store-dir <writable-store> --cache-dir <writable-cache> # sandbox note below
node build.mjs # -> lib/index.js (host ESM) + lib/client.js (browser CJS)
node watch.mjs # dev watcher (optional)
Sandbox note: pnpm's default store sqlite cannot open under the harness file
sandbox; point--store-dir/--cache-dirat a writable location (the repo
uses.pnpm-store/.pnpm-cache).
Wiring into a profile
ln -s <repo>/dsh-rerun $DSH_HOME/plugins/dsh-rerun- Add
"dsh-rerun": "link:../../plugins/dsh-rerun"to the profile's
package.jsondependencies andpnpm install. - Add a loader entry to the profile's
cordis.patch.yml:- insert: - id: rerun name: dsh-rerun - Restart
dsh web— new plugins are loader entries fixed at boot.
Dev iteration after restart
lib/client.jschanges hot-swap without a page refresh (dsh-client-hmr polls
the bundle).lib/index.jschanges need anhmrwatch row covering this package's real
path in the profile'scordis.patch.yml, otherwise adsh webrestart.