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>
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>
[ -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>
'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>
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>
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>
When both flags are provided, the wizard downloads a pre-configured
controller.yaml from the Hub API, extracts key variables for subsequent
setup steps, and skips the interactive wizard entirely. Falls back to
manual wizard on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1: Deprecate restic as Tier 2 method (rsync only), auto-migrate on startup
Phase 2: Enhanced per-app migration with backup awareness, DB dump copy, auto-cleanup
Phase 3: Full drive migration with decommissioned state, rollback support, wizard UI
Phase 4: Hub report includes decommissioned drive state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add StorageBars to backupsHandler so all registered storage paths appear
- Update backups.html to use StorageBars loop (replacing single HDDConfigured block)
- Rename "SSD (/)" → "Rendszer (/)" on backup, monitoring, and dashboard pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New Settings wizard to attach drives with existing filesystems without
formatting. Mounts partition at staging path, lets user browse and pick
a subfolder, then bind-mounts it at /mnt/<name> with fstab entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Tier 2 cross-drive backup now configurable for all apps (not just HDD apps)
- Non-HDD apps (Mealie, Gokapi) can back up config + DB to secondary drive
- Status dot: removed "auto" gray — all apps start yellow, green = 2+ tiers OK
- Backup page: Tier 2 row always shown, Tier 3 placeholder added
- Deploy page: cross-drive config visible for all deployed apps
- Meta badges: non-HDD apps show "Konfig" or "Konfig + DB" instead of "Auto"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Cross-drive now copies DB dumps (_db/) and config (_config/) alongside user data
- restic cross-drive includes config dir + full DB dump dir
- UI: per-tier rows (1. mentés / 2. mentés) instead of per-layer (DB/Konfig/Data)
- UI: BackupContents label shows what each tier protects (DB + Konfig + Adatok)
- UI: rsync backups show browsable indicator (📁)
- Cleanup: removed unused filterSnapshotsByPaths + pathCovers from router.go
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix 1: HDD data backup is now mandatory for all deployed apps.
resolveAppBackupPaths() iterates ListDeployedStacks() directly — no
longer reads GetAppBackupMap() or checks the Enabled flag. DiscoverAppData()
drops backupPrefs parameter; BackupEnabled is set from HasHDDData.
Five dead settings methods removed: IsAppBackupEnabled, SetAppBackup,
GetAppBackupMap, SetAppBackupBulk, GetAppBackupPrefs.
Fix 2: Cross-drive backup now triggers a fresh DB dump (DumpStackDB)
before running. DBDumper interface added to crossdrive.go; Manager
implements it; SetDBDumper wired in main.go. Non-fatal — proceeds with
user data backup even if DB dump fails.
Fix 3: Restore dropdown shows ALL deployed apps (not just HDD+enabled).
restore.go rewritten: always restores config+DB, adds user data if hasHDD.
UI shows restore type banner (full / config+DB / config only) with
color-coded styling. Snapshot API clarified for non-HDD apps.
Fix 4: "Docker kötetek" → "Konfiguráció" — named volumes are not in
the restic backup paths; compose files + app.yaml are what's backed up.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two rsync bugs found during Immich cross-drive backup testing:
Fix 3: Simplified destination path structure in runRsyncBackup.
Old SplitN logic kept "storage/immich" as a subpath, creating redundant
nesting: backups/rsync/immich/storage/immich/<data>. New logic: single
mount → rsync directly into the stack folder; multiple mounts → use
each mount's leaf dir name as subfolder (disambiguated by _N suffix).
Fix 4: Exclude app-internal DB dump files from rsync. Apps like Immich
store periodic pg_dumps in <data>/backups/*.sql.gz. The controller
already handles DB backups via pg_dump — copying them again wastes space.
Added --exclude backups/*.sql.gz/sql/dump to the rsync command.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix cross-drive backup failing for system-drive destinations. The
/mnt/hdd_placeholder folder is on the internal SSD, so IsMountPoint()
returned false and the old code hard-blocked it.
- ValidateDestination() (crossdrive.go): onSystemDrive flag replaces
boolean-only check; system drives require ≥10 GB free and <90% usage
to protect OS stability; external drives just need ≥100 MB free.
- CheckBackupDestination() (mounts_linux.go): Tier 4 now branches on
h.SystemDrive; system drive blocks at <10 GB free or ≥90% usage
(matches runner enforcement); external drive unchanged (warn 90%,
block 95%). Removes the && h.Severity == "ok" guard that was
preventing system-drive critical messages from overriding warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StackDataProvider interface extended with StopStack/StartStack
- backup.Manager.GetStackHDDMounts() delegates to stackProvider
- RestoreApp() auto-stops app before restic restore, restarts after (even on failure)
- stackAdapter in main.go wires StopStack/StartStack through to stacks.Manager
- GET /api/backup/snapshots?stack={name} filters snapshots by app HDD paths via filterSnapshotsByPaths()
- Restore section simplified: no path list, per-app filtered snapshots, human-friendly timestamp format, single calm warning, empty-result inline message
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Bug fixes:
- GetFullStatus() returns deep copy; CrossDriveSummary/UnconfiguredApps/CrossDriveWarnings
are always nil in the copy so the handler builds them fresh (fixes duplicate-apps bug)
- Replace binary IsMountPoint check with tiered CheckBackupDestination() — path-not-exist,
not-writable, system-drive (warning), disk >90-95% full; shown as warning vs critical
- Remove dead settingsAppBackupHandler / POST /settings/app-backup route (toggle wrote
to settings.json but nothing consumed the flag)
Architecture:
- Unified per-app backup rows: new AppBackupRow struct + buildAppBackupRows() replaces
the two old sections with expandable rows showing all 3 layers per app
- Sequential backup chaining: cross-drive runs immediately after restic (removed
independent cross-drive-daily/cross-drive-weekly scheduler jobs)
- Deploy page: remove "Csak kézi indítás" schedule option; add weekly consistency note
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Detect stale data on non-active storage paths after migration; show on
deploy/settings page with size info and two-step delete confirmation
- Add POST /api/storage/stale-cleanup handler with safety checks (active
path protection, registered-path validation, ProtectedHDDPaths guard)
- Export ProtectedHDDPaths() from stacks package for reuse in web handlers
- Sync FileBrowser mounts after successful app data migration
- Deploy page title/h2 now shows "Beállítások" for already-deployed apps
instead of always showing "Telepítés"
- Also add delete-old-data button on migration-done card in migrate.html
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>