Skip to content

feat(status-pages): status edge Worker and renderers - #1928

Merged
Makisuo merged 7 commits into
status-pages/1-foundationfrom
status-pages/2-edge
Oct 31, 2026
Merged

Makisuo merged 7 commits into
status-pages/1-foundationfrom
status-pages/2-edge

Conversation

@Makisuo

@Makisuo Makisuo commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Customer status pages, 2/5: the status edge Worker and renderers

apps/status is a new Worker that serves public status pages from edge storage alone. It has no Hyperdrive, no service bindings, no auth, no billing and no telemetry export, so a page keeps showing its last known state while the API, Postgres or AWS is down. An .oxlintrc.json override bans @maple/backend, @maple/db, effect-orm and the auth packages under apps/status/**.

Read path

  1. Find the page:
    • by host (KV host:, falling back to R2 hosts/);
    • or by /s/<slug> (KV slug:, falling back to R2 slugs/).
  2. Load the snapshot from the colo cache, then KV, then R2, then any cached copy however old.
  3. Merge the break-glass entries that haven't been imported yet.
  4. Render.

A lookup that failed because the stores errored answers an uncached 503, never a cached 404. A renamed page only answers under its current slug.

Outputs: server-rendered HTML with no client framework and one nonce'd script for relative times; status.json; a badge SVG; an Atom feed.

Break-glass: /_manage is a plain form. It checks a token against a SHA-256 published in the snapshot (constant-time compare), and has these limits:

  • 10 requests per minute per IP and page;
  • a 32 KB body cap, enforced on the bytes actually read;
  • 50 pending entries per page.

Each post is written once to its own KV key, so nothing rewrites a shared value. "No change" is the default for status and impact on follow-ups, and a status from the other lifecycle (incident vs maintenance) is a 400.

packages/status-page holds the renderers, and will also feed the editor preview.

Tests

  • apps/status, vitest run, 29 passed. Covers:
    • each fallback layer, and the uncached 503 when both stores fail;
    • the CSP nonce and the delayed banner;
    • break-glass: wrong token, no token, rate limit, oversized chunked body, the entry cap, dedupe, "no change" keeping critical impact, and off-kind statuses.
  • packages/status-page, vitest run, 15 passed. Covers escaping of hostile text, URLs and colours, bars and uptime, incident sections, status.json indicators, the badge and the feed.
  • The lint boundary was checked with a throwaway file importing backend, db, effect-orm and auth: 5 no-restricted-imports errors, then the file was deleted.

Stack: #1927 → #1928 → #1929 → #1930.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added public status pages showing service health, components, incident updates, scheduled maintenance, and uptime history.
    • Added status JSON, SVG badges, and Atom feeds for sharing status updates.
    • Added a management page for submitting incident updates when the primary service is unavailable, with input validation and rate limiting.
    • Added delayed-data notices when status information is out of date.

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 15 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e3faf2cc-c05e-4cb7-a0d0-2313fee1f016
Commits

Reviewing files that changed from the base of the PR and between 9195ef9 and 0571d31.

Files ignored due to path filters: 1
  • bun.lock is excluded by !**/*.lock
Files selected for processing: 36
  • .oxlintrc.json
  • alchemy.run.ts
  • apps/status/bunfig.toml
  • apps/status/package.json
  • apps/status/src/break-glass.ts
  • apps/status/src/handler.test.ts
  • apps/status/src/handler.ts
  • apps/status/src/ports.ts
  • apps/status/src/resources.ts
  • apps/status/src/responses.ts
  • apps/status/src/snapshot.ts
  • apps/status/src/storage.ts
  • apps/status/src/test-support.ts
  • apps/status/src/worker.ts
  • apps/status/tsconfig.json
  • apps/status/vitest.config.ts
  • knip.json
  • packages/infra/src/dev-urls.ts
  • packages/status-page/bunfig.toml
  • packages/status-page/package.json
  • packages/status-page/src/badge.ts
  • packages/status-page/src/document.ts
  • packages/status-page/src/escape.ts
  • packages/status-page/src/feed.ts
  • packages/status-page/src/fixtures.ts
  • packages/status-page/src/format.test.ts
  • packages/status-page/src/format.ts
  • packages/status-page/src/index.ts
  • packages/status-page/src/json.ts
  • packages/status-page/src/manage.ts
  • packages/status-page/src/page.ts
  • packages/status-page/src/render.test.ts
  • packages/status-page/src/select.ts
  • packages/status-page/src/styles.ts
  • packages/status-page/tsconfig.json
  • packages/status-page/vitest.config.ts

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f41f23aa-6ee1-4a9e-b0c6-ae10c23c793f
Commits

Reviewing files that changed from the base of the PR and between bc03470 and 9428a5b.

