DocsStatus pages and badges for people outside Yarrix
Status pages and badges
On this page
A status page is a read-only page that answers "is it working?" for people who do not use Yarrix: a team, customers, a wall display. A badge is a small image or JSON that says the same thing in one line, for a README or a dashboard.
Both are off by default. With nothing configured, /status/... and /badge/... answer 404 not found, as if they did not exist. Changing either field needs an admin signed in to the web app: an API token is refused with exec_field_requires_admin_session, because these fields decide what a visitor with no sign-in can see.
A status page#
In yarrix.yaml:
status_pages:
- id: office
title: Office systems
access: public # public: no sign-in. viewer (the default): any signed-in user.
show_reasons: false # default false
show_availability: true # default true: the 30-day figure
items:
- { ref: "check:web", label: Website }
- { ref: "host:nas-1", label: File server }
- { ref: "backup:tm", label: Backups }
- { tag: customer-facing, label: Customer-facing apps }
The page is at /status/office and its JSON at /status/office.json. Both are outside /api, need no session when access: public, and use no script.
What a visitor sees, for each item you list: the label you wrote, a state (operational, degraded, down, paused, maintenance or unknown) and, if show_availability is on and Yarrix has enough history, the 30-day availability. Nothing else. The page never shows a host name, address, check id or reason, and an object you did not list does not appear, whatever its state.
refpoints at one object:host:<id>,check:<id>(an app or custom check by its id, such ascheck:nas-1:disk, or a heartbeat),backup:<id>orheartbeat:<id>. An id that does not exist readsunknown, neveroperational.tagis one row for every app and heartbeat carrying that tag. Its state is the worst among them, and its figure the lowest. A paused or in-maintenance member does not make the rest look unwell.- A page with
access: viewersends a visitor with no session to the sign-in page. An API token is not a sign-in. show_reasons: trueadds the hub's reason line under a problem (connection refused to 10.1.2.3:5432). Reasons can name hosts, addresses, paths and commands, so Yarrix warns when a public page turns this on. Group rows never show one.
The page is plain HTML with a small inline stylesheet, set apart in apps/hub/src/status/style.ts. It refreshes itself every minute and carries noindex.
A badge#
badges:
- { id: backups, label: backups, tag: nightly }
- { id: web, items: ["check:web", "host:web-1"] }
- { id: pinned, tag: nightly, key: "${env:YARRIX_BADGE_KEY}" } # optional: 16 to 64 letters and digits
Set exactly one of tag and items. The badge says up, 2 down, 1 degraded or unknown, from the worst state among its objects.
The URL holds a key the hub made (22 letters and digits, kept in its database), not the badge id, so nobody can guess one. Find it in the API (an admin):
GET /api/v1/badges
→ [{ "id": "backups", "key": "…", "svg_path": "/badge/<key>.svg", "json_path": "/badge/<key>.json" }]
.svg is the image. .json is the shields.io endpoint format (schemaVersion, label, message, color) plus status.
To revoke a URL, POST /api/v1/badges/<id>/rotate (signed-in admin): the old one stops answering at once. Taking a badge out of config does the same, and adding it back makes a new key. A pinned key is changed in config; a pin that is missing, too short or already used leaves the badge with no URL.
Load and limits#
Everyone who looks at a page or badge shares one reading of the fleet, taken at most every 15 seconds, so a change can take that long to show and a crowd costs the hub no more than one visitor. Each address (a whole IPv6 /64 counts as one) gets 120 page reads and 300 badge reads a minute, and 30 wrong badge keys a minute; past that the answer is 429 with Retry-After: 60. Behind a reverse proxy, set trusted_proxies so the limit is per visitor and not per proxy.
Pages are served with Content-Security-Policy: default-src 'none'; style-src <hash of the page's stylesheet> and no framing.