Replace the placeholder SMTP configuration on the netcup deployment (/srv/librenotes/.env currently has LIBRENOTES_SMTP_HOST=localhost, port 1025, no creds) with a working transactional SMTP relay so magic-link login works end-to-end at https://ln.cloud.librete.ch/.
Context
librenotes uses magic-link auth only — no password fallback. Without a real SMTP relay, sign-in is non-functional.
internal/auth.SMTPMailer ships with the binary; configuration is purely env-var driven.
Current placeholders cause magic-link emails to be silently dropped (no relay listening).
Options
A reputable transactional provider (Postmark, SES, Mailgun, Brevo, Sendgrid)
Self-hosted relay (Postfix/exim on netcup or another host) — adds a maintenance burden, deferred unless deliberately chosen
Tasks
Pick provider and create credentials
Set up sender domain auth (SPF, DKIM, DMARC) on the envelope domain (e.g. librete.ch if using no-reply@librete.ch, or a dedicated subdomain like mail.librete.ch)
Update /srv/librenotes/.env:
LIBRENOTES_SMTP_HOST=...
LIBRENOTES_SMTP_PORT=587
LIBRENOTES_SMTP_USER=...
LIBRENOTES_SMTP_PASS=...
LIBRENOTES_SMTP_FROM=no-reply@<envelope-domain>
docker compose ... up -d librenotes to pick up the new env (recreate, not just restart)
Smoke-test: trigger a sign-in from https://ln.cloud.librete.ch/, click the link from a real inbox
Acceptance Criteria
A magic-link email lands in a real inbox within ~5s
Clicking the link signs the user in
SPF/DKIM/DMARC pass at the receiving MTA (mail-tester.com score ≥ 9/10)
Sender domain matches LIBRENOTES_SMTP_FROM
Credentials stored only in /srv/librenotes/.env (chmod 600), never committed
Dependencies
None (deployment is live; only config + creds blocking)
## Summary
Replace the placeholder SMTP configuration on the netcup deployment (`/srv/librenotes/.env` currently has `LIBRENOTES_SMTP_HOST=localhost`, port `1025`, no creds) with a working transactional SMTP relay so magic-link login works end-to-end at `https://ln.cloud.librete.ch/`.
## Context
- librenotes uses magic-link auth only — no password fallback. Without a real SMTP relay, sign-in is non-functional.
- `internal/auth.SMTPMailer` ships with the binary; configuration is purely env-var driven.
- Current placeholders cause magic-link emails to be silently dropped (no relay listening).
## Options
- A reputable transactional provider (Postmark, SES, Mailgun, Brevo, Sendgrid)
- Self-hosted relay (Postfix/exim on netcup or another host) — adds a maintenance burden, deferred unless deliberately chosen
## Tasks
- [ ] Pick provider and create credentials
- [ ] Set up sender domain auth (SPF, DKIM, DMARC) on the envelope domain (e.g. `librete.ch` if using `no-reply@librete.ch`, or a dedicated subdomain like `mail.librete.ch`)
- [ ] Update `/srv/librenotes/.env`:
- `LIBRENOTES_SMTP_HOST=...`
- `LIBRENOTES_SMTP_PORT=587`
- `LIBRENOTES_SMTP_USER=...`
- `LIBRENOTES_SMTP_PASS=...`
- `LIBRENOTES_SMTP_FROM=no-reply@<envelope-domain>`
- [ ] `docker compose ... up -d librenotes` to pick up the new env (recreate, not just restart)
- [ ] Smoke-test: trigger a sign-in from `https://ln.cloud.librete.ch/`, click the link from a real inbox
## Acceptance Criteria
- [ ] A magic-link email lands in a real inbox within ~5s
- [ ] Clicking the link signs the user in
- [ ] SPF/DKIM/DMARC pass at the receiving MTA (`mail-tester.com` score ≥ 9/10)
- [ ] Sender domain matches `LIBRENOTES_SMTP_FROM`
- [ ] Credentials stored only in `/srv/librenotes/.env` (chmod 600), never committed
## Dependencies
- None (deployment is live; only config + creds blocking)
librenotes container recreated to pick up the new env. Smoke test: POST /auth/login for mail@librete.ch returned 202; magic link landed in the inbox; clicking the link issued a valid JWT against user id cbd83f40-d082-4935-8eb6-359e2bcffbf5.
Credentials stored in /srv/librenotes/.env (chmod 600), never committed.
Done end-to-end as of 2026-04-29T14:58:57Z.
- Relay: uberspace U7 SMTP (`tuttle.uberspace.de:587`, STARTTLS, SMTP-AUTH user `tengo`).
- Sending domain: `no-reply@librete.ch` (librete.ch already registered via `uberspace mail domain add` and present in `uberspace mail domain list`).
- SPF: `v=spf1 include:spf.uberspace.de ~all` published at INWX (was already in place).
- DKIM: `uberspace._domainkey.librete.ch` TXT published with the U7 shared key (`uberspace records list` returned the same key for librete.ch).
- DMARC: `_dmarc.librete.ch` TXT `v=DMARC1; p=none; rua=mailto:dmarc@librete.ch` added via INWX JSON-RPC, ttl 300.
- librenotes container recreated to pick up the new env. Smoke test: POST `/auth/login` for `mail@librete.ch` returned 202; magic link landed in the inbox; clicking the link issued a valid JWT against user id `cbd83f40-d082-4935-8eb6-359e2bcffbf5`.
Credentials stored in `/srv/librenotes/.env` (chmod 600), never committed.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Replace the placeholder SMTP configuration on the netcup deployment (
/srv/librenotes/.envcurrently hasLIBRENOTES_SMTP_HOST=localhost, port1025, no creds) with a working transactional SMTP relay so magic-link login works end-to-end athttps://ln.cloud.librete.ch/.Context
internal/auth.SMTPMailerships with the binary; configuration is purely env-var driven.Options
Tasks
librete.chif usingno-reply@librete.ch, or a dedicated subdomain likemail.librete.ch)/srv/librenotes/.env:LIBRENOTES_SMTP_HOST=...LIBRENOTES_SMTP_PORT=587LIBRENOTES_SMTP_USER=...LIBRENOTES_SMTP_PASS=...LIBRENOTES_SMTP_FROM=no-reply@<envelope-domain>docker compose ... up -d librenotesto pick up the new env (recreate, not just restart)https://ln.cloud.librete.ch/, click the link from a real inboxAcceptance Criteria
mail-tester.comscore ≥ 9/10)LIBRENOTES_SMTP_FROM/srv/librenotes/.env(chmod 600), never committedDependencies
Done end-to-end as of 2026-04-29T14:58:57Z.
tuttle.uberspace.de:587, STARTTLS, SMTP-AUTH usertengo).no-reply@librete.ch(librete.ch already registered viauberspace mail domain addand present inuberspace mail domain list).v=spf1 include:spf.uberspace.de ~allpublished at INWX (was already in place).uberspace._domainkey.librete.chTXT published with the U7 shared key (uberspace records listreturned the same key for librete.ch)._dmarc.librete.chTXTv=DMARC1; p=none; rua=mailto:dmarc@librete.chadded via INWX JSON-RPC, ttl 300./auth/loginformail@librete.chreturned 202; magic link landed in the inbox; clicking the link issued a valid JWT against user idcbd83f40-d082-4935-8eb6-359e2bcffbf5.Credentials stored in
/srv/librenotes/.env(chmod 600), never committed.