Commit graph

2,126 commits

Author SHA1 Message Date
a82e507d87 Import checker-smtp into happydomain 2026-04-28 18:21:17 +07:00
b6444b1247 Import checker-resolver-propagation into happydomain 2026-04-28 18:21:17 +07:00
a5efe371fb Import checker-email-keys into happydomain 2026-04-28 18:21:17 +07:00
5fd5d107f3 Import checker-email-autoconfig into happydomain 2026-04-28 18:21:17 +07:00
d2b9f2c467 Import checker-dnsviz into happydomain (externalizable only) 2026-04-28 18:21:17 +07:00
71fa73a04d Import checker-dnssec into happydomain 2026-04-28 18:21:17 +07:00
3c28c69049 Import checker-delegation into happydomain 2026-04-28 18:21:17 +07:00
1fe20a746d Import checker-blacklist into happydomain 2026-04-28 18:21:16 +07:00
e348da75f2 Import checker-authoritative-consistency into happydomain 2026-04-28 18:20:56 +07:00
bfd82df8e0 Add LDAP checker 2026-04-28 18:20:28 +07:00
d000366434 Handling Kerberos records (analyzer + editor + checker) 2026-04-28 18:20:10 +07:00
dde034d0e0 Add checker STUN/TURN 2026-04-28 18:19:35 +07:00
0dceb15a76 Handling SIP records (analyzer + editor + checker) 2026-04-28 18:19:35 +07:00
fd843d6708 Add checker for SRV records 2026-04-28 18:19:35 +07:00
6260aaa74d Add XMPP checks + handle xmpps 2026-04-28 18:19:35 +07:00
f37eb45159 Handling CalDAV and CardDAV records (analyzers + editors + checker) 2026-04-28 18:19:35 +07:00
fd1918bd04 New service: libravatar
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-28 18:17:35 +07:00
1e38ead1db New service: DMARC-reports 2026-04-28 18:17:35 +07:00
b5e4094f15 New domain filtering method: domain contains 2026-04-28 18:17:35 +07:00
5962ea3146 Add TLSA checker
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-28 18:03:56 +07:00
4ac00009f8 web: revamp TLSA editor. Able to import certificate and fetch through API current chain to auto-fill 2026-04-28 18:03:55 +07:00
23f7af2f57 api: New route to retrieve current TLS certificate chain 2026-04-28 18:03:54 +07:00
6737e33fcb bimi: support BIMI declination records
All checks were successful
continuous-integration/drone/push Build is passing
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.
2026-04-28 17:59:38 +07:00
04e072a257 compliance: BIMI record sync validators
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.
2026-04-28 17:59:32 +07:00
8fcd40b9b7 web: BIMI editor and parser/serializer
Adds the frontend pieces needed to edit a BIMI record from the zone UI.
2026-04-28 17:59:19 +07:00
edbba509bf services: add BIMI service backend
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.
2026-04-28 17:58:48 +07:00
c91c2ca35f compliance: DMARC cross-domain report authorization
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".
2026-04-28 17:58:33 +07:00
93dd809cde resolver: POST /api/resolver/dmarc-report-auth endpoint
Adds a backend endpoint that resolves the RFC 7489 sec. 7.1 cross-domain
reporting authorization record, paired with the (incoming) DMARC async
validator on the front-end.

When a DMARC record at example.com publishes rua=mailto:dmarc@third.tld,
third-party receivers MUST refuse to deliver reports unless
example.com._report._dmarc.third.tld publishes a v=DMARC1 TXT. This is
the silent failure mode behind most "we configured DMARC but never see
any report" tickets.
2026-04-28 17:58:14 +07:00
1386a1056a compliance: MTA-STS cross-check policy mx vs zone MX
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.
2026-04-28 17:57:26 +07:00
a9cb3c6422 compliance: MX record sync validators
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.
2026-04-28 17:57:26 +07:00
c32efe0cf7 compliance: DMARC cross-checks against zone DKIM and SPF
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.
2026-04-28 17:57:26 +07:00
4dbc59725a compliance: MTA-STS async policy fetch
Wires the new POST /api/resolver/mta-sts-policy endpoint into the
MTA-STS validator. The async pass runs after the local TXT checks,
debounced and cancellable through EditorCompliance, and surfaces:

- Transport-level failures: dns-error, tls-error, fetch-error,
  too-large.
- HTTP-level failures: not-found (404), http-error (other non-2xx),
  redirect (server tried to redirect, RFC 8461 sec. 3.3 forbids it).
- Policy file content: missing/invalid version, missing/invalid mode,
  mode=none (warning, effectively disabled), mode=testing (info),
  missing mx in enforce/testing modes, missing/out-of-range max_age
  (0..31557600), short max_age (< 1 day, warning).
2026-04-28 17:57:26 +07:00
17e272f220 resolver: POST /api/resolver/mta-sts-policy endpoint
Adds a backend endpoint that fetches and parses an MTA-STS policy
file at https://mta-sts.<domain>/.well-known/mta-sts.txt per RFC 8461
sec. 3.3, paired with the (existing) MTA-STS TXT validator on the front-end.
2026-04-28 17:57:26 +07:00
577e8557a3 compliance: TLS-RPT record sync validators
Adds checks for svcs.TLS_RPT against RFC 8460 sec. 3.

The validator surfaces:

- Wrong owner name (must be _smtp._tls.<domain>).
- Missing or non-TLSRPTv1 v= tag.
- Missing rua= report destination.
- Empty entries inside rua=.
- rua URIs that are neither mailto: nor http(s):.
- Malformed mailto URIs (missing @ or domain).
2026-04-28 17:57:26 +07:00
b27b2745d6 compliance: MTA-STS record sync validators
Adds checks for svcs.MTA_STS against RFC 8461 sec. 3.1.

The validator surfaces:

- Wrong owner name (must be _mta-sts.<domain>).
- Missing or non-STSv1 v= tag.
- Missing id= tag.
- id= containing characters outside [A-Za-z0-9] or longer than 32 chars.
2026-04-28 17:57:26 +07:00
99dace151e compliance: DMARC record validators
Adds compliance checks for svcs.DMARC against RFC 7489.

The validator parses the published TXT and surfaces:

- Wrong owner name (record must live at _dmarc.<domain>).
- Missing or non-DMARC1 v= tag.
- Missing, unknown, or "monitoring-only" p= policy.
- Invalid sp= subdomain policy.
- Invalid adkim/aspf alignment values.
- pct= out of [0..100] (error) and pct < 100 (info, partial deployment).
- Non-positive or non-numeric ri=.
- Unknown fo= entries (0 / 1 / d / s) and unknown rf= formats (afrf).
- Empty or malformed rua/ruf URIs (mailto and http(s) accepted; mailto
  size suffix !N preserved).
2026-04-28 17:57:26 +07:00
e2bf286a56 compliance: SPF async recursive flatten
Wires the new POST /api/resolver/spf-flatten endpoint into the SPF
validator. The async path runs after the local checks, debounced and
cancellable through EditorCompliance, and surfaces:

- spf.recursive-many-lookups / spf.recursive-too-many-lookups based on
  the recursive lookupCount returned by the backend
- spf.too-many-void-lookups when more than 2 NXDOMAIN/NoData responses
  occur during the walk (RFC 7208 §4.6.4)
- per-include diagnostics: spf.include-loop, spf.include-no-spf,
  spf.include-resolver-error, spf.include-error — pointing at the exact
  domain and mechanism that failed
2026-04-28 17:57:26 +07:00
5f8862c384 resolver: POST /api/resolver/spf-flatten endpoint
Adds a recursive SPF flatten endpoint sized for the compliance UI:

- happydns.SPFFlattenRequest accepts a {domain, record?} pair so the UI
  can preview an unsaved record without persisting it first; the optional
  inline record bypasses the root TXT lookup.
- happydns.SPFFlattenResponse returns the recursive tree with per-node
  Mechanism / Domain / Record / LookupsHere / Error fields, plus the
  RFC 7208 §4.6.4 budget counters (LookupCount, VoidLookups, Exceeded,
  VoidExceeded, Truncated).
- Hard caps at 10 lookups, 2 void lookups, depth 12, 2s per query and
  10s overall. Cycle detection via the visited-domain set.
- Resolver selection mirrors ResolveQuestion (local / custom / default
  to 1.1.1.1) with the same IPv6-bracket handling.
2026-04-28 17:57:08 +07:00
48530dd212 compliance: SPF local validators
Extracts the SPF parser/serializer out of the editor into
$lib/services/spf.ts (matching dmarc.ts / mta_sts.ts) and adds a sync
validator that flags non-recursive issues against RFC 7208:

