Skip to content

Latest commit

 

History

History
141 lines (116 loc) · 11.8 KB

File metadata and controls

141 lines (116 loc) · 11.8 KB

Endur Developer Guide

This document explains the internal architecture, design patterns, and code structure of endur. It is intended for developers who wish to modify, debug, or extend the codebase.


System Architecture

The following diagram illustrates the relationship between the CLI client, the background Daemon, the filesystem, and the Git repository:

graph TD
    Client[CLI Client: endur watch/list-snapshots/restore]
    Daemon[Daemon: endur serve]
    LockFile[Lock File: runtime.lock]
    UDS[Unix Domain Socket: UDS IPC]
    FS[File System]
    Index[Isolated Git Index: endur_index]
    Repo[Git Repository]

    Client -->|IPC Commands| UDS
    UDS -->|RPC handler| Daemon
    Daemon -->|Exclusive Advisory Lock| LockFile
    Daemon -->|File Events| FS
    FS -->|Notify Event Channel| Daemon
    Daemon -->|Stage changes| Index
    Daemon -->|Commit to endur/HEAD branch| Repo
Loading

Core Components

1. Command-Line Interface (src/main.rs)

  • Subcommand Routing: Parses arguments using clap and routes them.
  • Daemon Notification: Subcommands like watch and unwatch perform the local configuration changes and notify the running daemon using the poller::send_uds_command helper.
  • Direct Command execution: Core recovery tasks like list-snapshots and restore execute directly in the client context by parsing the Git repository, avoiding UDS overhead.

2. Runtime Locking & Single Instance enforcement (src/database.rs)

  • RuntimeLock struct: Enforces that only one daemon instance is active.
  • Exclusive Advisory Locking: Utilizes fs2::FileExt::try_lock_exclusive to acquire an OS-level lock on the file ~/.cache/endur/runtime.lock (and database at runtime.db).
  • Auto-Cleanup: The lock is automatically released by the operating system when the daemon process terminates.

3. Client-Server IPC (src/poller.rs)

  • Unix Domain Sockets: The daemon binds to local sockets using tokio::net::UnixListener on both Unix and Windows 10/11 (where UDS is natively supported):
    • macOS/Linux: ~/.cache/endur/endur.sock
    • Windows: %AppData%\Local\endur\endur.sock
  • JSON-RPC Protocol: Communication uses simple JSON payloads:
    • reload: Reloads configuration when watched repositories change.
    • kill: Triggers a graceful shutdown of the daemon.
    • status: Responds with daemon metrics.

4. File Watcher & Filter Engine (src/watcher.rs)

  • WatcherManager struct: Manages recursive file system monitoring using the notify crate.
  • Path Canonicalization: Filesystem events on macOS and Linux often report relative paths or symlink roots (like /var vs /private/var). The watcher canonicalizes all event paths prior to comparison.
  • Git-Aware Filtering:
    • Excludes .git/ folder and its contents automatically.
    • Loads .gitignore files using ignore::gitignore::Gitignore to match change events against gitignore rules and discard ignored files.

5. Daemon Control Loop (src/poller.rs)

  • Event Await & Debounce: The daemon loop listens to four event sources using tokio::select!:
    • OS Signal Handler Channel: Listens to target-specific shutdown signals pinned to the stack (wait_for_signal()). On Unix, handles SIGINT, SIGTERM, and SIGHUP. On Windows, handles Ctrl+C and Ctrl+Break events.
    • IPC Command Channel: Process socket operations.
    • File Watcher Channel: Pushes modified files. Captured file changes are cached in a hash map with a 500ms delay.
    • Timer Channel: Triggers a capture operation for repositories that have modifications older than 500ms.
  • Graceful Exit Actions: When a shutdown signal is received, the loop sends a broadcast termination signal to UDS socket handlers, clears the RuntimeLock PID metadata value back to None, and exits cleanly.
  • Optimization: Debouncing prevents multiple rapid writes (e.g., compilation artifacts or quick editor saves) from creating dozens of intermediate commits.

6. Isolated Snapshot Capture & SQLite Caching (src/snapshots.rs, src/cache.rs)

  • snapshots::capture: Stages changes and commits them.
  • Git Index Isolation: Rather than using the user's primary Git index (.git/index), it creates and maintains a dedicated staging index file at .git/endur_index via git2::Index::open.
  • Branch Architecture:
    • Backups are committed to local branches named endur/<base-commit-hash>.
    • The parent of the first endur snapshot is the user's HEAD commit. Subsequent backups chain off the previous endur backup commit, keeping histories completely linear.
  • SQLite Metadata Cache:
    • To keep reads fast, snapshot metadata is cached in a local SQLite database at $ENDUR_CACHE_HOME/snapshot_cache.db (usually ~/.cache/endur/snapshot_cache.db).
    • Schema: snapshots (repo_path TEXT, commit_hash TEXT PRIMARY KEY, base_hash TEXT, timestamp INTEGER, message TEXT, files_changed TEXT).
    • An index is placed on (repo_path, timestamp DESC) to speed up list queries.
    • The cache database uses Write-Ahead Logging (WAL) mode to permit concurrent reads during background writes.
    • Transparent Fallback: The caching module functions on a best-effort basis. If the SQLite database is corrupt, locked, or fails to open, it fails silently and falls back directly to walking the repository's Git commits.
  • Snapshot Filtering:
    • snapshots::list_snapshots(path, show_all) queries the cache first (falling back to a Git walk if cold or failed).
    • When show_all is false (default), snapshots are filtered to only those whose base_hash matches the current HEAD commit hash. This hides snapshots that are older than the latest formal commit.
    • When show_all is true, all snapshots recorded for the repository are returned.

