The backend already supports warmup_schedule for periodic cache warmup,
but the field was missing from the web config editor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 4-step progress modal to config apply flow (validate, write, reload, services ready)
- Poll SSE-updated data-share-health attributes to detect when services finish restarting
- Fix stale health bug: recalculate health for affected shares based on actual mount
success instead of preserving old health from before reload
- Add modal overlay/card/step CSS matching the dark theme
- Include connection refactor (multi-protocol support) and probe helpers from prior work
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Task A: Offline mode banner in layout (nas_offline field in LayoutTemplate)
- Task B: Safe-to-disconnect sync indicator on dashboard (all_synced field)
- Task C: Preset apply buttons (photographer/video/office) in config tab with POST /api/preset/{profile} endpoint
- Task D: Reconnect button and error banner in share detail panel
- Added nas_offline/all_synced fields to DaemonStatus for integration contract
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Avoids competing with write-back uploads or read-cache fill I/O by
returning early if transfers > 0 or speed exceeds 10 KiB/s threshold.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add multi_thread_streams (default 4) and multi_thread_cutoff (default "50M")
fields to ReadConfig, wired into rclone mount args
- Expose both fields in Web UI config editor under Read Tuning section
- Add Samba performance options: TCP_NODELAY, large readwrite, max xmit
- Update config.toml.default with new fields and sftp_connections guidance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
update_status() previously acquired the shared_status write lock on the
first line and then called rc::vfs_stats() and rc::core_stats() (blocking
ureq HTTP) for every share while holding it. With dir-refresh flooding
the rclone RC port, these calls could take seconds, starving all web
handler reads and making the portal completely unresponsive.
Refactor to a two-phase approach: Phase 1 collects all RC stats with no
lock held; Phase 2 applies the results under a short-lived write lock
(pure memory writes, microseconds). Lock hold time drops from seconds to
microseconds regardless of rclone response latency.
Also included in this batch:
- vfs_refresh now reads the response body and surfaces partial failures
- dir-refresh iterates top-level FUSE subdirs instead of refreshing "/"
(rclone does not accept "/" as a valid vfs/refresh target)
- Track per-share dirs_ok / dirs_failed counts in DaemonStatus and
expose them through the web API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace scattered println!/eprintln! with structured tracing macros throughout
supervisor, scheduler, and web modules. Add LogConfig (file + level) to Config
and a new logging module that initialises a stderr + optional non-blocking file
appender on `warpgate run`. Remove the in-memory LogBuffer/LogEntry from
AppState; the web /api/logs endpoint now reads the log file directly with
from_line/lines pagination. `warpgate log` replaces journalctl with `tail`,
and the Logs tab Alpine.js is updated to match the new API response shape.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a ScheduledTask mechanism that periodically calls rclone RC
vfs/refresh to keep directory listing caches warm (no file downloads),
with two-level config (global default + per-share override). Adds
dir-refresh status badges and timestamps to the web UI shares tab and
CLI status output, following the same pattern as warmup/warmed.
- src/scheduler.rs: New generic ScheduledTask runner with generation-based
cancellation and parse_interval() helper
- src/rclone/rc.rs: Add vfs_refresh() RC API call
- src/config.rs: Add DirRefreshConfig, per-share dir_refresh_interval
override, effective_dir_refresh_interval() resolution method
- src/config_diff.rs: Track dir_refresh_changed for hot-reload
- src/daemon.rs: Track per-share last_dir_refresh timestamps (HashMap),
add dir_refresh_ago_for() helper and format_ago()
- src/supervisor.rs: spawn_dir_refresh() per-share background threads,
called on startup and config reload
- src/web/api.rs: Expose dir_refresh_active + last_dir_refresh_ago in
ShareStatusResponse
- src/web/pages.rs: Populate dir_refresh_active + last_dir_refresh_ago
in ShareView and ShareDetailView
- templates/web/tabs/shares.html: DIR-REFRESH badge (yellow=pending,
green=N ago) in health column; Dir Refresh row in detail panel
- templates/web/tabs/config.html: Dir Refresh section and per-share
interval field in interactive config editor
- src/cli/status.rs: Append Dir-Refresh suffix to mount status lines
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use rclone transferring array to show only active transfers instead of
cumulative count; zero out speed when no transfers are active
- Add SFTP retry/timeout flags to rclone mount for flaky Tailscale tunnels
- Skip auto-refresh on config tab to prevent editor resets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
spawn_warmup() returned early when rules were empty without clearing
status.warmup, leaving orphaned entries visible in web UI and CLI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Periodic client-side refresh for Shares/Config tabs using Alpine.js
setInterval, with toggle and configurable interval (2-30s) in header.
Dashboard (SSE) and Logs (own polling) are excluded. Shares tab
preserves row expansion state across refreshes via ?expand= param.
Adds [x-cloak] CSS rule and conditional x-cloak on detail rows to
prevent flash during content swaps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Warmup config changes via the web UI now actually run warmup without requiring
a daemon restart. Adds generation-based warmup tracking with progress reporting
across CLI status, JSON API, SSE live updates, and web UI badges/detail panels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace raw TOML textarea with Alpine.js interactive form editor (10 collapsible
sections with change-tier badges, dynamic array management for connections/shares/
warmup rules, proper input controls per field type)
- Add SSE-based live dashboard updates replacing htmx polling
- Add log viewer tab with ring buffer backend and incremental polling
- Fix SMB not seeing new shares after config reload: kill entire smbd process group
(not just parent PID) so forked workers release port 445
- Add SIGHUP-based smbd config reload for share changes instead of full restart,
preserving existing client connections
- Generate human-readable commented TOML from config editor instead of bare
toml::to_string_pretty() output
- Fix Alpine.js 2.x __x.$data calls in dashboard/share templates (now Alpine 3.x)
- Fix toggle switch CSS overlap with field labels
- Fix dashboard going blank on tab switch (remove hx-swap-oob from tab content)
- Add htmx:afterSettle → Alpine.initTree() bridge for robust tab switching
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Before mounting, probe each share's remote path with `rclone lsf`
(10s timeout, parallel execution). Failed shares are skipped — they
never get mounted or exposed to SMB/NFS/WebDAV — preventing the
silent hang that occurred when rclone mounted a nonexistent directory.
- ShareHealth enum: Pending → Probing → Healthy / Failed(reason)
- Supervisor: probe phase between preflight and mount, protocol
configs generated after probe with only healthy shares
- Web UI: health-aware badges (OK/FAILED/PROBING/PENDING) with
error messages on dashboard, status partial, and share detail
- JSON API: health + health_message fields on /api/status
- CLI: `warpgate status` queries daemon API first for tri-state
display (OK/FAILED/DOWN), falls back to direct mount checks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the hierarchical single-mount design with independent mounts:
each [[shares]] entry is a (name, remote_path, mount_point) triplet
with its own rclone FUSE mount process and dedicated RC API port
(5572 + index). Remove top-level connection.remote_path and [mount]
section. Auto-warmup now runs in a background thread to avoid blocking
the supervision loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single-crate project doesn't need a subdirectory. Moves Cargo.toml,
src/, templates/ to root for standard Rust project layout. Updates
.gitignore and test harness binary paths accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Spawn all children (rclone, smbd, webdav) in isolated process groups
so Ctrl+C doesn't reach them directly — supervisor controls shutdown order
- Wait for rclone VFS write-back queue to drain before unmounting (5min cap)
- Prefer fusermount3 over fusermount, skip if already unmounted
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Warmup now checks the rclone VFS cache directory before reading each file
through the FUSE mount, skipping already-cached files for fast re-runs.
Also adds WarmupConfig with configurable rules that auto-execute when
the supervisor starts (best-effort, non-blocking).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- warmup: read files through FUSE mount instead of rclone copy to temp
dir. Files now actually land in rclone VFS SSD cache.
- samba: derive share name from mount point dir name instead of
hardcoded [nas-photos] (e.g. /mnt/projects → [projects])
- supervisor: use smbd long flags (--foreground, --debug-stdout,
--no-process-group, --configfile) for compatibility with Samba 4.19
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full Rust implementation of the warpgate NAS cache proxy:
- CLI: clap-based with subcommands (run, setup, status, cache, warmup,
bwlimit, speed-test, config-init, log)
- Config: TOML-based with env var override, preset templates
- rclone: VFS mount args builder, config generator, RC API client
- Services: Samba config gen, NFS exports, WebDAV serve args, systemd units
- Deploy: dependency checker, filesystem validation, one-click setup
- Supervisor: single-process tree managing rclone mount + smbd + WebDAV
as child processes — no systemd dependency for protocol management
Supervisor hardening:
- ProtocolChildren Drop impl prevents orphaned processes on startup failure
- Early rclone exit detection in mount wait loop (fail fast, not 30s timeout)
- Graceful SIGTERM → 3s grace → SIGKILL (prevents orphaned smbd workers)
- RestartTracker with 5-min stability reset and linear backoff (2s/4s/6s)
- Shutdown signal checked during mount wait and before protocol start
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop the read-only cache + SD Uploader design in favor of rclone VFS
native read-write caching. Key changes:
- SMB shares are now read-write, writes go to SSD and async write-back to NAS
- Remove SD card import/upload, metadata DB, self-built polling
- Simplify remote change detection to rclone --dir-cache-time
- Add dirty file management, write-back config, and related risks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of moving conflict files to a separate conflict/ directory,
keep them in the original directory with naming convention:
{name} (Warpgate Conflict {YYYY-MM-DD HH-mm}).{ext}
Benefits:
- Lightroom/Finder see both versions side by side
- Preserved extension ensures app compatibility
- Matches Dropbox/iCloud behavior users already know
- Conflict copies auto-sync to NAS via rclone (backed up)
Remote-deleted + local-dirty: file stays in place (no rename),
marked as orphan-conflict, user decides whether to re-upload.
Updated: decision matrix diagrams, scenario walkthroughs,
cache_files lifecycle, CLI commands, config section, directory
structure description.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Throughput-based congestion detection: when sustained throughput
drops >30% over sliding window with rising RTT, auto-reduce
write-back speed to 50% of current throughput, then probe back up
at +10% every 2 minutes.
- Throttle state visible via `warpgate status`
- User can disable with BW_ADAPTIVE=no
- Only affects write-back uploads, not read fetches
- New config: BW_ADAPTIVE, BW_ADAPTIVE_WINDOW, BW_ADAPTIVE_PROBE_INTERVAL
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SQLite WAL depends on POSIX file locks and shared memory (-shm),
which FUSE/network filesystems cannot support correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hotel/airport WiFi requires web-based captive portal authentication,
which is impossible on a headless device without this feature.
- New P1 feature 4.11: Setup AP + Captive Portal proxy
- Box auto-enters setup mode when no network is available
- Phone connects to temporary AP, completes portal auth via proxy
- Requires WiFi AP+STA concurrent mode
- Fallback options: USB tethering, mobile hotspot, ethernet, MAC clone
- New CLI commands: warpgate setup-wifi, warpgate clone-mac
- New config section for setup AP parameters
- Updated hardware requirements: WiFi module must support AP+STA
- Updated roadmap v1.5 to include setup AP
- Added risk entry and glossary terms
- Renumbered 4.12-4.23 accordingly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>