Each alert carries a generatorURL pointing at the Prometheus rule that fired it. The server now parses that link, queries the same Prometheus for the rule's expression and draws it under the alert, with the current value beside it. The browser never talks to Prometheus, so the CSP stays at connect-src 'self'. The URL comes out of a webhook payload, so whoever holds an integration key chooses it. Querying it is therefore opt-in and bounded by an allowlist, TERDUT_PROMETHEUS_ALLOWED_HOSTS (names, "*" as one label, a leading "**" as several, IPs, CIDRs). The check runs on the address the dialer is about to use, a name that resolves to loopback or link-local is refused even when it matches, and redirects and proxy variables are ignored. A worker takes a snapshot when an incident opens and another when it closes, so the graph outlives Prometheus' retention. It finds its work in the database rather than being triggered from the webhook, so ingestion is untouched and a restart loses nothing. While an incident is open the metrics endpoint also queries live, cached for 15 seconds. The rule expression keeps its threshold, so Prometheus returns data only while the alert condition holds; "no data" for the current value means the condition cleared. Stripping the threshold needs a PromQL parser and is left out. Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
5.7 KiB
Configuration
Environment variables and settings. Back to the README and the documentation index.
Two kinds of setting, split by who changes them and how often.
Where the server is plugged in stays in the environment: the listen address, the database DSN, the ntfy URL and token, the public URL. They are needed before the database is open, and two of them are credentials.
How the server behaves lives in the database and is edited by an
administrator in the web UI or through PUT /api/admin/settings, taking effect
on the next sweep rather than at the next restart. The variables below marked
seed are the value each of those starts from: written once, on first start,
and never overwritten afterwards — a redeploy cannot put a chart's default back
over an administrator's edit.
| Variable | Default | Description |
|---|---|---|
TERDUT_ADDR |
:8080 |
TCP address to listen on |
TERDUT_DB_DSN |
— | Required. Postgres connection string, e.g. postgres://terdut:secret@localhost:5432/terdut?sslmode=require |
TERDUT_ARCHIVE_AFTER |
168h (7d) |
seed. How long a resolved alert or incident stays in the default list before being auto-archived |
TERDUT_STALE_AFTER |
6h |
seed. How long a firing alert may go without a refreshing webhook before it is treated as resolved — must exceed your Alertmanager repeat_interval |
TERDUT_NTFY_URL |
— | ntfy server to publish push notifications to; configures the ntfy notification provider. With no provider configured, notifications are off entirely |
TERDUT_NTFY_TOKEN |
— | Bearer token for an access-controlled ntfy |
TERDUT_NTFY_FALLBACK_TOPIC |
— | ntfy topic used when nobody on call can be reached |
TERDUT_PUBLIC_URL |
— | Base URL a phone uses to reach this server: the notification's link into the web UI, its Acknowledge button, and whether the session cookie is Secure |
TERDUT_NOTIFY_REPEAT |
15m |
seed. How long an incident may sit unacknowledged before it is paged again. 0 notifies once and never repeats |
TERDUT_PASSWORD_LOGIN |
true |
false refuses password login and password sign-up (403), leaving single sign-on the only way in. Refused at startup unless SSO is configured |
TERDUT_TRUSTED_PROXIES |
1 |
How many reverse proxies in front of the server append to X-Forwarded-For; the per-address rate limits use the entry that many hops from the right. 0 ignores the header |
TERDUT_OPERATOR_KEY |
— | At least 32 characters. When set, the instance-scoped service account terdut-operator is created if missing and its seed key replaced with this value at every start — how terdut-operator authenticates without a bootstrap handshake. An instance-scoped account acts as owner of every team (team configuration) but is not a member of any, so it reads no incidents |
TERDUT_OPERATOR_MODE |
false |
Declares this install gitops-managed: a session's or a user's own API key's writes to teams, escalation policies, dead man's switches and integrations are refused (403 reason:"operator_managed"); a service account's are not. Team membership and the schedule stay editable regardless |
TERDUT_PROMETHEUS_ALLOWED_HOSTS |
— | Comma-separated Prometheus servers the metrics on an incident may be fetched from, matched against the host in each alert's generatorURL. Host names (prometheus.monitoring.svc), wildcards (* is one DNS label, so *.example.com or prom-*.monitoring.svc; a leading ** is one or more labels), IPs or CIDR ranges, each with an optional :port. Empty turns the feature off. A bare * is refused at startup |
TERDUT_PROMETHEUS_PREROLL |
1h |
How far before an alert started its metrics graph begins |
TERDUT_PROMETHEUS_TIMEOUT |
10s |
Per-request timeout for a Prometheus query |
TERDUT_OIDC_ISSUER |
— | Turns single sign-on on. The provider's issuer URL; discovery is read from <issuer>/.well-known/openid-configuration. See Single sign-on |
TERDUT_OIDC_CLIENT_ID / TERDUT_OIDC_CLIENT_SECRET |
— | Required with an issuer. The confidential client registered at the provider. Keep the secret in a Secret, not in values |
TERDUT_OIDC_NAME |
SSO |
What the sign-in button calls the provider |
TERDUT_OIDC_SCOPES |
openid profile email |
Scopes requested, comma or space separated. Authentik puts groups behind profile |
TERDUT_OIDC_USERNAME_CLAIM / _EMAIL_CLAIM / _GROUPS_CLAIM |
preferred_username / email / groups |
ID token claims read for the username, email and groups |
TERDUT_OIDC_TRUST_EMAIL |
false |
Link a first sign-in to an existing local user by email even if the provider does not mark the address verified |
TERDUT_OIDC_ALLOWED_GROUPS |
— | Comma-separated. Only people in one of these may sign in. Empty admits everybody the provider authenticates |
TERDUT_OIDC_ADMIN_GROUP |
— | Members are system administrators |
TERDUT_OIDC_SESSION_MAX_AGE |
12h |
Hard ceiling on a session made by an SSO sign-in |
Durations use Go syntax (30m, 12h, 168h). An unparseable value falls back to the default.
Note that TERDUT_STALE_AFTER and a dead man's switch timeout point in opposite directions. Staleness
is a generous grace period around a repeat_interval you do not control; a dead man's switch is a
deadline you set deliberately, and the heartbeat's route is configured to beat faster than it.
In the Helm chart the two sweeper durations are set via sweeper.staleAfter and sweeper.archiveAfter, notifications via the notify.* values, single sign-on via oidc.* and passwordLogin, and operator mode via operatorMode.