Skip to content

Latest commit

 

History

59 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

StatusBeam

CI Quality Gate Status codecov License: MIT Reviewed by Greptile Reviewed by cubic Live demo Deploy on Cloudflare

An open-source, CDN-native status page generator — a modern take on upptime.

🔗 Live demo: demo.statusbeam.dev — a StatusBeam instance monitoring a few public services, running on Cloudflare.

StatusBeam monitors your services, records their uptime as durable time-series data, and publishes a fast, good-looking status page to the edge. It keeps the parts of upptime that people love — config-as-YAML, zero servers to babysit, badges, a public JSON API — while fixing upptime's biggest structural weaknesses:

  • No client-side rate limits. upptime's page calls the GitHub API from the visitor's browser (unauthenticated, 60 req/h/IP), so popular pages break with a "rate limit exceeded" screen. StatusBeam renders every byte at the edge from its own store — the browser never talks to a third-party API.
  • Reliable scheduling. upptime rides GitHub Actions cron, which is best-effort (a "every 5 min" job can slip to 15–60 min). StatusBeam uses Cloudflare Cron Triggers, which fire on time.
  • A store that scales. upptime treats git commit history as its database and walks it through a rate-limited API. StatusBeam uses Cloudflare D1 + KV, purpose-built for time-series reads.
  • A current, maintained frontend. upptime's page is built on Svelte 3 + Sapper, both end-of-life. StatusBeam is Astro + shadcn/ui.

Status

🚧 Active development. The core pipeline is live end-to-end — HTTP checks on Cloudflare Cron write to D1/KV, the Astro page renders 90-day uptime bars, response-time charts, and an incident timeline at the edge, and status changes fan out to Slack/webhooks while purging the edge cache. A live demo runs on Cloudflare. TCP/SSL checks, a public API + badges, and more notification channels are next — see the Roadmap.


How it works

StatusBeam is deliberately split into three independent layers. Each can be understood, deployed, and replaced on its own.

        ┌──────────────────────────────────────────────────────────────┐
        │  1. CHECK LAYER — Cloudflare Cron Worker                       │
        │     • Cron Triggers ping every configured service on schedule  │
        │     • Derives up / degraded / down from status + response time │
        │     • Writes time-series to D1, current snapshot to KV         │
        │     • On a status change: enqueue a notification event, and    │
        │       purge the page/badge cache by tag (ctx.cache.purge)      │
        └──────────┬────────────────┬─────────────────────┬────────────-┘
                   │ writes         │ enqueues            │ purges on change
                   ▼                ▼                     │
        ┌───────────────────┐  ┌──────────────────────┐   │
        │ D1 (time-series,  │  │ 2. NOTIFY LAYER —    │   │
        │     incidents)    │  │    Queue consumer    │   │
        │ KV (current       │  │  • Email, Slack,     │   │
        │     snapshot)     │  │    webhook, RSS/Atom │   │
        └─────────┬─────────┘  └──────────────────────┘   │
                  │ reads at the edge                      │
                  ▼                                        ▼
        ┌──────────────────────────────────────────────────────────────┐
        │  3. DISPLAY LAYER — Astro site on Cloudflare                   │
        │     • Renders the page at the edge from D1/KV (no browser →    │
        │       third-party API calls, so no client rate limits)         │
        │     • Fronted by Workers Cache (tiered edge cache): renders    │
        │       set Cache-Control + stale-while-revalidate, hits skip    │
        │       the Worker + D1, concurrent requests collapse; the check │
        │       layer purges by tag on change, so updates are near-      │
        │       instant, not TTL-bound                                   │
        │     • shadcn/ui via React islands for the interactive bits     │
        │       (charts, time-range filters); everything else ships 0 JS │
        │     • Emits shields.io-compatible badge JSON + a public API    │
        └──────────────────────────────────────────────────────────────┘

Because the check and display layers live on Cloudflare — not on the infrastructure being monitored — your status page stays up even when your own services are down. That resilience is the whole point of a status page.


Tech stack

Layer Choice Why
Frontend Astro 7 Static-first (ideal for a mostly-read page), ~0 KB JS by default, a Cloudflare first-party framework (acquired Jan 2026) with workerd dev/prod parity, a Rust compiler (15–61% faster builds), and a stable Astro.cache route-caching API plus an experimental cacheCloudflare() provider for Workers Cache.
UI components shadcn/ui (React islands) + Tailwind CSS Copy-in-your-repo components you own and can fork — perfect for OSS. Used natively via Astro's React islands; hydrated only where interactivity is needed.
Charts shadcn/ui charts (Recharts) Response-time graphs, themed and dark-mode-ready out of the box.
Check scheduler Cloudflare Cron Triggers (Worker) On-time execution, unlike GitHub Actions cron.
Data store Cloudflare D1 (SQLite) + KV D1 for time-series & incident history; KV for the current snapshot.
Edge cache Cloudflare Workers Cache Tiered cache in front of the Astro Worker: Cache-Control + stale-while-revalidate, request collapsing, and tag-based purge on status change — near-instant updates without hammering D1. Today via Cache-Control headers; Astro 7's Astro.cache / cacheCloudflare() is the forward path.
Notifications Cloudflare Workers + Queues Email / Slack / webhook / RSS on status change, decoupled from the UI.
Deploy target Cloudflare (primary) · Vercel (supported) Astro adapters target both; Cloudflare is the native, batteries-included path.
Tooling Bun · Wrangler · TypeScript Bun for install/scripts; Wrangler for Worker + D1 + KV.

The full rationale — including why Astro over SvelteKit and TanStack Start, and why a Cron Worker over GitHub Actions — is in docs/adr/0001-tech-stack.md.


Design

The UI follows the information architecture proven by Statuspage.io and the modern, static-first aesthetic of Instatus:

  • Overall-status banner — one calm, unambiguous line ("All Systems Operational") in a single color that rolls up the worst component state.
  • Component rows — one per service, grouped and collapsible, each with a status pill: Operational / Degraded / Partial Outage / Major Outage / Maintenance.
  • 90-day uptime bars — the signature timeline: one colored bar per day, hover for date + uptime % + linked incidents, gray for no-data days. Adaptive intervals (Instatus-style) let the same component render other windows.
  • Incident timeline — a reverse-chronological, date-grouped feed; each incident threads timestamped updates through the Investigating → Identified → Monitoring → Resolved lifecycle. Scheduled maintenance is a distinct, forward-looking entry.
  • One severity token system — five states as CSS variables (light + dark, OKLCH), driving the banner, pills, and bars from a single source of truth. Dark mode ships by default. Color is paired with icon + text for accessibility.

Project layout

A Bun-workspaces monorepo. The three runtime layers map to three workspaces, with the domain logic shared in core:

statusbeam/
├── apps/
│   ├── web/       # Astro status page (Cloudflare adapter + Workers Cache)
│   └── worker/    # Cron Worker: checks + notifications (D1/KV, schema.sql)
├── packages/
│   └── core/      # shared config schema (zod), types, status derivation
├── status.config.example.yml
├── mise.toml      # pinned toolchain (node, bun)
└── orca.yaml      # worktree setup

Local development:

mise install        # pinned node + bun
bun install         # install workspaces
bun run test        # core unit tests (bun:test)
bun run dev         # Astro dev server (renders sample data without bindings)

Configuration

A single YAML file is the only thing you edit — the same idea as upptime's .upptimerc.yml. Copy status.config.example.yml to status.config.yml:

# status.config.yml
name: Acme Status
sites:
  - name: Website
    url: https://example.com
    check: http # http | tcp | ssl | statuspage | incidentio | sentry | aigateway
    expectedStatusCodes: [200]
    maxResponseTime: 2000 # ms → "degraded" above this
  - name: API
    url: https://api.example.com/health
    check: http
  - name: Claude # mirror an Atlassian Statuspage (status.claude.com, *.statuspage.io, …)
    url: https://status.claude.com # base URL; /api/v2/summary.json is appended for you
    check: statuspage
  - name: Claude API # or track one service on that page by component name/id
    url: https://status.claude.com
    check: statuspage
    component: Claude API (api.anthropic.com)
  - name: OpenAI # incident.io status pages read the same way (Statuspage-compatible)
    url: https://status.openai.com
    check: incidentio
notifications: # all optional; keep the real Slack URL (a secret) in your KV config
  slack:
    webhookUrl: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXX
  webhooks:
    - url: https://example.com/status-hook
theme:
  logoUrl: /logo.svg
  darkMode: true
  locale: en # fallback UI language: en | zh | ja | ko (default en)

Check types

Each site sets a check kind:

check What it does
http Fetches url; up/degraded/down from the status code and response time.
tcp Reserved — currently falls through to http (roadmap).
ssl Reserved — currently falls through to http (roadmap).
statuspage Mirrors an Atlassian Statuspage's own verdict. See the Statuspage adapter guide.
incidentio Mirrors an incident.io status page (Statuspage-compatible). See the incident.io adapter guide.
sentry Mirrors a Sentry Uptime monitor via issue webhook (real-time) + optional poll backstop. See the Sentry adapter guide.
aigateway Reads a model endpoint's published health from the Vercel AI Gateway or OpenRouter. See the AI Gateway adapter guide.

The Statuspage adapter reads a vendor's /api/v2/summary.json (Claude, Vercel, *.statuspage.io, …) and maps their overall indicator — or a single component you name — to a status. Full reference, status-mapping tables, and edge behavior: docs/adapters/statuspage.md.

The incident.io adapter reads the same Statuspage-compatible /api/v2/summary.json that incident.io status pages (status.openai.com, status.incident.io, …) serve — so incidentio and statuspage behave identically; use whichever names your vendor: docs/adapters/incidentio.md.

The Sentry adapter presents a Sentry Uptime monitor on your status page. Sentry runs the checks; StatusBeam ingests the verdict in real time from a Sentry issue webhook (POST /webhooks/sentry/:slug), with an optional cron poll of Sentry's Issues API as the backstop. Binary (up/down). Setup, status mapping, and the webhook-only vs. poll modes: docs/adapters/sentry.md.

The AI Gateway adapter puts a model's health on your status page by reading what the Vercel AI Gateway or OpenRouter already publishes about the providers serving it — uptime and latency from their own production traffic. It sends no probe requests to the model, so it spends no tokens and needs no API key. Grade the whole model (best endpoint wins, since the gateway routes around a failing provider) or one provider endpoint by name. Note the two gateways aggregate over different windows, and OpenRouter currently publishes no latency: docs/adapters/aigateway.md.

Internationalization

The status page UI is translated into English (en), Simplified Chinese (zh), Japanese (ja), and Korean (ko); dates and relative times localize automatically.

Each language is a URL prefix — /en/, /ja/, /ko/, /zh/ — so every language is cached independently at the edge (no cache fragmentation). Visiting the bare / redirects to the visitor's language, chosen in this order:

  1. their remembered choice (a locale cookie, set when they pick a language),
  2. their browser's Accept-Language,
  3. the deployment's theme.locale (used only when the above don't match a supported language),
  4. English.

A language switcher in the footer lets visitors change and remember their choice.


Badges & public API

