Tell which watcher is live¶
Nerthus.Core (until cutover). This page describes the frozen system that runs today and is deleted at cutover. Replaced by: none, retired with Core.
Two things can be sweeping the fleet (The two watchers). This page is how you find out which, in the first minute of an incident, and how to hand the sweep back on purpose.
From the panel¶
Open Flota. The Czuwanie pane in the right-hand column answers directly:
Czuwa: Monitor (host własny)— the monitor swept recently and the Worker is standing down.Czuwa: Worker (Cloudflare)— the monitor is unreachable, its document is unreadable, or its last sweep is 40 minutes old or older, so the Worker has resumed.Czuwa: Nieznane— no document at all.
Under it: the age of the last sweep, which producer wrote the document, and a reminder that the monitor's home is on a deletion clock.
The pane fails towards the Worker, never towards the monitor. Every unclear answer reads as Worker, because that is what the Worker's own gate does with the same input — a panel that guessed "monitor" on a malformed document would tell you the fleet was watched by something that had stopped.
The panel needs the monitor to allow its origin
The pane reads the document from the browser, so the monitor must name the panel's origin in NERTHUS_RESTORATION_STATE_ALLOW_ORIGIN. Unset, the pane says brak odpowiedzi lub CORS — and the same document still answers curl perfectly. If the panel disagrees with the command line below, suspect the header before you suspect the box.
A dashboard with no monitor configured for its hostname says so in the pane rather than showing a blank. ?monitor=<url> points it at one for the length of a page load, without persisting.
From the outside, with curl¶
The monitor's document carries its own producer and its own age:
curl -s https://dev-restoration.nerthus.pl/state.json | jq '{producer, sweptUtc, activeSinceUtc, roster}'
{
"producer": "monitor",
"sweptUtc": "2026-08-20T21:20:55.636Z",
"activeSinceUtc": "2026-08-20T21:05:54.033Z",
"roster": { "stale": false, "error": null, "head": "a012733020a9ac3f386744a56deb19e91464f40b", "staleForMs": 0 }
}
producer— who wrote this document. This is the field to read, notworkerMode.sweptUtc— the last sweep that completed. Under 40 minutes old and the Worker stands down.activeSinceUtc— when the monitor's current run began, not when the document was written.-
roster— the state of the monitor's own input, and the reason it is here rather than only in the log: through the sweep, a roster that was never cloned, one whose fetch failed, and one the parser refuses are indistinguishable — all three read as an absenthosts.json. Those are three different problems in three different places.what you see what it means stale: false,headsetthe roster is current, at that fleet-contract commit errornaming a path under the state dirnothing has been cloned yet errorstartingfleet clone fetch:the tree is on disk and behind origin; staleForMssays how farerrornaming ahosts.yamllinethe roster is there and the parser refuses it headis the answer to "which roster is it watching", which the KV projection could never give.
workerMode does not mean what its name suggests on this document
The monitor runs the same code the Worker does, finds no MONITOR_STATE_URL, sweeps, and stamps workerMode: "active" into its own document. Read as "the Cloudflare Worker is sweeping", that field tells you the opposite of the truth on the one document that proves the Worker is standing down. producer is the field that carries this meaning.
And the Worker's own view, which needs no credential:
On the Worker's document workerMode does mean what it says: active while it is sweeping, standby from the moment it first stood down.
This used to name fleet-health.nerthus.workers.dev, and that hostname is gone
The Worker answered on two hostnames, and the workers.dev one was an oversight rather
than a policy: it is not in zone nerthus.pl, so it sat outside the zone's rate-limit ruleset,
its managed WAF and its cache rules, while the account's other two Workers already had it
disabled. Project 91 turned it off (workers_dev = false in worker/wrangler.toml, so a
redeploy cannot bring it back).
dev-fleet-health.nerthus.pl serves the identical document — measured 2026-08-24T21:00Z, both
hostnames 200 with the same 2 011 bytes. Only the address changes.
The call is still anonymous and still needs no credential. That is deliberate: two runbooks
and one project's evidence rest on it, and closing it without a replacement instrument would
leave this estate with fewer ways to see a wedged failover than it had before. What did change
is the method — anything but GET and HEAD now answers 405, because POST / used to
return the state document to anyone who asked.
What the HTTP status alone tells you¶
The monitor's hostname is served by a cloudflared tunnel, and three failures look the same from a browser and have fixes in three different places:
| status | what it means | where to look |
|---|---|---|
200 |
the monitor is answering | — |
502 |
the tunnel's ingress rule is correct and nothing is listening on :8788 |
the nerthus-restoration service is down |
503 |
the connector has no ingress rule | ~/.cloudflared/config.yml never landed |
530 |
Cloudflare has the DNS record and no connector is registered | the nerthus-restoration-tunnel service is down |
On the box¶
ssh -p 11190 frog@frog01.mikr.us 'rc-status | grep nerthus; tail -3 /var/log/nerthus-restoration.log'
One JSON line per sweep:
{"event":"sweep","utc":"2026-08-20T21:20:55.636Z","durationMs":421,"peakRssMb":74.9,
"roster":{"stale":false,"error":null,"head":"a012733020a9ac3f386744a56deb19e91464f40b"}}
roster.head is the fleet-contract commit the sweep's expectations came from — the answer to "which roster is it watching", which the KV projection could never give.
A clone-failed line means the roster fetch failed; the sweep continues against the last good copy and marks it stale, so a broken clone degrades visibly instead of silently.
Hand the sweep back deliberately¶
Comment MONITOR_STATE_URL out under [vars] in worker/wrangler.toml and deploy from a fleet-contract checkout:
How long the fleet is unwatched: at most one cron tick, so under 15 minutes, and usually zero. The Worker goes active on its very next scheduled run after the deploy, and the monitor does not stop — it keeps sweeping and publishing, so for that window both are watching and nothing is unwatched at all. This is the safe direction.
The reverse — arming the gate — belongs to Stand the health worker down, and it is the one with a prerequisite.
If the monitor dies rather than being stood down¶
Nothing to do: the Worker notices by itself. sweptUtc ages past 40 minutes, the next cron sees a stale document, and the Worker resumes sweeping and posts its resume notice to kanal-wewnetrzny-it. Worst case is 40 minutes of gate hysteresis plus one cron tick, so under 55 minutes.
See also¶
- The two watchers — the model behind all of the above
- Stand the health worker down — arming the gate, and what it requires first
- Keep the monitor box alive — the clock the monitor's home runs on
- Manage notifications — where the standby and resume notices land