6b. Snapshot Pruning Logic (src/prune.rs)

  • prune::prune: Filters and deletes historical snapshot branches.
    • By Ancestry: Resolves the target commit OID, walks back the history to identify its ancestors, and marks any branch endur/P where P is an ancestor of T (and P != T) for deletion.
    • By Count: Walks back from HEAD history to keep the last N formal commits, marking older branches for deletion.
    • By Age: Compares the timestamp of the latest backup commit in endur/P branches to system time and prunes branches older than a duration.
    • SQLite Cache Eviction: Synchronously deletes database cache entries matching pruned base hashes via cache::delete_snapshots_for_base to ensure metadata queries instantly reflect the pruning.
    • Git GC: Optionally spawns a git gc --prune=now subcommand to purge deleted commits and reclaim disk space immediately.

7. Interactive TUI Restore & Control Center (src/tui.rs)

  • Decoupled TuiState: Separates core snapshot-browsing state management (watched repositories, snapshots, modified file listings, checkmarked files, and pane focusing) from the rendering engine. This keeps state management clean and modular, allowing the backup-browsing logic to be reused and integrated within the unified multi-tab Control Center.
  • Asynchronous Control Center TUI (endur tui):
    • Tokio Event Loop: Uses an async main loop running on Tokio to merge multiple non-blocking event sources: Crossterm keyboard events, periodic daemon PID/uptime status checks, and log file tail changes.
    • Live Log Streaming: Spawns an asynchronous log tail watcher that tails changes to endur.log and feeds new lines directly into the state machine to trigger real-time UI updates.
    • Multi-Tab Architecture: Tracks the active view using a ControlCenterTab enum (tabs: Repositories, Backups, Log, Metrics).
  • Safe Terminal RAII: An RAII wrapper TerminalGuard manages terminal raw mode, alternate screen buffers, and cursor visibility, ensuring the shell is always cleanly restored on program exit or panic.

8. Background Service Management (src/service.rs)

  • macOS (launchctl Integration): Creates ~/Library/LaunchAgents/com.endur.daemon.plist configured to run endur serve in the background, and registers it using the modern launchctl bootstrap framework (falling back to launchctl load if necessary).
  • Linux (systemd Integration): Configures a systemd user unit at ~/.config/systemd/user/endur.service and executes systemd commands to daemon-reload, enable, and start/stop the service.

9. Desktop GUI Application (crates/endur-desktop)

The Desktop GUI is structured as a Cargo Workspace member separate from the core endur library to ensure independent versioning and lean dependencies.

  • Tauri 2.0 Backend (crates/endur-desktop/src-tauri):
    • IPC Commands (src-tauri/src/commands.rs): Exposes Tauri command endpoints (e.g. get_daemon_status, control_daemon, get_watched_repositories, toggle_watch_repo, get_snapshots, get_snapshot_files, get_snapshot_diff, restore_files, get_metrics_summary, get_log_tail) that translate IPC calls into calls to the core endur library APIs.
    • Core Integration: Directly links the local endur crate path dependency, sharing the same RuntimeLock, Config, poller, and snapshots modules.
    • Shared Log Formatting: Uses the public format_log_line API from endur::tui to parse and filter daemon logs into clean, human-readable lines in reverse-chronological order (most recent first) before returning them to the UI.
  • Svelte 5 Frontend (crates/endur-desktop/src):
    • SPA Structure (src/routes/+page.svelte): Fully interactive Single Page App styled with a premium glassmorphic dark-theme palette using modern CSS variables, scrollbars, and active status animations.
    • IPC Bridge: Communicates with the Rust backend using Tauri's invoke API.
    • Log Polling Loop: Uses a 1.5s interval to pull daemon logs from get_log_tail and refresh the live log panel.
  • CI/CD Release Packaging (.github/workflows/release-desktop.yaml):
    • Runs on tag pushes matching desktop-v* or manual workflow runs.
    • Builds Node dependencies, compiles the Rust backend, and uses tauri-apps/tauri-action to compile and bundle native installers: macOS DMG, Windows MSI/EXE, and Linux DEB/AppImage.

Development & Testing Workflow

Compiling

cargo build

Running Tests

The test suite consists of several test suites covering locking, IPC, watcher, and snapshotting:

cargo test

Key Integration Tests

  • tests/startup_test.rs: Tests UDS communication (now verified on both Unix and Windows), OS signal graceful shutdown, invalid lock files, and double-lock prevention.
  • tests/watch_test.rs: Tests watcher registration and event-driven backup snapshots.
  • tests/snapshots_test.rs: Tests Git index isolation, git ignore support, listing snapshots, and restoring snapshots.
  • tests/poll_guard_test.rs: Tests poller lock mechanics and state changes.