Make CheckerScope.domainId optional so callers can target the global
admin scope (no domain). getScopedCheckStatus, getScopedCheckOptions,
and updateScopedCheckOptions now short-circuit to the admin /checkers/*
routes when domainId is absent. All narrower-scope callsites gain the
non-null assertion required by the type change.
Add a `disableMetrics` store that gets set when metrics fail to load,
preventing users from switching to a broken metrics view. Also disable
the JSON view button when no observations data is present.
Remove the invalid bind:rr chain between RecordLine and RecordText (RecordText only reads rr, never writes back), and use refreshDomains + invalidateAll instead of directly setting thisZone when the returned zone has a new ID, matching the pattern already used in the domain layout.
Treat io.EOF from ShouldBindJSON as "no body provided" so callers can
POST without a request body to trigger random password generation. Also
fix double-pointer bug (&urp where urp is already *resetPassword) and
bump checker dependencies.
When POST /auth returned 401 with an errmsg (e.g. "Invalid username or
password"), customFetch was ignoring it and attempting a session refresh
via GET /auth. The refresh also failed, ultimately throwing
NotAuthorizedError("Not authenticated") instead of the actual credential
error. This also triggered an extra toast in some cases.
Fix by reading errmsg from any 401 response body first: if present, throw
it directly as a plain Error, bypassing the session-refresh logic entirely.
The previous click-to-copy behavior wasn't obvious to users. Show the
URL in a modal with an explicit copy button and a brief scrape_configs
example so users understand what the endpoint is for.
When switching between checkers via the sidebar, plan.enabled retained
the previous checker's values because the guard condition prevented
re-initialization. Reset all derived state eagerly on checkerId change
and always repopulate plan.enabled from fresh status.
- Move enable/disable toggle to card header (inverted to "Enable scheduling")
- Replace Save button with debounced autosave (700ms) with inline status indicator
- Show interval controls only when enabled; paused hint when disabled
- Remove obsolete no-schedule-yet alert and save button translation keys
The HTML placeholder attribute is rendered in plaintext even on
type="password" inputs, so an inherited secret value injected as a
placeholder was visible without toggling the eye button. Replace the
placeholder with bullets of the same length while the secret is hidden, and
reveal the real value only when the user explicitly toggles visibility.
Rules are now collapsed under the Advanced options toggle instead of
being always visible, and each rule's description is displayed as
helper text below the checkbox when available.
The GetCheckerOptions controller appends a synthetic positional carrying
resolved auto-fill values (consumed by RunCheckModal). splitPositionalOptions
was treating that last entry as the current scope, pushing actual stored
options into inheritedValues — they then showed as placeholders, broke
<select> value matching, and were sent as nil overrides on save. Callers now
pass the auto-fill key set so the synthetic positional is filtered out before
splitting.
Adds an explicit Disabled field on CheckPlan so users can pause a
checker's automatic execution without clearing per-rule state. The
scheduler skips Disabled plans alongside fully-disabled ones, and the
schedule card exposes a switch bound through the existing save path.
Switch the bulk toggle from every-enabled to some-enabled so the
"All" switch reflects an OR across rules: on when at least one is
active, off only when all are disabled.
Rule toggle switches now call a dedicated handleSavePlan() wrapper with
"rules-updated"/"rules-update-failed" messages, separate from the schedule
card's "Schedule saved" toast. CheckerScheduleCard.save() is split into a
raw export (no toasts) and an internal handleSave() used by its own button.
The SDK's new POST /definition returns per-rule precheck errors for the
current options. happyDomain now calls it (or the in-process equivalent
when no remote endpoint is configured) while building each
CheckerStatus, exposes the result as CheckerStatus.PrecheckFailures,
and the rules card in the UI disables the toggle and shows the reason
for any rule whose prerequisites are unmet.
Replace class?: string with class?: ClassValue across Svelte components,
adopting the idiomatic Svelte 5 array syntax (class={["base", className]})
instead of string interpolation for combining static and dynamic classes.
When opening an execution detail page while the execution is still
pending or running, defer fetching observations/results/metrics and
poll the execution status every 3s until it reaches a terminal state.
The loading view shows a "Running" label in the meantime.
- Replace DRONE_TAG##v with DRONE_SEMVER across all pipelines
- Add missing VERSION build_args and tag-specific Docker publish steps to amd64 pipeline
- Add prerelease: true to amd64 release publish steps for consistency with arm64
- Pin swag and go-licenses versions with renovate annotations
- Add renovate regex manager to track go install versions in .drone.yml
- Add BuildKit cache mounts and OCI labels to Dockerfiles
- Improve Dockerfile layer efficiency (separate web/web-admin asset copies, smaller COPY targets)
- Move npm build/generate directives to separate generate_npm.go files with //go:build ignore
- Update README build instructions to include web-admin and correct step order
Provides a minimal table UI to manage the list of reporting domains
authorised via <reportingdomain>._report._dmarc TXT records, replacing
the Orphan editor fallback.
The BIMI draft lets a domain explicitly opt out of BIMI by publishing
a record with v=BIMI1 and an empty l= tag. Surface that in the editor
and treat it as a first-class case in compliance.
Validators run synchronously and surface:
- Owner-name shape: <selector>._bimi (rejects empty selectors and
non-label characters).
- Version: only "BIMI1" is accepted by the current draft.
- Logo URL: l= is mandatory, must be HTTPS, warns when the path does
not end in .svg.
- VMC URL: optional but flagged as info when missing (Gmail and Yahoo
need it). Must be HTTPS when present; info if it does not look like
a .pem file.
- Evidence URL: must be HTTPS when present.
- Cross-record DMARC check: warns when no DMARC is published or when
every DMARC at the apex sits at p=none, since BIMI is only honoured
with an enforcing DMARC policy.
Adds support for Brand Indicators for Message Identification (BIMI),
the emerging standard that lets receiving mail clients display verified
brand logos next to authenticated messages.
Surfaces the RFC 7489 sec. 7.1 trap: when a DMARC record at example.com
publishes rua/ruf addresses on a third-party domain, that domain must
publish "v=DMARC1" at example.com._report._dmarc.<external> to authorize
report delivery. Without it, reports are silently dropped, which is by
far the most common cause of "DMARC is published but no report ever
arrives".
Adds zone-aware checks to the MTA-STS async validator. Once the policy
file is fetched and parsed, compare its mx patterns to the apex MX
records of the current zone (RFC 8461 sec. 4.1):
- mta_sts.zone-no-mx (warning): the policy lists mx entries but the
zone has no MX records, so receivers will refuse delivery.
- mta_sts.zone-mx-not-covered (error in enforce, warning in testing):
one of the apex MX hosts is not matched by any policy pattern.
Senders enforcing the policy will reject mail to that host.
- mta_sts.policy-mx-unused (info): a policy pattern matches no MX in
the zone, hinting at a stale entry.
Validates a MX record set (svcs.MXs) at edit time:
- Null MX (RFC 7505): a "." target must be the only MX in the set, with
preference 0. Both deviations are surfaced.
- Targets: invalid hostnames, out-of-range preferences (uint16) and
duplicate targets (case-insensitive on the FQDN).
- Cross-zone: flags MX targets that are CNAME owners in the same zone
(RFC 5321 sec. 5.1) and warns when an in-zone target lacks any
A/AAAA service. External targets are left to runtime checkers.
Extends the compliance context with findAllServices(type?) so a
validator can iterate every service in the zone, not just a single
subdomain. The DMARC validator now uses it to flag configurations
where alignment is structurally impossible:
- p=quarantine|reject and the zone has neither a DKIM nor an SPF
record -> error: every legitimate message will fail DMARC.
- p=none in the same situation -> warning: DMARC has nothing to
align against, monitoring data will be empty.
- adkim=s (strict DKIM alignment) with no DKIM record published
anywhere in the zone -> warning: only SPF alignment can succeed.