Skip to content

RFC-0043: Site status pages and maintenance mode - #118

Merged
Madraka merged 3 commits into
mainfrom
rfc/status-pages-maintenance
Nov 8, 2026
Merged

Madraka merged 3 commits into
mainfrom
rfc/status-pages-maintenance

Conversation

@Madraka

@Madraka Madraka commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

From #117. Status: Accepted (2026-11-08). Every open question decided as recommended; the design notes of 2026-11-08 are worked in. Implementation starts with rollout PR 1 (status pages in the renderer), which ships on its own.

Status pages (the renderer serves them before Astro, from a pure statusPage(state, input) → Response)

State Code Retry-After noindex
not-published 503 3600 yes
maintenance 503 until the end (≥ 60 s) no (Google's pause guidance)
unavailable 503 300 no
error 500 – no; shows only cf-ray
preview-expired 410 – yes
  • All no-store on browser and edge, Vary: Host on top: Workers Cache keys on the path, not the host (the cloudhal.com www-redirect lesson), so a cached status page would be shared across hosts; a workerd test asserts two hosts of one Worker never share one. A self-contained page of at most 8 KB with its own CSP, no admin link.
  • Language: host or path-prefix locale, then the site default, then en (no Accept-Language); RTL from the registry, Western digits for Arabic and Persian. The renderer's catalog covers every registered Studio language through pnpm i18n:export/import (a second source of the exchange script); messages use simple ICU arguments only, so no compiler ships in the site Worker.
  • Look: maintenance and preview-expired may use the published snapshot's theme tokens (brand, surface, text colors, the font pairing); the other states stay neutral.
  • Theme hook: an optional statusPage in theme.ts returns HTML only. It falls back on throw, > 50 ms or > 64 KB, and is never used for error. Old themes get the built-in page unchanged; the starter's routes use statusPage/statusPlain as their own fallback.
  • Health: GET /__cloudhal/health answers { "ok": true, "state": "published" | "not-published" | "maintenance" }, the admin's /api/health shape plus state, so Docker, Caddy and cloudhal.com's post-deploy check read both with one rule.
  • Static builds: cloudhal build --published writes the status page as the whole output of an unpublished or hidden site (index.html, 404.html, robots.txt, _headers, a .cloudhal-status marker); the Docker site-build passes the flag and the Caddyfile serves it with 503. The self-host static path never serves an empty directory.

Maintenance mode

  • Storage: a per-site D1 record (SiteMaintenance, a new root contract) outside snapshots, bundles and push/pull, so a rollback never toggles it.
  • Modes: off, on (optional end) and window (start and end). Wording is maintenance or coming-soon, with a plain-text message per locale of at most 280 characters. It ends per request; no scheduler is involved.
  • Who: people with site:settings. Agents use set_maintenance only with approval from a person holding site:settings, in every mode (turning it off too). Every change is audited. get_site gains a status field so an agent learns the state before it acts; RFC-0044 tokens carry the same key scopes and never site:settings.
  • Owner bypass: a signed link to the published site, never a cookie, because Workers Cache doesn't key on cookies. Responses are private, no-store; links last 1 hour by default, 24 hours at most.
  • Still served during maintenance: robots.txt (a 503 there would pause Google crawling), media, static assets and /.well-known. Feeds, sitemap and llms.txt answer a plain 503.
  • Caching: turning it on purges the site tag with takedown priority (a tag, never a URL, because the edge key is the path), plus a second purge after 15 s. A window expires cached pages at its start, like RFC-0008's gates.
  • Guard: features report {gates: 2, status: 1}; until then, writes get status_unsupported (409).
  • Surfaces: REST, MCP (get_maintenance, set_maintenance), CLI (cloudhal maintenance …) and Studio (a card and a banner). The playground blocks all of it.

Decisions (2026-11-08): the 14 recommended defaults, listed one line each at the end of the RFC, plus the seven design notes and where each went. Rollout: 7 PRs; PR 1 status pages in the renderer (self-contained), PR 2 static builds, PR 3 maintenance in the renderer (with the contract and its ADR), PR 4 writers, PR 5 MCP, PR 6 Studio, PR 7 ops and docs.

Schema check: the new site-maintenance.schema.json, with 4 valid and 12 invalid fixtures, passed pnpm gen, the core-contracts tests and cargo test on the draft branch, then was reverted; it lands with PR 3.

🤖 Generated with Claude Code

Renderer-owned status pages served before Astro (not-published,
maintenance, unavailable, error, preview-expired), translated, with an
optional theme hook; and a production maintenance mode: a per-site D1
record outside snapshots, windows with Retry-After, person approval for
agents, signed bypass links, purge on toggle, a status feature
handshake and a health endpoint that tells maintenance from down.
The contract was checked with pnpm gen (TypeScript and Rust) and
reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
@Madraka Madraka added the rfc A design proposal in docs/rfcs label Oct 23, 2026
Madraka and others added 2 commits November 8, 2026 02:40
Resolve the 14 open questions as recommended in a Decisions section and
work in the design notes: the site health endpoint shares the admin's
{ ok, state } shape, static builds of an unpublished or hidden site
write the status page (cloudhal build --published, Docker site-build,
a Caddy 503 block), the published site's theme tokens on maintenance
and preview-expired pages, no-store plus a tag purge for the path-keyed
Workers Cache with a two-host test, every registered Studio language
through the i18n exchange flow with simple ICU arguments only, the
site state on get_site for agents with the RFC-0044 scope mapping, and
status-page views through the existing render events. Reorder the
rollout so PR 1 (status pages in the renderer) ships on its own and PR 2
is the static-build path. Wrap lines under 100 characters.

Refs #117

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
@Madraka Madraka changed the title RFC-0043: Site status pages and maintenance mode (draft) RFC-0043: Site status pages and maintenance mode Nov 7, 2026
@Madraka
Madraka merged commit cbb1783 into main Nov 8, 2026
5 checks passed
@Madraka
Madraka deleted the rfc/status-pages-maintenance branch November 8, 2026 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rfc A design proposal in docs/rfcs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant