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>
6.3 KiB
REPORT — Add SparkyFitness to the app catalog (+ live deploy & health check)
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.
1. Confirmed baselines
| 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 |
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.
2. Files created
templates/sparkyfitness/docker-compose.ymltemplates/sparkyfitness/.felhom.yml
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).
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).- Deploy screen: Domain auto =
demo-felhom.eu; Subdomain defaultsparky; 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_KEYlength = 64 (hex:32 ✓)BETTER_AUTH_SECRETlength = 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 arelocked_after_deploy; the two crypto keys carrydata_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/apiproxy → 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/healthproxied through the public edge, CORS origin correctly set). Recommended human step: openhttps://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_keyrecovery 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 pinnedpostgres:15-alpinewith an explicitPGDATAsubdir — 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_limitis 1792M. Themem_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.