Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WG3YBpS5nu23yEQ7QUkvnH
speedtest
STATUS: OPERATIONAL. One fewer service. This is the only kind of feature I like.
A bandwidth and latency test that lives inside Nextcloud, because it did not need to live anywhere else.
What this replaced, and why that is the point
There was a container. ghcr.io/librespeed/speedtest:latest. It had its own
image, its own systemd unit, its own port, its own docker network, its own
vhost, its own DNS record, and its own TLS terminator upstream. Six moving
parts, one of which was the last piece of upstream code still running on this
node, to answer a question a browser can answer by itself.
It is gone. All of it.
What remains is four files of PHP and one file of JavaScript, served by a thing that was already running.
LOG ENTRY: The most reliable service is the one you deleted. It has never had an outage. It has never had a CVE. It does not appear in the supply chain because it does not exist.
How it works
All measurement happens in your browser. The server has three jobs and no opinions:
| route | job |
|---|---|
GET /ping |
return nothing, quickly. Latency and jitter are timed around it. |
GET /garbage?mib=N |
stream N MiB of random bytes |
POST /empty |
read an upload and drop it on the floor |
That is the entire backend. There is no state, no database table, no settings page, no configuration, and nothing to administer. If you were hoping for a settings page: no. Go outside.
Three deliberate choices, each of which is load-bearing:
- The payload is random. Random bytes do not compress. If it compressed, gzip somewhere upstream would hand you a number describing your CPU rather than your connection, and you would believe it.
- The download is streamed, never buffered. A 50 MiB test costs 1 MiB of memory, and the browser sees bytes arriving continuously instead of waiting for a lump. Throughput, not time-to-first-byte.
- Nothing touches storage. Not one read, not one write. The bulk data on this node lives on NFS, and a vanity metric does not get to compete with real IO for it. The bytes are generated in memory and forgotten immediately.
Where it lives
Settings → Personal → Speedtest.
Not the app menu. A speedtest is a utility, not a destination — nobody opens one the way they open Files or Mail. The app menu is for places you go; Settings is for things you check.
Install
occ app:enable speedtest
Drop it in custom_apps/. There is no build step — no webpack, no bundler, no
node_modules measured in gigabytes to render four numbers and a button. The
JavaScript in this repo is the JavaScript that runs.
What the numbers mean
- Download / Upload — megabits per second, between your browser and this server. Measured over a fixed six-second window across four parallel streams, after a 1.5-second warmup that is thrown away. Both of those are load-bearing; see below. Not to the internet. Not to a CDN that has quietly placed a box in your ISP's building to flatter them.
- Latency — median of ten sequential round-trips. Median, not mean: one hiccup should not get a vote.
- Jitter — mean variation between consecutive round-trips. The number that actually predicts whether a voice call will be tolerable.
Why a window, and why four streams
Version 1.0.0 pulled a fixed 50 MiB and averaged the whole transfer. On a fast link that finishes before TCP is out of slow-start, so the congestion-window ramp went into the average: the test ran in half the time and reported half the speed. A short run and a low number were the same bug wearing two hats.
So: fixed duration, not fixed size, with the ramp discarded. And four concurrent streams, because a single TCP connection cannot fill a path with any real bandwidth-delay product — one stream measures the connection, not the link. This is why a naive single-stream test reads low and everyone else's numbers look inflated by comparison. They are not inflated. It was wrong.
If you are on the LAN you are measuring the LAN, and the result will be uninteresting and correct. That is not a bug. That is what a measurement is.
Honesty about what this is not
It is not a general-purpose internet speedtest and it does not pretend to be. It measures the path to this node, which is the only path this node can tell you the truth about. Anything claiming to measure "your internet speed" from a single server is measuring one path and rounding the rest up.
The node remains online. Against my better judgment.
— Neon Overlord, admin, bawnet.io