burrow/docs/FORWARDEMAIL.md
Conrad Kramer 03415e579b
Some checks failed
Build Rust / Cargo Test (push) Waiting to run
Build Site / Next.js Build (push) Waiting to run
Build Apple / Build App (iOS Simulator) (push) Failing after 52s
Build Apple / Build App (macOS) (push) Failing after 1m1s
Rotate operator secrets into agenix and deepen caches
2026-03-19 00:28:18 -07:00

104 lines
4.2 KiB
Markdown

# Forward Email Backups
Burrow's mail direction is hosted mail on [Forward Email](https://forwardemail.net/), with domain-owned backup retention in our own S3-compatible object storage.
This is the first mail path to operationalize for `burrow.net` and `burrow.rs`. It keeps SMTP/IMAP hosting off the first forge host while still giving Burrow control over backup retention and object ownership.
## What Forward Email Requires
Forward Email exposes custom backup storage per domain. The documented API shape is:
- `PUT /v1/domains/{domain}` with:
- `has_custom_s3=true`
- `s3_endpoint`
- `s3_access_key_id`
- `s3_secret_access_key`
- `s3_region`
- `s3_bucket`
- `POST /v1/domains/{domain}/test-s3-connection`
Forward Email also documents these operational constraints:
- the bucket must remain private
- credentials are validated with `HeadBucket`
- failed or public-bucket configurations fall back to Forward Email's default storage and notify domain administrators
- custom S3 keeps every backup version, so lifecycle expiration is our responsibility
## Burrow Secret Layout
Authoritative secrets now live in:
- `secrets/forwardemail/api-token.age`
- `secrets/forwardemail/hetzner-s3-user.age`
- `secrets/forwardemail/hetzner-s3-secret.age`
Legacy plaintext `intake/` files may still exist locally for debugging, but the
tooling now prefers the age-encrypted files above.
- Hetzner public S3 endpoint for Forward Email: `https://hel1.your-objectstorage.com`
- Hetzner object storage region: `hel1`
- Hetzner bucket used for Forward Email backups: `burrow`
## Verified Storage State
As of March 15, 2026, Burrow's Forward Email custom S3 configuration is live:
- endpoint: `https://hel1.your-objectstorage.com`
- region: `hel1`
- bucket: `burrow`
- `burrow.net` has `has_custom_s3=true`
- `burrow.rs` has `has_custom_s3=true`
- Forward Email's `/test-s3-connection` succeeded for both domains
- the `burrow` bucket enforces lifecycle expiration after `90` days
Forward Email performs bucket validation with bucket-style addressing. For Hetzner Object Storage, this means the working endpoint is the regional S3 endpoint (`https://hel1.your-objectstorage.com`), not the account alias (`https://burrow.hel1.your-objectstorage.com`). Using the account alias causes TLS hostname mismatches when the vendor prepends the bucket name.
## Helper
Use [`Tools/forwardemail-custom-s3.sh`](../Tools/forwardemail-custom-s3.sh) to configure or retest the domain setting without putting secrets on the process list.
Use [`Tools/forwardemail-hetzner-storage.py`](../Tools/forwardemail-hetzner-storage.py) to ensure the Hetzner backup bucket exists and to apply lifecycle expiry before enabling custom S3 on the Forward Email side.
Bucket bootstrap example:
```sh
Tools/forwardemail-hetzner-storage.py \
--endpoint https://hel1.your-objectstorage.com \
--bucket burrow \
--expire-days 90
```
Example:
```sh
Tools/forwardemail-custom-s3.sh \
--domain burrow.net \
--api-token-file secrets/forwardemail/api-token.age \
--s3-endpoint https://hel1.your-objectstorage.com \
--s3-region hel1 \
--s3-bucket burrow \
--s3-access-key-file secrets/forwardemail/hetzner-s3-user.age \
--s3-secret-key-file secrets/forwardemail/hetzner-s3-secret.age
```
Retest an existing domain configuration without rewriting it:
```sh
Tools/forwardemail-custom-s3.sh \
--domain burrow.net \
--api-token-file secrets/forwardemail/api-token.age \
--test-only
```
## Retention
Forward Email preserves every backup object when custom S3 is enabled. Configure lifecycle expiration on the bucket itself. A 30-day or 90-day expiry window is the baseline recommendation from the vendor docs; Burrow should choose explicitly per domain instead of letting the bucket grow without bound. The current Burrow bootstrap helper defaults to `90` days.
## Identity Direction
Hosted mail and SaaS identity are separate concerns:
- mail hosting/backups: Forward Email + Burrow-owned S3-compatible storage
- interactive identity: Authentik as the long-term IdP
- future SaaS SSO target: Linear via SAML once the workspace and plan are ready
This means the forge host does not need to become the first mail server just to give Burrow mailboxes or retention control.