Skip to content

admin: a domain verified by hand is not an alert until a check has passed - #49

Merged
cemhurturk merged 12 commits into
mainfrom
fix/hand-verified-domains
Sep 26, 2026
Merged

cemhurturk merged 12 commits into
mainfrom
fix/hand-verified-domains

Conversation

@cemhurturk

Copy link
Copy Markdown
Contributor

A domain added with clickmonk domain add --verified has no TXT record by design, so its DNS check fails every five minutes and it used to count as needing attention for ever. This records when a domain's check last passed, and a verified domain that has never passed one is shown as verified by hand rather than as a problem.

The rule

A verified domain that has never passed a DNS check is not an alert. Once a check has passed, a later failure is an alert again, because then something changed: a record removed, a nameserver moved. An unverified domain waiting for its record is still an alert, as before. The rule is one SQL condition (ALERT_CONDITION), and both GET /api/alerts and the status route's count use it.

Only a verified result counts as a pass. A resolver error or a missing record never sets the time. If either did, one resolver outage would put every domain verified by hand back in the alert list.

The API says so rather than leaving the interface to work it out. Domain objects gain passedAt (when a check last passed, or null) and handVerified (verified with no passing check). The domain card reads "Verified by hand, no TXT record", then the last check's own result and time in muted text. It still shows the record to publish, and it no longer says the record proved anything.

Storage

Postgres 010 adds domain_dns_checks.passed_at timestamptz NULL and backfills it from rows whose last check was verified. SCHEMA_VERSION is 10. The column goes on the check table, not on domains, for the reason the table exists: a write to domains fires config_changed and makes the redirect reload its configuration, and the check runs every five minutes. The redirect's snapshot load never reads this table, so the migration's lock cannot hold it up. The worker and the on-demand check decide "passed" once, in one upsert (COALESCE(EXCLUDED.passed_at, domain_dns_checks.passed_at)), so a failing check keeps the earlier time.

The backfill cannot tell two cases apart. It only knows the last stored result, so a domain verified by DNS whose last stored check happened to fail looks exactly like one verified by hand. After this migration it would stop alerting until its next passing check. No release has been tagged, so no install is upgrading into this.

Closes #47

Verification

The gate ran in CI's order: pnpm lint, pnpm lint:sh, pnpm typecheck, pnpm build, then pnpm test, which ran 1,558 service tests and 562 interface tests. The interface suite passed five runs in a row. The stack suites passed 86 tests, including the browser suite, whose screenshot shows the hand-verified domain left out of "1 domain needs attention". The durability suite passed.

Every guard was mutated individually and caught by exactly its own test: the pass-only stamp, the COALESCE on update, the d.verified clause in the alert condition, the backfill, the API fields on every route that returns a domain, and the card's order.

@cemhurturk
cemhurturk merged commit 4912a6f into main Sep 26, 2026
5 checks passed
@cemhurturk
cemhurturk deleted the fix/hand-verified-domains branch September 26, 2026 19:23
@cemhurturk cemhurturk mentioned this pull request Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

admin: a domain verified by hand is an alert for ever

1 participant