atproto pds in zig pds.zat.dev
pds atproto
24

Configure Feed

Select the types of activity you want to include in your feed.

Document Comail mail delivery

zzstoatzz (Jun 12, 2026, 2:50 PM -0500) f78cf8e7 a9a88a30

+118
+1
README.md
··· 33 33 34 34 - [architecture](docs/architecture.md) 35 35 - [account security](docs/account-security.md) 36 + - [comail](docs/comail.md) 36 37 - [development](docs/development.md) 37 38 - [operator guide](docs/operations.md) 38 39 - [invite codes](docs/invite-codes.md)
+114
docs/comail.md
··· 1 + # comail 2 + 3 + ZDS uses [Comail](https://comail.at/) as its default email delivery provider. 4 + Comail is cooperative email infrastructure for atproto identities: a sending 5 + domain is enrolled under an atproto DID, and requests to send mail authenticate 6 + with both an API key and that DID. 7 + 8 + ZDS uses Comail for account and identity email tokens: 9 + 10 + - account email confirmation 11 + - email update confirmation 12 + - PLC operation confirmation 13 + 14 + These emails are user-triggered transactional messages. ZDS sends them through 15 + Comail's HTTP Send API with `category: "verification"`, which tells Comail the 16 + message is part of an auth or verification flow rather than broadcast mail. 17 + 18 + ## identity model 19 + 20 + ZDS keeps the PDS service identity separate from the Comail sending identity. 21 + 22 + - `ZDS_SERVER_DID` is the PDS service DID, usually `did:web:<pds-host>`. 23 + - `ZDS_COMAIL_DID` is the atproto DID that enrolled the sending domain with 24 + Comail. 25 + - `ZDS_EMAIL_FROM` is the bare sender address on that enrolled domain. 26 + 27 + For the hosted `pds.zat.dev` deployment: 28 + 29 + ```sh 30 + ZDS_MAIL_PROVIDER=comail 31 + ZDS_COMAIL_DID=did:plc:mkqt76xvfgxuemlwlx6ruc3w 32 + ZDS_EMAIL_FROM=noreply@zat.dev 33 + ``` 34 + 35 + That means mail is sent as `noreply@zat.dev`, authorized by the `zat.dev` 36 + account DID. It does not mean mail is sent by `did:web:pds.zat.dev`. 37 + 38 + ## configuration 39 + 40 + The Comail provider uses these ZDS settings: 41 + 42 + ```sh 43 + ZDS_MAIL_PROVIDER=comail 44 + ZDS_COMAIL_API_KEY=... 45 + ZDS_COMAIL_DID=did:plc:... 46 + ZDS_EMAIL_FROM=noreply@example.com 47 + ``` 48 + 49 + `ZDS_EMAIL_FROM` must be a bare email address. Comail rejects display-name 50 + forms such as: 51 + 52 + ```sh 53 + ZDS_EMAIL_FROM='ZDS <noreply@example.com>' 54 + ``` 55 + 56 + That form is accepted by some providers, including Resend, but Comail returns 57 + `INVALID_REQUEST`. 58 + 59 + ## request shape 60 + 61 + All provider-specific behavior stays in `src/core/mail.zig`. The rest of ZDS 62 + calls `mail.sendCode(...)` and does not know which provider is active. 63 + 64 + For Comail, ZDS sends: 65 + 66 + ```http 67 + POST https://smtp.atmos.email/v1/send 68 + Authorization: Bearer <ZDS_COMAIL_API_KEY> 69 + X-Atmos-DID: <ZDS_COMAIL_DID> 70 + Content-Type: application/json 71 + ``` 72 + 73 + with JSON shaped like: 74 + 75 + ```json 76 + { 77 + "from": "noreply@example.com", 78 + "to": "user@example.com", 79 + "subject": "Confirm email", 80 + "text": "Hello handle. Your email confirmation code is ABCDE-FGHIJ. This code will expire in ten minutes.", 81 + "category": "verification" 82 + } 83 + ``` 84 + 85 + ZDS does not log successful message bodies or tokens in production. If Comail 86 + returns a non-2xx response, ZDS logs the provider, status code, and a short 87 + response excerpt so operators can diagnose configuration or delivery issues. 88 + 89 + ## local development 90 + 91 + Local development can run without Comail secrets. When `ZDS_PUBLIC_URL` is a 92 + localhost URL and mail config is incomplete, ZDS logs the generated code instead 93 + of sending mail. 94 + 95 + For non-local deployments, incomplete mail config is a hard failure. This is 96 + intentional: a production PDS should not tell clients that verification mail was 97 + sent when the provider was not configured. 98 + 99 + ## operational notes 100 + 101 + - The sending domain must be enrolled in Comail before production delivery will 102 + work. 103 + - New domains start in Comail's warming tiers. Verification emails are low 104 + volume, but repeated manual retries can still consume the early hourly/daily 105 + allowance. 106 + - Keep `ZDS_COMAIL_API_KEY` in deployment secrets, not in committed files. 107 + - If Bluesky reports "Failed to send email", check Fly logs for 108 + `mail delivery failed provider=comail`. 109 + 110 + Useful Comail docs: 111 + 112 + - [HTTP Send API](https://comail.at/docs/send-api) 113 + - [DNS records](https://comail.at/docs/dns) 114 + - [Warming tiers](https://comail.at/docs/warming)
+3
docs/operations.md
··· 90 90 sends it as `X-Atmos-DID`. 91 91 - `ZDS_RESEND_API_KEY`: Resend API key, used only when 92 92 `ZDS_MAIL_PROVIDER=resend`. 93 + 94 + See [comail](comail.md) for the default provider's identity model, request 95 + shape, and production troubleshooting notes. 93 96 - `ZDS_BLOB_UPLOAD_LIMIT`: upload body limit. Default: `100000000`. 94 97 - `ZDS_BLOBSTORE_PATH`: disk blobstore root. 95 98 - `ZDS_CRAWLERS`: comma-separated relay crawl targets.