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

176 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
## 2026-06-29 — App-email rollout: gitea + rallly (calcom/nextcloud/immich = findings)
- **gitea 1.23.4** — added `smtp_mapping` (STARTTLS via `GITEA__mailer__PROTOCOL=smtp+starttls` +
`FORCE_TRUST_SERVER_CERT=true` to trust the shim's self-signed cert; single `GITEA__mailer__FROM`). Compose
references the injected `GITEA__mailer__*` keys; env applied every boot.
- **rallly** — added `smtp_mapping` (Nodemailer STARTTLS, `SMTP_SECURE=false` + `SMTP_REJECT_UNAUTHORIZED=false`
to accept the self-signed cert; single `NOREPLY_EMAIL`). **Also fixed three pre-existing template bugs** that
made rallly undeployable (never caught because the bad pin never ran): (1) image pin `3.12.1` doesn't exist →
`3.11.2`; (2) healthcheck used `wget`, absent from the rallly image (exit 127) → container unhealthy →
**Traefik wouldn't route it** → replaced with a Node http check; (3) added required `SUPPORT_EMAIL` + a valid
`NOREPLY_EMAIL` default (rallly refuses to boot without them).
- **Both gitea and rallly send-tested live** end-to-end (app → shim → hub → Resend): gitea password-reset
(From `gitea@felhom.eu`) and rallly registration code (From `rallly@felhom.eu`) both delivered.
- **NOT wired — reported as findings** (`felhom.eu/documentation/audits/FINDING-app-email-rollout-2026-06-29.md`):
- **cal.com v4.8.7** — hard-codes TLS `rejectUnauthorized:true` with no override; opportunistic STARTTLS
against the self-signed shim fails. Needs a non-STARTTLS-advertising plaintext listener (mechanism change).
- **nextcloud 31** — no cert-skip env (same opportunistic-STARTTLS gap) **and** a split From
(`MAIL_FROM_ADDRESS`+`MAIL_DOMAIN`) the single-`from_var` mapping can't express.
- **immich v2.5.5** — no SMTP env vars at all; config is admin-UI/DB or an `IMMICH_CONFIG_FILE` JSON. Does not
fit env-injection; left for a future config-file-injection mechanism (or manual admin-UI setup).
## 2026-06-29 — App-email: smtp_mapping for Vaultwarden + Mealie
- Added the `smtp_mapping` block to `templates/vaultwarden/.felhom.yml` and `templates/mealie/.felhom.yml`,
enabling managed outbound email (app → in-controller shim → hub → Resend) for the two spike-proven apps
(`SPIKE-smtp-app-relay-2026-06-28`). The controller injects `SMTP_*` at deploy/redeploy when app-email is
on (global + per-app); the From address is `<app>@felhom.eu`. SMTP auth creds are intentionally left unset
(the shim accepts no-auth on the Docker network).
- **Vaultwarden:** STARTTLS (`SMTP_SECURITY=starttls`) + `SMTP_ACCEPT_INVALID_CERTS/HOSTNAMES=true` to
accept the shim's self-signed cert.
- **Mealie:** plaintext (`SMTP_AUTH_STRATEGY=NONE`) on :2525 — Mealie has no accept-invalid-cert option, so
STARTTLS to a self-signed shim would fail; plaintext to the Docker-network-only shim is the spike-validated
mode.
- Both `docker-compose.yml` files now reference the injected `${SMTP_*}` keys (with harmless defaults) so the
values reach the container; empty `SMTP_HOST` keeps mail disabled when the toggle is off.
- Documented the `smtp_mapping` pattern in `README.md` so further apps are easy adds.
## 2026-06-28 — Add SparkyFitness (v0.17.2) — nutrition/workout tracker
- New app `templates/sparkyfitness/{docker-compose.yml,.felhom.yml}`: a self-hosted nutrition/calorie +
workout/weight tracker (alternative to wger). Three containers — nginx **frontend** (SPA :80, the sole
Traefik ingress, proxies `/api`+`/uploads` internally) + Node **server** (:3010) + dedicated
**postgres:15-alpine**. Server + DB stay on the internal network with no Traefik labels.
- **Native email/password auth** (no OIDC/Authentik — that's DooPlex-specific); subdomain `sparky`
(deliberately ≠ wger's `fitness` to avoid a Host() collision). `pi_compatible: false`, `needs_hdd: false`.
- **Two DB roles**: `sparky` (POSTGRES superuser, runs init/migrations) + `sparkyapp` (limited app role the
server auto-creates on first boot) — separate `DB_PASSWORD`/`APP_DB_PASSWORD`. `PGDATA` in a `pgdata`
subdir of the named volume. Four auto-generated, `locked_after_deploy` secrets; `API_ENCRYPTION_KEY` +
`BETTER_AUTH_SECRET` carry `data_key: true` (restore recovers, never regenerates — both are 64-char hex).
- Transcribed from the validated k3s manifest `homelab-manifests/workout-system/sparkyfitness.yaml`
(pinned image tags, two-DB-role model, never-change crypto keys, `/api/health`, pg15 + PGDATA subdir).
- **Image-probe findings (build server, v0.17.2):** server keeps the `node -e` `/api/health` probe (node
present); frontend keeps the `wget --spider` probe (both `wget` and `curl` present). No probe changes needed.
- **Live-validated on guest 9201 (controller v0.87.0):** synced via "Sablonok frissítése"; deployed through
the real dashboard flow (Domain auto, Subdomain `sparky`, 4 secrets auto-gen). All 3 containers healthy;
server log shows clean migrations + `sparkyapp` role created + RLS applied, no crash loop, no uploads
EACCES; `GET /api/health` through the public edge returns `{"status":"UP"}`; login/register page serves
over a valid TLS cert at `https://sparky.demo-felhom.eu`.
## 2026-06-26 — crafty-controller: image bump 4.4.8→4.10.7 + publish Java port range + connection guidance
- **Image bump** `crafty-4:4.4.8``4.10.7` (latest stable; 4.10.8/4.11.0 don't exist in the registry).
6 minor versions of fixes incl. security CVEs. **Java 25 verified present** in 4.10.7
(`/usr/lib/jvm/java-25-openjdk-amd64`, default `java -version` = openjdk 25.0.3; 8/11/17/21 also
available) — so the latest-Minecraft (`26.x`, needs Java 25) blocker is resolved. Healthcheck + Traefik
https-backend labels unchanged (Crafty still serves HTTPS on 8443).
- **Published the Java game-port range** `25565-25575:25565-25575` (TCP, 11 ports = up to 11 Java
servers; first server 25565, rest 2556625575). No `network_mode: host` (would break Traefik routing).
Bedrock UDP 19132 intentionally out of scope.
- **App-page guidance** (`.felhom.yml` first_steps + prerequisites): how to set the server port within
2556525575, how to connect on the LAN (manual IP:port — "scan for LAN" won't auto-list), and that
internet access needs operator port-forwarding. (Static text — can't show the live LAN IP.)
- **Live-verified on guest 9201:** 4.10.7 healthy; public URL 302; the guest's bridged LAN IP
`192.168.0.121` reaches the real Crafty "test" server on `25565` (TCP OPEN + Minecraft SLP handshake
returns JSON status); `:25575` reachable, `:25600` closed (negative control). In-place upgrade preserved
the admin, the operator's configured MFA, and the test server.
- **Correction (earlier draft was wrong):** an earlier note here claimed the upgrade "locked out the
admin (TOTP)." That was a misdiagnosis — the `totp_data` row + recovery codes were **operator-configured
MFA**, so the 401 on a password-only login was correct behaviour, NOT an upgrade bug. There is **no
upgrade regression**; the bump preserves data and MFA correctly.
## 2026-06-26 — crafty-controller: seed a felhom-generated admin password (replaces Crafty's ugly random one)
- **crafty-controller**: instead of reading Crafty's auto-generated (long, symbol-laden) random admin
password, we now **seed** a clean felhom-generated one — same pattern as gokapi, so initial passwords are
consistent across the catalog.
- Crafty's image ships `app/config_original/default.json = {"username":"admin","password":"crafty"}`;
"crafty" is 6 chars < Crafty's 8-char minimum, so Crafty rejected it and generated a random password.
- New `CRAFTY_PASSWORD` deploy field (`type: password`, `generate: password:24`, locked after deploy —
mirrors gokapi's `GOKAPI_PASSWORD`). The compose **entrypoint** overwrites the `default.json` template
with this password before the launcher runs; on fresh install Crafty creates the `admin` user with it.
- `initial_credentials.file` repointed `default-creds.txt``default.json` (same json/username/password
keys), so the controller's app-page "Kezdeti belépési adatok" card shows the **seeded** password — the
customer sees the same value at deploy time and on the app page.
- Catalog-only change (reuses felhom-controller v0.84.0's initial_credentials reader + the gokapi-style
seed). Requires a fresh install to take effect (the seed is only read on first run).
## 2026-06-26 — crafty-controller: surface the auto-generated initial admin password on the app page
- **crafty-controller**: Crafty writes a random admin password to `/crafty/app/config/default-creds.txt`
at first boot (its built-in default is rejected as "too short"). Customers had to read the container
logs to find it. Added an `initial_credentials` block (new general felhom-controller v0.84.0 mechanism):
`file` + `format: json` + `username_key`/`password_key` + a `note`. The controller reads the file live
from the container and shows username + password (masked, reveal/copy) on the app's page under "Kezdeti
belépési adatok". Requires felhom-controller ≥ v0.84.0.
- Updated `first_steps` to point at the app page for the initial login instead of "find it in the logs".
## 2026-06-26 — crafty-controller: Traefik https backend + scoped skip-verify (fixes 502)
- **crafty-controller**: the healthcheck fix un-withheld the Traefik route, exposing a pre-existing
**502** — Traefik proxied `http://…:8443` to Crafty's **HTTPS-only** self-signed backend (Crafty serves
no plain-HTTP panel; `:8000` only redirects). Added two service labels:
- `loadbalancer.server.scheme=https` — Traefik now speaks HTTPS to the backend.
- `loadbalancer.serverstransport=insecure-skip-verify@file` — references the **named**
serversTransport defined in the controller-managed Traefik dynamic config (felhom-controller v0.83.0),
which skips verifying Crafty's per-container self-signed cert. Verification stays ON for every other
backend (scoped Option B; no global `insecureSkipVerify`). The `@file` suffix is the cross-provider
reference from the docker provider to the file-provider transport.
- Requires felhom-controller ≥ v0.83.0 (which renders the `insecure-skip-verify` transport). `port=8443`
and the router/tls labels are unchanged.
## 2026-06-26 — crafty-controller healthcheck fix (curl-absent + http-vs-TLS probe)
- **crafty-controller**: container was permanently `unhealthy` → route withheld (`routeUnpublished`).
Two independent healthcheck root causes, both fixed in one change:
- **Docker healthcheck** ran `curl -fk https://localhost:8443`, but the `crafty-4:4.4.8` image has
**no `curl` and no `wget`** (`exec: "curl": not found`, FailingStreak 150). Replaced with a
dependency-free **python3 TLS-socket** liveness probe (`/usr/bin/python3` is present): completes a
TLS handshake to `127.0.0.1:8443` (unverified context mirrors the old `-k`; Crafty's cert is
self-signed). `start_period` 30s → 60s for cold-boot headroom (cert gen + migrations).
- **Controller-side probe** (`.felhom.yml healthcheck.checks`) was `type: http` against Crafty's
**TLS-only** 8443 → `probeHTTP` sent plaintext HTTP, got a TLS record → `HealthProbe.Healthy=false`,
which `manager.go` re-applies to override Docker's verdict back to `unhealthy`. Changed `http`
`tcp` (`probeTCP` dial succeeds against a TLS listener). Both layers had to change together.
- Live-validated on guest 9201 (`demo-felhom`): synced → recreated via the update path → Docker
`State.Health: healthy` (ExitCode 0), `health_probe.healthy: true` (tcp :8443, 5ms), http-vs-TLS
WARNs stopped, stable green 3+ min, Traefik now **publishes** the route (`crafty-controller@docker`).
- **Known follow-up (separate, out of this fix's scope):** the public URL still returns **502** — a
distinct pre-existing bug the un-withheld route exposed: Traefik proxies `http://…:8443` to Crafty's
HTTPS-only backend. Needs a Traefik HTTPS-backend + self-signed `serversTransport`
(`insecureSkipVerify`) in the controller-generated Traefik config — tracked separately.
## 2026-06-23 — gokapi: index redirect + admin username display
- **gokapi**: seed `RedirectUrl` repointed from Gokapi's GitHub default → `https://${SUBDOMAIN}.${DOMAIN}/admin`.
Gokapi's bare root `/` redirects to `RedirectUrl`; the controller's "Megnyitás" link is always the bare
subdomain root, so it was landing on Gokapi's GitHub instead of the app. Now `/``/admin` → login.
Applied to the live demo (config.json edit + restart) and the seed (future deploys).
- **gokapi**: added `app_info.default_creds` ("Felhasználó: admin · jelszó a Beállítások oldalon") so the
app-info page shows the initial admin user like other apps; fixed `first_steps` (no more setup wizard).
## 2026-06-23 — gokapi reproducible headless setup (fixes public "maintenance mode")
- **gokapi**: was stuck in "maintenance mode" on the public URL since first deploy — Gokapi's one-time
`/setup` wizard was never completed, and (verified against the docs + the v1.9.6 binary) **no Gokapi
version supports env-var headless setup** for admin credentials. Worse, the unconfigured `/setup` was
publicly reachable = an unauthenticated admin-takeover window.
- Fix: the compose `entrypoint` now seeds a `config.json` on first boot (admin user, this app's public
URL `https://${SUBDOMAIN}.${DOMAIN}/`, local storage, Encryption Level 0 so it restarts without a
prompt) with `Password`/`SaltAdmin`/`SaltFiles` cleared, then runs Gokapi's documented
`--deployment-password` one-shot to set the **felhom-generated** admin password **before** the
server starts serving. The admin account is claimed at first boot → `/setup` is never exposed.
- `.felhom.yml`: new `GOKAPI_PASSWORD` deploy field (`type: password`, `generate: password:24`,
shown to the customer, locked after deploy). Admin username is `admin`.
- Seed is pinned to Gokapi **v1.9.6** (`ConfigVersion 21`) — re-capture the seed if the image is bumped.
- Live-validated on guest 9201: fresh remove+redeploy → headless auto-config, public login works, no
maintenance page, admin claimed at first boot (browser-verified login).
## 2026-06-22 — gitea healthcheck fix (unattended test campaign)
- **gitea**: healthcheck probe repointed `/api/v1/version``/api/healthz` (docker HC + controller
`.felhom.yml` probe), `start_period` 30s → 90s.
- Surfaced during the Phase-2 deploy sweep: a fresh gitea reported `unhealthy` because
`/api/v1/version` returns 404 until the install wizard / INSTALL_LOCK completes, while the
container was serving fine on :3000 (`/api/healthz` → 200). Same class as the komga fix.
## 2026-06-22 — komga healthcheck fix (unattended test campaign)
- **komga**: healthcheck probe repointed `/api/v1/actuator/health``/actuator/health`.
- Root cause: komga's Spring Boot actuator endpoint is served unauthenticated at `/actuator/health`
(HTTP 200), while everything under the `/api/v1` prefix is auth-gated — so the old probe got
HTTP 401, `curl -f` exited 22, and the container reported `unhealthy` despite serving normally
on :25600. Diagnosed live on guest 9201 (probe matrix: `/`, `/actuator/health`,
`/api/v1/oauth2/providers`, `/login` all 200; `/api/v1/actuator/health` → 401).
- The `gotson/komga:1.20.0` image ships `curl` (verified), so the probe tool is unchanged.