Deployed-but-stopped apps were included in telemetry reports with all-zero
memory/CPU values, dragging down hub-side averages. Now isStackRunning()
filters to only running/starting/unhealthy/restarting states.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CatchAllMiddleware was intercepting Docker healthcheck requests (Host:
localhost) and internal API calls, returning 404 instead of passing
through. Also removed certresolver from catch-all Traefik router to
avoid cert provisioning issues with HostRegexp(.+).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stopped/undeployed app subdomains now show a branded page instead of
Traefik 404. Deploy settings page gains start/stop/restart controls.
Dashboard shows "Megnyitás" button for running apps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously used bare "docker compose restart" which doesn't inject
env vars or pick up template changes. Now matches StartStack behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deploy API now returns immediately after validation + config save.
docker compose up -d runs in a background goroutine so the UI shows
progress during image pulls instead of blocking for 30-60s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New GET /api/debug/telemetry endpoint runs full telemetry pipeline on-demand
- GetTelemetryPreview callback added to DebugCallbacks, wired in main.go
- BuildAppTelemetryForDebug() exported wrapper in report/telemetry.go
- Debug page: new collapsible section with per-app table (memory, CPU, log errors/warnings, issues) and raw JSON viewer
- Available regardless of hub configuration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New internal/metrics/telemetry.go: MetricsStore.GetContainerTelemetry()
aggregates container memory/CPU from SQLite over the last 15 min
- New internal/metrics/logscanner.go: ScanContainerLogs() scans docker logs
for errors/warnings, deduplicates via fingerprinting (strips timestamps,
replaces 6+ digit numbers, hex strings, UUIDs)
- New internal/report/telemetry.go: buildAppTelemetrySection() assembles
per-stack AppTelemetry by aggregating container metrics and log summaries
- internal/report/types.go: added AppTelemetry field to Report struct plus
AppTelemetry type with memory/CPU/log fields and LogIssue references
- internal/report/builder.go: calls buildAppTelemetrySection() in BuildReport()
- Backward-compatible: old Hub versions silently ignore app_telemetry field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deploy page, pre-start check, and deploy validation now use actual
/proc/meminfo usage instead of declared mem_request sums. New
GetMemoryMB() helper for lightweight real-time memory reads. Monitoring
page gains a stacked memory distribution bar showing per-container
usage, OS overhead, and free memory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Memory validation: stopped apps excluded from CommittedMemory()
- Pre-start memory check (409 Conflict) on stack start
- hungarian_ui metadata field in resources
- USB badge on storage cards
- Manual Tier2 triggers now push infra backup to Hub
- showAlert() replaces native alert() for copyable error text
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The filebrowser stack has no .env file — domain is baked into compose labels
by docker-setup.sh. The sync always bailed with a WARN and storage paths
were never applied to FileBrowser's config.yaml or docker-compose.yml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove "Automatikusan generálva" badge from domain field (it's not
generated, it's the customer's configured domain)
- Shrink subdomain input width (8rem) so the .domain suffix appears
directly next to it on the same line
- Suppress redundant "Az alkalmazás aldomainje" description hint for
subdomain fields (the warning hint is sufficient)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users can now customize the subdomain for each app during deployment
instead of using a fixed value. The deploy page shows an editable text
input with the default pre-filled and the base domain as a suffix.
New "subdomain" deploy field type with DNS-safe format validation,
reserved name blocklist, and uniqueness check across deployed stacks.
Locked after deploy — changing requires Remove + Redeploy.
Backward compatible: InjectMissingFields() auto-fills SUBDOMAIN from
.felhom.yml defaults for existing deployed apps on next sync/restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Domain field now displays subdomain.base_domain (e.g. wiki.demo-felhom.eu)
instead of just the base domain, matching the app card display.
Applies to both pre-deploy and post-deploy settings pages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Pre-generate domain + secret field values when deploy page loads,
so user sees actual domain and masked passwords (with reveal button)
before deploying. Same values submitted as hidden inputs → saved to app.yaml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- detect_storage_paths(): also matches drives that have .felhom-infra-backup/
so sys_drive-style mounts (internal SSD partitions with DR markers) are
detected even when settings.json is gone and felhom-data/ doesn't exist.
- do_nuclear_wipe(): rmdir all empty /mnt/*/ dirs at end of nuclear wipe
to clean up leftover mount point directories (e.g. /mnt/hdd_1 when the
raw mount was already cleaned by a prior wipe run). rmdir is safe — it
refuses non-empty directories.
- print_plan(): show per-drive .felhom-infra-backup entries for nuclear level.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After unmounting /mnt/hdd_1 (bind) and /mnt/.felhom-raw/hdd_1 (raw),
the /mnt/hdd_1 directory remained as an empty directory. Now rmdir is
called on each bind target after unmounting so the mount point is fully
cleaned up. rmdir (not rm -rf) ensures we only remove truly empty dirs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[ -f "$f" ] && rm -f "$f" && info "..." returns exit code 1 when the
file doesn't exist, triggering set -euo pipefail. Nuclear wipe was
silently stopping after settings.json and metrics.db (the only two
state files present), never reaching the controller/full/nuclear steps.
Fix: if [ -f "$f" ]; then rm -f "$f" && info "..."; fi
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docker-setup.sh install_filebrowser(): removed /mnt/* auto-discovery;
FileBrowser now installed with no drive volumes. Initial config.yaml
written with /srv fallback. Controller's SyncFileBrowserMounts() takes
over on first startup and manages volumes/config going forward.
- Added ./config.yaml bind mount to initial docker-compose.yml so
FileBrowser starts correctly before controller syncs.
- Fixed ((step_num++)) → step_num=$(( step_num + 1 )) to prevent
set -euo pipefail trap when var starts at 0 (same class of bug as
the found_mounts fix in the previous commit).
- scripts/README.md: step 7 updated to reflect controller-managed volumes.
- CHANGELOG.md: added entry for all scripts changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
((found_mounts++)) with found_mounts=0 evaluates the post-increment
expression to 0, which bash treats as exit code 1 under set -e,
silently killing the script on the first mount discovered.
Use arithmetic assignment instead to avoid the zero-exit trap.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hub YAML is generated by Go's yaml.v3 which uses 4-space indentation.
The yaml_get helper was matching " key:" (2 spaces) so all extractions
silently returned empty — BASE_DOMAIN stayed as homeserver.local and
CF_TUNNEL_TOKEN was never set from hub config.
Strip leading whitespace before key matching, making yaml_get
indentation-agnostic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After docker system prune, the nuclear wipe now also removes:
- /opt/docker/felhom-controller/ (compose + .env)
- /opt/docker/traefik/ (configs + acme.json)
- /opt/docker/cloudflared/ (configs)
- /opt/docker/stacks/ (empty dir)
These were left behind previously, preventing a clean redeploy since
docker-setup.sh checks for existing installations and skips steps
if directories already exist.
Also updated print_plan to show these deletions in the dry-run output.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs prevented /mnt/sys_drive (and similar drives) from being detected:
1. controller.yaml is root-owned (permission denied from host), so data_dir
could not be read. Settings.json was never loaded, falling back to /mnt/*
scan only. Fix: also try `docker volume inspect felhom-controller_controller-data`
to locate the actual settings.json in the Docker volume.
2. Fallback /mnt/* scan only checked for felhom-data/ or appdata/, missing
drives that only have backups/ (e.g. sys_drive pre-v0.26.0). Fix: also
check for backups/ in the scan condition.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
'full' was ambiguously described as 'Controller + felhom-data/' making
it sound like the controller container is removed. Clarified that 'full'
runs controller-level cleanup (app containers only) and infra containers
(felhom-controller, traefik, etc.) are preserved throughout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Debug-mode-only dashboard (/debug) with 8 collapsible sections:
system diagnostics, notification testing, backup triggers, storage
simulation, hub & connectivity, self-update dry-run, DR/setup wizard,
and in-memory log viewer. Migrates debug dump from API router to web
server. Adds ring buffer log capture, storage disconnect simulation,
event history tracking, and cross-drive/self-update test methods.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dashboardHandler, stacksHandler, monitoringHandler used blank identifier
for the request param but now call executeTemplate(w, r, ...).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add internal/assets package that downloads and caches app assets from
Hub API with SHA-256 change detection. Assets resolve from synced cache
first, falling back to baked-in directory. Daily sync schedule +
on-demand POST /api/assets/sync endpoint.
Config: assets.sync_enabled + assets.sync_schedule (default 05:00)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The logo handler tried os.ReadFile() on a non-existent filesystem path.
The SVG only exists as an embedded string constant in the web package.
Export FelhomLogoSVG and serve it directly in the setup handler.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- NeedsSetup: only check for empty customer.id (not "demo-felhom")
- renderError: pass *http.Request to ensureCSRFToken (was nil → panic)
- Welcome template: remove redundant "v" prefix from version display
- IP detection: read HOST_IP env var for Docker container awareness
- docker-setup.sh: inject HOST_IP into generated docker-compose.yml
- Add logging for Hub config download in setup wizard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New controller features:
- Web-based setup wizard replaces docker-setup.sh interactive config
- Dual listener: :8080 (Traefik) + :8081 (direct HTTP for LAN)
- Drive scanner finds .felhom-infra-backup/ on all block devices
- Hub recovery pull (GET /api/v1/recovery/{id}) with retrieval password
- Fresh install: Hub config download or manual wizard
- CSRF protection, state persistence, Hungarian UI
- Local infra backup written to all connected drives after each backup cycle
- .felhom-infra-backup/backup.json + metadata.json with SHA256 checksum
- Hub verification: parse customer_blocked from report push response
- Limited mode after 7 days without verification
- Recovery info page on Settings + recovery-info.txt file generation
- Pending events queue: DR events sent to Hub on next report push
- docker-setup.sh v6.0.0: removed interactive wizard, minimal controller.yaml only
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After successful config apply, immediately push infra backup to Hub
so the config sync status updates right away. Also fix startup event
message that showed "vv0.21.2" instead of "v0.21.3".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
os.Rename() fails with "device or resource busy" on bind-mounted files.
Fall back to direct os.WriteFile when rename fails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Comment out ping_uuids section in controller.yaml.example (deprecated)
- Architecture diagram: remove status.felhom.eu, update to Hub event system
- Mark Healthchecks references as deprecated throughout README
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New endpoint returns raw controller.yaml content (text/yaml) for Hub
live diff and pull operations. Same auth as other config endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add config apply endpoint and config hash in reports to REST API
section, roadmap, and changelog.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>