EXECUTION RUNTIME: GROK BOT LINUX SANDBOX (/workspace) LIVE TEMPLATES: 15 AVAILABLE (FREE + $19, $29, $39 & $49 · CARD) · ZERO SUBSCRIPTIONS PRIVACY: LOCAL CREDENTIAL VAULT · ZERO DATA EXFILTRATION
● LIVE NOW · $19 · card (ONE-TIME · 1-YEAR LICENSE · 3 REBINDS)

Domain, TLS & Email Auth Hygiene Auditor — Find the expiring cert, the spoofable domain and the dangling CNAME first.

One read-only pass over the domains you own: DNS and DNSSEC (validated by the resolver AD bit), the live TLS certificate chain, expiry and CAA, SPF expanded against the RFC 7208 10-lookup limit, DMARC, DKIM, MTA-STS and TLS-RPT, HTTPS security headers, dangling CNAME takeover risk and Certificate Transparency. Every finding comes with the exact DNS record, Nginx line, vercel.json block or Netlify _headers entry to fix it.

SKU: domain-infra-hygiene · Skills: domain-infra-hygiene-setup + domain-infra-hygiene · Runtime: 1,877 lines stdlib Python · Price: $19.00 USD one-time · card, Apple Pay or Google Pay

⚡ Works in Grok Bot + Cursor + Claude Code + Any Terminal (Zero-Dep Python 3 CLI Included)
See Real Sample Audit View in Homepage Catalog

What It Does

  • DNS & DNSSEC: A/AAAA/CNAME/NS/MX/CAA/TXT/DNSKEY over DNS-over-HTTPS (Cloudflare, Google fallback). DNSSEC is VALIDATED only with a DNSKEY and the AD bit; a DNSKEY without AD, or a SERVFAIL that resolves with checking disabled, is a broken chain (HIGH). Optional --expect-ns catches nameserver drift.
  • Email authentication: SPF expanded recursively and counted against the RFC 7208 10-lookup limit (include, a, mx, ptr, exists, redirect), with loop, +all/?all and missing-include detection; DMARC p/sp/pct/rua; DKIM RSA key size per selector; MTA-STS and TLS-RPT; and v=spf1 -all + p=reject + null MX 0 . for domains that never send mail.
  • TLS & CAA: one handshake on :443 — chain verification, SAN match, protocol (< TLS 1.2 flagged), weak ciphers, expiry windows at 30/14/7 days, and the live issuer cross-checked against your CAA policy.
  • HTTPS, takeover & CT: HSTS (max-age, includeSubDomains), CSP, X-Content-Type-Options, Referrer-Policy, frame protection, Permissions-Policy, the HTTP→HTTPS redirect, dangling-CNAME provider fingerprints on your own apex and www, and Certificate Transparency issuers vs CAA.
  • Remediation & monitoring: copy-paste BIND records, Nginx add_header lines, a vercel.json headers block and a Netlify / Cloudflare Pages _headers file; --state --delta for daily or weekly routines that alert on cert rotation, NS/SPF/DMARC changes, DNSSEC loss, new CT names and header regressions.

How It Runs (6 Steps)

1 · License pre-flight
.wattcoin_license_check.py check --sku domain-infra-hygiene must return "state": "OK" before every run.
2 · Pinned runtime check
Setup verifies the canonical SHA-256 of domain_infra_hygiene.py and bot_common.py against the pinned values; any mismatch stops the run.
3 · DNS & DNSSEC
DoH queries for A/AAAA/CNAME/NS/MX/CAA/TXT/DNSKEY, AD-bit validation, NS drift, CAA, dangling CNAMEs.
4 · Email authentication
Recursive SPF with 10-lookup counting, DMARC, DKIM key size, MTA-STS, TLS-RPT, no-mail null-MX checks.
5 · TLS, HTTPS & CT
One :443 handshake (chain, SAN, protocol, cipher, expiry vs CAA), one HTTPS GET for headers, one :80 GET for the redirect, CT issuers vs CAA.
6 · Report, remediate, monitor
Severity-ranked findings, copy-paste DNS / Nginx / Vercel / Netlify blocks, --state --delta events and fail-closed exit codes.

Live audit: botappstore.com and coinloader.com

Sample disclosure: this is a representative sample, not a customer report. It was produced against public or fictional test targets; account and credential values are placeholders or redacted (for example wck_<REDACTED>), and no private keys, seed phrases or secrets are ever shown. Your own report will differ in content and length.

