@@ -8,7 +8,7 @@ S05 brings the first real domain surface to shithub: email/password signup, emai |
| 8 | - argon2id password hashing with PHC string encoding. | 8 | - argon2id password hashing with PHC string encoding. |
| 9 | - Strict username whitelist + reserved-name list. | 9 | - Strict username whitelist + reserved-name list. |
| 10 | - Common-password rejection from an embedded SecLists 10k corpus. | 10 | - Common-password rejection from an embedded SecLists 10k corpus. |
| 11 | -- Email Sender abstraction with stdout (dev), SMTP (MailHog/local), and Postmark (prod) backends. | 11 | +- Email Sender abstraction with stdout (dev), SMTP (MailHog/local), Postmark, and Resend backends. |
| 12 | - Counter-based rate-limiting backed by Postgres (no Redis dep). | 12 | - Counter-based rate-limiting backed by Postgres (no Redis dep). |
| 13 | - Auth handlers: signup, login, logout, password reset (request + confirm), email verification, verification resend. | 13 | - Auth handlers: signup, login, logout, password reset (request + confirm), email verification, verification resend. |
| 14 | - Constant-time login: missing usernames still trigger an argon2 hash against a pre-computed dummy. | 14 | - Constant-time login: missing usernames still trigger an argon2 hash against a pre-computed dummy. |
@@ -78,17 +78,18 @@ To refresh: replace `internal/passwords/common_passwords.txt` with a newer SecLi |
| 78 | | 78 | |
| 79 | ## Email service | 79 | ## Email service |
| 80 | | 80 | |
| 81 | -`internal/auth/email` defines the `Sender` interface. Three implementations: | 81 | +`internal/auth/email` defines the `Sender` interface. Four implementations: |
| 82 | | 82 | |
| 83 | - `StdoutSender` — writes a human-readable dump to a writer. Default in dev when no SMTP is configured. Convenient for tests. | 83 | - `StdoutSender` — writes a human-readable dump to a writer. Default in dev when no SMTP is configured. Convenient for tests. |
| 84 | - `SMTPSender` — plain SMTP for MailHog locally. Authenticated and TLS-upgrade variants supported. | 84 | - `SMTPSender` — plain SMTP for MailHog locally. Authenticated and TLS-upgrade variants supported. |
| 85 | -- `PostmarkSender` — Postmark transactional API. Production default. | 85 | +- `PostmarkSender` — Postmark transactional API. |
| | 86 | +- `ResendSender` — Resend transactional API (https://resend.com). Comparable feature set to Postmark; preferred where onboarding speed matters (no human approval queue). |
| 86 | | 87 | |
| 87 | `messages.go` hosts the `VerifyMessage` and `ResetMessage` builders. Both produce HTML + plaintext bodies — every transactional email shithub sends works in plain-text-only clients. Templates are inlined (short, rarely change); when they grow, promote to `templates/email/*.{html,txt}`. | 88 | `messages.go` hosts the `VerifyMessage` and `ResetMessage` builders. Both produce HTML + plaintext bodies — every transactional email shithub sends works in plain-text-only clients. Templates are inlined (short, rarely change); when they grow, promote to `templates/email/*.{html,txt}`. |
| 88 | | 89 | |
| 89 | ### Wiring | 90 | ### Wiring |
| 90 | | 91 | |
| 91 | -`auth.email_backend` chooses the implementation: `stdout | smtp | postmark`. The `smtp` backend additionally requires `auth.smtp.addr`; `postmark` requires `auth.postmark.server_token`. Validation enforces these. | 92 | +`auth.email_backend` chooses the implementation: `stdout | smtp | postmark | resend`. The `smtp` backend additionally requires `auth.smtp.addr`; `postmark` requires `auth.postmark.server_token`; `resend` requires `auth.resend.api_key`. Validation enforces these. |
| 92 | | 93 | |
| 93 | ```sh | 94 | ```sh |
| 94 | # Dev: capture mail in MailHog | 95 | # Dev: capture mail in MailHog |
@@ -179,11 +180,12 @@ All auth settings flow through `internal/infra/config` (see `docs/internal/confi |
| 179 | | `auth.base_url` | string | `http://127.0.0.1:8080` | Used for absolute links in emails. | | 180 | | `auth.base_url` | string | `http://127.0.0.1:8080` | Used for absolute links in emails. | |
| 180 | | `auth.site_name` | string | `shithub` | Branding token for email subjects/bodies. | | 181 | | `auth.site_name` | string | `shithub` | Branding token for email subjects/bodies. | |
| 181 | | `auth.email_from` | string | `shithub <noreply@shithub.local>` | Envelope From for outgoing email. | | 182 | | `auth.email_from` | string | `shithub <noreply@shithub.local>` | Envelope From for outgoing email. | |
| 182 | -| `auth.email_backend` | string | `stdout` | `stdout | smtp | postmark`. | | 183 | +| `auth.email_backend` | string | `stdout` | `stdout | smtp | postmark | resend`. | |
| 183 | | `auth.smtp.addr` | string | `127.0.0.1:1025` | Required when `email_backend=smtp`. | | 184 | | `auth.smtp.addr` | string | `127.0.0.1:1025` | Required when `email_backend=smtp`. | |
| 184 | | `auth.smtp.username` | string | `""` | Optional. | | 185 | | `auth.smtp.username` | string | `""` | Optional. | |
| 185 | | `auth.smtp.password` | string | `""` | Optional. Redacted by `config print`. | | 186 | | `auth.smtp.password` | string | `""` | Optional. Redacted by `config print`. | |
| 186 | | `auth.postmark.server_token` | string | `""` | Required when `email_backend=postmark`. Redacted. | | 187 | | `auth.postmark.server_token` | string | `""` | Required when `email_backend=postmark`. Redacted. | |
| | 188 | +| `auth.resend.api_key` | string | `""` | Required when `email_backend=resend`. Redacted. | |
| 187 | | `auth.argon2.memory_kib` | uint32 | `65536` | argon2id memory cost (KiB). | | 189 | | `auth.argon2.memory_kib` | uint32 | `65536` | argon2id memory cost (KiB). | |
| 188 | | `auth.argon2.time` | uint32 | `3` | argon2id iterations. | | 190 | | `auth.argon2.time` | uint32 | `3` | argon2id iterations. | |
| 189 | | `auth.argon2.threads` | uint8 | `2` | argon2id lanes. | | 191 | | `auth.argon2.threads` | uint8 | `2` | argon2id lanes. | |