From a518023a3beb83d0d06b4f801b8774a40f92a7ae Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Fri, 3 Jul 2026 11:25:54 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20CLAUDE.md=20refresh=20=E2=80=94=20stabl?= =?UTF-8?q?e=20orientation=20(full=20package=20map,=20verified=209201=20de?= =?UTF-8?q?ploy,=20skills=20pointers;=20no=20version-pinned=20state)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6 --- CHANGELOG.md | 10 ++ CLAUDE.md | 413 +++++++++++++++------------------------------------ CONTEXT.md | 6 +- REPORT.md | 56 ++----- 4 files changed, 149 insertions(+), 336 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 104bc64..a1d5869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ ## Changelog +### docs — CLAUDE.md refresh: slim-down to stable orientation (2026-07-03) + +No code change, no version bump. CLAUDE.md 338 → ~160 lines: full 30-package layout map (was 7); +stale bare-metal `/opt/docker` deploy steps replaced with the verified 9201 bootstrap deploy +(`/etc/felhom-controller-image` + `felhom-controller-bootstrap.service`); embedded hub build section +deleted (points to felhom.eu); deep runbooks/design/testing content moved to the new skills +(`felhom-build-deploy`, `felhom-ui-design`, `felhom-testing` — source `felhom.eu/skills/`); "Key +patterns"/"lessons" pruned to session-critical invariants (rest live in REUSE.md). Standing rule +adopted: CLAUDE.md carries no version-pinned current state — that lives in CONTEXT/CHANGELOG/REUSE. + ### docs — REUSE.md introduced (2026-07-03) Cross-repo reuse-map rollout (docs-only, no code change, no version bump). New `REUSE.md` at the diff --git a/CLAUDE.md b/CLAUDE.md index 53ffc0f..b0290ae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,8 @@ -# CLAUDE.md — Project Instructions for Claude Code +# CLAUDE.md — Project Instructions for Claude Code (`felhom-controller`) -> This file is read automatically by Claude Code at the start of every session. It replaces the "Instructions" panel from the claude.ai Project. Keep it updated as the project evolves. +> 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!! @@ -8,332 +10,155 @@ ## Project overview -Creating a business (Felhom) for home-server deployment for Hungarian customers. This repository (`felhom-controller`) contains the felhom-controller — a Go application that manages Docker Compose stacks on customer hardware via a Hungarian-language web dashboard. +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. -See `controller/README.md` for full architecture and status (update after each session, keep track of how different functions/features operate, like backup, monitoring, storage handling, app management, user settings, update workflow, notification system, etc-etc...). -See `CHANGELOG.md` for recent work (update after each session — see "Working with CHANGELOG.md" below). -See `CONTEXT.md` for current project state, decisions and roadmap (update after each session). -See `TASK.md` for the current task to implement (if it exists). -See `REUSE.md` before writing new code. +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). -Claude in Chrome extension is available — can be used to test web UI on demo-felhom.eu or verify dashboard deployments in browser. +## System context — the three-component model -## System context — the Proxmox re-platform (READ THIS FIRST) - -The project has **re-platformed onto Proxmox**, with a locked **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/`, formerly `proxmox-controller`) — 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**. +- **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. -**This repo is being de-privileged.** In the target model, host/disk/Proxmox/Cloudflare responsibilities move OUT of the controller into the **host agent**: System info, Storage (disk scan/format/mount/migrate), the disk-tier Backup (restic, cross-drive, drive-restore, infra-backup), and the Cloudflare-API geo enforcement. The controller keeps the **app domain**: stack/deploy management, the Hungarian web UI, app-data backup (DB dumps + Docker-volume tars), metrics/telemetry, integrations, git-sync, notifications. +> **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. -> **Authoritative map:** `felhom.eu/documentation/architecture/02-controller-module-map.md` — the per-package **KEEP / PORT / DELETE(→agent) / DELETE(obsolete) / MODIFY** classification. Read it before touching `backup/`, `storage/`, `cloudflare/`, `system/`, or `config/`. Also doc 01 (topology/trust) and doc 03 (the host agent). +**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`). -**Status — de-privileging is DONE (slice 8C; as of controller v0.60.0).** The bulk strip HAS happened: the privileged disk subsystem (`internal/storage/*` scan/format/migrate, restic, cross-drive-to-other-disks, drive-restore, infra-backup, `monitor/watchdog`+`pinger`, the raw-disk storage UI) is **deleted**; `backup.Manager` is **app-data only** (DB dumps + Docker-volume tars + per-app recovery units + Tier-2 off-drive copies); disk management is **delegated to the host agent** via the pinned local-API client (`internal/agentapi` → agent `/disks`); the container is **de-privileged** (Docker-only, no Proxmox creds). Whole-guest backup (PBS vzdump) is the agent's. **Authoritative, code-verified architecture now lives in `felhom.eu/documentation/controller/`** (module map + feature docs). Match the current code, not this summary, if they ever drift. +## Layout (verified against the tree) -**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`). +``` +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) +``` -## Cross-repo & artifacts +Per-package helpers/seams/traps: **`REUSE.md`** (maintained same-commit as helper changes). -- Workspace orientation (the felhom system, shared conventions, access) lives in the workspace-root `e:\git\CLAUDE.md`. Sibling per-repo files: `felhom-agent/CLAUDE.md`, `felhom.eu/CLAUDE.md`. -- **Artifact taxonomy:** `TASK.md` / `TASK-*.md` = a spec for YOU to implement (then push + update CHANGELOG + CONTEXT + README). -- **`RUNBOOK-*.md`** — an operational procedure. CC executes the steps it has access and capability for, including live validation on the demo nodes and the demo Proxmox host (CC has root@felhom-pve SSH + the felhom-agent token). A step is human-only only when it genuinely needs physical presence, a real-world decision, or credentials CC truly lacks — mark those steps HUMAN. Do not decline a whole procedure because it touches a live host or a privileged token. (Judgment still applies: confirm before irreversible ops on real customer data — but demo scratch guests are fair game.) +## 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`** — a cumulative log of **all** changes; newest entry on top. -> - **`REPORT.md`** — **overwrite** with a summary of the **most recent** implementation (or significant validation/operational run) only; not cumulative. +> - **`CHANGELOG.md`** — cumulative log, newest on top. +> - **`REPORT.md`** — **overwrite** with the most recent implementation/validation summary only. > -> **Never write secrets** — tokens, passwords, private keys, API keys — into `CHANGELOG.md`, `REPORT.md`, or any committed file. Reference them as "stored out-of-band" instead. - -## Trunk-based — no branches - -All shippable work commits **directly to `main`**; `main` is always equal to what is deployed. **Do NOT create feature/fix branches.** - -- **Report-only artifacts** (audits, findings, fixspecs, reconciliations) → `felhom.eu/documentation/` (`audits/`, `backlog/`), committed to `main`. Never a branch, never left loose at the repo root. -- **Risky/supervised fixes** (agent / golden / provisioning / destructive) are spec'd, then implemented **during the supervised session itself, directly on `main`** — not prepared ahead on a branch. -- **Unattended escape hatch:** if a fix can't be cleanly verified/shipped, **revert it and report it** for a supervised redo (or paste the diff into the spec doc in `documentation/`) — do not park it on a branch. -- This supersedes any older "prepared on branch `fix/…`, pending review" pattern. (The build/deploy workflow below already pushes `main` directly.) - -## Code quality rules - -- Always double-check generated code for bugs, logic issues, syntax errors -- Handle edge cases without overcomplicating the script/program -- Add debug capabilities (logging, verbose output) for easier troubleshooting -- If you need more input or troubleshooting command output, ask first — don't guess +> **Never write secrets** into any committed file — reference them as "stored out-of-band". ## Live validation -Live validation of a user-facing feature must exercise the SERVER-SIDE PIPELINE a real user triggers, end-to-end (e.g. connect → enroll → deploy). The forbidden shortcut is BYPASSING that pipeline — e.g. raw agent guest-attach + hand-set state instead of the enrollment flow (the F9 episode) — which gives false confidence and leaves the system inconsistent. INVOKING THE EXACT ENDPOINT THE UI INVOKES — so the full server pipeline (gates, env injection, pre-create belts, compose generation) runs — is an ACCEPTABLE proxy when a browser-automation tool isn't available: it differs fundamentally from the F9 bypass because no server logic is skipped, only the browser rendering. The residual that proxy does NOT cover is purely client-side (progress panels, card/health rendering, client-side guards like checkBeforeDeploy); for strict end-to-end UI coverage use a real browser tool or a manual click-through — and SAY which was used. Low-level mechanism tests where the direct call IS the mechanism remain exempt. +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. -**Browser automation is available** for that real-UI validation: the Claude Code↔Chrome bridge -(`claude-in-chrome`) is installed, so CC can drive the dashboard / FileBrowser / app admin UIs at -`*.demo-felhom.eu` (click Telepítés, upload via FileBrowser, register an immich library, etc.) and -screenshot evidence. **Caveat:** the browser tools attach only to a session STARTED AFTER the bridge -connected — if they're missing, open a fresh task (don't infer the feature is broken). `/mcp` should -list `claude-in-chrome · connected`; otherwise `/chrome` → reconnect. (CC env note: the long-installed -claude.ai host is a DIFFERENT bridge — Claude Code needs `com.anthropic.claude_code_browser_extension`.) +## Environment & access -## Environment +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). -| Machine | OS | IP | Purpose | -|---------|----|----|---------| -| **Local (this machine)** | Windows 11 | — | Development, Claude Code runs here. Repos in `E:\git\` | -| **Build server (k3s, infra)** | Debian 13 | 192.168.0.180 | Build + push container images, k3s cluster | -| **Demo node** | Debian 13 | 192.168.0.162 | Test deployment (demo-felhom.eu) | -| **Demo node 2** | Debian 13 | router.abonet.hu:33022 | Remote test deployment | +| 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 | -## Workspace layout +External access via Cloudflare Tunnel → Traefik; Pi-hole forwards `*.demo-felhom.eu` → .162 locally. -Claude Code runs on Windows 11. The working directory is `E:\git\` (mapped as `/e/git/` in Git Bash). This repo is at: +## Build & deploy — MANDATORY after code changes -``` -E:\git\felhom-controller\ (or /e/git/felhom-controller/ in Git Bash) -├── controller/ # Go application (main codebase) -│ ├── cmd/controller/ # Entry point (main.go) -│ ├── internal/ -│ │ ├── config/ # YAML config loading -│ │ ├── settings/ # settings.json persistence (password hash, DB cache) -│ │ ├── stacks/ # Docker Compose operations, deploy flow -│ │ ├── sync/ # Git sync — periodic pull of app catalog repo -│ │ ├── api/ # REST API endpoints -│ │ ├── system/ # System info (memory, disk) -│ │ └── web/ # Dashboard UI -│ │ ├── server.go # Server struct, routing, static serving -│ │ ├── auth.go # Session auth, login/logout handlers -│ │ ├── handlers.go # Page handlers (dashboard, stacks, deploy, etc.) -│ │ ├── funcmap.go # Template function map -│ │ ├── embed.go # go:embed directive for templates -│ │ ├── templates.go # Felhom logo SVG constant -│ │ └── templates/ # go:embed HTML/CSS files (Hungarian UI) -│ ├── Dockerfile -│ ├── Makefile -│ └── go.mod -├── scripts/ # Setup scripts for customer nodes -├── CLAUDE.md # This file -├── CHANGELOG.md # Changelog -├── CONTEXT.md # Project memory / state / architectural state/decisions/roadmap -└── TASK.md # Current task (if exists) -``` +**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`): -Related repos (same parent directory): -``` -E:\git\app-catalog-felhom.eu\ # Docker Compose templates + .felhom.yml metadata per app -E:\git\felhom.eu\ # Website (htmls) + k3s manifests -E:\git\homelab-manifests\ # k3s cluster manifests (dooplex.hu services) -E:\git\misc-scripts\ # Helper scripts -``` +| 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 | -All repos hosted at `gitea.dooplex.hu/admin/`. Git credentials are stored (`git config credential.helper store`). +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. -## SSH access +## Session-critical invariants (the rest live in REUSE.md) -SSH key-based authentication is configured and working. No password prompts. - -**IMPORTANT — SSH binary:** Claude Code runs in Git Bash, which has its own SSH at `/usr/bin/ssh` (= `C:\Program Files\Git\usr\bin\ssh.exe`). This binary does NOT have access to the Windows SSH agent and will fail silently (exit 0/141 with no output). Always use the Windows native OpenSSH binary with the full path: - -``` -SSH=/c/Windows/System32/OpenSSH/ssh.exe -``` - -All SSH commands in this file use `$SSH` — set it at the start of your session or substitute the full path manually. - -| Host | OS | IP | User | Role | -|------|----|----|------|------| -| Build server | Debian 13 | 192.168.0.180 | kisfenyo | Build + push container images | -| Demo Proxmox host | 192.168.0.162 | root@pam (SSH alias felhom-pve, root, no sudo) | pveum/pct + live Proxmox validation — available to CC | - -## Test environments - -| Node | OS | Hardware | Domain | IP | Notes | -|------|-----|----------|--------|----|-------| -| demo-felhom | Debian 13 | Acemagic N100, 16G RAM, 512G SSD + 1TB HDD | demo-felhom.eu | 192.168.0.162 | Primary test node, Cloudflare Tunnel | -| felhotest | Debian 13 | Proxmox VM (4-16G RAM, 8 vCPU, 200G + 100G SCSI) | — | router.abonet.hu:33022 | Remote test node | -| pi-customer-1 | Debian 13 | Raspberry Pi 3B+, 1G RAM, 32G SD | pi-customer-1.local | 192.168.0.161 | Secondary test, not yet active | - -- Pi-hole DNS on local network forwards `*.demo-felhom.eu` → 192.168.0.162 -- External access via Cloudflare Tunnel → Traefik reverse proxy - -> **⚠️ Re-platform note:** per the host-agent work, `192.168.0.162` is now a **Proxmox host** (`demo-felhom`, PVE 9.2.2) — the demo-node tables above predate that. Confirm how/where the controller is currently deployed and tested post-re-platform before relying on the bare-metal `docker compose` deploy steps below; on the re-platformed node the controller may now run inside an LXC guest rather than directly on the host. - -## Build & deploy workflow — MANDATORY - -After making code changes to the controller, you **MUST** build, push, and deploy the new image. Do NOT leave code changes uncommitted or undeployed. The full cycle is: - -### Step 1: Commit and push changes - -```bash -cd /e/git/felhom-controller -git add -A && git commit -m "" && git push -``` - -### Step 2: Build + push the container image on the build server - -The build server (192.168.0.180) has the build toolchain. The version tag should be incremented from the current running version. - -!! Important: use "kisfenyo" user for SSH, as written below - -First, set the SSH variable (required for every session — Git Bash's built-in ssh does NOT work): -```bash -SSH=/c/Windows/System32/OpenSSH/ssh.exe -``` - -Check the current running version: -```bash -$SSH kisfenyo@192.168.0.162 "docker ps --filter name=felhom-controller --format '{{.Image}}'" -``` - -Then build with the next version (e.g., if current is 0.2.10, use 0.2.11): IMPORTANT!: Build directory is: ~/build/felhom-controller -```bash -$SSH kisfenyo@192.168.0.180 "cd ~/build/felhom-controller && git -C ~/git/felhom-controller pull && ./build.sh --push" -``` - -The build script: -- Pulls latest code from Gitea -- Builds a multi-arch Docker image (amd64 + arm64) if `--multiarch`, or current arch if `--push` -- Pushes to `gitea.dooplex.hu/admin/felhom-controller:` -- Expects the version as first argument (e.g., `0.2.11`) - -### Step 3: Deploy on demo nodes - -```bash -# Demo node 1 (local) -$SSH kisfenyo@192.168.0.162 "cd /opt/docker/felhom-controller && sudo docker pull gitea.dooplex.hu/admin/felhom-controller: && sudo sed -i 's|image: gitea.dooplex.hu/admin/felhom-controller:.*|image: gitea.dooplex.hu/admin/felhom-controller:|' docker-compose.yml && sudo docker compose up -d" - -# Demo node 2 (remote) -$SSH -p 33022 kisfenyo@router.abonet.hu "cd /opt/docker/felhom-controller && sudo docker pull gitea.dooplex.hu/admin/felhom-controller: && sudo sed -i 's|image: gitea.dooplex.hu/admin/felhom-controller:.*|image: gitea.dooplex.hu/admin/felhom-controller:|' docker-compose.yml && sudo docker compose up -d" -``` - -### Step 4: Verify the deployment - -```bash -$SSH kisfenyo@192.168.0.162 "docker ps --filter name=felhom-controller --format '{{.Image}} {{.Status}}'" -$SSH -p 33022 kisfenyo@router.abonet.hu "docker ps --filter name=felhom-controller --format '{{.Image}} {{.Status}}'" -``` - -Should show the new version and "Up" status. Also check logs for startup errors: -```bash -$SSH kisfenyo@192.168.0.162 "docker logs felhom-controller --tail 20" -$SSH -p 33022 kisfenyo@router.abonet.hu "docker logs felhom-controller --tail 20" -``` - -### Build workflow summary - -| Step | Command | Where | -|------|---------|-------| -| 0. Set SSH var | `SSH=/c/Windows/System32/OpenSSH/ssh.exe` | Local (once per session) | -| 1. Commit + push | `git add -A && git commit -m "..." && git push` | Local (this repo) | -| 2. Build + push image | `$SSH kisfenyo@192.168.0.180 "cd ~/build/felhom-controller... ./build.sh --push"` | Build server | -| 3. Deploy (node 1) | `$SSH kisfenyo@192.168.0.162 "... docker compose up -d"` | Demo node | -| 3b. Deploy (node 2) | `$SSH -p 33022 kisfenyo@router.abonet.hu "... docker compose up -d"` | Demo node 2 | -| 4. Verify | `$SSH kisfenyo@192.168.0.162 "docker ps ..."` + same for router.abonet.hu | Both nodes | - -### Build & deploy workflow — Hub (felhom-hub) - -The central hub (`hub.felhom.eu`) is a separate Go app in the `E:\git\felhom.eu\hub\` repo. The controller pushes periodic reports to it (when `hub.enabled: true` in `controller.yaml`). - -| Step | Command | Where | -|------|---------|-------| -| 1. Commit + push | `cd /e/git/felhom.eu && git add -A && git commit && git push` | Local | -| 2. Build + push image | `$SSH kisfenyo@192.168.0.180 "cd ~/build/felhom-hub && ./build.sh --push"` | Build server | -| 3. Deploy to k3s | `$SSH kisfenyo@192.168.0.180 "sudo kubectl set image -n felhom-system deploy/hub hub=gitea.dooplex.hu/admin/felhom-hub:"` | Build server | -| 4. Verify | `$SSH kisfenyo@192.168.0.180 "sudo kubectl get pods -n felhom-system -l app=hub && sudo kubectl logs -n felhom-system -l app=hub --tail 10"` | Build server | - -See `E:\git\felhom.eu\CLAUDE.md` for full hub details. - -**IMPORTANT:** If you make changes to the app-catalog-felhom.eu repo, commit and push those too: -```bash -cd /e/git/app-catalog-felhom.eu -git add -A && git commit -m "" && git push -``` -The controller's git sync will pick up catalog changes within 15 minutes, or you can trigger it manually via the dashboard "Sablonok frissítése" button. - -## Tech stack - -- **Language:** Go 1.22+ -- **Web framework:** stdlib `net/http` + `html/template` (no frameworks) -- **Templates:** go:embed HTML files in `internal/web/templates/` (Hungarian UI) -- **CSS:** go:embed CSS file in `internal/web/templates/style.css` -- **Auth:** bcrypt password hash + session cookies -- **Container orchestration:** Docker Compose via CLI (`docker compose up -d`) -- **Reverse proxy:** Traefik (separate stack, managed by controller) -- **Tunnel:** Cloudflare Tunnel (cloudflared, separate stack) - -## Key patterns - -- All UI text is in Hungarian (Budapest timezone, Hungarian locale) -- Templates use Go template functions: `stateColor`, `stateLabel`, `stateIcon`, `stateStr`, `isOperational`, `logoURL`, `logoPNGURL`, `appPageURL` -- Container states: `running`, `starting`, `unhealthy`, `stopped`, `exited`, `restarting`, `paused`, `not_deployed` -- Docker `.State` field is combined with `.Status` field to detect health substatus -- Stacks are sorted alphabetically by DisplayName -- Protected stacks (traefik, cloudflared, felhom-controller) can't be stopped from UI -- `app.yaml` persists deploy config; `deployed: true` flag controls UI state -- In-memory `Deployed` flag is set BEFORE `docker compose up -d` (avoids race condition with slow image pulls); reverted on failure -- Password fields require explicit user input or generation (no silent auto-fill) -- App cards on dashboard and stacks pages are clickable via `data-href` attribute (skip protected stacks) -- Logs page uses AJAX polling (`?raw=1` query param returns plain text) with auto-scroll and pause/resume -- Memory bar on deploy page uses two-segment stacked bar (committed = solid green, new = translucent green) -- Deploy flow shows 3-step progress panel (config → containers → health), polls `GET /api/stacks/{name}` every 3s until running/unhealthy/timeout(120s) -- Telepítés buttons have `checkBeforeDeploy()` onclick guard — fetches live state from API before navigating to deploy page -- App info pages at `/apps/{slug}` — detail view with use cases, setup guide, screenshots, optional config -- Optional config saves to `app.yaml` and restarts deployed apps via `docker compose up -d` -- `optional_config` fields in `.felhom.yml` define post-deploy configurable env vars (e.g., API keys) -- `app_info` in `.felhom.yml` provides tagline, use_cases, first_steps, prerequisites, default_creds, docs_url - -## Git sync module (internal/sync) - -- Uses `os/exec` to call `git` CLI — no Go git library dependency -- On startup: clones repo to `{data_dir}/catalog-cache/` (shallow clone, `--depth 1`) -- Periodically: `git fetch --depth 1` + `git reset --hard origin/{branch}` -- Copies only `docker-compose.yml` and `.felhom.yml` to stacks dir -- **Never overwrites** `app.yaml` — this contains deployed secrets -- Content-hash comparison (SHA-256) — only writes if file actually changed -- After sync, triggers `ScanStacks()` rescan for dashboard update -- `POST /api/sync` triggers immediate sync (30s debounce) -- "Sablonok frissítése" button on Alkalmazások page -- Sync status exposed in `/api/system/info` response - -## Debug logging - -The controller has two-tier logging controlled by `logging.level` in `controller.yaml` (or `FELHOM_LOGGING_LEVEL` env var): - -- **`info`** (default): Operation success/failure with elapsed time, post-start container states, scan counts -- **`debug`**: All of above plus env var keys per compose command, local image availability checks, compose command completion times, log fetch byte counts - -Key patterns used in `internal/stacks/`: -- `time.Since(start)` for operation timing — always logged at INFO level -- `m.isDebug()` gates verbose output (env var keys, image checks) -- `truncateStr(s, 500)` caps stdout/stderr in error logs -- `logPostStartStatus()` runs async (goroutine + 3s sleep) after start/restart/update/deploy — never blocks or fails the operation -- `checkLocalImages()` parses compose YAML for `image:` lines, runs `docker image inspect` per image -- Env var **keys** are logged, never values (secrets safety) - -## Important lessons learned - -1. `PAPERLESS_OCR_LANGUAGES` (plural, with S) **installs** tesseract packs; `PAPERLESS_OCR_LANGUAGE` (singular) **selects** which to use -2. `docker compose restart` does NOT pick up new images — always use `docker compose up -d` -3. Go map iteration order is random — always sort before displaying in UI -4. Docker's `.State` field says "running" even for unhealthy containers — must parse `.Status` for health info -5. In-memory `Deployed` flag must be set BEFORE `docker compose up -d` (not after) — compose can take 30-60s for image pulls; revert both in-memory and disk on failure -6. `docker compose up -d` returns exit 0 even when containers crash-loop — post-start status check is essential for detecting failures -7. Mealie image has no wget/curl — use Python TCP socket check for healthcheck; set `start_period: 60s` for DB migration time -8. Always verify container images have the healthcheck tool (`wget`, `curl`, etc.) before using it — Alpine has BusyBox wget, Python images have `python3` +- `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 (29K+ tokens) and will waste context or fail. - -- **At session start:** Do NOT read CHANGELOG.md. Use `CONTEXT.md` and `controller/README.md` for current state. -- **To add a new entry:** Read only the top ~30 lines (`limit: 30`) to see the format and insertion point, then use Edit to insert the new entry after line 1 (`## Changelog`). -- **To check history:** Use Grep to search for specific topics instead of reading the file. +**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 -Before ending a session, always: - 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, update architectural state and what's next +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) \ No newline at end of file +7. **Update REUSE.md** if you added/changed/deprecated a shared helper or pattern (same commit) diff --git a/CONTEXT.md b/CONTEXT.md index 31fa59e..13b748c 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -7,7 +7,11 @@ > > Ask Claude Code: "Please update CONTEXT.md with what we did today" -Last updated: 2026-07-03 (docs: REUSE.md introduced) +Last updated: 2026-07-03 (docs: CLAUDE.md refreshed — stable orientation; runbooks live in the felhom skills) + +> **2026-07-03 — CLAUDE.md slimmed to stable orientation** (full package map, verified 9201 deploy +> summary, no version-pinned state). Deep runbooks/design/testing doctrine now in the personal +> skills `felhom-build-deploy` / `felhom-ui-design` / `felhom-testing` (source: `felhom.eu/skills/`). > **2026-07-03 — `REUSE.md` exists at the repo root** (canonical helpers / patterns / traps / seams, code-verified). Check it before writing new code; update it in the same commit that adds/changes a shared helper (maintenance rule now in CLAUDE.md). diff --git a/REPORT.md b/REPORT.md index cf91938..dc6beed 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,44 +1,18 @@ -# REPORT — docs: REUSE.md introduced (cross-repo reuse map) +# REPORT — docs: CLAUDE.md refresh (stable orientation) -**Date:** 2026-07-03 · **Class:** docs-only (no code change, no version bump, no deploy) -**Baseline:** `main` @ `d0c70a7da6f4060422a545cea2396f9df5f7d61f` (v0.98.3) +**Date:** 2026-07-03 · **Class:** docs-only (no code, no version bump, no deploy) +**Baseline:** `main` @ `eda33400da` (v0.98.3 live on 9201 — unchanged) -## What was done +CLAUDE.md rewritten 338 → ~160 lines (diffstat: +119/−294): +- Full 30-package `controller/internal/` map (verified against the tree; old file listed 7). +- Stale bare-metal `/opt/docker` compose deploy steps for the demo node **replaced** with the + verified 9201 bootstrap mechanism (`/etc/felhom-controller-image` + + `felhom-controller-bootstrap.service` — confirmed live: enabled, running 0.98.3). felhotest noted + as the legacy compose mechanism. +- Embedded hub build/deploy section deleted (owned by felhom.eu); build/deploy compressed to a + 4-row summary table + `felhom-build-deploy` skill pointer. +- "Key patterns" (~20 bullets) pruned to 6 session-critical invariants; the rest live in REUSE.md. + "Lessons learned" absorbed likewise. No version-pinned state remains (standing rule). -- **`REUSE.md`** created at the repo root (210 lines): the curated reuse map — canonical helpers - (62 rows across 9 domains), canonical patterns (10), dangerous lookalikes (8), test seams + - cross-repo edges, extension points, and observed duplication (12 clusters, recorded NOT fixed). -- Sourced from a full code sweep (1174 non-test `func` declarations scanned; call-site counts + - body reads per candidate; mandatory trap sweep over `--delete`/`os.RemoveAll`/`mkfs`/etc.). - Every entry cites file + symbol, verified in code — not copied from README/docs. -- **`CLAUDE.md`**: two hook lines added — "See `REUSE.md` before writing new code" (read-first - pointers) and end-of-session checklist item 7 (update REUSE.md in the same commit as a shared - helper change). No other CLAUDE.md edits. -- `CHANGELOG.md` entry (no version number) + `CONTEXT.md` note. - -## Verification - -`python felhom.eu/scripts/reuse_refs_check.py e:/git/felhom-controller` → -`OK [felhom-controller]: 65 cited paths, all exist` (the check caught and fixed one wrong citation: -the template/emoji gate scripts live at `controller/scripts/`, not `scripts/`). - -## Notable traps recorded (§3 highlights) - -`rsyncMirror` (`--delete`, tier-2 only) vs `rsyncCopy`; raw `os.RemoveAll` vs the -`ProtectedHDDPaths`-gated delete path; fresh `agentapi.New` per request (the v0.74.0 conn-leak -incident); `timeAgo` on string timestamps; `DumpAppVolumes` on a running DB app; -no-timeout `composeExec` for new long-running calls. - -## Observations (NOT acted on) - -- Atomic-write helper exists ×4 (+1 inline) with differing semantics; two of them have a deliberate - NON-atomic bind-mount fallback — a convergence candidate needs care. -- `util.TruncateStr` (rune-safe) has 1 caller while the byte-based `stacks.truncateStr` twin is the - widely used one. -- 5 JSON writers / 3 envelope shapes; 6 dir-size implementations; 2 CSRF implementations - (intentional but unlabeled). Full list in REUSE.md §6. -- CLAUDE.md "Workspace layout" lists ~7 internal packages; code has ~30. Its bare-metal - `/opt/docker` deploy steps are superseded by the 9201 bootstrap mechanism (already flagged - in-file). A CLAUDE.md refresh is a separate task. - -Cross-repo summary + full sweep counts: `felhom.eu/REPORT.md`. +Verified-live commands recorded: build (with the load-bearing explicit `git pull`), 9201 deploy +chain, verify. Consolidated cross-repo report: `felhom.eu/REPORT.md`.