Files
app-catalog-felhom.eu/REPORT.md
T
2026-06-29 11:06:57 +02:00

5.6 KiB

REPORT — App-email rollout: gitea + rallly wired (calcom/nextcloud/immich = findings)

Date: 2026-06-29 Task: Roll out the shipped app-email relay (controller v0.88.0 shim + smtp_mapping; hub v0.18.0 passthrough) to calcom, rallly, gitea, nextcloud (+ investigate immich). Catalog-only; no controller/hub code change.

Baselines (verified live)

  • felhom-controller v0.88.0 + hub v0.18.0 live (relay mechanism shipped). Global app-email toggle ON on guest 9201. Guest RAM 12 GB (raised from 2 GB) — confirmed before deploying heavier apps.

Per-app outcome

App Wired? Shape Self-signed handling From Env consumed on redeploy Live send-test
gitea 1.23.4 STARTTLS (smtp+starttls) FORCE_TRUST_SERVER_CERT=true single GITEA__mailer__FROM Yes (env→app.ini every boot) arrived — password-reset email, From gitea@felhom.eu
rallly 3.11.2 STARTTLS (SMTP_SECURE=false) SMTP_REJECT_UNAUTHORIZED=false single NOREPLY_EMAIL Yes (read at send time) arrived — registration code, From rallly@felhom.eu
cal.com v4.8.7 finding none (hard-coded rejectUnauthorized:true) single EMAIL_FROM n/a not wired
nextcloud 31.0.14 finding none + split From MAIL_FROM_ADDRESS+MAIL_DOMAIN n/a not wired
immich v2.5.5 finding ignoreCert (config-file/UI only) single (config field) n/a not wired

Findings detail: felhom.eu/documentation/audits/FINDING-app-email-rollout-2026-06-29.md.

gitea 1.23.4 — WIRED, send confirmed

  • Mapping (smtp_mapping): host_var=GITEA__mailer__SMTP_ADDR, port_var=GITEA__mailer__SMTP_PORT, security_var=GITEA__mailer__PROTOCOL (smtp+starttls), from_var=GITEA__mailer__FROM, from_local=gitea, extra={GITEA__mailer__ENABLED:true, GITEA__mailer__FORCE_TRUST_SERVER_CERT:true}.
  • Verified live: toggle ON → controller injected all six GITEA__mailer__* env vars (confirmed in the running container) → gitea wrote the correct [mailer] section in app.ini (env→ini every boot). Completed the install (admin = the test inbox) and triggered a password-reset (forgot-password): the shim logged relayed (from="gitea@felhom.eu" rcpts=1 bytes=2195 hub=200), the hub logged /api/v1/mail: relayed. Go net/smtp STARTTLS to the self-signed shim, trusting it via FORCE_TRUST_SERVER_CERT — works.

rallly 3.11.2 — WIRED, send confirmed (+ 2 pre-existing template bugs fixed)

  • Mapping: host_var=SMTP_HOST, port_var=SMTP_PORT, security_var=SMTP_SECURE (false), from_var=NOREPLY_EMAIL, from_name_var=NOREPLY_EMAIL_NAME, from_local=rallly, extra={SMTP_REJECT_UNAUTHORIZED:false}.
  • Verified live: toggle ON → injected env confirmed (SMTP_HOST=felhom-controller, NOREPLY_EMAIL=rallly@felhom.eu, SMTP_SECURE=false, SMTP_REJECT_UNAUTHORIZED=false). Drove the real registration flow in the browser → rallly's Nodemailer sent the verification code: shim logged relayed (from="rallly@felhom.eu" rcpts=1 bytes=12755 hub=200), hub logged /api/v1/mail: relayed. Nodemailer opportunistic STARTTLS to the self-signed shim, accepted via reject-unauthorized=false — works.
  • Side-fixes (pre-existing, undeployable template):
    1. Image pin lukevella/rallly:3.12.1 does not exist (3.x stops at 3.11.x) → corrected to 3.11.2.
    2. Healthcheck used wget, which the rallly image does not ship (exit 127) → container marked unhealthy → Traefik refused to publish a route (the "URL nem elérhető" warning). Replaced with a Node http check. After the fix rallly is healthy and Traefik routes it. (Diagnosis credit: the operator spotted the unhealthy↔no-route correlation.)
    3. rallly requires SUPPORT_EMAIL + a valid NOREPLY_EMAIL at boot (refuses to start otherwise) — added valid defaults so it boots with app-email off; the relay overrides NOREPLY_EMAIL when on.

immich investigation (Task §6)

immich v2.5.5 has no SMTP environment variables. Email is system config (admin UI → DB) or a JSON file via IMMICH_CONFIG_FILE (which supports ignoreCert:true for the self-signed shim). It does not fit the env-injection smtp_mapping model. Left unwired — needs a future config-file-injection mechanism or manual admin-UI setup. See the finding doc.

Observations / mechanism gaps (for follow-on)

  • Gap 1 — self-signed STARTTLS vs opportunistic-upgrade clients with no skip-verify (cal.com; nextcloud's TLS half). The shim advertises STARTTLS on :2525; Nodemailer/Symfony clients auto-upgrade and validate the cert. Apps without a skip flag fail. Fix: a plaintext-only listener that does NOT advertise STARTTLS (controller change) — the highest-coverage unblock.
  • Gap 2 — split From (nextcloud MAIL_FROM_ADDRESS+MAIL_DOMAIN): the single from_var can't express it. Fix: add an optional from_domain_var.
  • Gap 3 — no-SMTP-env apps (immich): needs config-file injection (a separate, larger capability).
  • An app that needs SMTP only at first-install was NOT found among these — gitea and nextcloud both re-apply every boot; rallly reads at send time.
  • Remaining email-capable apps still unwired (future passes): ghost, outline, grafana, docmost, bookstack, n8n, paperless-ngx, … — each needs the same per-app determination, and the opportunistic-STARTTLS ones need Gap-1's plaintext listener first.

No secrets in any committed file; the Resend key stays hub-side (out-of-band).