# Felhom Controller — Module Map (current)

**Source of truth:** `felhom-controller/controller/internal/` at v0.59.0 (2026-06-13).
**Supersedes** the planning map `../architecture/02-controller-module-map.md` (a v0.33 KEEP/PORT/DELETE
plan written before slice 8C). That plan is now **executed**: the disk/storage/restic/watchdog
subsystems were deleted or moved to the host agent; the controller is Docker-only and holds no Proxmox
credentials. The old doc is kept for history; this is the live map.

## What the controller is (and is not)

The in-guest controller is **one per customer LXC, Docker-only**. It owns the **app domain**: stack
lifecycle, the Hungarian web UI, app-data backup (DB dumps + volume tars + recovery units + Tier-2
off-drive copies), metrics/telemetry, hub reporting, catalog sync, integrations, geo-restriction, and
self-update. It does **not** touch Proxmox or raw disks: whole-guest backup (PBS vzdump), disk
classification, and destructive storage ops live in the **host agent** (`felhom-agent`), reached over a
TLS-leaf-pinned local API (`internal/agentapi`).

## Package map

| Package | Role | Notes / doc |
|---|---|---|
| `cmd/controller` | entry point, wiring, schedulers | `Version` via ldflags |
| `internal/stacks` | stack model + deploy/start/stop/update/remove lifecycle, protected-stack enforcement | [deploy-and-stack-lifecycle](deploy-and-stack-lifecycle.md) |
| `internal/infra` | base-infra bring-up (`EnsureBaseStack`: traefik/cloudflared/filebrowser), self-heal | [deploy-and-stack-lifecycle](deploy-and-stack-lifecycle.md) |
| `internal/sync` | catalog git-sync (compose/.felhom.yml; never overwrites app.yaml) | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/appbackup` | app-data backup primitives: DB dumps, recovery units, restore | [backup-architecture](backup-architecture.md) |
| `internal/backup` | backup orchestration: scheduling, Tier-2 cross-drive, running mutex | [backup-architecture](backup-architecture.md) |
| `internal/appexport` | `.fab` encrypted app export/import (AES-CTR+HMAC); path-segment validation (CTRL-001) | [backup-architecture](backup-architecture.md) §export |
| `internal/quiesce` | app-consistent quiesce loop (stop→backup→restart, crash-safe) | [backup-architecture](backup-architecture.md) |
| `internal/agentapi` | TLS-leaf-pinned client to the host agent (disks, host-metrics, whole-guest backup) | [storage-monitoring-metrics](storage-monitoring-metrics.md) |
| `internal/settings` | `settings.json` persistence (password hash, storage registry, caches) | RWMutex; atomic writes |
| `internal/config` | `controller.yaml` loading + defaults; `IsProtectedStack` | — |
| `internal/crypto` | AES-256-GCM at-rest secret encryption | — |
| `internal/web` | dashboard UI + JSON API: auth, CSRF, storage/disk handlers, host-metrics, templates | [auth-hub-sync-integrations](auth-hub-sync-integrations.md), [storage-monitoring-metrics](storage-monitoring-metrics.md) |
| `internal/api` | REST router (`/api/*`) | route→CSRF/auth coverage |
| `internal/setup` | first-run setup wizard (pre-auth) | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/bootstrap` | first-run `bootstrap.json` ingestion (seed config, skip setup) | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/report` | hub report builder + pusher (zero-knowledge; no app secrets) | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/notify` | event notifications + history | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/integrations` | app-to-app integrations (adapter pattern; OnlyOffice↔FileBrowser/Nextcloud) | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/cloudflare` | geo-restriction via CF WAF (`[felhom-geo]` rules, geosync) | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/selfupdate` | self-update (version check; pinned tags, never `:latest`) | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/assets` | hub-sourced asset sync (cache + baked-in fallback) | [auth-hub-sync-integrations](auth-hub-sync-integrations.md) |
| `internal/metrics` | SQLite (WAL) metrics store, docker-stats collector, log scanner/telemetry | [storage-monitoring-metrics](storage-monitoring-metrics.md) |
| `internal/monitor` | health checks (`healthcheck.go`). **watchdog/pinger deleted in 8C** | [storage-monitoring-metrics](storage-monitoring-metrics.md) |
| `internal/system` | system info; `dockervol.go` = OS/Docker-data-split prevention layer (v0.58) | [storage-monitoring-metrics](storage-monitoring-metrics.md) |
| `internal/scheduler` | cron-like job scheduler (Europe/Budapest); late-registration safe; per-job `recover()` | — |
| `internal/recovery` | recovery-file generation | — |
| `internal/selftest` | startup self-test (hub reachability, metrics DB, etc.) | — |
| `internal/util` | shared helpers | — |

## Cross-repo boundary

- **Host agent (`felhom-agent`):** owns Proxmox + all destructive storage. The controller calls it via
  `internal/agentapi`; the agent enforces the data-bearing gate + operator signatures. See
  `../architecture/03-host-agent.md` and `../audits/deep-sweep-2026-06-13.md` (agent destructive-path).
- **Hub (`felhom.eu/hub/`):** operator backend; the controller pushes zero-knowledge reports/events to
  it. See `../architecture/05-hub-architecture.md`.

## De-privileging — what is GONE from the controller (executed in 8C)

`internal/storage/*` (scan/format/migrate), restic, cross-drive-to-other-disks execution,
`monitor/watchdog.go` + `pinger.go`, drive-restore, infra-backup, the storage UI's raw-disk operations.
The controller now **delegates** all of these to the agent. Do not document the controller as performing
them.
