Skip to content
SwornMail

IP reputation is arithmetically dead on IPv6. Let the sender declare the unit.

SwornMail lets a sending operator publish a signed, verifiable claim: this IPv6 prefix is one accountable entity, and we stake our domain name on it.

The problem

One allocation. Eighteen quintillion addresses.

A single IPv6 /64 — the ordinary subnet boundary, the one every SLAAC network uses — contains 18,446,744,073,709,551,616 addresses.

← the entire IPv4 internetone /64 →
Drawn to scale, the whole IPv4 address space would be about one four-millionth of a pixel wide. The hairline on the left is roughly four million times too generous.

Blocklists cannot converge

A sender rotating source addresses never repeats one. Per-address listings never catch up, and per-address lookups approach a 100% cache-miss rate — degrading the blocklist infrastructure itself.

Aggregating is a guess

Aggregate at /64 and you punish shared infrastructure. Aggregate wider and you punish an entire provider for one tenant. There is no correct answer, because the boundary you actually want is invisible.

So IPv6 mail gets treated as suspect

Receivers respond to an unanswerable question with blanket caution. Email has become the last major workload that penalises operators for adopting IPv6 — at a point where IPv6 carries more than half of global internet traffic.

The missing fact

Which addresses constitute one accountable entity is an administrative fact. The operator knows it. The receiver cannot see it. Nothing in the mail stack carries it.

How it works

The operator states the boundary, and signs their name to it.

Two DNS TXT records. Receivers verify at connection time, before message data, and key reputation on (operator domain, prefix) instead of on individual addresses.

Sending operator

mailer.example.com

_prefixes._sworn.mailer.example.com. IN TXT "v=SWORN1; p=2001:db8:f00::/48; u=64"

2026a._sworn.mailer.example.com. IN TXT "v=SWORN1; k=ed25519; pk=…"

  • Declares the prefix it stands behind
  • Declares the unit receivers should aggregate at (u=64)
  • Stakes the domain’s own reputation on both

Receiving MTA

connection from 2001:db8:f00:1234::25

  1. Discover the operator accountable for the source address
  2. Fetch and verify their published policy
  3. Confirm the address really falls inside the attested prefix
  4. Key reputation on 2001:db8:f00:1234::/64

Verification is stateless and O(1) per connection, with no verifier-initiated fetch to attacker-named endpoints beyond DNS.

Two modes

DNS-only. Publish the records; nothing in your mail software changes. Receivers discover the operator from the connecting address. This is deployable today and is the baseline the protocol is designed around.

Signed token. An SMTP extension carries a compact signed token verified statelessly at connection time, for stronger source authenticity. Optional, and it requires software that speaks the extension.

Attestation is accountability, not endorsement. Publishing a record does not ask anyone to trust you. It says who to hold responsible. Receivers and reputation services decide what that is worth — and SwornMail is fail-open by design: its absence or failure must never make treatment worse than a receiver’s existing default for unattested IPv6.

Deploy

Three commands and two DNS records.

Real output from the sworn CLI, abbreviated only where marked. Addresses are from the documentation range 2001:db8::/32.

1. Generate a signing key

$ sworn keygen --selector 2026a

selector    2026a
private key 2026a.key (mode 0600 — keep it secret, back it up)
public key  gJvTSUnyzNPsehUuIhWlLwPOcCRvbiM+fbCLseUpAf0=

2. Generate the records

$ sworn genrecord --domain mailer.example.com --selector 2026a \
      --key 2026a.key --prefix 2001:db8:f00::/48 --unit 64

Publish these records for mailer.example.com.

1. key record — the signing key receivers fetch
   zone file:
     2026a._sworn.mailer.example.com. 3600 IN TXT "v=SWORN1; k=ed25519; pk=gJvTSUnyzNPsehUuIhWlLwPOcCRvbiM+fbCLseUpAf0="

2. policy record — the prefixes you stand behind
   zone file:
     _prefixes._sworn.mailer.example.com. 3600 IN TXT "v=SWORN1; p=2001:db8:f00::/48; u=64; t=y"

3. reverse-tree pointer (optional) — publish in your reverse zone if you
   control it; otherwise discovery uses your MTA's forward-confirmed PTR
     _sworn.0.0.f.0.8.b.d.0.1.0.0.2.ip6.arpa. 3600 IN TXT "v=SWORN1; d=mailer.example.com"

notes:
  - t=y is set, so this is observe-only: receivers report sworn=none policy.testing=y
    and stake no reputation on you, for credit or blame. Watch your traffic, then
    re-run with --testing=false to accept accountability.

