Skip to content

feat: opt-in cluster-native status page via cluster.toml #119

Description

@mighty840

Motivation

Every non-trivial Orca cluster ends up wanting a status page. Today the pattern is to add a Gatus/Kuma/Cachet service in your service.toml, configure endpoints by hand, and wire alerts to your provider. This duplicates data that Orca already has — every [[service]] block already declares its own domain, port, health-check semantics, and the cluster already has an [ai.alerts.channels.*] block for notifications.

Turn the status page into a first-class Orca feature that's opt-in via cluster.toml.

Proposed API

[status]
enabled = true
domain  = "status.example.com"

# Optional: which service groups to render as sections
[status.groups]
core       = { pattern = "services/core/**" }
platform   = { pattern = "services/platform/**" }
customer   = { pattern = "services/customer/**" }
# Hidden by default: services matching these are monitored but not published on the public page
private    = { pattern = "services/personal/**", public = false }

# Optional: extra probes not declared as [[service]]
[[status.probe]]
name  = "Hetzner nbg1 object storage"
type  = "http"
url   = "https://nbg1.your-objectstorage.com/"
expect = "status == 403"  # unauth GET returns 403; anything else means the endpoint is down
group = "upstream"

[[status.probe]]
name  = "PowerDNS UDP 53"
type  = "udp"
address = "46.225.100.82:53"
group = "core"

Reconciliation:

  • Orca walks every [[service]] with a domain = field and generates HTTP checks: https://<domain> → status 200 + TLS cert-expiry ≥ 168h.
  • Orca walks [[status.probe]] blocks for the custom TCP/UDP/HTTP/DNS checks.
  • Every check group renders as a section on status.example.com.
  • Alerts route through the cluster's existing [ai.alerts.channels.*] blocks (email, Slack, whatever's wired). No separate config.
  • The status page is served by orca-proxy itself (no extra container) — same TLS auto-provisioning path as any other service.

Storage: SQLite in /var/lib/orca/status.db for check history + incident timeline (Gatus does this in ~50MB for a year of data at 1-minute resolution).

Why in Orca and not "just install Gatus"

  1. Zero-config for the common case. 90% of what a status page monitors is [[service]] domains that Orca already knows about. Repeating them in a Gatus YAML is boilerplate + a drift source.
  2. Group hygiene. Orca already groups services by project directory. The status page can render that hierarchy for free.
  3. Alert plumbing. Cluster owners already declared [ai.alerts.channels.email] for AI-driven alerts. Status-page alerts should ride the same channel.
  4. TLS + routing. Orca-proxy handles TLS + Host-header routing for every other service; the status page shouldn't need a separate container to negotiate the same thing.
  5. Backup + IaC. status.db sits alongside other Orca state and follows the cluster's backup policy automatically.

Trade-offs

  • Ties status-page semantics to Orca's release cadence. Community won't get a Gatus-flavored feature the day Gatus ships it. Mitigated by keeping the [[status.probe]] escape hatch open for anything Orca-native doesn't cover.
  • Adds ~5-10 MB to the Orca binary if we embed the renderer (or we could serve a static SPA from an embedded assets crate).
  • Not every cluster wants a status page — but enabled = false (default) is a clean off switch.

Prior art

  • Gatus (Go, YAML-config, embedded SQLite) — closest match to the proposed shape
  • Kener (Node, richer UI)
  • Cachet (PHP + Laravel, heaviest)
  • Infra.page (Rust, presentation only — not a checker)

Non-goals

  • Public incident management (subscribers, SMS updates). Keep that in a separate status-incidents add-on if ever needed.
  • Historical percentiles beyond 90 days. Ship default 30-day retention; make configurable.

Related

  • User workflow that motivated this: status.meghsakha.com for a live 4-cluster orca deployment; forced to stand up Gatus as a separate service where Orca already had 100% of the target endpoints declared.

🤖 Filed via Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions