# {{capProjectName}} {{capAppName}} — public status page

A public status page, and a clean **live-`useSubscription`** showcase. Consumes
the `api-status` backend. Bilingual (en/de), no auth.

Scaffold both halves together:

```bash
voltro init my-status --api=api-status --web=frontend-status
voltro dev
```

## What it does

- **SSR first paint.** The index loader pulls the current incidents, components
  and updates over the api's `/rpc` before the HTML is sent, so the page is
  correct + crawlable on first load (no client round-trip).
- **Live upgrade.** The component then subscribes with `useSubscription('app',
  'incidents.live' | 'components.list' | 'updates.list')`, so when an operator
  declares or resolves an incident on the api, the banner, uptime grid and
  timeline update on every open page within seconds — no polling.
- **Derived state.** The overall banner and the 90-day uptime grid are computed
  from incident timestamps (`src/lib/status.ts`), not external monitoring — the
  operator controls the narrative. That math is pure and unit-tested.

## No auth by design

The api leaves the read queries ungated, and this page carries no session gate —
a status page must stay up when the product (and its login) is down. Operators
post incidents against the api's operator-gated mutations from a separate
authenticated surface.

## Files

```
src/pages/
  layout.tsx      header (brand + language) + stylesheet
  page.tsx        the status page — SSR loader + 3 live subscriptions
  page.test.tsx   loader + render + the pure status math
src/lib/status.ts deriveOverall() + uptimeGrid() + uptimePercent() (pure, tested)
src/locales/{en,de}.ts  bilingual catalogs
```

## Anti-patterns

- **Polling for updates.** The whole point is the live subscription — don't add
  a `setInterval`; `useSubscription` pushes deltas.
- **Gating the reads.** These must work for anonymous visitors. Keep the api's
  read queries ungated; the rbac guard belongs on the write mutations.
- **A secret in this app's env.** The browser bundle is public — secrets live on
  the api.
