Cooperative email for PDS operators
Go 99.7%
Dockerfile 0.3%
5 1 0

Clone this repository

https://tangled.org/scottlanoue.com/atmosphere-mail https://tangled.org/did:plc:dy67wyyakm7u4v2lthy5zwbn/atmosphere-mail
git@tangled.org:scottlanoue.com/atmosphere-mail git@tangled.org:did:plc:dy67wyyakm7u4v2lthy5zwbn/atmosphere-mail

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

Atmosphere Mail#

A cooperative email reputation layer for atproto. PDS operators pool sending volume and reputation through shared infrastructure, verified by a labeling service that checks DNS configuration and publishes signed attestations to the atproto network.

Starting with transactional email (verification codes, password resets, notifications) to build collective domain reputation, with the long-term goal of making self-hosted personal email viable again.

See atmosphere-mail-vision.md for the full proposal.

Status#

Phase 0: labeler service (local development, not yet deployed).

How it works#

  1. A PDS operator publishes an email.atmos.attestation record declaring their mail domain and DKIM selectors
  2. The labeler watches the atproto firehose (via Jetstream) for these records
  3. For each attestation, it verifies:
    • Domain control — the operator's DID handle matches the domain, or a _atproto.<domain> TXT record points to the DID
    • MX — at least one MX record exists
    • SPF — a v=spf1 record exists without +all
    • DKIM — every declared selector has a v=DKIM1 record
    • DMARC — a record exists at _dmarc.<domain> with policy quarantine or reject
  4. If all checks pass, the labeler signs and publishes verified-mail-operator (and optionally relay-member) labels on the operator's DID
  5. Labels are queryable via standard atproto XRPC endpoints (com.atproto.label.queryLabels, com.atproto.label.subscribeLabels)
  6. A scheduler re-verifies every 24 hours and negates labels if DNS degrades

Lexicons#

The NSID namespace is email.atmos.*, backed by atmos.email. See lexicons/README.md for schemas and open questions.

Building#

go build ./cmd/labeler

Running#

Initialize state directory and signing key:

./labeler -init

This creates ./state/config.json (if missing) and generates a secp256k1 signing key. The labeler's did:key is printed to stdout.

Start the labeler:

./labeler -config ./state/config.json

Configuration#

Copy config.json.example to ./state/config.json. All fields have defaults:

Field Default Description
listenAddr :8081 XRPC server bind address
stateDir ./state SQLite database and key storage
jetstreamURL wss://jetstream1.us-east.bsky.network/subscribe Jetstream endpoint
signingKeyPath ./state/signing.key secp256k1 private key (hex)
reverifyInterval 24h Re-verification frequency

Config files support comments and trailing commas (hujson).

Testing#

go test ./...

Docker#

docker build -t atmosphere-mail-labeler .
docker run -v ./state:/app/state -p 8081:8081 atmosphere-mail-labeler

Architecture#

cmd/labeler/          Entry point, wiring, graceful shutdown
internal/
  config/             hujson config loading
  store/              SQLite persistence (labels, attestations, cursor)
  label/signer        CBOR + secp256k1 label signing, did:key derivation
  label/manager       Verification orchestration, label create/negate
  dns/                MX, SPF, DKIM, DMARC verification (injectable resolver)
  domain/             DID-to-domain control verification
  server/             queryLabels (HTTP) + subscribeLabels (WebSocket)
  jetstream/          Firehose consumer with collection filtering
  scheduler/          Periodic re-verification

License#

MIT

Author#

Scott Lanoue (@scottlanoue.com)