fix(admin): say where error reports go, and stop calling a silent target configured

There is no default error-reporting DSN in this tree and there should not be:
a self-hoster's crash reports must not leave their host because nobody told
them not to. That part was already right, so nothing here adds a default.

What was wrong is what the console said about it. The GlitchTip card showed a
green "Configured" badge as soon as a DSN existed, whether or not the switch
that actually sends anything was on, and it never said where reports go. An
operator reading that badge has been told their errors are being collected
somewhere, which may be true of neither half.

The badge now tracks reports actually leaving, and one line under the switch
states the case: the host they go to, or that they stay here, or that tracking
is on with no target. The host only — the public key in the DSN answers a
different question. The deploy runbook says the same thing for a
maintainer-operated instance.
This commit is contained in:
Marc-André Bombeck committed 2026-09-10 09:28:12 +02:00
1 parent 1c10bbbde8
commit 532483e782
9 files changed
+65 -2

No files matched your search

+12
View File
@@ -143,6 +143,18 @@ itself still works: it walks the journey against an in-process instance
once per leg with that leg broken, and fails if any of them comes back
green.
## Error reporting
HealthLog ships no default error-reporting target and never will: an
operator's crash reports must not leave their host because nobody told them
not to. Reporting starts only when someone types a DSN into **Admin →
Integrations → GlitchTip**, and that card states plainly whether reports are
going anywhere and which host they go to.
For a maintainer-operated instance, set the DSN there after the first deploy —
it lives in the database, not in the environment, so it survives an image
upgrade and does not need a compose change.
## Verify the image signature
Every release image (the multi-arch manifest **index**, covering both
+3
View File
@@ -6597,6 +6597,9 @@
"glitchtipDsnPlaceholder": "https://<public-key>@glitchtip.example.com/<project-id>",
"glitchtipEnvironment": "GlitchTip Environment",
"glitchtipEnvironmentPlaceholder": "production",
"glitchtipTargetOn": "Fehlerberichte gehen an {host}.",
"glitchtipTargetOff": "Fehlerberichte bleiben auf diesem Host. Es wird nichts verschickt.",
"glitchtipTargetNoTarget": "Fehler-Tracking ist an, aber kein Ziel gesetzt, also wird nichts verschickt.",
"webPushVapidDescription": "Konfiguriere VAPID-Schlüssel und Subject direkt hier, damit Browser-Push ohne ENV-Variablen funktioniert.",
"webPushVapidPublicKey": "VAPID Public Key",
"webPushVapidPublicKeyPlaceholder": "BElx... (Base64URL)",
+3
View File
@@ -6597,6 +6597,9 @@
"glitchtipDsnPlaceholder": "https://<public-key>@glitchtip.example.com/<project-id>",
"glitchtipEnvironment": "GlitchTip environment",
"glitchtipEnvironmentPlaceholder": "production",
"glitchtipTargetOn": "Error reports go to {host}.",
"glitchtipTargetOff": "Error reports stay on this host. Nothing is sent anywhere.",
"glitchtipTargetNoTarget": "Error tracking is on, but no target is set, so nothing is sent.",
"webPushVapidDescription": "Configure VAPID keys and subject here so browser push works without environment variables.",
"webPushVapidPublicKey": "VAPID public key",
"webPushVapidPublicKeyPlaceholder": "BElx... (Base64URL)",
+3
View File
@@ -6597,6 +6597,9 @@
"glitchtipDsnPlaceholder": "https://<public-key>@glitchtip.example.com/<project-id>",
"glitchtipEnvironment": "Entorno de GlitchTip",
"glitchtipEnvironmentPlaceholder": "production",
"glitchtipTargetOn": "Los informes de errores van a {host}.",
"glitchtipTargetOff": "Los informes de errores se quedan en este host. No se envía nada.",
"glitchtipTargetNoTarget": "El seguimiento de errores está activado, pero no hay destino, así que no se envía nada.",
"webPushVapidDescription": "Configura aquí las claves VAPID y el subject para que el push del navegador funcione sin variables de entorno.",
"webPushVapidPublicKey": "Clave pública VAPID",
"webPushVapidPublicKeyPlaceholder": "BElx... (Base64URL)",
+3
View File
@@ -6597,6 +6597,9 @@
"glitchtipDsnPlaceholder": "https://<public-key>@glitchtip.example.com/<project-id>",
"glitchtipEnvironment": "Environnement GlitchTip",
"glitchtipEnvironmentPlaceholder": "production",
"glitchtipTargetOn": "Les rapports d'erreur vont vers {host}.",
"glitchtipTargetOff": "Les rapports d'erreur restent sur cet hôte. Rien n'est envoyé.",
"glitchtipTargetNoTarget": "Le suivi des erreurs est activé, mais aucune cible n'est définie, donc rien n'est envoyé.",
"webPushVapidDescription": "Configurez ici les clés VAPID et le subject pour que le push du navigateur fonctionne sans variables d'environnement.",
"webPushVapidPublicKey": "Clé publique VAPID",
"webPushVapidPublicKeyPlaceholder": "BElx... (Base64URL)",
+3
View File
@@ -6597,6 +6597,9 @@
"glitchtipDsnPlaceholder": "https://<public-key>@glitchtip.example.com/<project-id>",
"glitchtipEnvironment": "Ambiente GlitchTip",
"glitchtipEnvironmentPlaceholder": "production",
"glitchtipTargetOn": "Le segnalazioni di errore vanno a {host}.",
"glitchtipTargetOff": "Le segnalazioni di errore restano su questo host. Non viene inviato nulla.",
"glitchtipTargetNoTarget": "Il tracciamento degli errori è attivo, ma non c'è una destinazione, quindi non viene inviato nulla.",
"webPushVapidDescription": "Configura qui le chiavi VAPID e il subject affinché il push del browser funzioni senza variabili d'ambiente.",
"webPushVapidPublicKey": "Chiave pubblica VAPID",
"webPushVapidPublicKeyPlaceholder": "BElx... (Base64URL)",
+3
View File
@@ -6577,6 +6577,9 @@
"glitchtipDsnPlaceholder": "https://<public-key>@glitchtip.example.com/<project-id>",
"glitchtipEnvironment": "GlitchTip 환경",
"glitchtipEnvironmentPlaceholder": "production",
"glitchtipTargetOn": "오류 보고서는 {host}(으)로 전송됩니다.",
"glitchtipTargetOff": "오류 보고서는 이 호스트에 남습니다. 아무것도 전송되지 않습니다.",
"glitchtipTargetNoTarget": "오류 추적은 켜져 있지만 대상이 없어 아무것도 전송되지 않습니다.",
"webPushVapidDescription": "환경 변수 없이 브라우저 푸시가 동작하도록 VAPID 키와 subject를 여기서 설정하세요.",
"webPushVapidPublicKey": "VAPID 공개 키",
"webPushVapidPublicKeyPlaceholder": "BElx... (Base64URL)",
+3
View File
@@ -6597,6 +6597,9 @@
"glitchtipDsnPlaceholder": "https://<public-key>@glitchtip.example.com/<project-id>",
"glitchtipEnvironment": "Środowisko GlitchTip",
"glitchtipEnvironmentPlaceholder": "production",
"glitchtipTargetOn": "Zgłoszenia błędów trafiają do {host}.",
"glitchtipTargetOff": "Zgłoszenia błędów zostają na tym hoście. Nic nie jest wysyłane.",
"glitchtipTargetNoTarget": "Śledzenie błędów jest włączone, ale nie ustawiono celu, więc nic nie jest wysyłane.",
"webPushVapidDescription": "Skonfiguruj tutaj klucze VAPID i subject, aby push przeglądarki działał bez zmiennych środowiskowych.",
"webPushVapidPublicKey": "Klucz publiczny VAPID",
"webPushVapidPublicKeyPlaceholder": "BElx... (Base64URL)",
+32 -2
View File
@@ -34,7 +34,30 @@ export function GlitchtipSection() {
const glitchtipEnvironmentValue =
glitchtipEnvironmentDraft ?? settings?.glitchtipEnvironment ?? "production";
const configured = Boolean(settings?.glitchtipDsn);
/**
* The host the DSN addresses, and nothing else from it. An operator asking
* "where do my errors go" is asking for a hostname; the public key in the
* DSN answers a different question and does not belong in a status line.
*
* There is deliberately no default here and none anywhere else in the tree:
* a self-hoster's crash reports must not leave their host because nobody
* told them not to. Reporting starts when this operator types a target.
*/
const targetHost = ((): string | null => {
const dsn = settings?.glitchtipDsn;
if (!dsn) return null;
try {
return new URL(dsn).host || null;
} catch {
return null;
}
})();
// "Configured" has to mean reports are actually going somewhere. A DSN with
// the switch off sends nothing, and a green badge over that is the kind of
// reassurance an operator only discovers was wrong when they go looking for
// a crash that was never reported.
const reporting = Boolean(settings?.glitchtipEnabled && targetHost);
const testGlitchtip = useMutation({
mutationFn: async () => {
@@ -76,7 +99,7 @@ export function GlitchtipSection() {
icon={AlertTriangle}
title={t("admin.glitchtipTitle")}
description={t("admin.glitchtipDescription")}
status={configured ? <ConfiguredBadge /> : null}
status={reporting ? <ConfiguredBadge /> : null}
/>
<div className="space-y-3">
@@ -89,6 +112,13 @@ export function GlitchtipSection() {
}
disabled={updateSettings.isPending}
/>
<p className="text-sm" data-slot="glitchtip-target">
{reporting && targetHost
? t("admin.glitchtipTargetOn", { host: targetHost })
: settings?.glitchtipEnabled
? t("admin.glitchtipTargetNoTarget")
: t("admin.glitchtipTargetOff")}
</p>
<div className="grid gap-3 sm:grid-cols-2">
<div className="space-y-1.5 sm:col-span-2">
<Label htmlFor="admin-glitchtip-dsn" className="text-xs">