atproto pds in zig
zds docs comail.md
3.6 kB
Markdown
at main

comail #

ZDS uses Comail as its default email delivery provider. Comail is cooperative email infrastructure for atproto identities: a sending domain is enrolled under an atproto DID, and requests to send mail authenticate with both an API key and that DID.

ZDS uses Comail for account and identity email tokens:

  • account email confirmation
  • email update confirmation
  • PLC operation confirmation

These emails are user-triggered transactional messages. ZDS sends them through Comail's HTTP Send API with category: "verification", which tells Comail the message is part of an auth or verification flow rather than broadcast mail.

identity model #

ZDS keeps the PDS service identity separate from the Comail sending identity.

  • ZDS_SERVER_DID is the PDS service DID, usually did:web:<pds-host>.
  • ZDS_COMAIL_DID is the atproto DID that enrolled the sending domain with Comail.
  • ZDS_EMAIL_FROM is the bare sender address on that enrolled domain.

For the hosted pds.zat.dev deployment:

ZDS_MAIL_PROVIDER=comail
ZDS_COMAIL_DID=did:plc:mkqt76xvfgxuemlwlx6ruc3w
ZDS_EMAIL_FROM=noreply@zat.dev

That means mail is sent as noreply@zat.dev, authorized by the zat.dev account DID. It does not mean mail is sent by did:web:pds.zat.dev.

configuration #

The Comail provider uses these ZDS settings:

ZDS_MAIL_PROVIDER=comail
ZDS_COMAIL_API_KEY=...
ZDS_COMAIL_DID=did:plc:...
ZDS_EMAIL_FROM=noreply@example.com

ZDS_EMAIL_FROM must be a bare email address. Comail rejects display-name forms such as:

ZDS_EMAIL_FROM='ZDS <noreply@example.com>'

That form is accepted by some providers, including Resend, but Comail returns INVALID_REQUEST.

request shape #

All provider-specific behavior stays in src/core/mail.zig. The rest of ZDS calls mail.sendCode(...) and does not know which provider is active.

For Comail, ZDS sends:

POST https://smtp.atmos.email/v1/send
Authorization: Bearer <ZDS_COMAIL_API_KEY>
X-Atmos-DID: <ZDS_COMAIL_DID>
Content-Type: application/json

with JSON shaped like:

{
  "from": "noreply@example.com",
  "to": "user@example.com",
  "subject": "Confirm email",
  "text": "Hello handle. Your email confirmation code is ABCDE-FGHIJ. This code will expire in ten minutes.",
  "category": "verification"
}

ZDS does not log successful message bodies or tokens in production. If Comail returns a non-2xx response, ZDS logs the provider, status code, and a short response excerpt so operators can diagnose configuration or delivery issues.

local development #

Local development can run without Comail secrets. When ZDS_PUBLIC_URL is a localhost URL and mail config is incomplete, ZDS logs the generated code instead of sending mail.

For non-local deployments, incomplete mail config is a hard failure. This is intentional: a production PDS should not tell clients that verification mail was sent when the provider was not configured.

operational notes #

  • The sending domain must be enrolled in Comail before production delivery will work.
  • New domains start in Comail's warming tiers. Verification emails are low volume, but repeated manual retries can still consume the early hourly/daily allowance.
  • Keep ZDS_COMAIL_API_KEY in deployment secrets, not in committed files.
  • If Bluesky reports "Failed to send email", check Fly logs for mail delivery failed provider=comail.

Useful Comail docs: