Files
felhom-controller/CLAUDE.md
T

9.8 KiB

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.
  • Update REUSE.md if you added/changed/deprecated a shared helper or pattern (same commit).

In every repository where you make a change, update both files in that repo:

  • CHANGELOG.md — cumulative log, newest on top.
  • REPORT.mdoverwrite 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 <VER> --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:<VER> && echo gitea.dooplex.hu/admin/felhom-controller:<VER> > /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)