-
v0.4.1 - Auto-updater fix. Stable
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
-
released this
2026-06-25 12:42:04 -04:00 | 3 commits to main since this releaseAdded — 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_urlmeans existing installs are unchanged after auto-update. - Heartbeat mirror (best-effort). After the primary heartbeat POST, the same payload is fired at
mirror_api_urlwhen 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_urlusing 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 URLfields (blank = off). The mirror rides along with the primary — there is no separate enable flag (heartbeat-mirror active iff the primary heartbeat is on andmirror_api_urlis set; event-mirror active iff primary forwarding is on andmirror_sfm_urlis set). - New unit tests in
test_event_forwarder.pycovering 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.jsonkeys (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.jsonChanged
- Bumped
VERSIONto0.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_atis after the snapshot. SFM-side dedup covers any overlap. Seedocs/mirror-dual-send-design.md.Downloads
- 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
-
v0.3.1 - Settings fix Stable
released this
2026-06-01 17:36:28 -04:00 | 13 commits to main since this releaseLittle fix to help the gitea updater work. Should be good with auto updates now!
Downloads
-
v0.3.0 Stable
released this
2026-05-19 18:43:30 -04:00 | 16 commits to main since this releaseAdds the ability to set an SFM server location to forward events to sfm db.
fixes the auto updater too.Downloads
-
v0.2.0 Stable
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 itemsthor_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
:failexit,.exe.oldbackup - Configurable update source:
update_source(gitea / url / disabled),update_urlfor custom server - Remote push support:
update_availableflag in API heartbeat response triggers update regardless ofupdate_sourcesetting build.bat— PyInstaller build script; outputs versioned exe for Gitea and plain copy for Inno Setupinstaller.iss— Inno Setup installer script with startup shortcut_update_log()helper writes timestamped[updater]lines to the watcher loglog_tailincluded in heartbeat payload (last 25 lines) for terra-view displayrun_watcher(state, stop_event)pattern inseries4_ingest.pyfor background thread use from tray
Changed
series4_ingest.pyrefactored into tray-friendly background thread module;main()retained for standalone use- Config key
sfm_endpointrenamed toapi_urlfor consistency with series3-watcher - Heartbeat payload now uses
source_id,source_type,versionfields matching terra-view WatcherAgent model - AppData folder:
ThorWatcher(was not previously defined)
Downloads