Conormis status

The manual

Whitelabel: running this service as another org's own

This service is a SaaS-shaped product: one checkout + one wrangler.toml + one instance YAML = one org's status site. The engine is estate-agnostic; nothing about the chrome, the registry, the feeds, or the admin carries a brand you did not declare. This is the runbook for standing up your own.

The five steps

  1. Declare your instance in config/status.yaml (the full field contract: docs/config-schema.md):

    site:
      name: Acme Status
      base_url: https://status.acme.example
      description: The live status of every Acme service.
      logo:
        light: https://www.acme.example/brand/logo.svg
        dark: https://www.acme.example/brand/logo-white.svg
      links:
        - label: Acme
          href: https://www.acme.example/
      source_url: https://github.com/acme/status
      fonts_url: https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap
      theme:
        --accent: "#0f766e"
        --ink: "#1c1917"
    groups: [Platform, Website]
    vantages: { relay_stale_ms: 300000, list: [{id: home, kind: home, label: home, description: the status worker's own scheduling location}] }
    services:
      - id: website
        # ... every surface you probe, with its content assertion
    

    site.fonts_url replaces the self-hosted estate font stack (absent, the page serves its own OFL woff2 subset from /fonts/ with zero third-party requests); site.theme overrides design tokens (hex / rgb colors only — the loader refuses anything that could carry CSS). Migrating a registry that still lives in TypeScript? scripts/convert-registries.ts does it mechanically.

  2. Your manifest: copy wrangler.example.toml to wrangler.toml, name your worker, create + wire your KV namespace. The relay (the second vantage) is optional: copy wrangler.relay.toml alongside and share the STATUS_RELAY_TOKEN secret on both sides. The plan matters: a per-minute multi-vantage prober is a paid-tier workload by design — on the Workers FREE plan the 1,000 KV writes/day quota dies ~70 minutes after midnight UTC and the page honestly goes stale for the rest of the day (the playbook: docs/manual/04-operations.md). Budget Workers Paid before going live.

  3. Deploy: npm run deploy (and npm run deploy:relay), with your Cloudflare token + account id in the environment. Then attach your domain: bash scripts/attach-custom-domain.sh — it derives the hostname and zone from your base_url.

  4. Operate: set the secrets — OPS_TOKEN (the admin + ops gate), STATUS_RELAY_TOKEN (if you run the relay), MAIL_PROVIDER_KEY + the mail: block (the subscribe surface stays an honest 503 without one). The admin interface (/admin, login with OPS_TOKEN) is your runtime operate path: publish the automation's drafts, compose incidents, drive the update trail. The git-PR path (incidents/*.md) stays available as the strongest review posture.

  5. Watchdog: the heartbeat workflow runs from this repo's GitHub Actions against your base_url — enable Actions, and the one-canonical-issue alert lifecycle is live with no secrets.

The whole surface is yours

A whitelabel instance runs the FULL product, not a reduced one: the public board with its uptime windows and the per-service latency drill-down (/latency/<id>), the embeddable /badge.svg, RSS/Atom

What is NOT yours to assume

Two deployment postures

  1. Own deployment (the flagship posture): one repo, one config/status.yaml, the build compiles the instance. The whitelabel seam is the shell.
  2. Shared deployment: many pages on one deployment, registered through the page registry (docs/manual/07-shared-deployments.md) or the control plane's emitted YAML. The quota ceiling is the sum of the pages' services x vantages — Workers Paid is a prerequisite for the deployment, not per page.

The runtime overrides (TODO 78)

The config declares the identity; the console can also carry it at run time: the Design register (owner-gated, page-scoped) uploads a logo pair and layers hex-only theme tokens over the config theme. The config file stays the source of truth for everything else — fonts, links, the strings — and the runtime rows ride in the backup like every other register.