# CLAUDE.md — Project Instructions for Claude Code (`felhom-controller`) > Read automatically at session start. Stable orientation only — **current state lives in > `CONTEXT.md` and the top of `CHANGELOG.md`**, never here. Cross-repo orientation: workspace-root > `e:\git\CLAUDE.md`. !!! IMPORTANT !!! - Always update CHANGELOG.md whenever you modified the code, and pushed to git!! - IF controller feature changed (new/modify/remove) always update the relevant part of controller/README.md with the architectural change!! ## Project overview Felhom is a managed home-server business for Hungarian customers. This repo contains the **felhom-controller** — the Go application that manages Docker Compose stacks inside each customer LXC guest via a Hungarian-language web dashboard. Read in this order: - **`REUSE.md`** — before writing new code (canonical helpers, patterns, traps, seams). - `CONTEXT.md` — current project state, decisions, roadmap (update after each session). - `controller/README.md` — full feature/architecture reference (update when features change). - `TASK.md` — the current task to implement (if it exists). ## System context — the three-component model The project runs **on Proxmox**, with a locked three-component model: - **Hub** (`felhom.eu/hub/`) — operator backend on k3s. - **Host agent** (`felhom-agent/`) — one per Proxmox host; operator-tier; owns ALL Proxmox interaction. - **In-guest controller** (THIS repo) — one per customer LXC; **Docker-only; holds NO Proxmox credentials**. De-privileged: disk/host/Proxmox concerns are delegated to the host agent via the pinned local-API client (`internal/agentapi`); the controller keeps the app domain — stack/deploy management, the Hungarian web UI, app-data backup, metrics/telemetry, integrations, git-sync, notifications. Whole-guest backup (PBS vzdump) is the agent's. > **Authoritative maps:** `felhom.eu/documentation/architecture/01/02/03-*.md` (topology/trust, > controller module map, host agent) + the code-verified feature docs in > `felhom.eu/documentation/controller/`. Match the current code, not summaries, if they drift. **Don't confuse the two ex-"controllers":** `felhom-agent` (host, operator-tier, was `proxmox-controller`) vs this `felhom-controller` (in-guest, was `deploy-felhom-compose`). ## Layout (verified against the tree) ``` controller/cmd/controller/ entry point + startup wiring (scheduler block, init-only setters) controller/internal/ agentapi/ pinned-TLS client to the host agent's per-guest local API (THE disk seam) api/ REST /api/* router (writeJSON envelope, limitBody, config writes) appbackup/ felhom-data paths/namespaces, DB dumps, userdata skeleton (shared primitives) appexport/ .fab export/import bundles (password crypto, strict segment validation) assets/ app logo/screenshot sync from the hub backup/ app-data backup manager, recovery units, tier-2 copies, offbox restic bootstrap/ bootstrap.json ingest → controller.yaml (Day-0 + refresh) channelhealth/ agent-channel health checker (debounce + born-down alerting) cloudflare/ geo-enforcement remnant (agent-delegated) config/ controller.yaml load/validate (LoadPermissive = setup-mode only) crypto/ AES-256-GCM app.yaml secret encryption (ENC: prefix) infra/ traefik/cloudflared/filebrowser base-stack templates integrations/ app-to-app integrations (e.g. OnlyOffice) mailrelay/ app-email SMTP shim → hub relay metrics/ telemetry collection monitor/ health checks, protected containers notify/ hub event push (typed Notify* wrappers) quiesce/ quiesce loop for whole-guest backup (marker + recover) recovery/ recovery-unit restore report/ hub report builder/pusher + pull-based config refresh scheduler/ background jobs (Every/Daily, Budapest DST-safe) selftest/ startup self-checks selfupdate/ controller image self-update via the agent swap settings/ settings.json persistence (registry, flags, corruption recovery) setup/ first-boot setup wizard (own CSRF) stacks/ compose ops: deploy/delete/migrate/state (THE app domain core) sync/ git-sync of the app catalog system/ mounts/probes (linux + permissive _other stubs) util/ small shared helpers web/ dashboard UI: server, auth/CSRF, handlers, funcmap, templates (Hungarian) ``` Per-package helpers/seams/traps: **`REUSE.md`** (maintained same-commit as helper changes). ## Conventions & cardinal rules - **Trunk-based — no branches.** All shippable work commits directly to `main`; `main` equals what is deployed. Report-only artifacts → `felhom.eu/documentation/` (`audits/`, `backlog/`). Risky fixes are implemented during the supervised session itself, on `main`; if a fix can't be verified/shipped, revert + report — never park on a branch. - Code quality: double-check for bugs/edge cases; add debug logging; **ask rather than guess**. - All UI text is Hungarian (Budapest timezone). Design tokens/gates: use the `felhom-ui-design` skill; templates must pass `controller/scripts/template_id_gate.py` + `emoji_gate.py`. - Testing doctrine (non-hollow tests, red-proofs, seams): use the `felhom-testing` skill. - **Logging**: new leveled lines use `internal/logx` (DEBUG always reaches the debug ring; stdout respects `logging.level`); English, keys-never-values, durations on outcomes — full rules in `felhom.eu/documentation/runbooks/logging-conventions.md`. - Update `REUSE.md` if you added/changed/deprecated a shared helper or pattern (same commit). - **Coupled features** (controller behavior that depends on a specific agent version): add a `featureProbes` table row in `internal/agentapi/features.go` + a `Supports` gate call at the feature's entry point; declare `MinAgent: X.Y.Z` in the CHANGELOG entry header. Rules: `felhom.eu/documentation/runbooks/publish-train-rules.md`. > **In every repository where you make a change, update both files in that repo:** > - **`CHANGELOG.md`** — cumulative log, newest on top. > - **`REPORT.md`** — **overwrite** with the most recent implementation/validation summary only. > > **Never write secrets** into any committed file — reference them as "stored out-of-band". ## Live validation Exercise the SERVER-SIDE PIPELINE a real user triggers, end-to-end (connect → enroll → deploy). The forbidden shortcut is BYPASSING that pipeline (the F9 episode: raw agent guest-attach + hand-set state). Invoking the exact endpoint the UI invokes is an acceptable proxy when a browser tool isn't available — no server logic is skipped, only rendering; say which method was used. For strict end-to-end UI coverage use claude-in-chrome (attaches only to sessions started AFTER the bridge connected) or a manual click-through. ## Environment & access Claude Code runs on Windows 11; repos in `E:\git\` (`/e/git/` in Git Bash). All repos hosted at `gitea.dooplex.hu/admin/`. **SSH binary MUST be** `SSH=/c/Windows/System32/OpenSSH/ssh.exe` (Git Bash's ssh lacks the Windows agent — fails silently). | Host | Access | Role | |------|--------|------| | Build server (k3s) | `$SSH kisfenyo@192.168.0.180` | build + push images (`~/build/felhom-controller`) | | Demo Proxmox host `demo-felhom` | `$SSH felhom-pve` (root@192.168.0.162) | `pct` into guests; live validation | | Demo guest 9201 | `pct exec 9201 -- ...` on felhom-pve | the live demo controller (golden/bootstrap-managed) | | felhotest (legacy) | `$SSH -p 33022 kisfenyo@router.abonet.hu` | OLD /opt/docker compose mechanism | External access via Cloudflare Tunnel → Traefik; Pi-hole forwards `*.demo-felhom.eu` → .162 locally. ## Build & deploy — MANDATORY after code changes **Full runbook: use the `felhom-build-deploy` skill.** Summary (guest 9201 is bootstrap-managed — **no compose file**; `felhom-controller-bootstrap.service` runs the tag in `/etc/felhom-controller-image`): | Step | Command | |------|---------| | 1. Commit + push | `git add -A && git commit -m "..." && git push` | | 2. Build + push image | `$SSH kisfenyo@192.168.0.180 "cd ~/build/felhom-controller && git -C ~/git/felhom-controller pull && ./build.sh --push"` (build.sh does NOT pull — the explicit pull is load-bearing) | | 3. Deploy (9201) | `$SSH felhom-pve "pct exec 9201 -- bash -c 'docker pull gitea.dooplex.hu/admin/felhom-controller: && echo gitea.dooplex.hu/admin/felhom-controller: > /etc/felhom-controller-image && systemctl restart felhom-controller-bootstrap.service'"` | | 4. Verify | `$SSH felhom-pve "pct exec 9201 -- docker ps --filter name=felhom-controller --format '{{.Image}} {{.Status}}'"` + container logs | Hub build/deploy lives in `felhom.eu` (GitOps) — see that repo's CLAUDE.md / the skill. Catalog changes (`app-catalog-felhom.eu`): commit+push; controller sync picks them up ≤15 min or via the "Sablonok frissítése" button. ## Session-critical invariants (the rest live in REUSE.md) - `docker compose restart` does NOT pick up new images/env — always `up -d` (`RedeployFromEnv`). - Docker's `.State` says "running" even for unhealthy containers — `.Status` parse is the truth. - In-memory `Deployed` flag is set BEFORE `compose up -d` (slow-pull race); reverted on failure. - `compose up -d` exits 0 on crash-loops — post-start status check is the detection. - Env var KEYS are logged, never values. Protected stacks (traefik, cloudflared, felhom-controller) can't be stopped from the UI. - Verify a container image HAS the healthcheck tool before using it (BusyBox wget / python3 / curl — catalog REUSE.md maps the families). ## Working with CHANGELOG.md **DO NOT read the full file** — it is large and will waste context. - Session start: use `CONTEXT.md` + `controller/README.md` for current state. - Adding an entry: Read only the top ~30 lines for format, then Edit-insert after line 1. - History: Grep for topics instead of reading. ## End-of-session checklist 1. **Commit and push** all code changes 2. **Build, push, and deploy** the new controller image (if controller code changed) 3. **Update CHANGELOG.md** with what was done 4. **Update CONTEXT.md** with decisions made, state and what's next 5. **Update controller/README.md** if architecture or features changed 6. **Verify** the deployment is working (check `docker ps` and logs) 7. **Update REUSE.md** if you added/changed/deprecated a shared helper or pattern (same commit)