- missing or wrong v=spf1
- absence / multiplicity / non-final placement of ‘all’
- redirect= combined with ‘all’ or duplicated
- ptr deprecation (RFC 7208 §5.5)
- local DNS-lookup budget (warn ≥8, error >10) — recursive flatten will
  come later via an async backend endpoint
- mechanisms missing values, empty terms, duplicates, length cap
2026-04-28 17:55:40 +07:00
faf21f63b1 compliance: DKIM record sync validators
Validates a DKIM TXT record (svcs.DKIMRecord) at edit time:

- Selector: must be present, must match the label charset.
- Version: only "DKIM1" is accepted (RFC 6376 sec. 3.6.1).
- Public key: detects missing p=, empty p= (revocation, warning), and
  non-base64 payloads. Warns on RSA keys shorter than ~2048 bits and
  errors on RSA keys shorter than ~1024 bits per RFC 8301.
- Algorithms: warns on SHA-1 (RFC 8301) and unknown hashes; flags
  unknown key types or service types.
- Flags: surfaces t=y (testing) as info; warns on unknown flags.
- Granularity: marks g= as deprecated since RFC 6376.
2026-04-28 17:55:40 +07:00
893597688e compliance: add records-compliance infra (types, registry, editor panel)
Introduces the frontend-only compliance framework that lets each editor
contribute spec-conformance checks.

No validators are registered yet.
2026-04-28 17:55:40 +07:00
4d37d77772 compliance: Document common wanted scenarios 2026-04-28 17:55:40 +07:00
7251d93619 web: simplify service editors with single-source-of-truth state
Replace dueling parse/stringify $effects across service editors with
one-time top-level init plus a single write-back $effect. Remount
editors via {#key value} in ServiceEditor so children no longer need
inbound-sync logic.
2026-04-28 17:55:40 +07:00
93e148e8c0 Add external_whois checker for dangling pointer expiry
All checks were successful
continuous-integration/drone/push Build is passing
Subscribes to dangling.external-target.v1 entries via AutoFillDiscoveryEntries
and runs RDAP per registrable domain (deduped, parallelised, capped at 8
concurrent), publishing a per-Ref Facts map consumed by checker-dangling.
2026-04-28 13:06:23 +07:00
cb400979c2 Add legacy records checker 2026-04-28 13:06:23 +07:00
4a4505c9cc Add TLS checker 2026-04-28 13:06:23 +07:00
2e6c5c6be6 Bump checker-sdk to v1.5.0 2026-04-28 13:06:23 +07:00
1297898628 storage: widen ListDiscoveryEntriesByTarget to narrower scopes
Domain- and user-scoped consumers were missing every discovery entry
published below their scope. The exact-match prefix dscent-tgt|{u/d/s}|
introduced in 9c6398b1b only returned entries stored at the literal
target string, so a domain-scoped consumer like checker-tls or
checker-caa never received the tls.endpoint.v1 entries that
service-scoped producers (checker-dane, checker-smtp, checker-sip,
checker-srv, checker-stun-turn) publish under the same domain. The
symptom on the consumer side was "No TLS endpoints have been discovered
for this target yet." even when producers had run.

Drop the trailing "|" from the iteration prefix when the target lacks a
ServiceId (and the DomainId for user scope) so the prefix scan matches
narrower scopes too. RawURLEncoded identifiers contain neither "/" nor
"|", so slash boundaries in the encoded "u/d/s" target form remain
unambiguous. Service-scoped lookups stay exact. Each matching key is
parsed back into its actual stored target before fetching the primary
record, so the returned StoredDiscoveryEntry.Target reflects where the
entry was published, not the (broader) target that found it.
2026-04-28 13:06:23 +07:00
392e7fb344 checkers: thread rule States into ReportContext for reports
Propagate the persisted CheckEvaluation.States through BuildReportContext
and the HTTP report transport so reporters can render rule-driven
sections (hints, severity) without re-deriving them from raw data. When
no evaluation is available the context carries nil states, matching the
SDK's documented nil-safe fallback to data-only rendering.
2026-04-28 13:06:23 +07:00
fa781a537d checkers: migrate to checker-sdk-go v1.2.0 []CheckState signature
Rules now return []CheckState, the engine stamps RuleName from the rule,
and the HTTP rule-result lookup matches on RuleName rather than Code.
domain_contact emits one state per role (Subject) instead of a
concatenated single-state message.
2026-04-28 13:06:23 +07:00