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"
- 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.
- Group hygiene. Orca already groups services by project directory. The status page can render that hierarchy for free.
- Alert plumbing. Cluster owners already declared
[ai.alerts.channels.email] for AI-driven alerts. Status-page alerts should ride the same channel.
- 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.
- 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
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 owndomain,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
Reconciliation:
[[service]]with adomain =field and generates HTTP checks:https://<domain>→ status 200 + TLS cert-expiry ≥ 168h.[[status.probe]]blocks for the custom TCP/UDP/HTTP/DNS checks.status.example.com.[ai.alerts.channels.*]blocks (email, Slack, whatever's wired). No separate config.Storage: SQLite in
/var/lib/orca/status.dbfor 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"
[[service]]domains that Orca already knows about. Repeating them in a Gatus YAML is boilerplate + a drift source.[ai.alerts.channels.email]for AI-driven alerts. Status-page alerts should ride the same channel.status.dbsits alongside other Orca state and follows the cluster's backup policy automatically.Trade-offs
[[status.probe]]escape hatch open for anything Orca-native doesn't cover.enabled = false(default) is a clean off switch.Prior art
Non-goals
status-incidentsadd-on if ever needed.Related
status.meghsakha.comfor 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