Every deployment exposes a small public JSON surface at /api/*, served from the same edge-cached KV snapshot as the status page (and purged on the same status changes, so badges never lag the page).

Badges

The badge routes speak the shields.io endpoint protocol — point shields.io at one and it renders the SVG; StatusBeam only emits the JSON. Replace <origin> with your status page's URL and <slug> with a component's slug (the slug from status.config.yml, or the slugified name):

Badge Endpoint
Overall status /api/badge.json
Site status /api/badge/<slug>.json
Site uptime /api/badge/<slug>/uptime.json (?period=day|week|month, default month)
Response time /api/badge/<slug>/response-time.json
![status](https://img.shields.io/endpoint?url=https://status.example.com/api/badge.json)
![uptime](https://img.shields.io/endpoint?url=https://status.example.com/api/badge/api/uptime.json)

Colors are derived from severity (green → operational, yellow → degraded, red → down), uptime ratio, and response time. Add any shields.io query (?style=flat-square, ?label=API, ?logo=cloudflare) to restyle the rendered badge.

Status API

  • GET /api/status.json — the whole dashboard: rolled-up status plus a lean per-site summary (status, response time, day/week/month uptime).
  • GET /api/status/<slug>.json — one site's full record, including the 90-day history and response-time samples.

Both send Access-Control-Allow-Origin: *, so a browser can fetch them directly.


Deployment

StatusBeam deploys to any Cloudflare account (Workers + D1 + KV + Pages). Vercel is also supported for the display layer via Astro's Vercel adapter.

You deploy StatusBeam as a package, not a fork (ADR-0002): your repo holds only your config, and the app is a versioned dependency. Scaffold a thin project, then let the statusbeam CLI provision D1 + KV, wire your custom domain and cron, apply the schema, upload status.config.yml, and deploy both Workers — idempotent, safe to re-run.

bunx create-statusbeam my-status   # or "Use this template" on statusbeam-template
cd my-status
bunx wrangler login                # or export CLOUDFLARE_API_TOKEN
bun install
bunx statusbeam setup              # provisions, configures, deploys (--skip-deploy to stop before deploy)

Upgrading is bunx statusbeam update — no upstream merge. Prefer to modify the app source? You can still fork and deploy the monorepo directly; see the appendix in DEPLOYMENT.md.

Instant cache invalidation (optional)

By default the page is edge-cached for s-maxage=60, so a status change shows up within a minute. For near-instant updates, the check Worker purges the edge cache by Cache-Tag the moment a status flips. The page already emits a matching Cache-Tag response header (status-page + one status-site-<slug> per component); you just provide the Worker two secrets (purge-by-tag is available on all Cloudflare plans since April 2025):

bunx wrangler secret put CF_API_TOKEN   # API token with the "Cache Purge" permission
bunx wrangler secret put CF_ZONE_ID     # the zone serving your status page

When these are unset the purge is skipped (logged, not fatal) and the page simply refreshes on its 60s TTL.

Full runbook — the CLI, provisioning, config, secrets, CI deploy, and the fork-from-source appendix — is in DEPLOYMENT.md.


Roadmap

Shipped

  • Check layer — Cron Worker: HTTP checks, D1 time-series schema, KV snapshot.
  • Display layer — Astro site, shadcn/ui component set, severity token system.
  • Uptime bars & charts — 90-day adaptive timeline, per-component response-time graphs.
  • Incidents — lifecycle model (Investigating → Identified → Monitoring → Resolved) + timeline UI.
  • Notify layer (part 1) — Slack + generic webhook on status change, decoupled via Queues.
  • Edge cacheCache-Tag emit + purge-on-change loop between the check and display layers.
  • Badges & public APIshields.io endpoint badges + JSON status API, edge-cached.
  • Statuspage adapter — mirror any Atlassian Statuspage by page or component (guide).
  • incident.io adapter — mirror any incident.io status page by page or component (guide).
  • Statuspage webhooks — real-time ingest via POST /webhooks/statuspage/:slug, cron as the backstop (guide).
  • Sentry Uptime adapter — mirror a Sentry Uptime monitor via issue webhook (POST /webhooks/sentry/:slug) + optional Issues-API poll backstop (guide).
  • AI Gateway adapter — track a model endpoint's published health on the Vercel AI Gateway or OpenRouter, token-free (guide).

In progress / planned

  • TCP/SSL checks — extend the Cron Worker beyond HTTP (config + schema already accept them).
  • Scheduled maintenance — distinct, forward-looking incident entries.
  • Notify layer (part 2) — email + RSS/Atom feeds.
  • Migration guide — importing an existing .upptimerc.yml.
  • Vercel adapter path — documented alternative to Cloudflare.

Prior art & inspiration

  • upptime/upptime — the serverless-monitoring idea this project builds on.
  • Statuspage.io — the reference information architecture.
  • Instatus — static-first delivery and modern design.
  • statping/statping — a self-hosted, single-binary status server (Go) with its own monitoring engine, notifiers, and mobile app.
  • OpenStatus — open-source synthetic monitoring and status pages, with a globally distributed checker for latency-aware probing.
  • CachetHQ/Cachet — a long-standing open-source status page system (PHP/Laravel) centered on incident and component management.

Code review

Pull requests to StatusBeam are reviewed by two AI code reviewers, both free for open source:

  • Greptile — free for non-commercial MIT/Apache projects under its OSS program.
  • cubic — free for public repositories.

Reviewed by Greptile Reviewed by cubic


License

MIT

About

Open-source, CDN-native status page generator — a modern take on upptime. Astro + shadcn/ui on Cloudflare (Cron Worker + D1/KV + Workers Cache).

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

35 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages