## Changelog ### v0.25.0 — Debug Page: Operator Testing & Diagnostics Dashboard (2026-02-21) **Full debug dashboard with 8 sections for testing all controller subsystems in debug mode.** Only available when `logging.level: "debug"` — sidebar link, page, and all `/api/debug/*` endpoints return 404 otherwise. #### New files - `internal/web/logbuffer.go` — Ring buffer (1000 entries) implementing `io.Writer` for capturing log output. Parses Go standard log format (with/without `Lshortfile`), extracts level/source/timestamp. Supports filtered retrieval by level and timestamp. - `internal/web/handler_debug.go` — Debug page handler + 20 API endpoint handlers organized in 8 sections. `DebugCallbacks` struct (6 fields) for wiring main.go closures. - `internal/web/templates/debug.html` — Full debug dashboard template with 8 collapsible sections, complete JS framework (lazy-load, polling, action buttons, log viewer with filter/auto-refresh). #### Debug page sections 1. **Rendszer diagnosztika** — Diagnostic dump (migrated from `api/router.go`) with structured UI rendering: controller info, storage paths, deployed stacks, scheduler jobs, alerts. JSON download button. 2. **Értesítés teszt** — Send test events with configurable type/severity, view event history ring buffer (last 50 events, newest first). 3. **Mentés teszt** — Trigger individual backup phases: full backup, DB dump only, cross-drive only, restic integrity check, infrastructure backup. 4. **Tárhely teszt** — Storage watchdog status table with per-path probe state. Simulate disconnect (stops apps, marks disconnected, skips unmount) and reconnect (cleans locks, clears state). 5s auto-refresh. 5. **Hub & Kapcsolatok** — Hub report push, infra backup push, Hub/Gitea connectivity tests with latency, preference sync. 6. **Önfrissítés teszt** — Version check + dry-run (shows current/new image lines, compose writability, backup status). 7. **DR / Telepítő varázsló** — Infra backup status per drive (files, timestamps). "RESET" confirmation + infra backup pre-check before triggering setup mode via marker file. 8. **Naplóviewer** — In-memory log viewer with level filter (DEBUG/INFO/WARN/ERROR), 2s auto-refresh, color-coded entries, clear display. #### Module additions - `notify/notifier.go`: `PushTestEventSync()` (synchronous, returns Hub status), `GetEventHistory()` (ring buffer), `recordHistory()` for debug page. - `backup/crossdrive.go`: `RunAllConfigured()` — runs all enabled apps ignoring schedule filter. - `selfupdate/updater.go`: `DryRun()` — checks update availability, compose writability, backup status without performing changes. - `monitor/watchdog.go`: `SimulateDisconnect()` / `SimulateReconnect()` with `simulatedPaths` map, `GetDebugStatus()` for per-path probe state. Watchdog `Check()` skips simulated paths. - `setup/setup.go`: `NeedsSetup()` now checks `.needs-setup` marker file. `ClearSetupMarker()` for cleanup. #### Routing changes - **Mux carve-out**: `/api/debug/` routes to web server (same pattern as `/api/storage/`), with auth + CSRF. - **Removed** `SetDebugDumpDeps()` from `api/router.go` and the `/api/debug/dump` route — dump handler migrated to `handler_debug.go` using Server's existing fields. #### Infrastructure - `setupLogger()` now returns `(*log.Logger, *web.LogBuffer)`. In debug mode, creates `io.MultiWriter(os.Stdout, logBuffer)` so all log output is captured from the start. - Debug CSS: ~170 lines of styles for sections, result badges, log viewer, confirm input, danger button, spinner. ### v0.24.0 — Pre-Testing Observability (2026-02-21) **Three features for pre-testing diagnostics: verbose debug logging, diagnostic dump endpoint, and startup self-test.** #### Feature 1: Debug logging across all modules All `[DEBUG]` log lines are gated behind `logging.level: "debug"` — zero overhead at `info` level. - **New** `internal/util/strings.go`: shared `TruncateStr()` for safely truncating command output in logs. - **Backup** (`backup.go`, `dbdump.go`, `crossdrive.go`, `restore.go`, `local_infra.go`): added `isDebug()` method and per-operation debug logging. DB dump logs container discovery, per-dump command details (passwords masked as `***`), validation results. Cross-drive logs source/dest paths, rsync results, auto-enable decisions. Restore logs step-by-step progress. - **Storage** (`scan_linux.go`, `format_linux.go`, `attach_linux.go`, `migrate.go`): added `Logger`/`Debug` fields to request structs. Logs raw lsblk output (truncated), per-disk classification, pipeline steps for format/attach, rsync progress for migrate. Updated `*_other.go` stubs. - **Sync** (`sync.go`): logs masked clone URLs, per-file hash comparison, post-sync hook triggers. - **Self-update** (`updater.go`): logs registry API calls, tag parsing, version comparison, compose file edits. - **Monitor** (`watchdog.go`): smart logging — periodic 60-probe summaries (~5 min), immediate log on unexpected failures, reconnect attempt details. (`healthcheck.go`): logs raw check values and per-check results. - **Notify** (`notifier.go`): logs event push URL/type/response, preference sync details. - **Report** (`pusher.go`, `builder.go`): logs payload sizes, section summaries, push responses. - **Assets** (`syncer.go`): logs manifest fetch, per-file hash comparison, download/removal actions. - **Setup** (`scanner.go`, `handlers.go`): logs drive scan details, hub recovery/config write operations. #### Feature 2: Diagnostic dump endpoint (`GET /api/debug/dump`) Returns a comprehensive JSON snapshot of all controller state. Only available when `logging.level: "debug"` — returns 404 otherwise. - Sections: `controller` (version, uptime, config hash, PID), `storage` (per-path usage), `stacks` (deployed/running/stopped counts + list), `backup` (status, repo stats), `hub` (push status, consecutive failures), `scheduler` (all jobs with last_run/running/errors), `health` (fresh check), `notifications`, `self_update`, `alerts`. - API router expanded with `SetDebugDumpDeps()` setter for scheduler, hub pusher, alert manager, version, and start time. #### Feature 3: Startup self-test - **New** `internal/selftest/selftest.go`: runs 9 diagnostic checks on boot with 5s timeout each. - Checks: Docker socket, stacks directory, data directory (write test), system data path (mount point), storage paths (connected vs disconnected), git catalog (.felhom.yml files), Hub connectivity (/healthz), restic repos, metrics DB. - Results logged in a clear block: `[PASS]`/`[WARN]`/`[FAIL]` per check, summary at end. - Self-test summary (pass/warn/fail counts) sent to Hub via `NotifyControllerStarted` details map. - Never blocks startup — purely diagnostic. #### Constructor/signature changes - `notify.New()`: added `debug bool` param. `NotifyControllerStarted()`: added `details map[string]interface{}` param. - `report.NewPusher()`: added `debug bool` param. `BuildReport()`: added `logger *log.Logger` param. - `monitor.RunHealthCheck()`: added `logger *log.Logger` param (5 call sites in main.go). - `selfupdate.NewUpdater()`: added `debug bool` param. - `assets.New()`: added `debug bool` param. - `backup.NewCrossDriveRunner()`: added `debug bool` param. `WriteLocalInfraBackup()`: added `debug bool` param. - `backup.DiscoverDatabases()`, `DumpOne()`: added `debug bool` param. - `storage.ScanDisks()`: added `logger, debug` params. `FormatRequest`, `AttachRequest`, `MigrateRequest`: added `Logger`/`Debug` fields. - `setup.ScanDrivesForInfraBackups()`: added `debug bool` param. ### v0.23.0 — CSRF Protection (2026-02-21) **CSRF (Cross-Site Request Forgery) protection on all browser-facing POST endpoints — controller and hub.** **Controller changes:** - New `internal/web/csrf.go`: `CsrfProtect` HTTP middleware validates CSRF tokens on all state-mutating requests (POST/DELETE/PATCH). - Reads token from `_csrf` form field or `X-CSRF-Token` request header. - Exempt paths: `Authorization: Bearer` requests (selfupdate, config/apply hub→controller calls) — browsers cannot auto-send Bearer headers, so no CSRF risk. - Auth-disabled mode (no password set): CSRF check is skipped entirely. - On rejection: JSON error for `/api/` paths, HTTP 403 text for page routes. - `internal/web/auth.go`: `session` struct gains a `csrfToken string` field. `createSession()` generates a second 32-byte random CSRF token alongside the session token. New `csrfTokenForSession(sessionToken)` method returns the CSRF token for a given session. - `internal/web/server.go`: New `executeTemplate(w, r, name, data)` wrapper auto-injects `CSRFField` (`template.HTML` hidden input) and `CSRFToken` (raw string) into every page render data map. - `cmd/controller/main.go`: All route registrations wrapped with `webServer.CsrfProtect(...)` middleware. Version bumped to `v0.23.0`. - All handlers (`handlers.go`, `storage_handlers.go`, `handler_restore.go`): Switched from `s.render(w, ...)` to `s.executeTemplate(w, r, ...)`. - All templates updated: - `layout.html`: Added `` and inline `csrfHeaders()` JS helper (returns `{'X-CSRF-Token': ...}`) in `
` (before page-specific scripts). Updated 4 fetch POST/DELETE calls. - `settings.html`: Added `{{$.CSRFField}}` to 5 forms inside `{{range .StoragePaths}}` (must use `$` for outer scope inside range). Added `{{.CSRFField}}` to 3 page-level forms. Inline-label form uses `document.querySelector('meta[name="csrf-token"]').content`. Updated 5 fetch calls. - `deploy.html`: Added `{{.CSRFField}}` to cross-backup form. Updated 3 fetch calls. - `backups.html`: Updated 3 fetch calls. Dynamically-created restore form injects `_csrf` from meta tag. - `storage_init.html`, `storage_attach.html`, `migrate.html`, `migrate_drive.html`, `app_info.html`, `restore.html`: All fetch calls updated. - `storage_attach.html`: Replaced `navigator.sendBeacon()` with `fetch(..., {keepalive: true})` — `sendBeacon` cannot send custom headers, making CSRF impossible. **Hub changes (v0.3.8):** - `internal/web/server.go`: Replaced insecure literal `hub_session=authenticated` cookie with proper server-side session map. - New `hubSession` struct with `csrfToken string` and `expiresAt time.Time`. - `sessions map[string]*hubSession` + `sessionsMu sync.RWMutex` on `Server` struct. - `handleLogin`: Generates cryptographically random 64-char hex session token + 64-char hex CSRF token. Cookie gains `SameSite=Lax` and `Secure` (when TLS) attributes. Session expires after 7 days. - `RequireAuth`: Validates session token against map (constant-time compare), redirects to `/login` on failure. - `CleanupSessions(ctx)`: Goroutine that purges expired sessions every hour. - CSRF validation block at top of `ServeHTTP`: checks `X-CSRF-Token` header or `_csrf` form field on POST/DELETE/PATCH. Skips when no session cookie (Basic Auth / API path). - `csrfToken(r)`, `csrfField(r)` helpers for template data injection. - `internal/web/configs.go`: Added `html/template` import. All template render calls pass `CSRFField template.HTML` and/or `CSRFToken string`. `renderConfigForm` gains `r *http.Request` parameter. - Templates updated: - `config_form.html`: Added `{{.CSRFField}}` inside the `