• v0.4.1 d2b54323a4

    serversdown released this 2026-06-30 01:33:58 -04:00 | 0 commits to main since this release

    [0.4.1] - 2026-06-25
    Fixed
    Auto-updater no longer dies when settings are saved. Saving the Settings dialog restarts the watcher thread via _restart_watcher, which sets and then replaces the shared stop_event. Because the updater loop keyed off that same event, saving settings would terminate the auto-updater thread (a race — it could survive one save and die on the next). The watcher kept running and reporting heartbeats normally, but the agent silently stopped checking for / applying updates until the tray app was relaunched. The updater now keys off its own app-lifetime event (_app_stop) that watcher restarts never touch; it stops only on Exit or when applying an update.
    Added test_thor_tray.py covering the tray thread lifecycle (updater survives a watcher restart; stops on app exit; _exit/_do_update signal the lifetime event).

    Downloads
  • v0.4.0 baf8aade2d

    serversdown released this 2026-06-25 12:42:04 -04:00 | 3 commits to main since this release

    Added — Mirror (dual-send)

    • Optional best-effort mirror server. Each heartbeat and each forwarded event can now be sent to a second ("mirror") server in addition to the primary, so a standby box (e.g. the office NAS) stays a continuous replica during a migration — making the eventual cutover a non-event. Default off — blank mirror_api_url/mirror_sfm_url means existing installs are unchanged after auto-update.
    • Heartbeat mirror (best-effort). After the primary heartbeat POST, the same payload is fired at mirror_api_url when set. Bounded by the existing API timeout and fully wrapped — it can never delay or fail the primary heartbeat; its result is ignored except for a debug log line.
    • Event mirror (reliable, isolated). Events are re-forwarded to mirror_sfm_url using a separate sha256 state file (mirror_sfm_state_file, blank → <log_dir>/thor_forwarded_mirror.json), so the mirror tracks its own delivery independently of the primary. A quick reachability probe (~3 s) skips the mirror pass when the mirror server is down, so a dead mirror never stalls the loop on per-event timeouts; skipped events stay pending in the mirror state and deliver when the mirror returns — no data loss.
    • Isolation invariant (the one rule). Nothing on the mirror path can delay or fail the primary: its own state file, its own try/except, the reachability guard + bounded timeouts, all exceptions swallowed-and-logged. The primary path stays exactly as reliable as before.
    • Mirror tab in the Settings dialog with Mirror API URL + Mirror SFM URL fields (blank = off). The mirror rides along with the primary — there is no separate enable flag (heartbeat-mirror active iff the primary heartbeat is on and mirror_api_url is set; event-mirror active iff primary forwarding is on and mirror_sfm_url is set).
    • New unit tests in test_event_forwarder.py covering mirror reachability, the down-mirror skip, separate-state idempotency, and the isolation invariant (a mirror failure leaves the primary's result + state untouched).

    Configuration

    New config.json keys (all default-empty = off; existing 0.3.x deployments don't change behaviour on auto-update):

    Key Default Notes
    mirror_api_url "" Second heartbeat destination — same URL form as api_url (blank = off)
    mirror_sfm_url "" Second SFM base URL, e.g. http://10.0.0.x:8200 (blank = off)
    mirror_sfm_state_file "" Override the mirror's forwarded-sha256 state file. Blank → <log_dir>\thor_forwarded_mirror.json

    Changed

    • Bumped VERSION to 0.4.0

    Operational note (migration seeding). To make the mirror re-deliver only the gap since a one-time replica snapshot, copy the primary state file to the mirror state file and drop entries whose forwarded_at is after the snapshot. SFM-side dedup covers any overlap. See docs/mirror-dual-send-design.md.

    Downloads
  • v0.3.1 05429bdda3

    serversdown released this 2026-06-01 17:36:28 -04:00 | 13 commits to main since this release

    Little fix to help the gitea updater work. Should be good with auto updates now!

    Downloads
  • v0.3.0 76387ad17a

    v0.3.0 Stable

    serversdown released this 2026-05-19 18:43:30 -04:00 | 16 commits to main since this release

    Adds the ability to set an SFM server location to forward events to sfm db.
    fixes the auto updater too.

    Downloads
  • v0.2.0 0e2fe7c1e3

    v0.2.0 Stable

    serversdown released this 2026-03-20 18:25:15 -04:00 | 18 commits to main since this release

    [0.2.0] - 2026-03-20

    Note: this should probably be 1.0, but oh well. its 0.2 for now

    Added

    • thor_tray.py — system tray launcher with status icon (green/amber/red/grey), Settings and Open Log Folder menu items
    • thor_settings_dialog.py — Tkinter settings dialog with first-run wizard; tabs for Connection, Paths, Scanning, Logging, Updates
    • Hardened auto-updater: three-layer download validation (100 KB floor, 50% relative size floor, MZ magic bytes), safer swap bat with 5-retry cap and :fail exit, .exe.old backup
    • Configurable update source: update_source (gitea / url / disabled), update_url for custom server
    • Remote push support: update_available flag in API heartbeat response triggers update regardless of update_source setting
    • build.bat — PyInstaller build script; outputs versioned exe for Gitea and plain copy for Inno Setup
    • installer.iss — Inno Setup installer script with startup shortcut
    • _update_log() helper writes timestamped [updater] lines to the watcher log
    • log_tail included in heartbeat payload (last 25 lines) for terra-view display
    • run_watcher(state, stop_event) pattern in series4_ingest.py for background thread use from tray

    Changed

    • series4_ingest.py refactored into tray-friendly background thread module; main() retained for standalone use
    • Config key sfm_endpoint renamed to api_url for consistency with series3-watcher
    • Heartbeat payload now uses source_id, source_type, version fields matching terra-view WatcherAgent model
    • AppData folder: ThorWatcher (was not previously defined)
    Downloads