|
1 | 1 | # Pi-Stack status |
2 | 2 |
|
3 | | -A Cloudflare Worker that checks every public Pi-Stack site every 5 minutes and serves a status page with 24 hours of history. |
| 3 | +A Cloudflare Worker that checks every public [Pi-Stack](https://pi-stack.org) site every five minutes and serves a status page with 24 hours of history. |
4 | 4 |
|
5 | | -- `src/index.js` holds the site list, the cron check and the page. |
6 | | -- Results live in one KV key (`STATE` binding), written once per run, which keeps it inside the free tier's 1,000 writes a day. |
7 | | -- `/api/status` returns the latest results as JSON. |
| 5 | + |
8 | 6 |
|
9 | | -Deploy with `npx wrangler deploy` and a `CLOUDFLARE_API_TOKEN` that can edit Workers on the Pi-Stack account. |
| 7 | +The page is [status.pi-stack.org](https://status.pi-stack.org). The same results are available as JSON at [`/api/status`](https://status.pi-stack.org/api/status). |
| 8 | + |
| 9 | +## Monitored services |
| 10 | + |
| 11 | +The Worker checks these sites, in this order. The short descriptions come from each site's public homepage. |
| 12 | + |
| 13 | +| Service | URL | Public description | |
| 14 | +| --- | --- | --- | |
| 15 | +| Pi-Stack | https://pi-stack.org/ | AI products built on Claude, led by DebateLab | |
| 16 | +| DebateLab | https://debate.pi-stack.org/ | AI debate practice for students and competitive debaters | |
| 17 | +| Athel | https://athel.pi-stack.org/ | Interactive story set in an underwater city | |
| 18 | +| Escucha | https://escucha.pi-stack.org/ | Confidential channel for a school community | |
| 19 | +| ProtoPi | https://proto.pi-stack.org/ | Prototype deployment and review system | |
| 20 | +| YubiNotes | https://notes.pi-stack.org/ | YubiKey-only notes | |
| 21 | +| Chorely | https://chorely.pi-stack.org/ | Device-local demo of family chores, tokens, and rewards | |
| 22 | +| Wordle | https://wordle.pi-stack.org/ | Configurable word-guessing game with offline play | |
| 23 | + |
| 24 | +ProtoPi and YubiNotes answer with a login redirect. A redirect still counts as up, because the site itself responded. |
| 25 | + |
| 26 | +## How it works |
| 27 | + |
| 28 | +A [Cron Trigger](https://developers.cloudflare.com/workers/configuration/cron-triggers/) runs every five minutes (`*/5 * * * *`). Each run requests the eight sites from Cloudflare's network. Redirects are not followed, and each request gives up after 10 seconds. A status below 400 is stored as up. A 4xx or 5xx response, a timeout, or a network error is stored as down. |
| 29 | + |
| 30 | +The run writes one Workers KV key, `state`, through the `STATE` binding. That key holds the latest result for each site and 288 samples, which is 24 hours at one sample every five minutes. One write per run is 288 writes a day, under the Workers Free plan limit of [1,000 KV writes a day](https://developers.cloudflare.com/kv/platform/limits/). |
| 31 | + |
| 32 | +The page turns those samples into 48 bars, one per half hour. Green means every check in the block passed, amber means some failed, red means all of them failed, and grey means the block has no sample yet. The HTML refreshes once a minute. Page and API responses are cached for 30 seconds. |
| 33 | + |
| 34 | +`/api/status` returns `checkedAt` and one object per site: `name`, `host`, `url`, `ok`, `code`, `ms`, and `uptime`. `uptime` is the percentage of stored samples, or `null` before the first check. The bar series is only on the HTML page. |
| 35 | + |
| 36 | +If the KV key is missing, the first request to `/` or `/api/status` runs the checks itself and stores them. Any other path, and any method other than GET or HEAD, does not. |
| 37 | + |
| 38 | +## Tech stack |
| 39 | + |
| 40 | +- [Cloudflare Workers](https://developers.cloudflare.com/workers/) for the cron job and the HTTP server |
| 41 | +- [Workers KV](https://developers.cloudflare.com/kv/) for the 24-hour history |
| 42 | +- [Wrangler](https://developers.cloudflare.com/workers/wrangler/) for local development and deploys |
| 43 | +- `src/index.js`, with no frontend framework and no build step beyond Wrangler's bundle |
| 44 | + |
| 45 | +## Quick start |
| 46 | + |
| 47 | +Requires Node.js 22 or newer. |
| 48 | + |
| 49 | +```bash |
| 50 | +npm install |
| 51 | +npm test |
| 52 | +npm run dev |
| 53 | +``` |
| 54 | + |
| 55 | +`npm test` checks the Worker without calling the real sites. `npm run dev` starts a local Worker with a local KV store, so the page waits for a check before it has history. `npm run lint` syntax-checks the source. `npm run build` bundles the Worker with `wrangler deploy --dry-run` and does not deploy it. |
| 56 | + |
| 57 | +GitHub Actions runs the test, lint, and build scripts on every push and pull request. |
| 58 | + |
| 59 | +To deploy a copy: |
| 60 | + |
| 61 | +1. Run `npx wrangler login`, or export `CLOUDFLARE_API_TOKEN` with permission to edit Workers on the target account. |
| 62 | +2. If that login can see more than one account, export `CLOUDFLARE_ACCOUNT_ID`. The account id is not in `wrangler.jsonc`. |
| 63 | +3. Create a namespace with `npx wrangler kv namespace create STATE` and put its id in `kv_namespaces`. The id already in the file is the production namespace for status.pi-stack.org. It belongs to that account. A fork needs its own. |
| 64 | +4. Run `npm run deploy`. |
| 65 | + |
| 66 | +## License |
| 67 | + |
| 68 | +[MIT](LICENSE). |
0 commit comments