diff --git a/README.md b/README.md index 9353372..b09e3f0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Sandbar covers the ground of five tools: | Area | Like | What it does | |---|---|---| | **Topics** | ntfy | Publish a message to a topic with one HTTP request. Subscribe in the browser, with `curl`, or with the ntfy phone app | -| **Monitors** | Tianji, Uptime Kuma | Website, port, DNS and certificate checks, heartbeats for cron jobs, and a public status page | +| **Monitors** | Tianji, Uptime Kuma | Website, port, DNS and certificate checks, heartbeats for cron jobs, and a status page with groups, incident posts, maintenance and subscribers | | **Websites** | Tianji, Plausible, Umami | Page views, visitors, referrers, browsers, countries and custom events for your own sites, with no cookies and no personal data | | **On-call** | Opsgenie | Rotating schedules, escalation policies, and alerts that are acknowledged, snoozed or resolved | | **Incidents** | incident.io | Declare, assign, update and close an incident, with a timeline and a post-mortem | @@ -190,7 +190,7 @@ app to be paged on your phone when an escalation policy reaches you. | Heartbeat | Your job requests `/api/v1/hb/` within its interval plus the allowed delay | A request on a down heartbeat recovers it at once | A monitor goes down after the set number of failures in a row (default 2), raises an alert with the monitor's -escalation policy, and recovers on the first success. Monitors marked public appear on `/status`, together with +escalation policy, and recovers on the first success. Monitors marked public appear on the status page (see below), together with incidents marked public. The status page never shows staff names or private monitors. The status page shows each public monitor as 90 daily uptime bars, like most hosted status pages. A day is green with @@ -202,6 +202,64 @@ in a small per-day table, so the bars go back 90 days even though raw check resu Monitors cannot probe private or internal addresses unless you tick **Allow targets on private networks** on that monitor, so a user who can add a monitor cannot use Sandbar to scan your network. +## Status page + +The public page is at `/status` (and at the root of its own domain, see below). Everything is set up under +**Status page** in the sidebar. + +- **Components and groups.** Every monitor with **Show on the public status page** ticked is a component, shown as + 90 daily uptime bars (see Monitors). Create groups on the Groups tab, then pick a group, and optionally a different + public name, in the monitor's form. Groups are ordered by their order number; components without a group come first. +- **Incidents.** An incident marked public is posted with its status, the components it affects, and the updates you + write. Declare it as public, or switch it on later. **Post an update** (or changing the status) adds a post with + your text, or standard wording if you leave it empty. Customers see only the posts. The internal timeline, + comments, names and post-mortem never leave Sandbar. The page shows the last 14 days; everything older is on + `/status/history`, and each incident has its own page with the full list of updates. +- **What the page says.** A component is *Major outage* when its monitor is down or a sev1 incident names it, + *Partial outage* for sev2, *Degraded performance* for sev3, and *Under maintenance* during a maintenance window for + it. The banner shows the worst of them. +- **Scheduled maintenance.** Give a window (your local time in the form, stored as UTC) and the affected + components. It is announced when scheduled, listed on the page until it ends, and subscribers are told when it + starts and when it ends. During the window its components show as under maintenance instead of as an outage. + Windows can be moved or canceled, and subscribers are told. Alerts for failing checks still fire as usual. +- **Branding.** Title, description, logo (an https address), accent color, a contact link and footer text. The + accent is checked so that white text on it stays readable (WCAG 4.5 to 1). +- **Custom domain.** Enter a host name such as `status.example.com`, point its DNS at your server and have the + reverse proxy forward that host to Sandbar with the `Host` header unchanged (Caddy does this by default; with nginx + add `proxy_set_header Host $host;`). Sandbar then serves the status page at `/` on that host, and nothing else: + the app, API and topics are not reachable through it. Links in emails and feeds use `https://`. The proxy + provides the certificate. With `SANDBAR_TRUST_PROXY=1` the host is read from `X-Forwarded-Host` when present. + +### Following the page + +Visitors choose **Subscribe to updates** on the page: + +- **Email.** Needs `SANDBAR_SMTP_HOST`; the form is hidden without it. A visitor gets a confirmation link, and no + mail is sent until it is opened. Every message has an unsubscribe link, which asks for one click on the page, so + mail scanners cannot unsubscribe people. Unconfirmed addresses are forgotten after 7 days. +- **Webhook.** The address must answer a test request with a 2xx status before it is subscribed. Requests to private + networks are refused. Each update is a JSON `POST`: + `{"page","event","title","status","message","url","time","unsubscribe"}`. The events are `incident.created`, + `incident.updated`, `incident.resolved`, `maintenance.scheduled`, `maintenance.rescheduled`, `maintenance.started`, + `maintenance.completed` and `maintenance.canceled`. The `unsubscribe` link works with a `POST` of + `{"token": ...}` to `/status/unsubscribe`, or can be opened in a browser. +- **RSS.** `/status/feed.rss` has every incident update and maintenance announcement, newest first. + +Subscribers can be seen and removed on the Subscribers tab (webhook addresses show only the host). Addresses are +stored encrypted. Subscribing is limited to 6 requests per hour per address. + +### Status page API + +| Request | | +|---|---| +| `GET /status.json` | Settings, groups, components with their 90 days, current incidents, upcoming and running maintenance | +| `GET /status/api/history?before=