Files
terdut-server/docs/configuration.md
T
Niklas Ye f344bb707b Graph the metric behind each alert on the incident page
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
2026-10-22 14:10:34 +02:00

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.