Audit: new-domain saga end to end with relay-only DKIM signing
closedGoal
Trace the full workflow for adding a new domain and check that each step is wired correctly for the target architecture. The architecture went through several rewrites, so some parts may be connected wrongly, half-migrated, or dead.
Target architecture (assumptions to hold the code to)
- Every configured domain sends outbound mail through a relay (SMTP2GO in production; Lettermint also supported).
- Stalwart DKIM signing is off. Only the relay signs. sovrn publishes no DKIM key of its own.
- The owner publishes:
- MX
@ → 10 <cell hostname>(mx[0-9]+.<region>.sovrn.at, plus any backup MX); - the relay’s required records. For SMTP2GO these are CNAME
s<dkim_selector>._domainkey → dkim.smtp2go.netand CNAMEem<rpath_selector> → return.smtp2go.net, with the selectors taken from the relay API.
- MX
Workflow to trace
Check each step for correctness, error handling, retries, idempotency and tests.
- Create a domain (UI
createDomain→appviewprovisionDomainSaga/domain_create.go).- Does the saga register the sending domain with the relay API (
relay.RegisterDomain) as a saga step, with compensation (RemoveDomain) on failure? - Or does registration only happen lazily in the verifier sweep (
verifier.relayReadiness)? If lazy, the setup page shows “records not ready yet” at first. Decide which is intended.
- Does the saga register the sending domain with the relay API (
- Stalwart domain and DKIM.
provision.gocallsdkim.Generateunconditionally and storesDkimSelector/DkimTxton the domain row.- With relay-only signing, this key should not be created. Stalwart should also not be signing with it. Check the Stalwart signing configuration and
relay.ApplyOutbound/MtaRoutesettings. - Check that removal (
dkim.DeleteForDomain) and orphan sweeps still make sense once the key is removed.
- Getting the records.
- Check that
RegisterDomainandCheckDomainreturn the correct names and values for each provider:- SMTP2GO
mapSMTP2GODomain: the fixtures assumedkim_selectoralready includes thesprefix andrpath_selectortheemprefix. Verify this against the real API. Also note the empty-selector fallbacks and that tracker CNAMEs are named at the apex. - Lettermint
toDNSRecord: FQDN vs hostname.
- SMTP2GO
- Check where the records are persisted. Today the verifier stores them in
relay_recordsafterCheckDomain, and the UI fetches them once on demand.
- Check that
- Setup page (
/domains/{id},ui.buildDNS).- With a relay it shows MX plus the relay’s required records by relative name.
- Confirm the non-relay branch (SPF TXT + local DKIM TXT) can be deleted, or is still needed for dev.
- Check the XRPC surface as well:
domain_get_dns_state.goand theapi/sovrnlexicondomaindetail/domaindefsstill exposeDkimSelector/DkimTxt, andappview/dns.gomay build the old record set.
- Verification and activation (
verifiersweep,dnsprober, recheck button).- Relay branch:
t.SPFandt.DkimTxtare now cleared. Activation requires the MX probe, plusCheckDomainreturning verified, plus the relay records viaRelayRecords. - Check whether the relay records are checked by direct DNS lookup (the CNAME targets), by the provider’s verified flags, or both. Decide which is authoritative and what happens when they disagree.
- Check the 15-minute
CheckDomaincache vs the “Check again” button: does a recheck bypass the cache? - Check the transition into
activeand what it triggers (Stalwart domain enable, the relay route).
- Relay branch:
- Ongoing.
- Re-verification or drift detection after activation (the owner changes DNS provider, or the relay revokes the domain).
- Domain removal:
RemoveDomainat the relay,relay_recordscleanup (DeleteDomainshould delete them), and route withdrawal.
Also check
router.gowiring: thatrelayInstreaches every consumer (saga, verifier, UI, webhooks). Check the behaviour when the relay is nil (dev), and whether production can start without a relay.- Config and ansible: the relay credentials, and
sovrn_mail_hostnamebeing the cell hostname. - Tests that still assert the old SPF/DKIM TXT record set as the production path.
Deliverable
A written trace of each step: the file and function, what it does today, and whether it matches the target. Then a list of concrete defects and gaps, each filed as a sub-issue under this one in priority order.
Context
The domain page polish work (relative DNS names, relay CNAMEs on the setup page, relay records persisted by the verifier, and SPF/DKIM TXT dropped from the relay readiness gate) is in the current working copy, not yet committed.
1 Comment
Superseded by 62aba1d (2026-10-08), which restates this audit from its current status after the live run on mx99: what’s verified (domain to active, SMTP2GO selectors, dkim/spf/dmarc pass), the defect found (Stalwart holds an unpublished per-domain DKIM key it marks as used for signing; dkim.Generate still runs in the saga), and the audit questions still open. Closing per the user: consolidate from current status rather than nest more children.