Deliverability check: each node asks what the internet sees of it #150

Merged
jcoffey-dev merged 1 commits from feat/deliverability into main 2026-10-05 23:29:28 +00:00
Owner

The server side of the deliverability check (inbuxa-drafts specs/deliverability.md, approved 2026-10-05). Admin UX roadmap item 7; the console page follows in inbuxa-admin.

What each sending node checks, about itself

  • Its addresses (DL-1, DL-2): the connection strategy's source addresses, or what its EHLO name resolves to when none are set (production's case: each node leaves from its own namespace). It never asks a third party what its address is.
  • Reverse DNS (DL-5): PTR, whether it resolves back, whether it's the EHLO name.
  • Blocklists (DL-4, DL-6, DL-12): Spamhaus ZEN, SpamCop, Barracuda, UCEPROTECT L1, Mailspike, PSBL for addresses; Spamhaus DBL, SURBL, URIBL for domains. Each list's answer is read by its own code table: a refusal (Spamhaus 127.255.255.254 to a public resolver, URIBL 127.0.0.1) or an undefined code is refused, never listed.
  • Per domain (DL-7 to DL-11): SPF for each address; each DKIM key, by signing a message that's never sent and verifying it as a receiver would (missing vs different); DMARC policy and alignment modes; MTA-STS record, fetched policy, mode, and MX not covered; TLS-RPT present.
  • Certificates (DL-13): EHLO names and the server's MX names that point at the node.

The report holds facts only; the console grades them.

When (DL-14 to DL-16): daily at a minute in the first hour (UTC) that's the node's own; two minutes after start if never run or overdue; lookups 5 s timeout, 8 at once. Check now wakes it on every node through a new BroadcastEvent::DeliverabilityCheck (wire code 14); a node asked again within 10 minutes keeps its report.

API

  • inbuxa:DeliverabilityReport/get (one per node), and /set create = Check now: returns at once with the node's last checkedAt; the console polls. Updates and destroys refused.
  • inbuxa:DeliverabilitySettings/get / /set (update): disabledLists, and the read-only lists (name, zone, scope, lookup link, note, e.g. Barracuda's resolver registration).
  • Tenant administrators get their tenant's domains only, no addresses or certificates (DL-20).

Permissions: sysDeliverabilityGet (685), sysDeliverabilityUpdate (686), sysDeliverabilityCheck (687), in the schema and enums*.rs. Get is in the superuser and tenant defaults, the other two superuser only and always off under a tenant ceiling; all granted once to existing admin roles (Get to tenant admin roles).

Choices against the spec, written back into it: Check now is a fork-owned create on the report rather than an x:Action variant (as the fork's other jobs are, and no hand-edits to generated action code), and it's queued rather than synchronous since a run is dozens of lookups.

Also: privacy catalog entries, a SPEC.md §4 line, settings changes go through the audit log.

Checked locally

  • cargo test --workspace --locked --no-run, cargo build -p inbuxa --locked.
  • Unit tests: inbuxa-features (lists, settings, tenant filter), services (daily slot), plus common, registry, jmap_proto.
  • System tests, --test-threads=1: new deliverability_tests (simulated DNS for every check above, JMAP get/set, Check now, tenant admin limits), security_acceptance_tests, sharing_policy_tests. Run in parallel in one process, two servers collide in the harness; serially all pass.
  • name-check, notice-check, context-check, privacy-check, expr-schema, fork tool unit tests.
  • Only new files are rustfmt'd; existing files keep their formatting.
The server side of the deliverability check (inbuxa-drafts `specs/deliverability.md`, approved 2026-10-05). Admin UX roadmap item 7; the console page follows in inbuxa-admin. **What each sending node checks, about itself** - **Its addresses (DL-1, DL-2):** the connection strategy's source addresses, or what its EHLO name resolves to when none are set (production's case: each node leaves from its own namespace). It never asks a third party what its address is. - **Reverse DNS (DL-5):** PTR, whether it resolves back, whether it's the EHLO name. - **Blocklists (DL-4, DL-6, DL-12):** Spamhaus ZEN, SpamCop, Barracuda, UCEPROTECT L1, Mailspike, PSBL for addresses; Spamhaus DBL, SURBL, URIBL for domains. Each list's answer is read by its own code table: a refusal (Spamhaus `127.255.255.254` to a public resolver, URIBL `127.0.0.1`) or an undefined code is *refused*, never *listed*. - **Per domain (DL-7 to DL-11):** SPF for each address; each DKIM key, by signing a message that's never sent and verifying it as a receiver would (missing vs different); DMARC policy and alignment modes; MTA-STS record, fetched policy, mode, and MX not covered; TLS-RPT present. - **Certificates (DL-13):** EHLO names and the server's MX names that point at the node. The report holds facts only; the console grades them. **When (DL-14 to DL-16):** daily at a minute in the first hour (UTC) that's the node's own; two minutes after start if never run or overdue; lookups 5 s timeout, 8 at once. **Check now** wakes it on every node through a new `BroadcastEvent::DeliverabilityCheck` (wire code 14); a node asked again within 10 minutes keeps its report. **API** - `inbuxa:DeliverabilityReport/get` (one per node), and `/set` create = Check now: returns at once with the node's last `checkedAt`; the console polls. Updates and destroys refused. - `inbuxa:DeliverabilitySettings/get` / `/set` (update): `disabledLists`, and the read-only `lists` (name, zone, scope, lookup link, note, e.g. Barracuda's resolver registration). - Tenant administrators get their tenant's domains only, no addresses or certificates (DL-20). **Permissions:** `sysDeliverabilityGet` (685), `sysDeliverabilityUpdate` (686), `sysDeliverabilityCheck` (687), in the schema and `enums*.rs`. Get is in the superuser and tenant defaults, the other two superuser only and always off under a tenant ceiling; all granted once to existing admin roles (Get to tenant admin roles). **Choices against the spec, written back into it:** Check now is a fork-owned create on the report rather than an `x:Action` variant (as the fork's other jobs are, and no hand-edits to generated action code), and it's queued rather than synchronous since a run is dozens of lookups. **Also:** privacy catalog entries, a SPEC.md §4 line, settings changes go through the audit log. **Checked locally** - `cargo test --workspace --locked --no-run`, `cargo build -p inbuxa --locked`. - Unit tests: inbuxa-features (lists, settings, tenant filter), services (daily slot), plus common, registry, jmap_proto. - System tests, `--test-threads=1`: new `deliverability_tests` (simulated DNS for every check above, JMAP get/set, Check now, tenant admin limits), `security_acceptance_tests`, `sharing_policy_tests`. Run in parallel in one process, two servers collide in the harness; serially all pass. - name-check, notice-check, context-check, privacy-check, expr-schema, fork tool unit tests. - Only new files are rustfmt'd; existing files keep their formatting.
jcoffey-dev added 1 commit 2026-10-05 23:21:56 +00:00
Deliverability check: each node asks what the internet sees of it
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 7m6s
a24ed3b60a
Deliverability spec (inbuxa-drafts specs/deliverability.md), the server
side. Every node that sends mail checks itself once a day, at its own
minute in the first hour (UTC), and when an administrator asks:

- its outgoing addresses (the connection strategy's, or what its EHLO
  name resolves to), their reverse DNS and whether it resolves back,
  and nine blocklists, read by each list's own codes so a refused
  query is never taken for a listing (DL-1 to DL-6);
- for every domain: SPF for each address, each DKIM key (by signing a
  message that's never sent and verifying it as a receiver would),
  DMARC, the MTA-STS policy against the MX, TLS reporting, and the
  domain blocklists (DL-7 to DL-12);
- whether it holds a certificate for its EHLO and MX names (DL-13).

It keeps one report per node, facts only; the console grades them.

- inbuxa:DeliverabilityReport: /get, and a create that asks every node
  to check now, broadcast as DeliverabilityCheck (DL-15). A tenant
  administrator gets their own domains only (DL-20).
- inbuxa:DeliverabilitySettings: which built-in lists are left out, and
  the lists themselves (DL-6).
- sysDeliverabilityGet, sysDeliverabilityUpdate, sysDeliverabilityCheck;
  a tenant ceiling always turns the last two off.
jcoffey-dev merged commit c50d5eb109 into main 2026-10-05 23:29:28 +00:00
jcoffey-dev deleted branch feat/deliverability 2026-10-05 23:29:28 +00:00
Sign in to join this conversation.