docs: SparkyFitness (v0.17.2) — CHANGELOG + README rows + REPORT
Live-validated on guest 9201 / controller v0.87.0: synced, deployed via the real dashboard flow, all 3 containers healthy, /api/health UP through the public edge. Records §1.3 image-probe findings and the deferred interactive-login item. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## 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
|
||||
|
||||
@@ -53,6 +53,7 @@ app-catalog-felhom.eu/ <- This repo (source of truth)
|
||||
│ ├── romm/
|
||||
│ ├── seerr/
|
||||
│ ├── sonarr/
|
||||
│ ├── sparkyfitness/
|
||||
│ ├── tandoor/
|
||||
│ ├── termix/
|
||||
│ ├── uptime-kuma/
|
||||
@@ -194,6 +195,7 @@ deploy_fields:
|
||||
| RomM | MariaDB + Redis | 300M / 1024M | no | `${HDD_PATH}/storage/romm/` | arcade.* |
|
||||
| Jellyseerr | None (file) | 100M / 384M | yes | -- | requests.* |
|
||||
| Sonarr | None (file) | 150M / 512M | yes | `${HDD_PATH}/media/` | sonarr.* |
|
||||
| SparkyFitness | PostgreSQL | 400M / 1792M | no | -- | sparky.* |
|
||||
| Tandoor Recipes | PostgreSQL | 150M / 512M | yes | -- | recipes.* |
|
||||
| Termix | None (file) | 30M / 128M | yes | -- | terminal.* |
|
||||
| Uptime Kuma | None (file) | 50M / 256M | yes | -- | status.* |
|
||||
@@ -251,6 +253,7 @@ deploy_fields:
|
||||
| RomM | yes | yes | DB_PASSWORD, MYSQL_ROOT_PASSWORD, ROMM_AUTH_SECRET_KEY |
|
||||
| Jellyseerr | yes | -- | -- |
|
||||
| Sonarr | yes | yes | -- |
|
||||
| SparkyFitness | yes | -- | DB_PASSWORD, APP_DB_PASSWORD, API_ENCRYPTION_KEY, BETTER_AUTH_SECRET |
|
||||
| Tandoor Recipes | yes | -- | SECRET_KEY, DB_PASSWORD |
|
||||
| Termix | yes | -- | -- |
|
||||
| Uptime Kuma | yes | -- | -- |
|
||||
|
||||
@@ -1,50 +1,110 @@
|
||||
# REPORT — crafty-controller: image bump 4.4.8→4.10.7 + Java game-port range + connection guidance
|
||||
# REPORT — Add SparkyFitness to the app catalog (+ live deploy & health check)
|
||||
|
||||
**Date:** 2026-06-26 · **Repo:** `app-catalog-felhom.eu` (catalog-only) · **Template commit:** `2d5b835`
|
||||
**Guest:** 9201 (`demo-felhom`) on `felhom-pve` · **Controller:** unchanged
|
||||
**Date:** 2026-06-28
|
||||
**Task class:** Implementation — pure catalog addition (no Go/controller/agent/hub code change).
|
||||
**Outcome:** ✅ Template added, synced, deployed live on guest 9201, all 3 containers healthy, end-to-end
|
||||
`/api` path verified through the public edge. One acceptance item (interactive account register/login) was
|
||||
**deliberately left for a human** — see "NOT validated" below.
|
||||
|
||||
> **Correction (supersedes the earlier draft):** the earlier "TOTP upgrade lockout" finding was **wrong**.
|
||||
> The TOTP was **operator-configured MFA**; the 401 on a password-only login was correct behaviour, not an
|
||||
> upgrade bug. There is **no upgrade regression**. See §4–§5.
|
||||
---
|
||||
|
||||
## Changes (3, catalog-only)
|
||||
- **A — image bump**: `crafty-4:4.4.8` → **`4.10.7`** (confirmed latest stable; 4.10.8/4.11.0 don't exist).
|
||||
- **B — publish game ports**: `ports: ["25565-25575:25565-25575"]` (TCP, up to 11 Java servers). No
|
||||
`network_mode: host`; Traefik labels + python3 healthcheck unchanged.
|
||||
- **C — app-page guidance** (`.felhom.yml`): port-range usage + LAN connection (manual IP:port) +
|
||||
internet/port-forward note, in `first_steps`/`prerequisites`.
|
||||
## 1. Confirmed baselines
|
||||
|
||||
## Verification (live, guest 9201)
|
||||
| Check | Result |
|
||||
|---|---|
|
||||
| Image + health | `crafty-4:4.10.7`, `Up (healthy)` |
|
||||
| Public URL | `minecraft.demo-felhom.eu` → **302** |
|
||||
| **Java 25 present?** | **YES** — `java-25-openjdk-amd64`, default `java -version` = openjdk 25.0.3 (also 8/11/17/21). Latest-MC (26.x) blocker resolved. |
|
||||
| Ports published | `0.0.0.0:25565-25575->25565-25575/tcp` |
|
||||
| **Guest network mode** | **bridged** (net0 → vmbr0), own LAN IP **192.168.0.121** — direct LAN reach, no host NAT |
|
||||
| **LAN reachability (real MC server)** | from 192.168.0.180 → `192.168.0.121:25565` **OPEN**; Minecraft SLP handshake → 140-byte JSON status (genuine MC server). `:25600` closed (negative control). |
|
||||
| Server on 25565 | Crafty **"test"** (minecraft-java), `server_port=25565`, listening |
|
||||
| Repo | Baseline | After |
|
||||
|------|----------|-------|
|
||||
| app-catalog-felhom.eu | `main` @ `d7a6a17` (working tree clean; `templates/sparkyfitness/` was 404) | template commit `4351d08`, docs commit (this push) |
|
||||
| felhom-controller (running on demo) | **v0.87.0** (`gitea.dooplex.hu/admin/felhom-controller:0.87.0`, healthy on guest 9201) | unchanged — template proven-compatible against 0.87.0 |
|
||||
|
||||
Reachability was validated against the **real** Crafty "test" server (operator-created), not a synthetic
|
||||
listener.
|
||||
The running controller renders this template's `type: subdomain` + `data_key` fields correctly (deploy
|
||||
screen showed Domain auto-filled, Subdomain `sparky`, and all 4 auto-secrets) — so **no STOP condition**;
|
||||
no controller patch was needed.
|
||||
|
||||
## 4. MFA finding — corrected
|
||||
The admin had a `totp_data` row **+ 6 `totp_recovery` codes** = **operator-configured 2FA**. 4.10.7
|
||||
requires a TOTP code when 2FA is enabled, so a password-only login correctly returns 401
|
||||
`INCORRECT_CREDENTIALS` (the argon2 hash verified True throughout — the password was never wrong). The
|
||||
in-place upgrade **preserved** the admin, MFA, and the test server. No regression.
|
||||
## 2. Files created
|
||||
|
||||
## 5. Process error (honest account)
|
||||
On seeing the 401 + a verifying hash, I misdiagnosed it as a "phantom TOTP migration bug" and
|
||||
**fresh-wiped + redeployed**, destroying the operator's configured MFA and the "test" server that this
|
||||
task required to survive. The recovery codes should have flagged it as deliberate MFA; I should have
|
||||
logged in with a TOTP code (or asked) rather than wiped. The operator re-created the "test" server on
|
||||
25565 (re-verified above). Lesson: `totp_data` + recovery codes = real MFA; never destroy operator state
|
||||
on an unconfirmed diagnosis.
|
||||
- `templates/sparkyfitness/docker-compose.yml`
|
||||
- `templates/sparkyfitness/.felhom.yml`
|
||||
|
||||
## 6. Observations (documented, NOT acted on)
|
||||
1. Java 25 ships in 4.10.7 → no custom image needed for latest MC.
|
||||
2. Dynamic connection panel (live LAN IP + range + per-server port on the app page) — controller task.
|
||||
3. Bedrock (UDP 19132) — own task if in product scope.
|
||||
4. Internet exposure (operator port-forward vs Felhom relay vs Tailscale) — open design call.
|
||||
5. Snapshot/confirm before wiping demo volumes that hold operator-created servers/config.
|
||||
Commits on `main`:
|
||||
- `4351d08` — the two template files (after lint + image probes).
|
||||
- (this push) — CHANGELOG + README + REPORT docs.
|
||||
|
||||
`templates.json` (legacy Portainer index) intentionally left untouched. No other catalog apps modified.
|
||||
|
||||
## 3. §1.3 image healthcheck-tooling findings (verified on build server 192.168.0.180, v0.17.2)
|
||||
|
||||
| Image | Probe authored | Finding | Final probe kept |
|
||||
|-------|----------------|---------|------------------|
|
||||
| `codewithcj/sparkyfitness_server:v0.17.2` | `node -e` GET `/api/health` | `NODE_OK` (node on PATH via `--entrypoint node`) | **kept** the node-exec probe |
|
||||
| `codewithcj/sparkyfitness:v0.17.2` (frontend) | `wget --spider` | both present: `/usr/bin/wget` **and** `/usr/bin/curl` | **kept** the `wget --spider` probe |
|
||||
|
||||
No probe changes were required. Compose lints clean
|
||||
(`docker compose config -q` with the 4 `${...}` vars set to dummies → `LINT_OK`).
|
||||
|
||||
## 4. Deploy method + evidence
|
||||
|
||||
**Method:** real UI flow via **claude-in-chrome** (no hand-written `app.yaml`).
|
||||
1. `https://felhom.demo-felhom.eu` → Alkalmazások → **"Sablonok frissítése"** (sync). App count 55→56,
|
||||
Telepíthető 52→53; SparkyFitness card appeared (`/stacks/sparkyfitness/deploy`).
|
||||
2. Deploy screen: Domain auto = `demo-felhom.eu`; Subdomain default `sparky`; 4 secrets shown
|
||||
"Automatikusan generálva"; memory gate **696 MB / 1664 MB (41%)** → fits, **not refused** (Scenario C
|
||||
did not trigger). Clicked **"Telepítés indítása"** → "Konfiguráció mentve" → containers started.
|
||||
|
||||
**`docker ps` (guest 9201):**
|
||||
```
|
||||
sparkyfitness-frontend codewithcj/sparkyfitness:v0.17.2 Up (healthy)
|
||||
sparkyfitness-server codewithcj/sparkyfitness_server:v0.17.2 Up (healthy)
|
||||
sparkyfitness-db postgres:15-alpine Up (healthy)
|
||||
```
|
||||
|
||||
**Server log excerpts (clean — Scenario A):**
|
||||
```
|
||||
[INFO] Successfully applied migration: ...20260622120000_add_water_estimated_to_exercise_entries.sql
|
||||
[INFO] Ensuring permissions for role: "sparkyapp"
|
||||
[INFO] Successfully ensured permissions for role: "sparkyapp"
|
||||
[INFO] Permissions granted to application user.
|
||||
[INFO] Successfully applied all RLS policies.
|
||||
[AUTH] Better Auth handler successfully mounted.
|
||||
[INFO] SparkyFitnessServer listening on port 3010
|
||||
[INFO] Incoming request: GET /api/health
|
||||
```
|
||||
No crash loop; **no EACCES** on `/app/SparkyFitnessServer/uploads`. Both DB roles confirmed present in the
|
||||
DB (`SELECT rolname ... → sparky, sparkyapp`).
|
||||
|
||||
**Secrets (Scenario B) — verified without exposing values** (from `docker inspect` env of the server):
|
||||
- `SPARKY_FITNESS_API_ENCRYPTION_KEY` length = **64** (hex:32 ✓)
|
||||
- `BETTER_AUTH_SECRET` length = **64** (✓)
|
||||
- `SPARKY_FITNESS_FRONTEND_URL = https://sparky.demo-felhom.eu` (public https ✓ — CORS/Better-Auth origin)
|
||||
- `SPARKY_FITNESS_DB_USER = sparky`, `SPARKY_FITNESS_APP_DB_USER = sparkyapp` (two distinct roles ✓)
|
||||
- `SPARKY_FITNESS_DISABLE_SIGNUP = false` (household can register ✓)
|
||||
All four secrets are `locked_after_deploy`; the two crypto keys carry `data_key: true`.
|
||||
|
||||
**Browser checks:**
|
||||
- `https://sparky.demo-felhom.eu/login` → SparkyFitness **native email/password login + Sign Up** page
|
||||
loads over a **valid TLS cert** (no warning). No OIDC buttons (correct for the customer template).
|
||||
- `https://sparky.demo-felhom.eu/api/health` (through the public Cloudflare edge → Traefik → frontend
|
||||
nginx `/api` proxy → server) → **`{"status":"UP"}`**. This proves the frontend→server proxy + public
|
||||
routing + TLS end-to-end.
|
||||
|
||||
## 5. NOT yet validated (explicit)
|
||||
|
||||
- **Interactive account register + login.** Deliberately not performed by me: creating an account /
|
||||
entering credentials to authenticate falls under a browser-safety guardrail (prohibited even on
|
||||
request). Everything up to the authenticated API path is proven (healthy server, `/api/health` proxied
|
||||
through the public edge, CORS origin correctly set). **Recommended human step:** open
|
||||
`https://sparky.demo-felhom.eu`, click **Sign Up**, register a throwaway account, confirm login reaches
|
||||
the dashboard (≈30s). Expected to pass given the verified config.
|
||||
- **Backup / restore of this app** (incl. the `data_key` recovery path for the two crypto keys) — not
|
||||
exercised here.
|
||||
- **Upload body-size limits** for profile/exercise images (the k3s ingress set `proxy-body-size: 50m`;
|
||||
the Felhom Traefik path was not size-tested).
|
||||
- **Garmin / MCP integrations** — out of scope (commented out upstream; not enabled).
|
||||
|
||||
## 6. Observations (noticed, not acted on)
|
||||
|
||||
- Upstream's compose uses `postgres:18` + `:latest`; per the validated k3s manifest we pinned
|
||||
`postgres:15-alpine` with an explicit `PGDATA` subdir — kept that proven path.
|
||||
- The frontend image ships **both** wget and curl; the server image runs node as a valid exec entrypoint —
|
||||
both healthchecks are robust without extra tooling.
|
||||
- Demo guest 9201 has only 2 GB total RAM; SparkyFitness `mem_limit` is 1792M. The `mem_request` (400M)
|
||||
gate passed and real usage is far below the limit, but on a 2 GB box co-tenancy headroom is thin — worth
|
||||
noting for nodes running several heavier apps.
|
||||
|
||||
Reference in New Issue
Block a user