The command also prints a DNS-panel form of each record, for providers without zone-file editing, and --json for a provider API.

3. Publish them, then check from anywhere

$ sworn record mailer.example.com --selector 2026a
$ sworn discover --ip 2001:db8:f00:1234::25

What it looks like when it works — and when it does not

$ sworn verify $TOKEN --ip 2001:db8:f00:1234::25 --key gJvTSUn…Af0=
sworn=pass op=mailer.example.com unit=2001:db8:f00:1234::/64
$? 0

# the same token, presented from outside the attested prefix
$ sworn verify $TOKEN --ip 2001:db8:999::25 --key gJvTSUn…Af0=
sworn=fail reason=off_prefix
$? 1

A stolen key alone buys nothing: the attestation is bound to the address space it was issued for, so it cannot be replayed from anywhere else.

Integrations

A Postfix milter and an rspamd module are available for the receiving side. We wrote both; neither implies any involvement by, or endorsement from, the Postfix or rspamd projects.

Before you commit to anything

Publish t=y and stake nothing.

Testing mode is not a flag you have to find. It is what the tooling publishes unless you explicitly turn it off.

An operator in testing mode is reported as sworn=none policy.testing=y. Conforming receivers stake no reputation on them in either direction — not credit, not blame. You can watch how your traffic would be classified without having accepted accountability for anything.

When you are satisfied, re-run genrecord with --testing=false and publish the updated record. That single change is the moment the oath is actually taken, and it is entirely yours to make.

This is enforced, not advisory. A testing-mode operator being reported as passing is treated as a conformance bug in this project — the observe-only on-ramp is worthless if implementations score you anyway.

Relationship to what you already run

It attests the connection, not the message.

SwornMail replaces nothing. SPF, DKIM and DMARC answer questions about a message and the domain it claims. SwornMail answers a question none of them ask.

MechanismWhat it authenticatesThe question it answers
SPFThe sending host, against a domain’s published list“Is this host authorised to send for the domain this message claims?”
DKIMThe message content, cryptographically“Was this message signed by the domain it claims, and unmodified since?”
DMARCAlignment and policy“Do those results line up with the visible From:, and what should I do if not?”
SwornMailThe connection’s address space“Which addresses are one accountable entity, so I have something stable to keep reputation on?”

“Doesn’t SPF already list IPv6 ranges?” It does — and that is a fair objection to raise first. The difference is what the range is for. An SPF range authorises hosts for a domain a message claims, and is only meaningful once you have a claimed identity to check it against. SwornMail’s prefix is an assertion about the address space itself: it gives you a stable reputation key at connection time, including for a sender whose domain you have never seen before, and it names who has volunteered to be held responsible for it.

Adoption

No public deployments yet.

SwornMail is new. We are not going to pretend otherwise on the page where you are deciding whether to believe us.

What does exist

  • A frozen -01 wire format with 62 published conformance vectors
  • Two independent implementations — Go and Rust — cross-checked by a differential harness
  • A sender CLI, a Postfix milter, and an rspamd module, all written by us
  • Apache-2.0, with a patent pledge

What we want

Operator feedback, more than adoption. If you run mail on IPv6 and this is wrong, impractical, or solves a problem you do not have, that is the most useful thing you could tell us right now.

val@sworn.email

Status and limitations

What is settled, and what is not.

Settled

  • The -01 wire format is frozen: token bytes, record syntax, and the conformance vectors are a public contract.
  • 62 conformance vectors (48 token, 14 record), published in the spec repository.
  • Two implementations agree. A differential harness generates an adversarial corpus and runs both verifiers over it — 3,048 cases at the default setting, zero accept/reject or result divergences. A second harness cross-checks record parsing between the Go and Lua implementations across 217 cases, also with zero divergences.
  • Apache-2.0. A provisional patent exists specifically to keep the mechanism open, with a published pledge never to assert it against implementations.

Not settled

  • This is not an IETF standard. It is an Internet-Draft, and one that has not yet been submitted. Nothing here is an RFC and nothing here has IETF consensus.
  • No public deployments. Nobody is running this in production, including us.
  • The SMTP extension mode needs software that speaks the extension; a milter cannot advertise it, because milters run after the command phase.
  • Post-quantum signatures are designed for but not yet registered.

The differential figure above is reproducible rather than a marketing number. The corpus size is a parameter of the harness; run it yourself with a larger fuzz count and the divergence count should stay at zero. If it does not, that is a bug worth reporting.

Get involved

The useful thing you can do is disagree with us.

Especially if you run mail at scale on IPv6, and especially if you think this is wrong.