This is the full developer documentation for StatusBeam
# StatusBeam
> An open-source, CDN-native status page generator — a modern take on upptime. Renders every byte at the edge from its own store, so your page never hits third-party rate limits.
## Why StatusBeam
[Section titled “Why StatusBeam”](#why-statusbeam)
No client-side rate limits
Upptime calls the GitHub API from the visitor’s browser (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 best-effort GitHub Actions cron (a “every 5 min” job can slip to 15–60 min). StatusBeam uses Cloudflare Cron Triggers, which fire on time.
A store that scales
Instead of walking git commit history through a rate-limited API, StatusBeam uses Cloudflare D1 + KV, purpose-built for time-series reads.
A current frontend
Upptime’s page is built on end-of-life Svelte 3 + Sapper. StatusBeam is Astro + shadcn/ui, shipping \~0 JS for everything but the interactive charts.
## AI-friendly by default
[Section titled “AI-friendly by default”](#ai-friendly-by-default)
Every page is available as clean Markdown at `.md.txt`, the whole site is published as [`/llms.txt`](/llms.txt) and [`/llms-full.txt`](/llms-full.txt), and each page has a **Copy Markdown** button plus **Open in ChatGPT / Claude** actions. See [AI & LLM endpoints](/reference/ai-and-llms/).
# Introduction
> What StatusBeam is, how the three-layer architecture works, and why it runs entirely 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](https://github.com/upptime/upptime) people love — config-as-YAML, zero servers to babysit, badges, a public JSON API — while fixing upptime’s biggest structural weaknesses.
## Architecture
[Section titled “Architecture”](#architecture)
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, derive `up` / `degraded` / `down` from the status code and response time, write time-series to D1 and the current snapshot to KV, and on a status change enqueue a notification and purge the page/badge cache by tag.
2. **Notify layer — Queue consumer.** Fans out status changes to Slack, webhooks, and (soon) email and RSS/Atom.
3. **Display layer — Astro site on Cloudflare.** Renders the page at the edge from D1/KV — the browser never calls a third-party API, so there are no client rate limits. Fronted by Workers Cache; the check layer purges by tag on change, so updates are near-instant rather than TTL-bound.
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.
## Next steps
[Section titled “Next steps”](#next-steps)
* [Quick Start](/getting-started/quick-start/) — get a page running locally.
* [Configuration](/guides/configuration/) — the single `status.config.yml` file.
* [Deploy to Cloudflare](/guides/deployment/) — provision D1/KV and ship it.
# Quick Start
> Clone StatusBeam, install the toolchain, and run the status page locally.
This gets a StatusBeam page running on your machine. To publish it to the edge, follow [Deploy to Cloudflare](/guides/deployment/) afterwards.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* [mise](https://mise.jdx.dev/) (manages the pinned Node + Bun toolchain)
* A Cloudflare account (only needed when you deploy)
## Steps
[Section titled “Steps”](#steps)
1. **Clone the repository.**
```bash
git clone https://github.com/pleaseai/statusbeam.git
cd statusbeam
```
2. **Install the toolchain and dependencies.**
```bash
mise trust && mise install
bun install
```
3. **Create your config.** Copy the example and edit the services you want to monitor (see [Configuration](/guides/configuration/)).
```bash
cp status.config.example.yml status.config.yml
```
4. **Run the dev servers.**
```bash
bun run dev
```
This starts the Turborepo `dev` pipeline — the Astro status page and the check Worker in watch mode.
## What’s next
[Section titled “What’s next”](#whats-next)
Your page renders from local data. When you’re ready to publish, provision D1 + KV and deploy the Worker and site to Cloudflare — see [Deploy to Cloudflare](/guides/deployment/).
# Configuration
> The single status.config.yml file — services, check types, notifications, and theme.
StatusBeam is configured by a **single YAML file**, `status.config.yml`. At deploy time you upload it to the Worker’s KV `config` key; that is the only thing you configure.
Tip
Start from `status.config.example.yml` in the repo: `cp status.config.example.yml status.config.yml`.
## Example
[Section titled “Example”](#example)
```yaml
name: Acme Status
sites:
- name: Website
url: https://example.com
check: http # http | tcp | ssl | statuspage | incidentio
expectedStatusCodes: [200]
maxResponseTime: 2000 # ms; slower responses are marked "degraded"
- name: API
url: https://api.example.com/health
check: http
# Read status straight from an Atlassian Statuspage. `url` is the page's base
# URL; StatusBeam appends /api/v2/summary.json automatically.
- name: Claude
url: https://status.claude.com
check: statuspage
# Add `component` to track one service on the page instead of the whole page.
- name: Claude API
url: https://status.claude.com
check: statuspage
component: Claude API (api.anthropic.com)
# incident.io status pages serve the same Statuspage-compatible summary.json,
# so `incidentio` reads them identically — use whichever names your vendor.
- name: OpenAI
url: https://status.openai.com
check: incidentio
notifications:
slack:
webhookUrl: https://hooks.slack.com/services/T00000000/B00000000/XXXX
webhooks:
- url: https://example.com/status-hook
theme:
logoUrl: /logo.svg
darkMode: true
locale: en # en | zh | ja | ko — fallback UI language
```
## Fields
[Section titled “Fields”](#fields)
### `sites[]`
[Section titled “sites\[\]”](#sites)
| Field | Type | Notes |
| --------------------- | --------- | ------------------------------------------------------------------------------------ |
| `name` | string | Display name for the service. |
| `url` | string | Endpoint to check (or status-page base URL for `statuspage`/`incidentio`). |
| `check` | enum | `http` \| `tcp` \| `ssl` \| `statuspage` \| `incidentio`. |
| `expectedStatusCodes` | number\[] | Codes treated as healthy (HTTP checks). |
| `maxResponseTime` | number | Milliseconds; slower responses are marked **degraded**. |
| `component` | string | For `statuspage`/`incidentio`: track one component by name (case-insensitive) or id. |
### `notifications`
[Section titled “notifications”](#notifications)
* `slack.webhookUrl` — incoming webhook for a Slack channel.
* `webhooks[].url` — arbitrary HTTP endpoints that receive status-change events.
Caution
Keep real webhook URLs out of the committed example — they are secrets. Put them only in the private config you upload to KV.
### `theme`
[Section titled “theme”](#theme)
* `logoUrl` — logo shown on the page.
* `darkMode` — enable the dark theme.
* `locale` — fallback UI language (`en` | `zh` | `ja` | `ko`), used only when a visitor’s browser language isn’t one of the supported locales.
# Deploy to Cloudflare
> Provision D1 + KV, wire the resource IDs, and deploy the Worker and status page to Cloudflare.
StatusBeam runs entirely on Cloudflare: a Cron Worker for checks and an Astro site for the page, backed by D1 (time-series) and KV (current snapshot). The repo ships a scripted path and a manual one — the outline below mirrors the repo’s `DEPLOYMENT.md`, which is the authoritative reference.
Tip
Fastest path: `bun run setup` runs the scripted provisioning. The steps below are the manual equivalent.
## Steps
[Section titled “Steps”](#steps)
1. **Provision D1 + KV.** Create the D1 database and KV namespace in your Cloudflare account.
2. **Wire the resource IDs** into each app’s `wrangler.jsonc` (the `database_id` and KV `id` placeholders).
3. **Write your config** — see [Configuration](/guides/configuration/).
4. **Apply the D1 schema** to create the time-series and incident tables.
5. **Upload the config to KV** under the `config` key.
6. **Set secrets** for optional integrations (Slack/webhook URLs live inside the KV `config`, cache-purge credentials as Worker secrets).
7. **Deploy.**
```bash
bun run deploy
```
This deploys the check Worker and the web app to Cloudflare.
8. **Verify** the first cron run has populated D1/KV and the page renders.
## Deploying these docs
[Section titled “Deploying these docs”](#deploying-these-docs)
This documentation site is a separate app (`apps/docs`). It is a fully static Starlight build deployed to a **Cloudflare Pages** project (`statusbeam-docs`) — no D1/KV, no server adapter:
```bash
bun run --filter '@statusbeam/docs' deploy
```
The site is reached at **`docs.statusbeam.dev`** (a custom domain on the Pages project), while its bundled assets (`_astro/*` JS, CSS, fonts) load from the project’s own **`statusbeam-docs.pages.dev`** origin. That split is configured via Astro’s [`build.assetsPrefix`](https://docs.astro.build/en/reference/configuration-reference/#buildassetsprefix) in `astro.config.ts`; the cross-origin assets are made CORS-readable by `public/_headers`.
Note
One-time setup on Cloudflare: create the Pages project (`bunx wrangler pages project create statusbeam-docs --production-branch main`) and attach the `docs.statusbeam.dev` custom domain in the dashboard (Pages → your project → Custom domains). The deploy token needs the **Cloudflare Pages: Edit** permission.
# AI & LLM endpoints
> How this documentation is made consumable by LLMs and AI agents — llms.txt, raw Markdown endpoints, and per-page AI actions.
This site is built to be consumed by LLMs and coding agents, not just humans. Three mechanisms are always available.
## `/llms.txt`, `/llms-small.txt`, and `/llms-full.txt`
[Section titled “/llms.txt, /llms-small.txt, and /llms-full.txt”](#llmstxt-llms-smalltxt-and-llms-fulltxt)
Following the [llms.txt standard](https://llmstxt.org/), the site publishes:
* **[`/llms.txt`](/llms.txt)** — a structured overview: title, description, and a curated list of links to every page.
* **[`/llms-small.txt`](/llms-small.txt)** — a compact variant of the overview, trimmed for smaller context windows.
* **[`/llms-full.txt`](/llms-full.txt)** — the entire documentation concatenated into one Markdown file, ready to paste into a model’s context.
Point an assistant at any of these URLs to give it the whole product’s docs at once.
*Generated at build time by [`starlight-llms-txt`](https://github.com/delucis/starlight-llms-txt).*
## Raw Markdown endpoints (`.md.txt`)
[Section titled “Raw Markdown endpoints (.md.txt)”](#raw-markdown-endpoints-mdtxt)
Every page is also served as clean, navigation-free Markdown by appending `.md.txt` to its path. For example, this page is available at:
```plaintext
/reference/ai-and-llms.md.txt
```
Custom components (asides, cards, tabs) are converted to plain Markdown, so an agent or crawler gets structured content without HTML, CSS, or scripts.
*Provided by [`starlight-md-txt`](https://www.npmjs.com/package/starlight-md-txt).*
## Per-page AI actions
[Section titled “Per-page AI actions”](#per-page-ai-actions)
Every page has, under its title:
* **Copy Markdown** — copies the page’s raw Markdown to your clipboard.
* **Open in …** — a dropdown that opens the current page in **ChatGPT**, **Claude**, or **Cursor** with a prompt referencing the page URL.
*Provided by [`starlight-page-actions`](https://github.com/dlcastillop/starlight-page-actions).*
Note
These three plugins are configured in `apps/docs/astro.config.ts`. `llms.txt` is owned solely by `starlight-llms-txt`; `starlight-page-actions` is intentionally configured without a `baseUrl` so it does not emit a competing `llms.txt`.