What lens checks
For any domain, lens calls five backends in parallel and aggregates the results:
| Section | Tool | What it measures | Weight |
|---|---|---|---|
| IP | ifconfig-rs | Network type, hosting reputation, geolocation | 10% |
| DNS | prism | DNSSEC, CAA records, NS delegation, HTTPS records | 20% |
| TLS | tlsight | Certificate chain, protocol version, cipher strength | 35% |
| HTTP | spectra | Security headers, HSTS, CORS, cookie attributes | 20% |
| beacon | SPF, DKIM, DMARC, MTA-STS, DANE, BIMI, DNSBL | 15% |
HTTP and Email sections are optional. When a backend is not configured, the remaining weights are rebalanced proportionally — a missing section does not penalise the grade.
Scoring algorithm
Each check has a verdict and a weight:
- pass → earns full weight
- warn → earns half weight (rounded down)
- fail / not found → earns zero
- skip → excluded from both earned and possible totals (not a penalty)
A section score is earned / possible × 100%.
The overall score is the weighted average of active section scores.
The letter grade is determined by comparing the overall percentage to
fixed thresholds:
| Grade | Score | Meaning |
|---|---|---|
| A+ | ≥ 97% | Exemplary — all checks pass |
| A | ≥ 90% | Excellent — minor gaps only |
| B | ≥ 75% | Good — some non-critical findings |
| C | ≥ 60% | Fair — notable gaps, action recommended |
| D | ≥ 40% | Poor — significant issues present |
| F | < 40% or hard-fail | Failing |
Hard failures
Two TLS conditions force the grade to F regardless of the numeric score:
- Untrusted chain — the certificate is not signed by a trusted CA. Browsers will show a security warning.
- Expired certificate — any certificate in the chain is past its validity period.
These conditions indicate an immediately broken service, so the score is irrelevant. All other findings contribute to the numeric score rather than triggering a hard override.
Email security: sending vs. receiving
Email security has two distinct concerns that apply to different domains:
-
Sending identity — SPF, DKIM, and DMARC.
These apply to every domain, because any domain can be spoofed in
the
From:header of an email, even domains that never send mail. A parked domain with no MX records still needs SPF and DMARC to prevent impersonation. - Receiving infrastructure — MX records, MTA-STS, DANE/TLSA, TLS-RPT, BIMI, FCrDNS, and DNSBL reputation. These only apply to domains that actively receive email. A parked or web-only domain with no MX records has no receiving infrastructure to check.
Lens reflects this split in four scored buckets within the Email section:
| Bucket | Checks | Applies to | Weight |
|---|---|---|---|
| Authentication | SPF, DKIM, DMARC | All domains | 10 |
| Infrastructure | MX, FCrDNS, DNSBL | Domains with MX records | 5 |
| Transport | MTA-STS, TLS-RPT, DANE | Domains with MX records | 5 |
| Brand Policy | BIMI, DMARC enforcement | Domains with MX records | 2 |
When lens detects no MX records, the three receiving buckets are marked
not applicable and use a skip verdict.
Skip verdicts are excluded from both earned and possible totals, so a
parked domain is not penalised for missing receiving infrastructure.
Only the Authentication bucket (weight 10) is scored.
Worked example: mail domain vs. parked domain
Mail domain (MX records present)
example.com runs email. All four buckets are scored.
SPF, DKIM, and DMARC pass; MTA-STS is missing (warn); everything else passes.
- Authentication: 10/10 (SPF, DKIM, DMARC all pass)
- Infrastructure: 5/5 (MX, FCrDNS, DNSBL all pass)
- Transport: 2.5/5 (MTA-STS warns, others pass)
- Brand Policy: 2/2
- Email section: 19.5/22 = 88.6% → grade B
Parked domain (no MX records)
parked.example.com is used only for web. No MX records exist.
Infrastructure, Transport, and Brand Policy are skipped automatically.
- Authentication: 10/10 (SPF and DMARC both pass — mandatory even for parked domains)
- Infrastructure: skipped (no MX)
- Transport: skipped (no MX)
- Brand Policy: skipped (no MX)
- Email section: 10/10 = 100% → grade A+
A parked domain with correct SPF and DMARC records scores A+ on the email section — the absence of receiving infrastructure is not a deficiency.
Testing DKIM selectors
By default, beacon uses a built-in map of known DKIM selectors for major
email providers (Google Workspace, Microsoft 365, etc.). For custom selectors,
pass them via the dkim_selectors parameter:
# Via URL parameter
https://lens.netray.info/?d=example.com&dkim_selectors=google,selector1
# Via curl
curl -s 'https://lens.netray.info/api/check/example.com?stream=false&dkim_selectors=default,s1' \
| jq '.email.checks[] | select(.name == "email_authentication")'
Selector validation rules:
- Characters:
[a-zA-Z0-9-]only - Length: 1–63 characters per selector
- Count: 1–10 selectors per request
- Separator: comma (whitespace around commas is trimmed)
Invalid selectors return HTTP 400 before any backend call.
Absent dkim_selectors parameter: beacon uses its built-in provider map.
Section states
A section can be in one of three states:
| State | Effect on scoring | When it occurs |
|---|---|---|
| Scored | Contributes to the overall weighted average | Backend responded successfully |
| Errored | Excluded from scoring silently | Backend unreachable or timed out |
| Not applicable | Excluded from scoring; reason recorded in summary.not_applicable |
Email backend timed out internally (grade: "Skipped") |
An errored section does not lower the grade — the overall score is computed
from whatever sections are available. A footnote appears in the UI when
not_applicable is non-empty.
Cache behaviour
Results are cached per domain for 5 minutes. The X-Cache response
header indicates HIT or MISS.
Requests with dkim_selectors always bypass the cache, since
selector-specific results cannot be shared.