Skip to content

Releases: Egyan07/GhostBackup

GhostBackup v3.2.0 — Security Hardening & CI Fixes

03 Apr 10:49

Choose a tag to compare

TypeScript Migration

  • Full frontend TypeScript conversion: all 17 source files migrated from JS/JSX to TS/TSX with strict mode enabled.
  • Shared type definitions (types.ts): 20+ interfaces covering API responses, config, alerts, run status, and restore results.
  • Window bridge typing (global.d.ts): window.ghostbackup Electron IPC bridge fully typed.
  • tsconfig.json: strict, react-jsx, bundler module resolution, no emit.
  • ESLint upgraded: typescript-eslint parser added for TS/TSX files.
  • tsc --noEmit in CI: TypeScript type checking added to the lint job.

Electron Tests

  • 73 new Electron main-process tests (electron/tests/main.test.mjs): covers env file parsing, backend health polling, API token generation, credential validation, notification server auth, preload bridge API surface, IPC handler registration, CSP configuration, and credential persistence.
  • Separate vitest config for Electron tests (vitest.electron.config.js).

Security

  • Assert guards replaced (api.py, syncer.py): 9 assert statements replaced with if/raise RuntimeError — assertions are stripped by Python's O flag, so security checks must not depend on them.
  • Path traversal hardened (api.py): restore endpoint now checks raw path for .. segments and null bytes before .resolve(), which silently normalizes away.
  • Windows drive-letter validation added.
  • Encryption fail-hard mode (syncer.py): require_encryption config flag now logs SECURITY WARNING and raises on missing key, preventing silent fallback to unencrypted backups.
  • CSV formula injection blocked (LogsViewer.tsx): exported CSV cells starting with =, +, -, @, \t, \r are prefixed with a single quote to prevent Excel formula execution.
  • Restore concurrency guard (api.py): _restore_active flag with try/finally prevents overlapping restore operations that could corrupt the destination.
  • Thread-safe dashboard reads (api.py): _get_active_run_snapshot() returns a mutex-guarded copy of _active_run, preventing partial reads from concurrent threads.
  • 0 npm vulnerabilities: upgraded vite 5→7, vitest 1→4, plugin-react 4→5 — eliminates all 6 moderate esbuild/vite CVEs.
    Frontend
  • Page-level error boundary (ErrorBoundary.tsx, GhostBackup.tsx): PageErrorBoundary component with key={screen} auto-resets on navigation. Catches per-page render crashes without tearing down the whole app.

