Security
The defaults, the guarantees, and the checklist before you publish.
A status page is public infrastructure that holds operational detail and, if you enable subscriptions, visitor addresses. These are the defaults Heyo Status ships with and the decisions left to you.
Defaults
| Area | Default |
|---|---|
| Management API | Disabled until a token is configured — 503, never open. |
| Vercel cron routes | Disabled until CRON_SECRET is set — 503. |
| Public subscriptions | Disabled. |
| Visitor webhooks | Off even when subscriptions are enabled; needs an explicit opt-in. |
| Email subscriptions | Double opt-in. |
| Prometheus | Disabled. |
| Automatic migrations | Never run from a request handler. |
Secrets
| Secret | Protects | Rotate by |
|---|---|---|
HEYO_STATUS_API_TOKEN | /admin/* | updating the platform secret |
CRON_SECRET | Vercel cron routes | updating both sides |
Webhook secret | Signature of outbound webhook deliveries | rotating with the receiver |
| Database credentials | All stored history | your provider's flow |
Both bearer comparisons are constant time, so a token cannot be discovered byte by byte.
What the API exposes
Public, by design:
- Monitor names, groups, descriptions and current state
- Uptime history, latency and the day grid
- Incidents, their timelines and severities
- Maintenance windows
Never exposed publicly:
- Monitor targets — URLs, hosts and ports — appear in notifications, not in the API or the page
- Request headers and credentials from HTTP monitors
- Subscriber addresses and webhook URLs
- Confirmation tokens
- Anything from a
private: truemonitor
Everything you type into name, description and group appears on the
page. Use private: true for internal services you still want to monitor
but not advertise.
Public subscriptions
Enabling subscribers creates an unauthenticated write endpoint. The
protections built in:
- Destination-based deduplication, so one address cannot be signed up twice
- Double opt-in by default, with single-use 32-byte random tokens
- Responses that never reveal whether a destination is already subscribed
no-storeandno-referreron the confirmation response- Unsubscribe as a
POSTbehind a confirm page, so a mail scanner's prefetch cannot unsubscribe anyone - Webhook URL validation against loopback, private, link-local, metadata, CGNAT, multicast and reserved-suffix destinations, and against credentials in the URL
What remains yours:
- Rate limiting. There is no built-in limiter. Put one in front of
POST /subscribersat the edge. - Egress policy. URL validation cannot see through DNS or stop rebinding. If visitor webhooks are enabled, restrict outbound network access.
- Email reputation. Confirmation mail goes out through your transport, under your domain.
Deployment checklist
- 1
Set the admin token
HEYO_STATUS_API_TOKENas a platform secret, never in the repository. Verify: an unauthenticatedPOST /admin/incidentsmust answer401, and a configured deployment must never answer503. - 2
Set siteUrl
Without it, feed links, notification buttons and confirmation URLs are built from the request origin, which a proxy can rewrite.
- 3
Review monitor visibility
Anything internal gets
private: true. Check that no description leaks a hostname or an internal service name. - 4
Decide on subscriptions
Leave them off unless you want them. If you enable webhooks, add the egress policy in the same change.
- 5
Decide on Prometheus
/metricsis public when enabled. Restrict the path at the edge if it should not be world-readable. - 6
Protect the cron routes
On Vercel,
CRON_SECRETmust be set —cleanupdeletes history andcheckssends outbound requests to every monitor. - 7
Migrate deliberately
Run
heyo-status db migrateas a release step, not from a request path.
Monitor credentials
HTTP monitor headers are sent verbatim on every check. Keep them in environment variables, scope the token to a health endpoint, and prefer an unauthenticated health route when one exists.
Credentials never reach the database: results store a name, a type, a status, a latency and a message.
Webhook receivers
Verify the signature before trusting a payload:
The signature covers ${timestamp}.${body}; reject old timestamps to prevent
replay.
Reporting a vulnerability
Report privately through the repository's security policy rather than a public issue: github.com/heyo-sh/heyo-status.