Carry SHA-256 hashes for the source zip, MSI, and setup EXE into release_digests.json while retaining prior release entries.
Co-authored-by: Cursor <cursoragent@cursor.com>
Re-cut v0.9.81 binaries from current main (which now includes the
private gate + DM hashchain spool from #326 and the gate-directory
test from #327). All three artifacts were signed with the same
minisign updater key as the original v0.9.81 release, so existing
v0.9.81 installs on Tauri auto-update accept the new bundles.
Updated hashes (verified against released assets):
- ShadowBroker_v0.9.81.zip f81f454bdc88e9a32c351df38212b8cfa624704d65764b971bb091eef62259c6
- ShadowBroker_0.9.81_x64-setup.exe 25e9a95d0d8ce959a7d08fe8e7406772ae24b596652793e81d1de5d02510a5a6
- ShadowBroker_0.9.81_x64_en-US.msi 34e655fc0c0f195ee4ac978f228a4b2b9d5565253b8771aca9ef4693409e9e70
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(start-scripts): find bundled privacy_core.dll next to script
start.bat and start.sh only checked the source-tree DLL path
(``privacy-core/target/release/privacy_core.dll``), not the bundled
location where MSI/AppImage/DMG installers stage the library directly
next to the script in backend-runtime/.
Users running start.bat from inside an MSI install dir (a documented
workaround when the desktop shell crashes) saw a scary "install Rust"
warning even though the DLL was sitting right next to them. See issue
#319 for the user-reported confusion.
Fix: add a fallback check for the bundled location before falling
through to the "build privacy-core from source" warning. Source-tree
behavior unchanged — the source path is still preferred when present.
Also re-stamps the v0.9.81 source archive: ``release_digests.json``
v0.9.81 zip hash updated to point at the rebuilt source archive that
contains these script changes. MSI/EXE/sig hashes are unchanged (the
scripts live at the repo root, not inside the desktop bundle).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(#319): bundle start.bat + start.sh into the MSI/EXE installers
Follow-up to the start-script DLL fallback fix in the prior commit.
ChrisMTheMan's report on #319 made it clear the workaround flow was:
1. MSI install crashes on launch (different bug, fixed in v0.9.81)
2. User goes looking for start.bat to launch the backend manually
3. start.bat isn't in their install dir, so they go fetch it from GitHub
4. They get a working script but it doesn't know about the bundled
privacy_core.dll layout, so they see a scary "install Rust" warning
The prior commit fixed step 4. This commit fixes step 3 — start.bat and
start.sh now ship inside the MSI/EXE installers (staged into
backend-runtime/ next to the privacy_core.dll they expect to find).
After the rebuild lands, an MSI user looking for these scripts finds
them right inside their install dir, already pointing at the correct
bundled DLL location.
What changed
------------
* ``build-backend-runtime.cjs`` now has a ``stageStartScripts()`` step
that copies start.bat and start.sh from the repo root into the
staged backend-runtime/. Preserves the executable bit on .sh under
POSIX.
* ``release_digests.json`` v0.9.81 block hashes refreshed for the
rebuilt MSI / EXE / source-zip (the scripts being bundled changed
the MSI/EXE contents; the source zip also includes the start-script
fix from the prior commit).
ShadowBroker_v0.9.81.zip 6.06 MB
af8c87ccdece8fbb9aadc6be63cce10d3fcba74e6d87ef83289dda6d555fd270
ShadowBroker_0.9.81_x64_en-US.msi 122.4 MB
8977c9a1c54e1f0d030436be9c4e3d81d766cc0080699eb747649095f360c7ff
ShadowBroker_0.9.81_x64-setup.exe 76.5 MB
4e866fa0423c0c2470ed32f4809167a7815dc23ee7762b69e95681c1f3a28250
Post-merge plan
---------------
Force-move the v0.9.81 tag to this commit and replace ALL release
assets on the GitHub release: zip, msi, exe, both .sig files,
latest.json, SHA256SUMS.txt, release-manifest.json.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
What this release does
----------------------
1. Establishes a fresh Tauri updater signing keypair. The previous keypair
(pubkey baked into v0.9.79 / v0.9.8) had no matching private key on
any maintainer-controlled machine — every prior release shipped
without signatures, so auto-update has never actually worked. v0.9.81
rotates to a new pubkey and ships signed installers + latest.json so
every release from here is a one-click upgrade.
2. Fixes the ``admin_session_required`` race in TopRightControls.tsx.
The updateAction state used to default to ``auto_apply`` at React-init
time. A click on the Update button before the async runtime probe
completed went down the auto_apply path (POST /api/system/update),
which throws ``admin_session_required`` on fresh sessions. Desktop
installs now default to ``manual_download`` based on synchronous
``window.__TAURI__`` detection at useState init.
One-time cost for current installs
----------------------------------
Anyone on v0.9.79 or v0.9.8 will see the in-app Update button still
trigger the broken path on their existing install (the fix only takes
effect once they're ON v0.9.81). The MANUAL DOWNLOAD button in the
update dialog opens the GitHub release page, where they grab the .msi
and run it. After that one manual hop, all future updates are seamless.
Release artifacts
-----------------
ShadowBroker_v0.9.81.zip 6.06 MB
42f8a51f9a5690d1e7349d90d8ecf2d163c9061d6cf90c69ee03647a785437ff
ShadowBroker_0.9.81_x64_en-US.msi 122.4 MB
a45b177c26c95d2b28d71592d7147e88ff4e104865f214fde11249d311ec9e25
ShadowBroker_0.9.81_x64-setup.exe 76.5 MB
eca884b9d37eeccd0f11c91dcc6f6ae1b3609d9dee72bd73c37c9a427babfef2
Plus .sig files for the .msi and .exe, plus a signed latest.json for
the Tauri updater endpoint.
Sizes match the v0.9.79 / v0.9.8 reference shape within drift for
the new TopRightControls patch.
release_digests.json keeps v0.9.79 + v0.9.8 blocks alongside v0.9.81
so operators still on those versions continue to validate cleanly
during the rollout transition.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Issues #319 and #296 reported that the installed v0.9.79 Windows MSI/EXE
crashed on launch with:
thread 'main' panicked ... failed to setup app: error encountered
during setup hook: ShadowBroker cannot start: the bundled local
backend failed to launch.
technical detail: managed_backend_exited_early:exit code: 103
Root cause: ``backend/pyproject.toml`` declares ``defusedxml>=0.7.1`` and
``PySocks==1.7.1`` as runtime dependencies, but the venv used to build
v0.9.79 (and the initial v0.9.8 publish) had both missing. When
``services/fetchers/aircraft_database.py`` does
``import defusedxml.ElementTree`` at startup, Python raises
``ModuleNotFoundError`` and uvicorn exits, which Tauri reports as
``managed_backend_exited_early``.
Both packages now installed in the build venv. ``main.py`` imports
end-to-end with only the expected ``plane_alert_db.json not found``
warning (runtime-state file, populated on first launch).
Rebuilt artifacts on the maintainer's local machine:
ShadowBroker_v0.9.8.zip 6.06 MB
183bb5cd62b9b9349d95df5ef7696cb6ca810ab4b991fa9dab6f898af4c7a175
ShadowBroker_0.9.8_x64_en-US.msi 122.4 MB
fe22f9d51e4360d74c18a7250c2fbb9ed4fa4c7a884b3ac0d04a21115466386b
ShadowBroker_0.9.8_x64-setup.exe 76.5 MB
94a0309862e9c81c92cdcbfea8eec9dbb97eef19ded82b26217b397defbc810c
After this merges, the v0.9.8 tag will be force-moved to this commit and
the GitHub release assets replaced so the integrity chain validates
against the working installers instead of the broken ones.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Bumps every hardcoded 0.9.79 → 0.9.8 across backend, frontend,
desktop-shell, helm, lockfiles, test fixtures. Refreshes the in-app
ChangelogModal HEADLINE_FEATURES, NEW_FEATURES, and BUG_FIXES with the
v0.9.8 highlights.
Release artifacts built locally and hashed into release_digests.json:
ShadowBroker_v0.9.8.zip 6.06 MB
d506f6b8462ccb12096f0cd9462233be58928094240416b65fb3127bdd1f3820
ShadowBroker_0.9.8_x64_en-US.msi 122.4 MB
d4be4cb68c3e6409fff54c225acdcdd08e27d5d6d2b31616d78d2a4f6812991d
ShadowBroker_0.9.8_x64-setup.exe 76.5 MB
1115d1f5cf37edd03ea2c21d821c7626e1bf3319c990402aaa0293bca46fea67
Sizes match the v0.9.79 reference shape (5.76 MB / 117 MB / 72.9 MB)
within expected drift for new code. The .zip is a `git archive` of the
v0.9.8 source tree (matching v0.9.79's approach).
Audit confirms no .env, .key, .venv-dir, or cache files leaked into the
backend-runtime bundle. Python 3.11.9 + 199 site-packages + privacy_core
all staged correctly.
Headline changes since v0.9.79:
* Cumulative fuel/CO2 per flight (#317) — running totals since first
observation, not just per-hour rate.
* AIS maritime resilience (#314, #316) — outage banner + AISHub REST
fallback when AISStream WebSocket primary is offline.
* Data-layer repair (#311, #312) — UAP fallback respects the 60-day
cutoff; GPS jamming threshold tuning + nac_p=0 inclusion so the layer
actually fires.
* Per-flight source attribution (#313) — source field on every record.
* Cross-node DM mailbox replication (#309).
* Infonet sync HTTP 429 honored (#310).
Test fixtures updated:
* test_per_operator_outbound_attribution.py — added v0.9.8 UA strings
to the banned-aggregate-literals list (alongside v0.9.79).
* updateRuntime.test.ts — bumped asset filename fixtures to v0.9.8.
release_digests.json keeps the v0.9.79 block alongside v0.9.8 so
operators still on 0.9.79 validate cleanly during the rollout.
The accent narrowing fix in ChangelogModal (one feature uses 'purple',
two use 'cyan' so the renderer's `accent === 'purple'` comparison
still type-checks) is included.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
External audit (@tg12, May 18) found that backend/services/updater.py
silently skipped all SHA-256 integrity verification whenever the
MESH_UPDATE_SHA256 env var was unset — which is the default. Nothing
in any install doc tells operators to set it, so practically every
deployment was running the auto-updater with zero integrity check.
That made GitHub release pipeline compromise a single-step path to
arbitrary code execution on every node that auto-updates.
Investigation surfaced a deeper bug too: the updater downloads
zipball_url (GitHub's auto-generated source archive) but the
maintainer's release process publishes SHA256SUMS.txt for a separate
named asset (ShadowBroker_v*.zip). So even if MESH_UPDATE_SHA256
WERE set, operators had no published digest to compare against — the
file they were downloading wasn't the file the maintainer had signed.
This PR fixes both issues with the same multi-source verification
chain we shipped for the Tor bundle in PR #261:
backend/services/updater.py
_download_release() now prefers a maintainer-signed release asset
matching ShadowBroker_v*.zip over zipball_url. Captures the
SHA256SUMS.txt asset URL when present.
_validate_zip_hash() rewritten as a four-source chain:
1. MESH_UPDATE_SHA256 env var (operator override, preserved)
2. SHA256SUMS.txt asset published with the release (primary —
the maintainer's release process already publishes this)
3. Baked-in backend/data/release_digests.json (second line of
defense for releases that lack the SHA256SUMS asset, or when
the asset can't be fetched at update time)
4. HTTPS-only fallback with a loud warning (preserves the auto-
update flow during transient outages)
Mismatch from any source that DID respond is fatal — the update
is refused and the existing install keeps running. Only the
"no source reachable at all" case falls back to HTTPS-only.
_fetch_sha256sums() new — fetches and parses a standard
SHA256SUMS.txt asset. Handles both "<digest> <name>" and binary-
marker "<digest> *<name>" formats. Tolerant to comments, blank
lines, and malformed entries.
backend/data/release_digests.json (new)
Baked-in digest list keyed by release tag. Seeded with the v0.9.79
entries copied from the published SHA256SUMS.txt:
ShadowBroker_v0.9.79.zip = f6877c1d6661...
ShadowBroker_0.9.79_x64-setup.exe = f7b676ada45c...
ShadowBroker_0.9.79_x64_en-US.msi = e0713c3cdda1...
Whitelisted in .gitignore alongside the other static reference
data files (kiwisdr_directory.json, tor_bundle_digests.json,
aisstream_spki_pins.json).
backend/tests/test_update_integrity_chain.py (new, 16 tests)
- Each source matches → success, identifies which source verified
- Each source mismatches → RuntimeError "mismatch"
- No source reachable → https-only fallback with loud warning
- Env override beats all other sources (preserved precedence)
- SHA256SUMS.txt parser handles standard, binary-marker, comments,
and network-failure cases
Validation:
pytest backend/tests/test_update_integrity_chain.py → 16 passed
pytest (all 15 security test files together) → 105 passed
UX impact: zero. Normal auto-update flow is unchanged for legitimate
releases (path 2 catches everything because the release publishes
SHA256SUMS.txt). Transient network failures during update gracefully
fall through to path 3 then path 4 — no operator intervention needed.
The only user-visible behavior change is in the compromised-release
case, where the update is now refused instead of silently applied.
Credit: @tg12 for the original bug report and the specific call-out
that MESH_UPDATE_SHA256 was unreachable by default operators.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>