Skip to content
SwornMail docs

Start

Deploy as an operator

Three commands and two DNS records. Nothing in your mail software changes, and you stake nothing until you decide to.

Output on this page is real, captured by running the CLI. Addresses are from the documentation range 2001:db8::/32. The only edits are truncating one base64 key and eliding a token value.

Get the CLI

$ go install github.com/swornmail/swornmail-go/cmd/sworn@latest

Source: github.com/swornmail/swornmail-go.

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=

The selector is an arbitrary label that lets you rotate keys without downtime — publish a new selector, move traffic to it, then retire the old record. keygen refuses to overwrite an existing key without --force.

2. Generate your 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=gJvTSUn…Af0="

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"

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.

It also prints a DNS-panel form of each record for providers without zone-file editing, and --json for feeding a provider API. Every rule on the records reference is validated before anything is printed, so a mistake surfaces here rather than in production.

You start in testing mode whether you ask to or not. genrecord emits t=y unless you pass --testing=false. Leaving observe-only is a deliberate act.

3. Publish, then check

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

record fetches and lints what you published. discover runs the same Mode 1 discovery a receiver runs, from one of your MTA’s addresses, and tells you which operator and unit it resolves to. Run it from a machine that can resolve public DNS.

What failure looks like

$ 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

Exit codes: 0 pass · 1 fail · 2 permerror or usage · 3 temperror · 4 none. Scriptable.

4. When you are ready, accept accountability

Watch your traffic in testing mode for as long as you like. When the results look right, re-run genrecord with --testing=false and publish the updated policy record. That one change is the moment the oath is taken, and it is entirely yours to make — and to reverse, by republishing t=y.

Verifying as a receiver

If you run inbound mail, see implementations for the Postfix milter and the rspamd module, and reputation semantics for the rules you must follow when acting on a result. The short version: never treat a failure as worse than no attestation at all.