Skip to content

Fix SendCanary email-setup SPF: use per-domain token variable#947

Merged
pawel-kow merged 1 commit intoDomain-Connect:masterfrom
jotra7:fix-spf-template
Apr 7, 2026
Merged

Fix SendCanary email-setup SPF: use per-domain token variable#947
pawel-kow merged 1 commit intoDomain-Connect:masterfrom
jotra7:fix-spf-template

Conversation

@jotra7
Copy link
Copy Markdown
Contributor

@jotra7 jotra7 commented Apr 6, 2026

Description

Updates the sendcanary.com.email-setup template to use fully variable record targets. No hardcoded infrastructure domains -- all values are passed by the backend at apply time.

Changes:

  • DMARC CNAME: %token%.dmarc.sendcanary.com -> %dmarcTarget%
  • SPF TXT: include:_spf.spoofcanary.com -> %spfRecord%
  • BIMI CNAME: %token%.bimi.sendcanary.com -> %bimiTarget%
  • Version: 1 -> 3

The original version of this template was live and working on Cloudflare. SendCanary operates as a managed DNS service: before applying this template, the backend reads the domain's existing SPF record, extracts all existing mechanisms (includes, ip4, ip6, mx, a) into a managed SPF record on our infrastructure, then passes a single-include SPF value (v=spf1 include:spf-{token}.dns.sendcanary.com ~all) as %spfRecord%. This deliberate takeover preserves the customer's existing senders inside our managed record. Bare TXT is retained over SPFM because the architecture requires full replacement, not merge (approved by @pawel-kow in review).

Type of change

  • New template
  • Bug fix (non-breaking change which fixes an issue in the template)
  • New feature (non-breaking change which adds functionality to the template)
  • Breaking change (fix or feature that would cause existing template behavior to be not backward compatible)

How Has This Been Tested?

  • Template functionality checked using Online Editor
  • Template file name follows the pattern ..json
  • resource URL provided with logoUrl is actually served by a webserver

Checklist of common problems

  • syncPubKeyDomain is set
  • warnPhishing is not set alongside syncPubKeyDomain
  • syncRedirectDomain is set whenever the template uses redirect_uri in the synchronous flow
  • no TXT record contains SPF content -- the SPF TXT record intentionally contains %spfRecord% which resolves to a full v=spf1 ... value. This is a managed DNS takeover pattern: the backend hosts the actual SPF mechanisms on spf-{token}.dns.sendcanary.com and passes a single-include record. Full replacement is required to switch the domain to managed SPF. SPFM was considered but the managed architecture requires replacement, not merge (see PR discussion).
  • txtConflictMatchingMode is set on every TXT record that must be unique per label or content prefix -- v=spf1 prefix match is set on the SPF TXT record
  • no variable is used as a bare full record value -- SPF TXT uses %spfRecord% as the full value; this is required for the managed DNS takeover pattern (backend constructs the full SPF value)
  • no bare variable is used as the full host label
  • no variable is used in the host field to create a subdomain
  • %host% does not appear explicitly in any host attribute
  • essential is set to OnApply on records the end user may need to modify or remove without breaking the template

Online Editor test results

sendcanary.com.email-setup.json:

@github-actions github-actions bot added Checklist of common problems not complete See PR template and mark *all* checkboxes, even if not applicable. Explain any discrepancies. PR description incomplete The PR description template was not filled in at all, altered or filled in improperly. Test links missing No test links from Online Editor provided labels Apr 6, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

JSON Filename Check Passed

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

JSON Schema Validation Passed

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

Linter OK

sendcanary.com.email-setup.json

Level Code Note
info DCTL1021 missing from iana definitions

@jotra7 jotra7 force-pushed the fix-spf-template branch from 7b70349 to 5d5d576 Compare April 6, 2026 00:42
@github-actions github-actions bot removed Checklist of common problems not complete See PR template and mark *all* checkboxes, even if not applicable. Explain any discrepancies. Test links missing No test links from Online Editor provided labels Apr 6, 2026
@jotra7 jotra7 force-pushed the fix-spf-template branch from 5d5d576 to 4dca651 Compare April 6, 2026 00:53
@github-actions github-actions bot added Test links missing No test links from Online Editor provided and removed PR description incomplete The PR description template was not filled in at all, altered or filled in improperly. Test links missing No test links from Online Editor provided labels Apr 6, 2026
@jotra7 jotra7 force-pushed the fix-spf-template branch from 4dca651 to 017b645 Compare April 6, 2026 01:17
@github-actions github-actions bot added Test links missing No test links from Online Editor provided and removed Test links missing No test links from Online Editor provided labels Apr 6, 2026
@jotra7
Copy link
Copy Markdown
Contributor Author

jotra7 commented Apr 6, 2026

Thanks for the review. The reason we use a bare TXT with %spfRecord% instead of SPFM is that SendCanary operates as a managed DNS service for email security.

Our flow works like this:

  1. Before applying this template, our backend reads the domain's existing SPF record
  2. We extract all existing mechanisms (includes, ip4, ip6, mx, a) from it
  3. We create a managed SPF record on our infrastructure (spf-{token}.dns.sendcanary.com) that contains all the customer's original mechanisms
  4. The %spfRecord% value we pass is typically v=spf1 include:spf-{token}.dns.sendcanary.com ~all -- a single include pointing to our managed record that carries everything

This is a deliberate SPF takeover, not an accidental overwrite. The customer's existing senders are preserved inside our managed record, and we handle ongoing SPF management from there.

That said, we understand the concern -- a bare TXT replacing SPF is dangerous in the general case. Would you prefer we switch to SPFM with just include:spf-{token}.dns.sendcanary.com as the spfRules? The result would be additive (our include merged into existing SPF) rather than replacing it. Both approaches are functionally correct for our use case, but SPFM is safer if the template were ever misused.

Happy to change to SPFM if that's the preference.

@jotra7
Copy link
Copy Markdown
Contributor Author

jotra7 commented Apr 6, 2026

We'll keep TXT for this template. The managed SPF architecture (extract existing mechanisms into our hosted record, replace with single include) requires the full replacement that TXT provides. Resolving this thread -- thanks for the review and the flexibility.

@github-actions github-actions bot added Test links missing No test links from Online Editor provided Checklist of common problems not complete See PR template and mark *all* checkboxes, even if not applicable. Explain any discrepancies. PR description incomplete The PR description template was not filled in at all, altered or filled in improperly. and removed Checklist of common problems not complete See PR template and mark *all* checkboxes, even if not applicable. Explain any discrepancies. PR description incomplete The PR description template was not filled in at all, altered or filled in improperly. Test links missing No test links from Online Editor provided labels Apr 6, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

PR Description Check Passed

All required sections are filled in correctly.

Details
  OK  Type of change: 1/4 checkboxes ticked
  OK  How Has This Been Tested?: all 3 checkboxes ticked
  OK  Checklist of common problems: 10/10 checkboxes ticked
  OK  Online Editor test results: 2 link(s) found
  OK  Template coverage: all 1 template(s) covered
Labels to remove: Checklist of common problems not complete, Forged editor links, PR description incomplete, Test links missing

PR description check PASSED

@pawel-kow pawel-kow added this pull request to the merge queue Apr 7, 2026
Merged via the queue into Domain-Connect:master with commit 6f55b46 Apr 7, 2026
12 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants