Source of the YABS board

These are the exact bytes the server runs. Compare the hash with your own sha256sum of the downloaded copy: if the two match, the page you read is the program you reviewed.
README.md · 220 lines · 10.3 KiB · sha256 8998503733b7ab0c467faf1411fd382ec9f453f7c76399543d1336066939023e · download · board · README

# YABS node board

A public board of [YABS](https://github.com/masonr/yet-another-bench-script)
runs (disk and network benchmarks) from several nodes. One PHP file, one
SQLite database, no framework.

Board URL: <https://05877bc1b477317f.mcx.fi/yabs/>

PMSS serves this account's docroot (`~/www/public`) at two addresses: the
panel path `https://le4-0-78-102caroline.pulsedmedia.com/public-vasilves/yabs/`
and the root of a customer domain. Use the customer domain: the panel path
names the account in every URL a stranger copies, and PMSS's proxy only
forwards that path, so `/yabs/` on the panel hostname answers PMSS's own 404.

## Install on a node

Requirements: `bash`, `curl`, a writable `$HOME`, and the right to edit your
own crontab. No root.

```sh
curl -fsSLO https://05877bc1b477317f.mcx.fi/yabs/probe.sh
sh probe.sh
```

This does three things:

1. downloads `yabs.sh` from <https://yabs.sh> into `~/.yabs-probe/`;
2. registers the short hostname with the board and stores the node token in
   `~/.yabs-probe/conf` (mode 0600);
3. adds one crontab line: a monthly benchmark at a random time between 01:00
   and 02:59.

Re-running `probe.sh` is safe: it keeps the existing token and does not add a
second cron line.

A node can have two cron lines: the benchmark (`run.sh`) and a self-update
(`probe.sh update`). `install` is the only command that writes either, and it
takes two rates:

```sh
sh probe.sh                       # benchmark monthly, no update line
sh probe.sh daily                 # benchmark daily, update line untouched
sh probe.sh install daily hourly  # benchmark daily, update every hour
sh probe.sh install weekly off    # benchmark weekly, drop the update line
```

The first word is the benchmark rate (`daily`, `weekly`, `monthly`), the second
the update rate (`hourly`, `daily`, `weekly`, `monthly`, `off`). A word you
leave out is left alone, so a bare `sh probe.sh` never resets a node to
monthly. The benchmark refuses `hourly`: one run takes 10 to 20 minutes, where
an update is a single 7 KB download.

Naming a rate draws a new random slot for the benchmark between 01:00 and 02:59,
so several nodes installed the same day run at different times instead of all
hitting their disks at once. That hour is the quiet part of the night: it stays
clear of the 00:13 disk probe, the 03:00 backup tarball and the 04:00 configarr
run. A bare `sh probe.sh` and `sh probe.sh update` keep the slot already in the
crontab. Move the window with `BENCH_FROM` and `BENCH_TO`:

```sh
BENCH_FROM=5 BENCH_TO=9 sh probe.sh install daily   # daily between 05:00 and 08:59
```

## History

[history](https://05877bc1b477317f.mcx.fi/yabs/history) plots every
stored run, one chart per metric (4k, IOPS, 64k, 512k, 1M, send, receive) and
one colour per node. Hover a point for its date and value; the legend compares
the last 1M value with the previous run. The page is server-rendered SVG, so it
needs no JavaScript and no chart library.

## Read the source

[source](https://05877bc1b477317f.mcx.fi/yabs/source) prints the exact
bytes of every file this board serves, with a SHA-256 per file and a download
link (`?f=probe.sh&raw=1`). Download a file, run `sha256sum` on it, compare with
the hash on the page: a match proves the code you read is the code that runs.

## 3D hangar

[racks](https://05877bc1b477317f.mcx.fi/yabs/racks) draws one hall per
hostname family (`le4`, `le6`) and one rack per node. Rack height and colour
come from the latest 1M fio throughput; hover a rack for its full numbers, drag
to orbit, wheel to zoom. The page reads the same database, read-only, and adds
no dependency: the projection is about 100 lines of canvas code.

The hall is the hostname family, not a real datacenter. Neither YABS nor the
database knows where a node sits, so any grouping here follows the name.

## Use it

```sh
sh probe.sh now      # one run in the foreground, output on screen
sh probe.sh status   # running or not, elapsed, last log line
sh probe.sh update   # fetch the newest probe.sh and re-install
```

`update` fetches `${BOARD}probe.sh`, checks it with `sh -n`, stores it as
`~/.yabs-probe/probe.sh` and re-runs it with no argument. The token, the
benchmark rate and both cron lines stay as they are, so `update` never rewrites
the crontab. It prints nothing when the copy it already has is current, which
keeps the hourly cron run from mailing anything.

The update cron line is separate from the benchmark one, so changing one never
disturbs the other.

`now` and `status` install nothing: they never register, never add a cron line
and never print the token. Run `sh probe.sh` once first; without a registration
`now` stops with `not installed`.

A run takes 10 to 20 minutes, mostly generating the fio test file. The cron run
logs to `~/.yabs-probe/last.log` and writes its state to
`~/.yabs-probe/progress`. Geekbench is skipped (`-g`): one score costs several
GB of downloads.

A node token can only post its own name. The board owner also holds an admin
token that can post any name; you do not need it.

## What the board stores

Per run: CPU model, cores, RAM and swap (GiB), disk size (GB), distribution,
kernel, virtualisation type, GPU, fio 4k/64k/512k/1m in MB/s with IOPS,
iperf3 send and receive in Mbit/s, and Geekbench single and multi when present.
YABS cannot report a GPU, so `probe.sh` sends the `/dev/dri/renderD*` name.

Two more fields come from the node's kernel, because YABS reports neither:

- **Disk media** (`virtio`, `HDD`, `SSD`, `NVMe`): `probe.sh` resolves the
  device under `$HOME` in `/sys/block`, reads its `queue/rotational` flag and
  its `lsblk` transport, and lets `ID_ATA_ROTATION_RATE_RPM` override the flag.
  NVMe settles it on the transport alone. A device under a `virtio` node in
  `/sys` gets `virtio`: QEMU leaves the rotational flag at 1 whatever the
  backing store is, so the flag says nothing there. The board never guesses the
  type from the measured IOPS: a stalled disk measures low, not slow.
- **IO stall** (%): the share of the run window that a task spent blocked on IO,
  from the kernel's pressure stall information (`/proc/pressure/io`). The probe
  reads the cumulative `some` and `full` counters before and after the run, so
  the difference over the elapsed time is the average stall of that window.

The disk averages are therefore weighted. A run measured while the disk was
stalled understates the hardware, so it counts as `1 - stall` (floored at 0.25)
instead of 1. The per-run tables keep the raw numbers, and a run with no stall
reading weighs 1, so numbers from an older probe stay in the average.

Averages cover the last 6 months, by run date.

## API

| Request | Answer |
|---|---|
| `POST ?do=register` with `{"name":"node","gpu":"renderD128"}` | 200 `{"ok":true,"token":"..."}`; the token is returned once |
| `POST /` with header `X-Upload-Token: <token>` and the raw `yabs.sh -j` JSON | 200 `{"ok":true,"saved":["node"]}` |
| `GET ?do=api` | JSON per node: hardware, run count, first and last run, averages |
| `GET ?do=list` | JSON, the 500 newest runs |

The node name comes from `?node=NAME` (or `?nodes=a,b` for one run posted
under several names). A node token forces its own name, whatever `?node=` says.
`?gpu=NAME` is optional and overrides the stored GPU for that run.

| Code | Meaning |
|---|---|
| 400 | not JSON, no node name, or a name that is not hostname-shaped |
| 401 | wrong or missing token |
| 405 | `?do=register` called with GET |
| 409 | this node already posted at this run time, or the name is taken |
| 413 | body over 256 KB |
| 422 | no disk or network number found, or a value outside the plausible range |
| 507 | the registry is full (500 nodes) |

## Known limits

- **A name is a label, not a proof.** Registration is open and uploads are not
  signed, so anyone can claim a free name and post numbers under it.
- **The token travels in the URL** when you use `yabs.sh -s`, so it lands in
  the web server log. A node token can only write its own name.
- **Numbers vary between runs.** The nodes share storage with other accounts,
  so a second run on the same box can differ by a large factor.
- **IO stall is the node's own view.** Pressure stall information counts the
  tasks on that machine. A slow host disk makes its requests slow, so the stall
  rises, but the number also rises when the node itself is busy: it says the
  measurement was congested, not who caused it.
- **The disk media comes from the node.** A virtual disk declares nothing:
  QEMU reports `rotational=1` for an SSD too, so the board shows `virtio`
  instead of `HDD`. A RAID array reports itself, not its members, and the probe
  does not read SMART, so a case like "flash on a SATA bridge" can be wrong.
- **Rows from before 2026-10-27 carry neither stall nor `virt`.** They keep
  their numbers and weigh 1 in the averages, and a virtual disk among them still
  reads `HDD` until the node benchmarks again with a newer probe. Only new runs
  fill those columns.
- **No delete.** Ask the board owner to remove a row.
- **The update runs what the board serves.** `sh probe.sh update` downloads
  `${BOARD}probe.sh` and runs it, and with the hourly line it does so from cron.
  Whoever controls that URL runs code on every node that installed the update.
  The download is checked with `sh -n` and rejected if it is not shell, but that
  only stops a broken file, not a hostile one. Use `update off` on a node that
  must not trust the board.
- **The token stays out of cron mail.** `probe.sh` prints it only on a terminal.

## For an AI agent

Use this checklist when you are asked to benchmark a machine and publish the
result:

1. Read `llms.txt` next to this file. It holds the same facts in one screen.
2. Check that you are on the machine to be measured: `hostname -s` must match
   the name you will register. Do not post a run executed elsewhere.
3. Install and register:
   `curl -fsSLO <board>/probe.sh && sh probe.sh`.
4. Run once and wait for the result:
   `sh probe.sh now`. Expect 10 to 20 minutes; do not interrupt it.
5. Confirm the upload:
   `curl -s '<board>?do=api' | head -c 400`. The node must appear with its
   hardware and at least one run.
6. If the node is already registered and you only need a fresh run, skip step 3
   and call `~/.yabs-probe/run.sh`.

Rules: do not re-register to work around a 409; the default is one run per
month, and each node's owner decides its own rate. Report the exact node name
and the run time with the numbers.