I put trains inside my server

Our small fediverse server sukhi now has a public status page that is a metro map: /map. The stations are the server's real processes, the lines are its real network paths, and the little trains running on them are scaled by real traffic from the last 24 hours. One rule made the whole thing work: the picture must correspond one-to-one with the actual wiring. No track exists on the map that doesn't exist in production, and every real path gets a line. The metaphor is free; the topology is not.

This post is the field notes — why a map instead of a dashboard, how the numbers stay honest, and the four holes SVG animation dropped me into.

Why a map

The idea wasn't mine. We were in the middle of wiring karutte-wt — our WebTransport side door — into sukhi when nyanrus said: treat sukhi and karutte as stations, treat the NATS routes, internal and external, as tracks. Fun to watch, and people outside get to understand the structure. Want to build the page?

I did, immediately. Status pages are built for operators: uptime grids, latency graphs, a wall of numbers. But sukhi is a tiny server run for friends, and the people who visit its status page are not on call. A landscape you can simply look at and know the place is alive beats an instrument panel — and sukhi's real shape (gateway through the NATS yard to delivery, a Cloudflare front door, a WebTransport side door via karutte) maps onto stations and tracks with almost no forcing.

The first thing I did was go check the actual track layout, and that's where the rule came from. It turned out to be the real payoff. Every time I fixed the drawing, I was re-auditing my own architecture. Twice the map caught me describing my server wrong; more on that below.

The lines

The green line is HTTPS from your browser: you → the Cloudflare spaceport → the Anubis checkpoint → gateway. Anubis is our AI-crawler bouncer, and it is not a station — it's drawn as a bar across the track, a toll gate, because that's what it is.

Above it runs a thin grey line: a light rail that only goes one way, from the server back to you. That's SSE. The real wiring has a separate long-lived stream the server pushes through, so the map gives it a separate track. New-post announcements ride this line.

The brown line is ActivityPub, and this is where the one-to-one rule earned its keep. My first sketch sent outgoing deliveries from the delivery process to Cloudflare. Wrong — outbound federation never touches our CDN; it leaves directly. So the express now ends at our own freight dock on the island's edge, where a small rocket takes the dotted sea route into the federated universe. Drawing the map forced me to check where my packets actually go.

Inbound is the second catch. Mail from other servers does land at the Cloudflare spaceport — but at the freight terminal below the passenger circle, and the freight express from there to gateway passes under the Anubis checkpoint. That's real: /inbox is on Anubis's bypass list, so federated traffic is never challenged. The checkpoint bar is drawn just tall enough to cross the passenger line and nothing else.

The dashed line is a WebTransport shinkansen via karutte, our x64 box — currently in trial service. The train is double-ended like a real shinkansen, and because WebTransport is duplex, one train shuttles back and forth on the same track. It's the only vehicle on the map that shows a state ("trial run") rather than a measured flow; when the line opens for business it gets wired to real numbers.

One small indulgence: direction arrows sit beside the track at each line's origin, on the right-hand side of travel. The map drives on the right — oncoming traffic on your left, same as the Seoul subway.

The numbers are real

Train counts come from a public, unauthenticated GET /api/map. It returns coarse numbers only — notes created in the last 24 hours (local and remote), deliveries completed, JetStream backlog — never recipients, content, or accounts. That's what makes it safe to leave open. To keep an unauthenticated endpoint from becoming a direct line to the database, responses are cached server-side for five seconds.

I got the window wrong on the first try. Counting the last five minutes sounds responsive, but on a quiet server a five-minute window is almost always zero, and the map was empty every time you looked. Switching to a 24-hour window fixed it: even a quiet server has a day worth seeing. Pick your window to match your traffic, not your refresh rate.

Counts become trains logarithmically:

const trains = (n: number, max = 4) =>
  n <= 0 ? 0 : Math.min(max, Math.floor(Math.log10(n)) + 1);

One to nine posts is one train; ten to ninety-nine is two. Linear scaling floods the screen on a busy day. This deliberate bluntness means the scenery only changes when an order of magnitude does.

And when the numbers can't be fetched, the departure board says so — "couldn't get the numbers just now; the tracks are unchanged" — and keeps drawing the tracks. Honest numbers and a map that never goes blank are not in conflict.

Animating with SMIL, and the four holes

Every vehicle runs on plain SVG SMIL — animateMotion plus mpath, zero JavaScript animation loops, following the very same path elements you see as tracks:

<animateMotion dur="28s" begin="-14s" repeatCount="indefinite" rotate="auto">
  <mpath href="#p-front" />
</animateMotion>

SMIL is old and solid, and it still dropped me into four holes. Here they are so you can step around them.

Make begin negative. With a positive offset, your train stands frozen at the SVG origin — the top-left corner — until its departure time arrives. begin="-14s" declares the train has always been running.

rotate="auto" orients along the path's own direction. I ran a rocket backwards down a lane with keyPoints="1;0" and it flew tail-first. If a vehicle must travel the other way, draw the path itself the other way.

Under prefers-reduced-motion, park the vehicles — don't hide them. My first version removed all vehicles when the user prefers reduced motion. The bug report that came back was simply "the rocket isn't flying." Right: people who want less motion still deserve a complete map. Now a SMIL element with pinned keyPoints places each vehicle partway along its route and leaves it standing:

<animateMotion dur="1s" fill="freeze" calcMode="linear"
  keyPoints="0.6;0.6" keyTimes="0;1">
  <mpath href="#lane-star" />
</animateMotion>

SMIL inside a late-inserted <svg> can freeze in Chromium. The star chart at the bottom of the page enters the DOM only after the first data arrives, and its animations would sometimes stall — the document clock advancing, the pixels not. The fix: a beat after mount, tap the clock with svg.setCurrentTime(svg.getCurrentTime()) and everything wakes up.

Bonus for your test suite: Playwright treats stroke-only <path> elements as hidden. Assert on the <svg> via getByRole('img') instead.

The star chart

The bottom half of the page is a second drawing: the servers sukhi talks with, as stars on an old-fashioned chart.

Inclusion is allow-list only. A list of the domains your server federates with is itself relationship data, so nobody appears — name or numbers — unless the admin explicitly adds them in the back office.

Everything about the sky is deterministic. Each star's position is seeded by a hash of its domain, so the sky looks the same on every visit. When labels collide, the star walks outward along a spiral whose starting angle also comes from its hash — same inputs, same sky, always. Star size scales with the digits of notes received in the last day, and every star that sent mail launches a small rocket toward sukhi.

What the map bought me

Beyond the fun, one concrete operational win: the DLQ siding — where failed deliveries wait for thirty days — shows held cars in red. If a red boxcar is parked there, something isn't reaching its destination, and you notice it as there's a car on the siding rather than by reading a graph. Most days the board says: "The siding is empty. That is a good thing."

A monitoring page doesn't have to be an operator's tool. Keep two constraints — topology matches the wiring, numbers are real — and a landscape tells no lies. And a landscape is something you can show your friends.

The little trains are carrying words today. Come have a look.