Unedited --format markdown output of /usr/bin/python3 -I /workspace/scripts/domain_infra_hygiene.py botappstore.com coinloader.com --format markdown on 2026-10-27 (exit 1: MEDIUM findings at the default --fail-on medium). Port 80 shows UNVERIFIED because the sandbox blocks outbound :80, which the report states rather than guessing.

# Domain, TLS & Email Auth Hygiene Report

```
snapshot   2026-10-27 20:04:30 UTC | tool=domain-infra-hygiene
endpoints  api.certspotter.com, cloudflare-dns.com, http:80, https:443, tls:443
```

**Domains:** 2 | **Worst:** MEDIUM | Critical 0 · High 0 · Medium 7 · Low 12 · Info 5 | **Exit code:** 1 (fail-on=medium)

| Domain | Worst | Crit | High | Med | Low | Info | DNSSEC | Mail | TLS expires | HSTS |
|---|---|---:|---:|---:|---:|---:|---|---|---|---|
| `botappstore.com` | MEDIUM | 0 | 0 | 3 | 6 | 3 | VALIDATED | no mail | 2027-01-23 (87d) | max-age=63072000 |
| `coinloader.com` | MEDIUM | 0 | 0 | 4 | 6 | 2 | UNSIGNED | no mail | 2027-01-23 (87d) | max-age=63072000 |

## `botappstore.com`

### DNS

| Record | Value |
|---|---|
| A | 216.198.79.1 |
| AAAA | - |
| NS | ns-cloud-e1.googledomains.com, ns-cloud-e2.googledomains.com, ns-cloud-e3.googledomains.com, ns-cloud-e4.googledomains.com |
| MX | - |
| CAA | - |
| DNSKEY / AD | 3 / True (VALIDATED) |
| CNAME `www.botappstore.com` | d3b71d5b846c057b.vercel-dns-017.com (status 0, resolves yes) |

### Email authentication

| Check | Result |
|---|---|
| Sends mail | no |
| SPF | none |
| DMARC | none |
| DKIM | - (tried: default,google,selector1,selector2,k1,s1,s2,mail,dkim) |
| MTA-STS | - |
| TLS-RPT | - |

### TLS (:443)

| Field | Value |
|---|---|
| Protocol / cipher | TLSv1.3 / TLS_AES_128_GCM_SHA256 (128-bit) |
| Issuer | Let's Encrypt (YR1) |
| Subject CN | botappstore.com |
| Valid | 2026-10-25T18:49:02Z → 2027-01-23T18:49:01Z (87 days left) |
| Verified | yes |
| Serial | `052B27E3DE4DE0A54400DC114CE04F93737B` |
| SHA-256 | `5f2c20a8d0679eb3929c16c9747e52b583728be9973510d5b50f167f28a1cbc7` |
| SAN | botappstore.com, www.botappstore.com |

### HTTPS headers & port 80

| Header | Value |
|---|---|
| `strict-transport-security` | max-age=63072000 |
| `content-security-policy` | **missing** |
| `x-content-type-options` | **missing** |
| `referrer-policy` | **missing** |
| `x-frame-options` | **missing** |
| `permissions-policy` | **missing** |
| port 80 | UNVERIFIED (outbound port 80 unreachable/blocked) |

### Certificate Transparency

Source: certspotter · status **OK** · issuances 1 · issuers Let's Encrypt · names: `botappstore.com`, `www.botappstore.com`

### Findings

| Severity | Code | Finding |
|---|---|---|
| MEDIUM | `CSP_MISSING` | no Content-Security-Policy header (XSS / injection blast radius unbounded) |
| MEDIUM | `DMARC_MISSING` | no _dmarc record; domain sends no mail: publish 'v=DMARC1; p=reject;' |
| MEDIUM | `SPF_MISSING` | no SPF record; domain sends no mail: publish 'v=spf1 -all' to block spoofing |
| LOW | `CAA_MISSING` | no CAA record: any public CA may issue certificates for this domain |
| LOW | `FRAME_PROTECTION_MISSING` | no X-Frame-Options and no CSP frame-ancestors (clickjacking) |
| LOW | `HSTS_NO_INCLUDESUBDOMAINS` | HSTS lacks includeSubDomains: subdomains can still be downgraded |
| LOW | `PERMISSIONS_POLICY_MISSING` | no Permissions-Policy header |
| LOW | `REFERRER_POLICY_MISSING` | no Referrer-Policy header |
| LOW | `XCTO_MISSING` | X-Content-Type-Options is not 'nosniff' |
| INFO | `DNSSEC_VALIDATED` | DNSSEC validated (DNSKEY present, AD=true) |
| INFO | `MX_MISSING` | domain has no MX record; if it never receives mail publish RFC 7505 null MX '0 .' |
| INFO | `TLS_CERT_VALID` | certificate valid for 87 more days (expires 2027-01-23) |

### Copy-paste remediation

DNS zone records (BIND syntax; in Cloudflare/Route 53 enter name, type and value):

```dns
botappstore.com. 3600 IN TXT "v=spf1 -all"
_dmarc.botappstore.com. 3600 IN TXT "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s"
botappstore.com. 3600 IN MX 0 .
botappstore.com. 3600 IN CAA 0 issue "letsencrypt.org"
botappstore.com. 3600 IN CAA 0 issuewild ";"
botappstore.com. 3600 IN CAA 0 iodef "mailto:security@botappstore.com"
```

Nginx (`server {}` block):

```nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;
```

Vercel (`vercel.json`):

```json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Strict-Transport-Security",
          "value": "max-age=63072000; includeSubDomains; preload"
        },
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests"
        },
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "Referrer-Policy",
          "value": "strict-origin-when-cross-origin"
        },
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "Permissions-Policy",
          "value": "camera=(), microphone=(), geolocation=(), payment=(), usb=()"
        }
      ]
    }
  ]
}
```

Netlify / Cloudflare Pages (`_headers`):

```text
/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  Content-Security-Policy: default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  X-Frame-Options: DENY
  Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()
```

- CAA: list every CA your CDN/host uses (check its docs) before publishing, or renewals will fail.
- CSP: the starter policy blocks third-party scripts/styles; ship it first as Content-Security-Policy-Report-Only, add the origins you need, then enforce.
- HSTS: includeSubDomains/preload commit EVERY subdomain to HTTPS for the max-age; confirm none serve plain HTTP before adding them (drop 'preload' unless you will submit to hstspreload.org).

## `coinloader.com`

### DNS

| Record | Value |
|---|---|
| A | 216.198.79.1 |
| AAAA | - |
| NS | ns-cloud-d1.googledomains.com, ns-cloud-d2.googledomains.com, ns-cloud-d3.googledomains.com, ns-cloud-d4.googledomains.com |
| MX | - |
| CAA | - |
| DNSKEY / AD | 0 / False (UNSIGNED) |
| CNAME `www.coinloader.com` | 4fadf6ff311d808b.vercel-dns-017.com (status 0, resolves yes) |

### Email authentication

| Check | Result |
|---|---|
| Sends mail | no |
| SPF | none |
| DMARC | none |
| DKIM | - (tried: default,google,selector1,selector2,k1,s1,s2,mail,dkim) |
| MTA-STS | - |
| TLS-RPT | - |

### TLS (:443)

| Field | Value |
|---|---|
| Protocol / cipher | TLSv1.3 / TLS_AES_128_GCM_SHA256 (128-bit) |
| Issuer | Let's Encrypt (YR1) |
| Subject CN | coinloader.com |
| Valid | 2026-10-25T18:39:39Z → 2027-01-23T18:39:38Z (87 days left) |
| Verified | yes |
| Serial | `062EC72524AD6E41A2A97E1CC25618826213` |
| SHA-256 | `d29f5f52f61f4f0e9f32617f3087885b81bbcdbd95a0b452a534c80dbc75ff4e` |
| SAN | coinloader.com, www.coinloader.com |

### HTTPS headers & port 80

| Header | Value |
|---|---|
| `strict-transport-security` | max-age=63072000 |
| `content-security-policy` | **missing** |
| `x-content-type-options` | **missing** |
| `referrer-policy` | **missing** |
| `x-frame-options` | **missing** |
| `permissions-policy` | **missing** |
| port 80 | UNVERIFIED (outbound port 80 unreachable/blocked) |

### Certificate Transparency

Source: certspotter · status **OK** · issuances 5 · issuers Let's Encrypt · names: `coinloader.com`, `www.coinloader.com`

### Findings

| Severity | Code | Finding |
|---|---|---|
| MEDIUM | `CSP_MISSING` | no Content-Security-Policy header (XSS / injection blast radius unbounded) |
| MEDIUM | `DMARC_MISSING` | no _dmarc record; domain sends no mail: publish 'v=DMARC1; p=reject;' |
| MEDIUM | `DNSSEC_NOT_ENABLED` | DNSSEC not enabled (no DNSKEY at apex): DNS answers can be spoofed in transit |
| MEDIUM | `SPF_MISSING` | no SPF record; domain sends no mail: publish 'v=spf1 -all' to block spoofing |
| LOW | `CAA_MISSING` | no CAA record: any public CA may issue certificates for this domain |
| LOW | `FRAME_PROTECTION_MISSING` | no X-Frame-Options and no CSP frame-ancestors (clickjacking) |
| LOW | `HSTS_NO_INCLUDESUBDOMAINS` | HSTS lacks includeSubDomains: subdomains can still be downgraded |
| LOW | `PERMISSIONS_POLICY_MISSING` | no Permissions-Policy header |
| LOW | `REFERRER_POLICY_MISSING` | no Referrer-Policy header |
| LOW | `XCTO_MISSING` | X-Content-Type-Options is not 'nosniff' |
| INFO | `MX_MISSING` | domain has no MX record; if it never receives mail publish RFC 7505 null MX '0 .' |
| INFO | `TLS_CERT_VALID` | certificate valid for 87 more days (expires 2027-01-23) |

### Copy-paste remediation

DNS zone records (BIND syntax; in Cloudflare/Route 53 enter name, type and value):

```dns
coinloader.com. 3600 IN TXT "v=spf1 -all"
_dmarc.coinloader.com. 3600 IN TXT "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s"
coinloader.com. 3600 IN MX 0 .
coinloader.com. 3600 IN CAA 0 issue "letsencrypt.org"
coinloader.com. 3600 IN CAA 0 issuewild ";"
coinloader.com. 3600 IN CAA 0 iodef "mailto:security@coinloader.com"
```

Nginx (`server {}` block):

```nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;
```

Vercel (`vercel.json`):

```json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Strict-Transport-Security",
          "value": "max-age=63072000; includeSubDomains; preload"
        },
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests"
        },
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "Referrer-Policy",
          "value": "strict-origin-when-cross-origin"
        },
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "Permissions-Policy",
          "value": "camera=(), microphone=(), geolocation=(), payment=(), usb=()"
        }
      ]
    }
  ]
}
```

Netlify / Cloudflare Pages (`_headers`):

```text
/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  Content-Security-Policy: default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  X-Frame-Options: DENY
  Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()
```

- CAA: list every CA your CDN/host uses (check its docs) before publishing, or renewals will fail.
- DNSSEC: enable signing at your DNS host, then publish the DS record it shows at your registrar (Cloudflare: DNS > Settings > DNSSEC; Route 53: Hosted zone > DNSSEC signing).
- CSP: the starter policy blocks third-party scripts/styles; ship it first as Content-Security-Policy-Report-Only, add the origins you need, then enforce.
- HSTS: includeSubDomains/preload commit EVERY subdomain to HTTPS for the max-age; confirm none serve plain HTTP before adding them (drop 'preload' unless you will submit to hstspreload.org).

---

SCOPE_NOTE: Passive, read-only public posture inspection for domains the user owns or is authorised to assess: DNS-over-HTTPS lookups (Cloudflare, Google fallback), one TLS handshake on :443, one HTTPS GET for response headers, one plain-HTTP GET on :80 for the redirect check, and public Certificate Transparency logs (Cert Spotter, crt.sh fallback). No port scanning, brute-forcing, fuzzing, subdomain enumeration beyond public CT logs, mail-server or credential probing, and no exploitation; the dangling-CNAME check only inspects the audited domain's own records against known provider fingerprints and never claims or registers anything. Results are point-in-time: DNS caches, CT log lag, CDN edge variation and resolver behaviour can differ by location and minute; DKIM presence is only confirmed for the selectors tried; an empty crt.sh answer is INCONCLUSIVE, not clean. The HTTP->HTTPS redirect is reported UNVERIFIED when outbound port 80 is blocked (common in sandboxes and corporate networks). Remediation records are templates to review before publishing. Not a penetration test, compliance attestation, or legal advice. Refuses: scanning domains the user does not control, subdomain-takeover hunting against third parties, and credential or mail-server probing.

CLI Flags & Exit Codes

FlagMeaning
DOMAIN ...Domains you own or are authorised to assess (max 50; URLs reduced to host; IPs and special-use names rejected).
--domains-file PATHRead more domains from a file (whitespace separated, # comments).
--dkim-selector SEL[,SEL]DKIM selectors to probe (default: default, google, selector1, selector2, k1, s1, s2, mail, dkim).
--no-mailAssert the domain never sends mail: expect v=spf1 -all, p=reject and null MX 0 ..
--no-httpSkip the HTTPS header GET and the port-80 redirect check.
--no-tlsSkip the TLS handshake on :443.
--ct / --no-ctQuery (default) or skip Certificate Transparency.
--expect-ns NS[,NS]Expected nameservers; any mismatch is NS_DRIFT (HIGH).
--state PATH --deltaPersist snapshots and report events / new / changed / resolved since the last clean run.
--format {text,markdown,json}Output format (markdown adds the copy-paste remediation blocks).
--jsonShortcut for JSON output.
--out PATHWrite the report to a file instead of stdout.
--fail-on {none,info,low,medium,high,critical}Severity that triggers a non-zero exit (default medium).
--timeout SECONDSPer-request timeout (default 15).
--offline PATHReplay a JSON fixture instead of the network (tests, reproducible runs).
Exit codeMeaning
0Completed; nothing at or above --fail-on.
1Worst finding at the threshold is MEDIUM (or LOW/INFO with --fail-on low/info).
2Worst finding at the threshold is HIGH.
3Worst finding at the threshold is CRITICAL.
10Bad input or refusal (invalid or special-use domain, bad flag, --delta without --state).
11Degraded data: a DoH, TLS, HTTPS or CT source failed with nothing at the threshold. Never a clean pass.

What It Does Not Do

  • Own or authorised domains only. Refuses to scan third-party domains, build target lists, rank domains, or hunt subdomain takeovers against infrastructure you do not control.
  • Passive only. Ordinary public lookups: DoH queries, one TLS handshake on :443, one HTTPS GET, one HTTP GET on :80 and CT log queries. No port scanning, brute-force or wordlist subdomain enumeration, fuzzing, zone transfers, SMTP or credential probing, and no exploitation.
  • Never claims or changes anything. It never registers a dangling CNAME target and never edits DNS, certificates, web servers or CDN settings; remediation records are templates for you to review and publish.
  • No “secure” verdicts. Results are point-in-time and resolver/edge dependent. DKIM is only confirmed for the selectors tried, and UNVERIFIED or INCONCLUSIVE are never passes. Not a penetration test, compliance attestation, or legal advice.

Scope note (printed with every report): passive, read-only public posture inspection for domains the user owns or is authorised to assess; an empty crt.sh answer is INCONCLUSIVE, not clean; the HTTP→HTTPS redirect is reported UNVERIFIED when outbound port 80 is blocked; remediation records are templates to review before publishing.

Pinned Runtime Artifacts (SHA-256)

Runtime URLs are license-gated: send X-License-Key: wcl_... or X-Agent-Key: wck_... (unauthenticated requests get 401). The served file carries a per-license watermark block (5 lines), so compare the X-Canonical-SHA256 response header (or the SHA-256 after stripping the block) against the pins above. The recommended path is the setup skill installer:

python3 /workspace/.wattcoin_license_check.py --sku domain-infra-hygiene --install-runtime ... --expected-sha256 73f7a04a48f55b6a9f0a733bdf4bba7cab2b9b6d245c0505567487585f0d1c14

The setup and audit skills verify both canonical digests before every run and refuse to execute either file on a mismatch. Both are locked at chmod 500.

Technical FAQ

How does the auditor count SPF DNS lookups against the RFC 7208 10-lookup limit?
It parses the apex v=spf1 record and recursively expands every include: and redirect= target, counting each DNS-querying term (include, a, mx, ptr, exists and redirect) exactly as RFC 7208 section 4.6.4 specifies; ip4, ip6 and all cost nothing. More than 10 lookups is SPF_LOOKUP_LIMIT_EXCEEDED (HIGH, receivers return permerror and treat SPF as failed), 8 to 10 is SPF_LOOKUP_NEAR_LIMIT (LOW). Include loops or nesting deeper than 15 hops are SPF_RECURSIVE_LOOP (HIGH), include targets without an SPF record are SPF_INCLUDE_NO_RECORD (MEDIUM), and the lookup count plus the full include tree are printed in the report.
How is DNSSEC validated, and what does the AD bit tell you?
DNSSEC is reported VALIDATED only when the apex publishes a DNSKEY and the validating DNS-over-HTTPS resolver (Cloudflare, with Google as fallback) returns the Authenticated Data (AD) flag, meaning it verified the full chain from the root through your registrar's DS record. No DNSKEY is DNSSEC_NOT_ENABLED (MEDIUM). A DNSKEY with AD=false means a missing or mismatched DS at the registrar and is DNSSEC_BROKEN_CHAIN (HIGH). If the validating resolver SERVFAILs but answers with checking disabled (cd=1), the chain is BOGUS (expired RRSIG or DS/DNSKEY mismatch) and is also flagged HIGH because validating clients cannot resolve the domain.
How do I protect a domain that never sends email from being spoofed?
Run the audit with --no-mail. The auditor then expects the three records that together block spoofing of a parked or web-only domain: an SPF record of exactly v=spf1 -all, a DMARC record with p=reject (the printed template also sets sp=reject, adkim=s and aspf=s), and an RFC 7505 null MX of 0 . so mail servers stop trying to deliver. Anything weaker is flagged: SPF_NO_MAIL_NOT_STRICT, DMARC_NO_MAIL_NOT_REJECT and NO_MAIL_MX_PRESENT. Even without the flag, a domain with no MX is treated as non-sending and the report prints the exact BIND-syntax TXT and MX records to publish.
What does the TLS handshake check, and how is it cross-checked against CAA?
It makes one TLS handshake to port 443 with SNI and full certificate verification, then records protocol, cipher, issuer, serial, SHA-256 fingerprint, SANs and validity. Verification failures and hostname mismatches are CRITICAL; TLS below 1.2 and weak ciphers are HIGH; expiry is CRITICAL under 7 days, HIGH under 14 and MEDIUM under 30. The live issuer is then compared with the domain's CAA issue tags: a certificate from a CA the CAA record does not authorise is TLS_ISSUER_CAA_MISMATCH (HIGH). With no CAA record (CAA_MISSING) it prints CAA issue records for the CAs seen on the live certificate and in CT logs, an iodef contact, and issuewild ";" when no wildcard certificate is in use.
How do Certificate Transparency and dangling CNAME detection work?
Certificate Transparency issuances are read from Cert Spotter (crt.sh as fallback) to list issuers and names; unexpired certificates from a CA not authorised by CAA raise CT_ISSUER_NOT_IN_CAA (MEDIUM), and an empty crt.sh answer is INCONCLUSIVE, never clean. For takeover risk it follows the CNAME chains of the audited domain's own apex and www records only: a chain ending in NXDOMAIN, or pointing at a takeover-prone provider such as GitHub Pages, Heroku, AWS S3, Azure, Netlify or Vercel without any A/AAAA answer, is DANGLING_CNAME_TAKEOVER_RISK (HIGH). It never registers or claims the target and never enumerates third-party subdomains.
How do stateful --state --delta monitoring routines work?
Pass --state /workspace/domain-hygiene/state.json --delta. The first run per domain stores a baseline snapshot of nameservers, SPF, DMARC, DNSSEC status, certificate fingerprint, issuer and expiry, CT names and findings. Later runs report new, changed and resolved findings plus events: CERT_ROTATED, CERT_EXPIRY_WINDOW, NS_CHANGED, SPF_CHANGED, DMARC_CHANGED (MEDIUM when the policy weakens), DNSSEC_LOST (HIGH), NEW_CT_NAMES, NEW_CT_ISSUANCES and HEADER_REGRESSED. Degraded runs (a resolver, TLS or CT source failed) skip the delta and never overwrite the baseline, and exit 11 so an outage is not mistaken for a quiet day. A daily or weekly Grok Bot routine alerts only on events or exit code 11.

Licensing & Setup

$19.00 USD one-time, paid by card, Apple Pay or Google Pay: a 1-year license with up to 3 workspace-reset rebinds and no subscription. On Turn 1 the domain-infra-hygiene-setup skill installs the read-only license helper (0500), verifies and installs the SHA-256-pinned runtime, registers and activates your agent key, binds your wcl_... code through stdin, and offers a live smoke test. The license code is never printed in chat.

One wcl_... key, two runtimes. Your license key works inside Grok Bot and from any local terminal or IDE agent (Cursor, Claude Code, Windsurf, CI/CD) via the standalone single-file Python 3 CLI (/runtime/domain_infra_hygiene.py) — Python 3.10+ standard library only, zero pip install dependencies, with --json / --markdown output.