Documentation

  • Disaster recovery guide (RECOVERY.md): 6 recovery scenarios (lost key, corrupted DB, deleted .env.local, SSD failure, app won't start, corrupted files) with step-by-step instructions and prevention checklist.

CI

  • mypy type narrowing (api.py): added typing.cast() calls after if not all([...]): raise guards — mypy cannot narrow types through all(), causing 26 union-attr errors.
  • Electron 33 -> 41 (package.json): bumped to fix 15 high-severity CVEs (ASAR bypass, IPC vulnerabilities, use-after-free).
  • lodash override (package.json): forced 4.18.1 to fix code injection and prototype pollution CVEs in transitive dependency.
  • OpenAPI schema validation: new CI job validates FastAPI auto-generated schema (31 endpoints) and uploads openapi.json as artifact.
  • TypeScript CI gate: tsc --noEmit runs in the lint job alongside eslint and mypy.

Testing

  • 89 new tests: Electron main process (73), assert-guard verification (4), path traversal (2), encryption fallback (2), error boundary (3), CSV sanitization (4), restore concurrency (1)
  • 599 tests passing — 377 backend + 149 frontend + 73 electron

GhostBackup v3.1.0 — CI Hardening & Windows Installer

01 Apr 18:21

Choose a tag to compare

This release transitions GhostBackup into a production-grade tool with strict quality gates and a professional
Windows installation experience.

▎ ⚔️ Key Changes

▎ - Professional Distribution: Added a full Windows NSIS installer (GhostBackup_Setup.exe) for one-click deployment.
▎ - Security & Compliance: Documented the "No Cloud" Disaster Recovery strategy using local encryption + manual sync
(OFFSITE.md).
▎ - CI/CD Hardening: Enforced 90% backend and 60% frontend coverage gates. Any PR dropping coverage will now fail CI.
▎ - Quality Assurance: 510 tests passing (368 backend + 142 frontend). Added E2E integration tests for the full backup
cycle.
▎ - Documentation: Completely overhauled README with feature categorization and a cleaner "Quick Start" guide.

GhostBackup v3.0.0 — Security, Monitoring & Compliance

31 Mar 14:47

Choose a tag to compare

Security

  • Keyring encryption key protection (config.py): Secrets migrate to Windows Credential Manager via keyring library. Env var fallback for CI/headless. Key storage method shown in Settings and /health endpoint.
  • Backup immutability window (syncer.py, config.py): Backups within immutable_days (default 7) cannot be deleted by any prune operation. - Configurable, minimum 7 days.

Monitoring

  • Startup self-check (api.py): On launch, 5 random files from the last backup are verified against the manifest. Critical alert if any are corrupt or missing.
  • Deep health endpoint (api.py): GET /health/deep returns SSD status, backup age, encryption, spot-check, scheduler, drill status, and overall assessment for external monitoring tools.

Compliance

  • Restore drill tracking (manifest.py, scheduler.py): Every non-dry-run restore is automatically recorded as a drill. Scheduler escalates reminders: info at 30 days, warn + email at 37, critical + email at 44 days. Drill history viewable via API. Schema v4 migration.

Developer Experience

  • Structured error codes (errors.py): All key API errors return {code, message, fix}. 18 codes (GB-E001 to GB-E061) documented in SETUP.md.

  • Frontend displays actionable fix suggestions.

  • E2E integration test (test_e2e_pipeline.py): Full backup → verify → restore → byte-compare pipeline test with real encryption. 3 test variants.

Testing

  • 365 backend tests passing (up from 338)

GhostBackup v2.9.1 — Security Hardening & Code Review Fixes

31 Mar 14:44

Choose a tag to compare

Security

  • Encryption fail-hard mode (syncer.py): if encryption is enabled in config but the key is missing or initialisation fails, the app now refuses to start instead of silently running unencrypted. Critical for accounting firms handling sensitive data.
  • Key fingerprint no longer accesses private API (syncer.py): key_fingerprint now uses the stored derived key bytes instead of reaching into AESGCM._key — immune to cryptography library upgrades breaking key rotation detection.
  • SSD path validation (config.py): ssd_path and secondary_ssd_path now reject null bytes and URLs, preventing path injection via the config API.

Reliability

  • Manifest DB rotation (api.py): after every backup run, the manifest database is now backed up with a timestamped filename. The 3 most recent copies are kept — protects against corruption overwriting the only backup copy.
  • Notification server port conflict resolution (electron/main.js): port 8766 now runs through the same killPortConflict() check as the API port (8765), preventing silent notification failure.
  • Thread-safe run status reads (api.py): /run/status and /dashboard now return snapshot copies of _active_run under the run mutex, preventing partial reads from the thread pool.

Frontend

  • Verify endpoint returns results (api.py, Settings.jsx): the /verify endpoint is now synchronous — the UI correctly displays --verified/corrupt/missing counts instead of showing all zeros.
  • Expanded timezone dropdown (BackupConfig.jsx): timezone selector expanded from 3 to 13 options covering UK, Europe, Americas, and Asia-Pacific.

Fixes

  • Version sync: api.py now correctly reports v2.9.1 (was stuck on 2.8.0)
  • .gitignore fix: split concatenated start.batsrc/coverage/ into two proper entries
  • record_file_count (manifest.py): moved from class variable to instance variable for correctness

GhostBackup v2.9.0 — Notifications, CSV Export, Dark/Light Theme & CI Coverage

30 Mar 20:35

Choose a tag to compare

What's new

  • Desktop notifications for all backup outcomes — success runs now fire a Windows toast alongside the existing partial
    and failed toasts. You no longer need to check the UI to know a backup completed.
  • Dark/light theme toggle — a ☀️/🌙 button in the topbar switches themes. The choice persists across restarts via
    localStorage.
  • Audit log CSV export — a new Export CSV button on the Logs page downloads the full run history (up to 10,000 runs)
    via the new GET /runs/export endpoint. Useful for compliance records or external reporting.
  • Frontend coverage reporting in CI — npm run test:coverage now runs on every push across Node 18/20/22, generating a
    v8 coverage report. Current baseline: 63% statements, 73% branches.

Fixes

  • ESLint globals fix: localStorage added to browser globals, resolving the CI lint failure introduced by the theme
    toggle.
  • CI now uses npm install --legacy-peer-deps instead of npm ci to match how the lock file is generated on Windows.

Tests

338 backend + 134 frontend = 472 passing

GhostBackup v2.8.0 — Dashboard & Restore frontend tests (134 total, 472 passing)

30 Mar 18:15

Choose a tag to compare

This release completes the Vitest frontend test suite with full coverage of the two most complex UI pages—Dashboard
and RestoreUI.

What's new

dashboard.test.jsx — 35 tests
Covers the full Dashboard lifecycle: loading and error states, stat strip values, active run banner (progress %, files
count), run history heatmap, empty states, StatusPill rendering, next scheduled run countdown, SSD storage gauge
(mounted/disconnected/path/free space), folder status table, and auto-refresh interval cleanup on unmount.

restore.test.jsx — 31 tests
Covers the full restore workflow: loading and error states, run list with failed-run filtering, library selection and
deselection, destination path input, dry-run toggle (enabled/disabled label + button text), restore button disabled
state, API call verification with correct params, dry-run result panel, restore complete panel, file list rendering,
and the safety warning banner.

GhostBackup v2.7.0 — Reliability Hardening, Key Rotation Safety & Integration Test Coverage

28 Mar 22:54

Choose a tag to compare

This release addresses the three highest-priority engineering gaps remaining after v2.6.0: unbounded WAL growth on long-running installations, the
inability to detect silent restore failures after key rotation, and zero test coverage on the core backup execution path.


Reliability

  • WAL checkpoint after every run — finalize_run() now calls PRAGMA wal_checkpoint(PASSIVE) after each run commit. On a dedicated backup machine running
    continuously for months, SQLite's WAL file grows indefinitely without explicit checkpointing. This prevents silent disk pressure accumulation with no
    user-visible indicator.

Security

  • Encryption key fingerprint tracking — Each backed-up file now records a 16-character SHA-256 fingerprint of the AES-256-GCM derived key in a new
    key_fingerprint column (schema v3). On restore, if the fingerprint of the stored file doesn't match the current active key, a warning is logged before
    decryption is attempted. This catches the silent failure mode where a user rotates the encryption key, discards the old one, then attempts to restore a
    pre-rotation backup — previously there was no way to detect this until decryption failed with a cryptic error or produced corrupt output.
  • New _CryptoHelper.key_fingerprint and LocalSyncer.key_fingerprint properties — the fingerprint is a one-way hash of the key, never the key itself, and
    is safe to store in the manifest database.

Testing

  • 11 integration tests for run_backup_job (test_backup_job.py) — the core backup execution function was previously at 0% coverage. New tests cover the
    full scan → copy → record → finalize pipeline:
    • Success paths: no changed files, files copied and recorded, skipped file count, full backup flag, scheduler reset on success
    • Failure paths: SSD not ready (no run created), no sources configured (fatal alert), copy failure (files_failed incremented), duplicate run guard
      (no-op), missing source folder (library skipped with error recorded)
    • Source filtering: sources=["Accounts"] correctly limits scanning to the requested label
  • api.py line coverage: 66% → 81%
  • Total: 325 → 336 backend tests, 88% → 90% overall coverage
  • CI coverage threshold raised from 85% → 88%

GhostBackup v2.6.0 — Security Hardening, Reliability & Architecture Completion

28 Mar 21:49

Choose a tag to compare

Minor version bump. No user-facing UI changes.


Security

  • Per-installation HKDF salt — GHOSTBACKUP_HKDF_SALT environment variable replaces the previously hardcoded static salt used to derive the AES-256-GCM
    encryption key from the Fernet key material via HKDF. A unique salt per installation means that if the Fernet key is ever exposed, the AES key cannot be
    trivially derived without also knowing the salt. Falls back to the static default for full backward compatibility — existing encrypted backups are
    unaffected. New installations should set this variable on first launch. Configurable via the Electron credentials panel.

Reliability

  • .ghosttmp orphan cleanup on startup — LocalSyncer now scans the configured SSD path for leftover .ghosttmp temporary files from any previously
    interrupted backup run and removes them at startup. These files were previously accumulating silently and consuming disk space with no way to detect them
    short of a manual check.
  • record_file() periodic SQLite commit — file records are now committed to the manifest database every 100 inserts instead of only at run finalisation.
    Reduces the potential data loss window from the entire run down to the last 100 files in the event of a process crash mid-backup.

Architecture

  • run_backup_job fully dependency-injected — all module-level globals (_config, _manifest, _reporter, _syncer, _scheduler) have been replaced with
    explicit parameters throughout the entire backup job body. The POST /run/start endpoint now passes all injected services through. Falls back to module
    globals only when called from the scheduler or file watcher, which cannot use FastAPI Depends().
  • PATCH /config surfaces ignored keys — unknown fields in a config update payload are no longer silently dropped. They are now returned in the response as
    ignored_keys, so callers can detect typos or stale field names immediately without inspecting the resulting config.
  • Real schema migration — ManifestDB._migrate() now reads the current schema_version value from the database and applies numbered delta migrations in
    order, committing each step atomically. Schema v2 adds the library_summary column via ALTER TABLE. Previously, the version table was written but never
    read — column additions would have silently failed on existing databases.

Frontend

  • Visibility-aware alert polling (AlertBell.jsx) — the 15-second alert poll is now skipped when document.visibilityState !== 'visible'. Eliminates
    unnecessary background API calls when the GhostBackup window is minimised or on an inactive tab.

Testing

  • 6 new tests: ConfigManager.update() ignored keys, hkdf_salt default and env override, schema version on fresh DB, migration idempotency, db_path
    property
  • Total: 319 → 325 backend tests passing, 88% line coverage

GhostBackup v2.5.3 — Public API Completion, Test Expansion & Bug Fix

28 Mar 21:04

Choose a tag to compare

Patch release. No user-facing changes.

Backend — Encapsulation

  • LocalSyncer.encryption_active property — replaces direct _crypto.enabled access from api.py; crypto state is now fully encapsulated within the syncer
  • ManifestDB.db_path property — replaces direct _path access from api.py; no module outside manifest now reads private attributes
  • All private attribute access from api.py into other modules is now eliminated

Backend — Bug Fix

  • generate_health_report crash fixed — _HEALTH_REPORT_TEMPLATE used Python .format() on a string containing CSS curly braces, which would raise KeyError
    every time the health report was generated. Replaced with .replace() calls. This was a silent production bug.

Testing

  • Scheduler tests — 10 new tests covering BackupScheduler lifecycle (start, stop, is_running, reschedule, reset_missed_alert, set_current_run_id) and
    retry logic (_run_with_retry success, retry on failure, exhausted attempts, retry count reset). Coverage: 28% → 70%
  • Reporter tests — 9 new tests covering _send_email (skipped with no user/recipients, SMTP called, STARTTLS, login with/without password),
    send_test_email, and generate_health_report (path returned, HTML content). Coverage: 56% → 71%
  • Total: 300 → 319 tests passing, 85% → 88% overall coverage

GhostBackup v2.5.2 — Exception Specificity & Test Coverage Expansion

28 Mar 20:53

Choose a tag to compare

Patch release. No user-facing changes.

Backend

  • Exception specificity in syncer.py — two broad except Exception blocks narrowed to specific types:
    • restore_files: now catches (PermissionError, OSError, RuntimeError) — the only errors that decrypt/copy operations can raise
    • verify_backups: now catches (OSError, RuntimeError) — the only errors from file hash reads
    • Remaining broad catches (secondary SSD copy, crypto init, disk partition scan) are intentionally kept — those paths must not propagate

Testing

  • 8 new FileWatcher unit tests — covers the previously untested lifecycle: start, stop, reload_sources, double-start idempotency, stop-when-not-running
    safety, status(), and dispatch with no/closed event loop. watcher.py coverage: 56% → 92%
  • 7 new API endpoint tests — watcher start (already running / with sources), watcher stop when running, prune accepted/rejected, dashboard keys, SSD
    status
  • Total: 285 → 300 tests passing, 83% → 85% overall coverage