Files ignored due to path filters: 1
  • bun.lock is excluded by !**/*.lock
Files selected for processing: 35
  • .oxlintrc.json
  • alchemy.run.ts
  • apps/status/bunfig.toml
  • apps/status/package.json
  • apps/status/src/break-glass.ts
  • apps/status/src/handler.test.ts
  • apps/status/src/handler.ts
  • apps/status/src/ports.ts
  • apps/status/src/resources.ts
  • apps/status/src/responses.ts
  • apps/status/src/snapshot.ts
  • apps/status/src/storage.ts
  • apps/status/src/test-support.ts
  • apps/status/src/worker.ts
  • apps/status/tsconfig.json
  • apps/status/vitest.config.ts
  • knip.json
  • packages/infra/src/dev-urls.ts
  • packages/status-page/bunfig.toml
  • packages/status-page/package.json
  • packages/status-page/src/badge.ts
  • packages/status-page/src/document.ts
  • packages/status-page/src/escape.ts
  • packages/status-page/src/feed.ts
  • packages/status-page/src/fixtures.ts
  • packages/status-page/src/format.ts
  • packages/status-page/src/index.ts
  • packages/status-page/src/json.ts
  • packages/status-page/src/manage.ts
  • packages/status-page/src/page.ts
  • packages/status-page/src/render.test.ts
  • packages/status-page/src/select.ts
  • packages/status-page/src/styles.ts
  • packages/status-page/tsconfig.json
  • packages/status-page/vitest.config.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Walkthrough

This pull request adds a status-page rendering package and a Cloudflare Worker. The Worker loads snapshots and overlays, serves HTML and auxiliary status endpoints, and accepts break-glass incident updates. The change also registers the app for deployment and development tooling.

Changes: Status Page Service

Layer / File(s) Summary
Rendering foundation
packages/status-page/package.json, packages/status-page/src/fixtures.ts, packages/status-page/src/format.ts, packages/status-page/src/escape.ts, packages/status-page/src/document.ts, packages/status-page/src/select.ts, packages/status-page/src/styles.ts, packages/status-page/{bunfig.toml,tsconfig.json,vitest.config.ts}
Adds shared formatting, escaping, document, selection, style, and fixture utilities, with package and test configuration.
Status-page outputs
packages/status-page/src/{badge.ts,feed.ts,json.ts,manage.ts,page.ts,index.ts,render.test.ts}
Adds page and management-form renderers, JSON, SVG badge, and Atom feed output. Exports the APIs and adds rendering tests.
Storage and snapshot loading
apps/status/src/{ports.ts,storage.ts,resources.ts,snapshot.ts}
Defines storage contracts and Cloudflare adapters. Loads snapshots and overlays through cache, KV, and R2, and loads heartbeat data.
Request handling and break-glass updates
apps/status/src/{responses.ts,handler.ts,break-glass.ts,handler.test.ts,test-support.ts}
Adds response helpers and status routes. Implements rate-limited management requests, validates and stores break-glass updates, and adds handler tests and test utilities.
Worker and development integration
apps/status/src/worker.ts, alchemy.run.ts, packages/infra/src/dev-urls.ts, .oxlintrc.json, apps/status/{bunfig.toml,package.json,tsconfig.json,vitest.config.ts}, knip.json
Adds the Cloudflare Worker and registers it with Alchemy and development tooling. Adds app package, lint, test, typecheck, and entry-point configuration.

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant StatusWorker
  participant handleRequest
  participant loadView
  participant nativeStorage
  participant KV
  participant R2
  Client->>StatusWorker: Send HTTP request
  StatusWorker->>handleRequest: Pass request and StatusEnv
  handleRequest->>loadView: Load status page view
  loadView->>nativeStorage: Read snapshot, overlay, and heartbeat
  nativeStorage->>KV: Read status data
  nativeStorage->>R2: Read fallback objects
  loadView-->>handleRequest: Return page view or no snapshot
  handleRequest-->>StatusWorker: Return HTTP response
  StatusWorker-->>Client: Send response
Loading

Possibly related PRs

Suggested reviewers: jeremyfunk

Merge Risk: Minimal · previous review 9428a

The status Worker’s request conversion and lookup behavior do not present a demonstrated merge blocker. Merge after normal checks.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely summarizes the main changes: adding a status edge Worker and status-page renderers.
Docstring Coverage Passed No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 27 files. (8 skipped: 8 unsupported.)

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@maple-review-bot

This comment has been minimized.

@Makisuo
Makisuo added this pull request to stack #1931 October 28, 2026 15:33
@Makisuo Makisuo changed the title status pages/2 edge feat(status-pages): status edge Worker and renderers Oct 28, 2026

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 inline notes from Maple's review. The score and summary are in the review comment above.

Comment thread apps/status/src/break-glass.ts Resolved
Comment thread apps/status/src/worker.ts Resolved
Comment thread packages/status-page/src/manage.ts Resolved
Comment thread packages/status-page/src/render.test.ts Resolved
Comment thread packages/status-page/src/feed.ts Fixed
@maple-review-bot

This comment has been minimized.

@maple-review-bot

This comment has been minimized.

@maple-review-bot

This comment has been minimized.

@Makisuo
Makisuo force-pushed the status-pages/2-edge branch from e54d75a to 185f722 Compare October 28, 2026 16:01
@maple-review-bot

This comment has been minimized.

@maple-review-bot

This comment has been minimized.

…KV, R2 and cache

@maple/status-page renders a page snapshot as server-side HTML, a
statuspage.io-style status.json, a shields-style badge, an Atom feed and
the break-glass form. All customer text is escaped, and a small inline
script ages the "updated" text and delayed banner on cached copies.

apps/status is a Worker that only reads edge storage: host or /s/<slug>
resolution, then colo cache, KV, R2 and finally any cached copy. It merges
the break-glass overlay and judges freshness from the publisher heartbeat.
Break-glass posts are rate limited, checked against the token digest in
constant time, validated and appended to the KV overlay. The KV namespace
and R2 bucket live in apps/status/src/resources.ts for the publisher to
bind. A lint override keeps backend, db, ORM and auth imports out of the
Worker.
…slug fallback, capped form reads

Adopt the new snapshot contract: heartbeat carries probesSeenAt, components are monitored,
overlay entries live one per KV key and are listed (cached 10s per colo, capped at 50 keys).
The manage form defaults status and impact to No change and rejects a status from the other
lifecycle. Slug lookups fall back to R2, a lookup with KV and R2 both down is an uncached 503,
form bodies are read with a 32 KB capped reader, and component ids are deduped.
…rouped by lifecycle, request span and trace export, regex-free feed base
@Makisuo
Makisuo force-pushed the status-pages/2-edge branch from 9195ef9 to b028783 Compare October 30, 2026 23:35
@maple-review-bot

This comment has been minimized.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 inline notes from Maple's review. The score and summary are in the review comment above.

Comment thread apps/status/src/snapshot.ts Resolved
Comment thread packages/status-page/src/format.ts Resolved
Comment thread apps/status/src/snapshot.ts Resolved
…rvives, and uptime percent floors past float error
@maple-review-bot

maple-review-bot Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 8/10 · likely safe to merge. Held at 8 because earlier reviews of this pull request found 5 defects.
3 notes · tests covered · risk medium · 5 defects fixed in earlier rounds

The head fixes the overlay-cache race and the uptime floor, each pinned by a test that fails on the old code; the three info findings in untouched files still stand. It does not touch apps/status/src/resources.ts, so none of the open HttpClientError/NotFoundError issues are addressed.

  • rememberOverlay re-reads the colo cache so a post that raced a lagging KV list survives
  • formatPercent rounds float error off before flooring, so 99.84% no longer prints as 99.83%
  • test-support gains a duringList hook that lands a write inside a live KV list

Before merge

  • Infra · Check every environment has what this config now expects · alchemy.run.ts

Still open from earlier reviews

Fixed since the last review

  • ✅ F6 · loadOverlay's list path caches over a break-glass post it raced
  • ✅ F7 · formatPercent floors a float artifact, printing uptime a cent too low
  • ✅ F8 · The list path overwrites the colo overlay cache with an assembly read before a concurrent post, dropping the newly posted entry

Production impact

Open errors in the changed files (2)
Issue Service Occurrences File
HttpClientError (5 issues) maple-web 83 apps/status/src/resources.ts
NotFoundError maple-web 4 apps/status/src/resources.ts

After this merges, Maple checks whether they stop.

0571d31 · What was checked, coverage and telemetry details are on the Maple / review check. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo merged commit 5904a97 into main Oct 31, 2026
39 checks passed
@maple-review-bot

Copy link
Copy Markdown

Merged with 1 step from "Before merge" still open. If it is done, tick it on the review comment; if not, now is the time.

  • Infra · Check every environment has what this config now expects

@Makisuo
Makisuo deleted the status-pages/2-edge branch October 31, 2026 01:32
@maple-review-bot

Copy link
Copy Markdown

Maple: after this shipped

✅ Shipped clean. Nothing this change touches got worse in the hour after it deployed.

Deployed to status (pr-2012) at 2026-10-31 09:49 UTC as 8cd64a8, the first version after the merge (the merge commit itself never reported). Compared the 60 minutes before and after.

Errors in the changed files:

  • ➖ HttpClientError: 0/h → 0/h
  • ➖ HttpClientError: 0/h → 0/h
  • ➖ NotFoundError: 0/h → 0/h
  • ➖ HttpClientError: 0/h → 0/h
  • ➖ HttpClientError: 0/h → 0/h
  • ➖ HttpClientError: 0/h → 0/h
Operation Calls/h Errors p95
POST 1 → 205 0.0% → 0.0% 194 → 262 ms
POST 0 → 8 0.0% → 12.5% 0 → 575 ms
GET 2 → 22 0.0% → 0.0% 16 → 1681 ms
GET 10 → 30 0.0% → 0.0% 1071 → 14208 ms
status.request 37 → 239 0.0% → 0.0% 88 → 99 ms

Maple looks once, an hour after the first deploy that carries the merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants