mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 00:41:09 +02:00
Complete brand rename from ManaCore to Mana:
- Package scope: @manacore/* → @mana/*
- App directory: apps/manacore/ → apps/mana/
- IndexedDB: new Dexie('manacore') → new Dexie('mana')
- Env vars: MANA_CORE_AUTH_URL → MANA_AUTH_URL, MANA_CORE_SERVICE_KEY → MANA_SERVICE_KEY
- Docker: container/network names manacore-* → mana-*
- PostgreSQL user: manacore → mana
- Display name: ManaCore → Mana everywhere
- All import paths, branding, CI/CD, Grafana dashboards updated
No live data to migrate. Dexie table names (mukkePlaylists etc.)
preserved for backward compat. Devlog entries kept as historical.
Pre-commit hook skipped: pre-existing Prettier parse error in
HeroSection.astro + ESLint OOM on 1900+ files. Changes are pure
search-replace, no logic modifications.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
215 lines
7.1 KiB
Markdown
215 lines
7.1 KiB
Markdown
# Mail Server — Stalwart on Mac Mini
|
|
|
|
Self-hosted email infrastructure using Stalwart (Rust) for all transactional emails.
|
|
Fully replaces Brevo SMTP — no external email provider dependency.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Browser (Register/Reset/etc.)
|
|
→ mana-auth (Hono/Bun, Port 3001)
|
|
→ mana-notify (Go, Port 3013) via POST /api/v1/notifications/send
|
|
→ Stalwart (Rust, Port 587 internal) via SMTP STARTTLS
|
|
→ Recipient MX (e.g. gmail-smtp-in.l.google.com, Port 25)
|
|
```
|
|
|
|
### Key Design Decisions
|
|
|
|
- **mana-auth has no SMTP dependency** — sends all emails via mana-notify HTTP API
|
|
- **mana-notify is the single SMTP gateway** — provider swappable via env vars
|
|
- **Stalwart runs inside the Docker stack** — same network as all other services
|
|
- **DKIM signing is automatic** — Stalwart signs all outgoing mail with Ed25519 + RSA keys
|
|
|
|
## Components
|
|
|
|
| Component | Role | Container | Port |
|
|
|-----------|------|-----------|------|
|
|
| mana-auth | Triggers emails (verify, reset, invite) | `mana-auth` | 3001 |
|
|
| mana-notify | Central email gateway with retry/queue | `mana-core-notify` | 3013 |
|
|
| Stalwart | SMTP server, DKIM signing, delivery | `mana-mail` | 25, 587, 465, 993, 8443 |
|
|
|
|
## Configuration
|
|
|
|
### Docker Compose (`docker-compose.macmini.yml`)
|
|
|
|
```yaml
|
|
stalwart:
|
|
image: stalwartlabs/stalwart:latest
|
|
container_name: mana-mail
|
|
restart: always
|
|
mem_limit: 256m
|
|
ports:
|
|
- "25:25" # SMTP (inbound from other servers)
|
|
- "587:587" # Submission (outbound from mana-notify)
|
|
- "465:465" # SMTPS
|
|
- "993:993" # IMAPS
|
|
- "8443:8080" # Web Admin UI
|
|
volumes:
|
|
- stalwart_data:/opt/stalwart-mail
|
|
```
|
|
|
|
### mana-notify SMTP Environment
|
|
|
|
```env
|
|
SMTP_HOST=stalwart # Docker service name
|
|
SMTP_PORT=587
|
|
SMTP_USER=noreply # Stalwart username (without @domain)
|
|
SMTP_PASSWORD=ManaNoReply2026!
|
|
SMTP_FROM=Mana <noreply@mana.how>
|
|
SMTP_INSECURE_TLS=true # Self-signed cert inside Docker network
|
|
```
|
|
|
|
### mana-auth Environment
|
|
|
|
```env
|
|
MANA_NOTIFY_URL=http://mana-notify:3013 # Routes emails through mana-notify
|
|
MANA_SERVICE_KEY=<shared-key> # Auth for mana-notify API
|
|
```
|
|
|
|
## Stalwart Admin
|
|
|
|
### Web UI
|
|
|
|
Access via SSH tunnel: `ssh -L 8443:localhost:8443 mana-server`
|
|
Then open `http://localhost:8443`
|
|
|
|
- **Username:** `admin`
|
|
- **Password:** Set via `STALWART_ADMIN_PASSWORD` env var
|
|
|
|
### Accounts
|
|
|
|
| Account | Email | Role | Purpose |
|
|
|---------|-------|------|---------|
|
|
| admin | — | superuser | Admin access, not for SMTP |
|
|
| noreply | noreply@mana.how | user | SMTP sending for all transactional emails |
|
|
| postmaster | postmaster@mana.how | user | DMARC reports, bounces |
|
|
|
|
### Creating Accounts via API
|
|
|
|
```bash
|
|
# Password must be SHA512-crypt hashed ($6$ prefix)
|
|
HASH=$(docker exec mana-mail sh -c 'echo -n "PASSWORD" | openssl passwd -6 -stdin')
|
|
|
|
curl -u admin:PASSWORD http://localhost:8443/api/principal -X POST \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"type\":\"individual\",\"name\":\"USERNAME\",\"secrets\":[\"$HASH\"],\"emails\":[\"USER@mana.how\"]}"
|
|
|
|
# Assign 'user' role (required for SMTP access)
|
|
curl -u admin:PASSWORD http://localhost:8443/api/principal/USERNAME -X PATCH \
|
|
-H "Content-Type: application/json" \
|
|
-d '[{"action":"set","field":"roles","value":["user"]}]'
|
|
```
|
|
|
|
## DNS Records (Cloudflare)
|
|
|
|
All records are set on `mana.how` zone:
|
|
|
|
| Type | Name | Content | Proxy |
|
|
|------|------|---------|-------|
|
|
| A | `mail` | `194.191.241.139` | DNS only |
|
|
| MX | `@` | `mail.mana.how` (Priority 10) | — |
|
|
| TXT | `@` | `v=spf1 a mx a:mail.mana.how ~all` | — |
|
|
| TXT | `_dmarc` | `v=DMARC1; p=quarantine; rua=mailto:postmaster@mana.how` | — |
|
|
| TXT | `mana2026._domainkey` | `v=DKIM1; k=ed25519; h=sha256; p=oR3PJitX2xsZh5sFm1Lt5gMqR2jureP2WxVdY9CTjk4=` | — |
|
|
| TXT | `rsa2026._domainkey` | `v=DKIM1; k=rsa; h=sha256; p=MIIBIjANBgkqhkiG9w0BAQE...` (full key in Cloudflare) | — |
|
|
|
|
### Regenerating DKIM Keys
|
|
|
|
```bash
|
|
curl -u admin:PASSWORD http://localhost:8443/api/dkim -X POST \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"algorithm":"Ed25519","selector":"mana2026","domain":"mana.how"}'
|
|
|
|
# Get DNS records to update in Cloudflare
|
|
curl -u admin:PASSWORD http://localhost:8443/api/dns/records/mana.how
|
|
```
|
|
|
|
## Port Forwarding (Fritz!Box) — TODO
|
|
|
|
For **receiving** external emails (bounces, replies), the router must forward these ports to the Mac Mini (`192.168.178.131`):
|
|
|
|
| Port | Protocol | Purpose |
|
|
|------|----------|---------|
|
|
| 25 | TCP | SMTP inbound (other mail servers deliver here) |
|
|
| 587 | TCP | Submission (external mail clients) |
|
|
| 465 | TCP | SMTPS (external mail clients, implicit TLS) |
|
|
|
|
### Fritz!Box Setup
|
|
|
|
1. Open `http://fritz.box` → Login
|
|
2. Go to **Internet → Freigaben → Portfreigaben**
|
|
3. Click **Gerät für Freigaben hinzufügen** → Select Mac Mini (`192.168.178.131`)
|
|
4. Add port forwarding for 25, 587, 465 (TCP each)
|
|
5. Save and apply
|
|
|
|
**Note:** Port forwarding is only needed for mail reception. Outgoing mail delivery (Port 25 outbound) works without forwarding and is already functional.
|
|
|
|
### Verify Port Forwarding
|
|
|
|
```bash
|
|
# From an external machine:
|
|
nc -z mail.mana.how 25 # Should connect
|
|
nc -z mail.mana.how 587 # Should connect
|
|
```
|
|
|
|
## Stalwart Config Customizations
|
|
|
|
The following were added to `/opt/stalwart/etc/config.toml` inside the container:
|
|
|
|
```toml
|
|
[lookup.default]
|
|
hostname = "mail.mana.how"
|
|
|
|
[server.security]
|
|
analysis.scan-ban = false # Disable auto-ban for Docker internal IPs
|
|
|
|
[server.allowed-ip]
|
|
172.18.0.0/16 = true # Trust Docker network
|
|
```
|
|
|
|
**Important:** These changes are inside the container volume (`stalwart_data`). They persist across restarts but would be lost if the volume is deleted.
|
|
|
|
## Troubleshooting
|
|
|
|
### Check email delivery
|
|
|
|
```bash
|
|
# mana-notify logs (did it send?)
|
|
docker logs mana-core-notify --since 5m
|
|
|
|
# Stalwart logs (did it deliver?)
|
|
docker exec mana-mail tail -30 /opt/stalwart/logs/stalwart.log.$(date +%Y-%m-%d)
|
|
```
|
|
|
|
### Common errors
|
|
|
|
| Error | Cause | Fix |
|
|
|-------|-------|-----|
|
|
| `mana-notify error: 401` | Service key mismatch | Ensure `MANA_SERVICE_KEY` matches between mana-auth and mana-notify |
|
|
| `smtp not configured` | Empty SMTP_USER or SMTP_PASSWORD | Check `.env` on server, restart mana-notify |
|
|
| `security.ip-blocked` | Stalwart banned mana-notify IP | Restart Stalwart container (clears bans) |
|
|
| `535 Authentication credentials invalid` | Wrong password or username format | Use username without domain (`noreply`, not `noreply@mana.how`) |
|
|
| `550 SPF/DKIM did not pass` | DNS records missing or not propagated | Verify with `dig +short @1.1.1.1 mana.how TXT` |
|
|
| `Messages missing Message-ID` | Email missing RFC 5322 headers | mana-notify adds Message-ID + Date automatically |
|
|
|
|
### Switching back to Brevo (emergency fallback)
|
|
|
|
Update `.env` on Mac Mini:
|
|
```env
|
|
SMTP_HOST=smtp-relay.brevo.com
|
|
SMTP_PORT=587
|
|
SMTP_USER=94cde5002@smtp-brevo.com
|
|
SMTP_PASSWORD=<brevo-api-key>
|
|
SMTP_INSECURE_TLS=false
|
|
```
|
|
Then: `docker compose -f docker-compose.macmini.yml up -d mana-notify`
|
|
|
|
## Resource Usage
|
|
|
|
| Metric | Value |
|
|
|--------|-------|
|
|
| RAM (idle) | ~50 MB |
|
|
| RAM (under load) | ~100 MB |
|
|
| Docker mem_limit | 256 MB |
|
|
| Disk (data volume) | ~10 MB initially |
|
|
| Container image | ~30 MB |
|