Atriumstatus

master9da7fa5

← all systems

Status app

Not monitorednormal@atrium/status

This app: the health of every agency system, plus the documentation for each one.

checked never

Checks

Status API

This dashboard is down, so nothing else is being watched either.

Set NEXT_PUBLIC_STATUS_URL to the deployed status URL so the app can watch itself from the outside.

What it is

Two surfaces, deliberately kept apart. Status is live and autonomous: a cron hits one endpoint, every monitor runs, results and incidents land in Upstash. Systems is the documentation: what each thing is, how it works, what to do when it breaks.

Nothing here is hand-updated to stay current. The only file a human maintains is the system's own definition, and adding a system is one file.

Adding a system

  1. Copy config/systems/_template.ts to config/systems/<id>.ts.
  2. Fill in the documentation and the monitors.
  3. Add one line to config/systems/index.ts.

That is the whole procedure. The dashboard, the per-system page, the cron, the uptime maths and the public JSON API all pick it up with no further changes. Monitor ids must be unique across all systems — they are the storage keys — and the registry validates that at import time.

How checks actually run

/api/cron/check runs every enabled monitor in parallel, classifies each result as up / degraded / down, writes the run to Upstash, and opens or closes an incident when the status flips. The request is rejected unless it carries CRON_SECRET.

A page view never probes directly. What it can do is notice the stored data has gone stale and schedule a sweep after the response is sent — so looking at the board can refresh it, but never slows it down, and the numbers on screen are always the ones that were measured.

Scheduling, and the Hobby-plan problem

Vercel's Hobby plan allows exactly one cron a day, which is a report, not a health check. Three layers cover it, in increasing order of reliability:

  1. Vercel Cron, daily, from vercel.json. It is the floor, and it is what Hobby permits.
  2. Stale-triggered sweeps. Opening the dashboard or calling GET /api/health refreshes the

data in the background when the last sweep is older than SWEEP_STALE_AFTER_MINUTES (10 by default). A short lock in Upstash means ten people opening the page cause one sweep between them, not ten.

  1. A GitHub Actions heartbeat at .github/workflows/status-sweep.yml, every 10 minutes,

calling the same endpoint with the same secret. The repository is public, so those minutes are free. It needs two repository secrets: STATUS_URL and CRON_SECRET.

Layer 2 alone keeps the board honest for anyone looking at it. Layer 3 is what catches an outage while nobody is looking, which is the only reason a status page exists. On a Pro plan, set the real schedule in vercel.json and delete the workflow.

Storage

Upstash Redis over its REST API, which works from any runtime:

  • atrium:monitor:{id}:last — the most recent run
  • atrium:monitor:{id}:runs — a capped list used for uptime and the sparklines
  • atrium:incident:{monitorId} — the currently open incident, when there is one
  • atrium:incidents:log — closed incidents, newest first

Without Upstash credentials the app falls back to an in-memory store so local development works unconfigured. That store is per-process and disappears on restart, which is fine locally and useless in production — the deployment must have the credentials.

What is local-only

The repo view runs turbo to typecheck, lint, test and build workspaces on demand. That cannot work on Vercel — there is no toolchain and no working tree in a serverless function — so those endpoints refuse outside development. The deployed app shows the snapshot committed by bun run checks:snapshot instead, with the commit and timestamp it came from.

When it breaks

Symptom

Every monitor reads 'unknown'

Check

Whether the cron has ever run: /api/cron/check in the Vercel logs, and UPSTASH_REDIS_REST_URL / _TOKEN.

Fix

Set the Upstash credentials and CRON_SECRET, then trigger the cron once manually.

Symptom

Cron returns 401

Check

CRON_SECRET in the deployment environment against the value Vercel sends.

Fix

Set CRON_SECRET in Vercel; the schedule in vercel.json is what invokes it.

Symptom

A monitor is red but the system is fine

Check

Its expectStatus — some endpoints legitimately answer 403 or 405.

Fix

Widen expectStatus in that system's definition. A monitor that cries wolf is worse than no monitor.

Configuration

variablestatuspurpose
UPSTASH_REDIS_REST_URLVercel project envmissingWhere run history and incidents are stored.Without it the app silently uses an in-memory store that resets on every cold start.
UPSTASH_REDIS_REST_TOKENVercel project envmissingAuth for the same.
CRON_SECRETVercel project env + GitHub repository secretmissingShared secret the sweep endpoint requires.Vercel sends it as a Bearer token on scheduled invocations; the GitHub Actions heartbeat sends the same value.
SWEEP_STALE_AFTER_MINUTESVercel project envmissingHow old the data may get before a page view refreshes it in the background.Defaults to 10. Lower means fresher and more outbound requests; higher means a quieter board that can be behind.
NEXT_PUBLIC_WEBSITE_URL / NEXT_PUBLIC_GRADER_URLVercel project envmissingProduction URLs the monitors point at.The website falls back to https://atrium.agency; the grader monitor stays paused until its URL is set.

Where to start reading

  • System registry

    apps/atrium.status/config/systems/index.ts
  • Monitor runner

    apps/atrium.status/lib/health/runner.ts
  • Storage adapters

    apps/atrium.status/lib/health/store.ts
  • Cron endpoint

    apps/atrium.status/app/api/cron/check/route.ts

In the repo

path

apps/atrium.status

version

0.0.1

files

49 (2 tests)

lines

5,541

routes

11

depends on

@atrium/ui
engineering view →

Incident history

No incidents recorded for this system.