mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-05-28 18:11:31 +02:00
a930497e14caba310b2d4fca5f87748e5b6f84b2
23 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a930497e14 |
fix(start-scripts): find bundled privacy_core.dll next to script (#319) (#324)
* 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> |
||
|
|
2dc1fcc778 |
release: v0.9.81 — signed auto-update + admin_session race fix (#323)
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>
|
||
|
|
896d1ae938 |
fix(#319,#296): v0.9.8 rebuild — bundle missing deps so backend launches (#322)
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> |
||
|
|
8dfa6a7199 |
release: v0.9.8 — Cumulative Fuel/CO2, AIS Resilience, Data-Layer Repair (#321)
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>
|
||
|
|
5e6bb8511a |
Fix #244/#245/#246: carrier tracker seed/cache/freshness model (#285)
Replace the dated editorial fallback positions baked into the registry with a one-shot seed file + persistent observation cache. The user's runtime cache now reflects what THIS install has actually observed, not what USNI published on March 9, 2026. A year from now, the cache holds a year of observations and the seed is irrelevant. == #244: dated editorial coordinates out of the registry == CARRIER_REGISTRY no longer carries fallback_lat/lng/heading/desc. Those fields are deleted. The registry is now identity + homeport only. New file: backend/data/carrier_seed.json - Read-only, shipped with every release. - Used ONCE on first-ever startup to bootstrap carrier_cache.json. - Each entry stamped with position_confidence="seed" and the actual as-of date (2026-03-09), NOT now(). == #245: approximate confidence for headline-derived positions == _parse_carrier_positions_from_news() now stamps every GDELT-derived entry with position_confidence="approximate" so the UI knows the coordinate is a region-centroid match, not a precise observation. After the freshness window the label rolls over to "stale_approximate" so old-and-imprecise is distinguishable from recent-and-imprecise. The article's actual seendate is used as position_source_at instead of now(), so the "last reported X days ago" badge is honest. == #246: freshness is labelling, not eviction == The cache always preserves the last position the system observed, forever. What changes is the position_confidence label: - within configurable window (default 14d, env-overridable via SHADOWBROKER_CARRIER_FRESHNESS_DAYS) -> "recent" - older -> "stale" - seed-bootstrap entries that were never refreshed -> "seed" - homeport defaults (carrier added post-install) -> "homeport_default" - headline-derived (any age, fresh) -> "approximate" - headline-derived (older than window) -> "stale_approximate" The position itself never reverts to the seed or the registry. The user always sees the last position the system observed. Per the user's explicit guidance: "from there have it be the last position the user has logged the carriers that way a year from now it doesnt revert to where the ships are today". == Other improvements == - CACHE_FILE moved to backend/data/carrier_cache.json so it lives in the volume-mounted dir under Docker compose. Previously it was at /app/carrier_cache.json which got wiped on every container restart (pre-existing bug). - Atomic cache write (temp + os.replace) so a crash mid-write does not leave a truncated cache file. == Public API shape == Every carrier object the API emits now includes: - position_confidence: seed | recent | stale | approximate | stale_approximate | homeport_default - position_source_at: ISO timestamp of when the underlying source was observed (NOT now()) - is_fallback: convenience boolean for the UI; true when the confidence is seed/stale/stale_approximate/ homeport_default Existing fields (estimated, source, source_url, last_osint_update, name, type, lat, lng, country, desc, wiki) are preserved exactly so the current ShipPopup frontend renders unchanged. last_osint_update now reflects position_source_at instead of now(), which is what the existing "last reported MM/DD" badge always meant to show. Tests: backend/tests/test_carrier_tracker_quality.py — 17 tests covering seed bootstrap, subsequent-startup ignoring seed, no-seed/ no-cache homeport fallback, registry no longer has fallback fields, freshness window labelling + env override, "year-old cache entry keeps its position, only the label flips" regression, approximate confidence for headline matches, GDELT seendate ISO parser, public response shape backward compat. Credit: tg12 (external security audit, three P1/P2 issues). |
||
|
|
7f96151e56 |
Fix #231: multi-source SHA-256 verification for the self-updater (#265)
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> |
||
|
|
729ea78cb2 |
Fix #258: AIS proxy SPKI pinning fallback for expired upstream cert (#262)
External report from @jmleclercq: AISStream's Let's Encrypt cert
expired on 2026-05-20 (verified — their renewal pipeline failed), so
the AIS WebSocket connection dies with CERT_HAS_EXPIRED and the
maritime layer empties out. The reporter worked around it locally by
passing { rejectUnauthorized: false } to the WebSocket constructor and
asked whether we should add an env var for that.
That fix is the wrong fix. Disabling TLS validation entirely lets any
network attacker MITM the WebSocket and inject fake ship positions —
same class as the GDELT plaintext-HTTP MITM we just closed in #199.
Adding an env var for it would be an attractive nuisance: operators
set it once during a bad cert week and then forget, leaving themselves
open to MITM forever.
Right fix: SPKI pinning, same pattern as the Tor bundle digest pinning
in #201. The insight is that Let's Encrypt renewals keep the SAME
public key by default, so the SPKI hash survives normal cert rotation.
We can relax the date check while keeping the identity check.
Mechanics:
backend/data/aisstream_spki_pins.json (new)
Pinned SHA-256 hashes of the DER-encoded SPKI bytes for
stream.aisstream.io. Captured 2026-05-20 from the live cert.
Format is base64(sha256(pubkey_der)), matching the canonical
openssl pipeline. Whitelisted in .gitignore alongside the other
static reference data files (KiwiSDR directory, Tor bundle
digests).
backend/ais_proxy.js
Path A (99.9% of the time): normal TLS validation. Untouched.
Path B (on CERT_HAS_EXPIRED only): re-handshake with
rejectUnauthorized=false JUST to read the leaf cert, compute its
SPKI hash, compare against the pinned list. If match → upstream
is still the genuine AISStream → re-open the WebSocket with
rejectUnauthorized=false and log DEGRADED MODE. If no match →
refuse the connection, log loudly: this would be a real MITM.
Pin file is looked up in three locations so the same code works
in the Docker backend, the Tauri desktop runtime, and any
operator-relocated layout (SHADOWBROKER_AIS_PINS env var).
Embedded fallback list inside the JS so portable installs that
haven't shipped the JSON still work.
backend/services/ais_stream.py
Captures the proxy's status markers from stdout
({"__ais_proxy_status": {"degraded_tls": true}}) into a module-
level snapshot. Exposes ais_proxy_status() for the health
endpoint. Doesn't touch the data plane — degraded mode keeps
receiving vessel data, just with weaker MITM protection.
backend/routers/health.py + backend/services/schemas.py
/api/health now includes an ais_proxy block with degraded_tls.
Top-level status escalates ok -> degraded when AIS is in
degraded TLS mode (but won't downgrade a worse SLO status).
Operators get a visible signal that they're in degraded mode
without needing to grep logs.
Tests: backend/tests/test_ais_spki_pinning.py (7 tests)
- Pin file structure validation (JSON, host entry, base64 SHA-256)
- ais_proxy_status() snapshot semantics (starts empty, defensive copy)
- /api/health surfaces ais_proxy.degraded_tls when set
- /api/health returns empty ais_proxy when proxy hasn't reported
Node.js syntax check passes (node --check) on both backend/ais_proxy.js
and the Tauri runtime mirror.
When AISStream renews their cert (likely within hours-to-days), the
normal-TLS path succeeds on next reconnect and degraded_tls clears
automatically. No operator action needed. If they instead rotate their
server key, the SPKI check will fail and we'll need to add the new
hash to backend/data/aisstream_spki_pins.json before removing the old
one.
Credit: @jmleclercq for the clear report and the careful workaround
verification (Node version, ws version, manual probe).
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
e36d1fc79c |
[security] Close tg12 audit issues #201–#214 seamlessly (#261)
External security audit by @tg12 (May 17, 2026) filed issues #201–#214 in addition to the #189–#200 batch already closed by PRs #227/#232/#260. This PR closes all eight that are real security bugs (the other six in the 201–214 range are either design discussions or upstream-abuse/TOS concerns we're keeping intentional, see issue triage notes on each). The user-facing principle for this PR: fix the security gap WITHOUT introducing a single hostile error or behavior change for legitimate users. Every fix follows the same template — fail forward, not loud. When the secure path is harder than the insecure one, build a fallback chain that ends in graceful degradation, not in a scary modal or 422 response. #205 — OpenMHZ audio redirect SSRF (services/radio_intercept.py) Replaced requests.get(..., allow_redirects=True) with a manual redirect loop that re-validates each hop's host against _OPENMHZ_AUDIO_HOSTS. Same-host redirects (CDN edge selection) still work, so legitimate audio playback is unaffected. Cross-host redirects to disallowed hosts return a generic 502 which the browser audio element handles gracefully. Cap at 5 hops. #207 — infonet/status verify_signatures DoS (routers/mesh_public.py) Silently downgrade verify_signatures=true to False for unauthenticated callers. No error surfaced — the response shape is identical, just without the O(n_events) signature verification. Authenticated callers (scoped mesh.audit) still get the full path. The frontend never passes this param so legitimate UI is unaffected. #211 — thermal/verify expensive analysis (routers/sigint.py) Added Depends(require_local_operator). Frontend has no direct callers (verified by grep); Tauri/AI agents use scoped tokens that pass the auth check. Anonymous abusers blocked silently — the legitimate UI keeps working through the Next.js admin-key proxy. #213, #214 — OpenMHZ calls/audio upstream abuse (routers/radio.py) Added Depends(require_local_operator) to both. Browser users hit these through the Next.js proxy at src/app/api/[...path]/route.ts which injects X-Admin-Key, so the auth check passes transparently. Direct attackers can no longer rotate sys_names to hammer api.openmhz.com or relay arbitrary audio streams through the backend's bandwidth. #202 — overflights unbounded hours (routers/data.py) Silently clamp `hours` to OVERFLIGHTS_MAX_HOURS (default 72, configurable). NO 422 — clients asking for an absurd window get a shorter window back with `requested_hours` and `effective_hours` hint fields. Postel's law: liberal in what we accept, conservative in what we compute. #203 — Meshtastic callsign UA leak (services/fetchers/meshtastic_map.py) Added MESHTASTIC_SEND_CALLSIGN_HEADER opt-out env var. Default is TRUE — preserves existing operator behavior (callsign sent so meshtastic.org can rate-limit per-install). Privacy-conscious operators set it to false to suppress. #206 — KiwiSDR upstream is HTTP-only (services/kiwisdr_fetcher.py) Upstream rx.linkfanel.net doesn't speak HTTPS (verified — Apache 2.4.10 only on port 80). We can't fix the transport. Instead added three layers: 1. Content validation on fetched data — reject responses with <50 receivers or >5% malformed entries (likely MITM injection). 2. Existing disk cache fallback (already present). 3. NEW: bundled static directory at backend/data/kiwisdr_directory.json shipping 798 known-good receivers. Used as last resort so the KiwiSDR map layer always renders something useful. #208 — Merkle proof DoS via /api/mesh/infonet/sync (services/mesh/mesh_hashchain.py) The endpoint is part of the cross-node federation protocol — peers legitimately call it without local-operator auth, so we can't add Depends(). Instead made the underlying operation O(1) per proof via a cached Merkle level structure on the Infonet instance: - _merkle_levels_cache + _merkle_levels_for_event_count on each Infonet instance - _invalidate_merkle_cache() called from every chain mutation point (append, ingest_events, apply_fork, cleanup_expired) - _get_merkle_levels() does the lazy recompute on first read after invalidation, then serves from cache thereafter Effect: anonymous attackers hammering the proofs endpoint hit a cached structure; the rebuild happens at most once per real chain advance. Federation untouched. #201 — Tor bundle SHA-256 bypass (services/tor_hidden_service.py) Docker users were already covered — backend/Dockerfile installs Tor via apt-get at build time (signed by Debian's package system). No runtime download needed for the 80%-of-users case. For Tauri desktop, replaced the single .sha256sum check with a multi-source verification chain implemented in _verify_tor_bundle(): 1. Try upstream .sha256sum (current behavior — fast path) 2. Try baked-in digest list at backend/data/tor_bundle_digests.json (pinned per-version, maintainer-updated) 3. If neither source is REACHABLE: HTTPS-only fallback with a loud warning (avoids breaking first-run onboarding while the maintainer hasn't yet pinned a new Tor release) A mismatch from a source that DID respond is always fatal — only the "no source reachable" case falls back to HTTPS-only. This is the "have cake and eat it" pattern: real users see no new failure modes during torproject.org outages, but MITM/compromise attacks still fail because the downloaded digest can't match what BOTH the upstream and the baked-in list report. Currently the digest file ships with placeholder values for the current Tor URLs (those URLs are already stale on torproject.org too). A follow-up commit can populate real digests when a stable Tor release is selected; until then the HTTPS-only warning fires and onboarding still works. Tests (82 total, all passing): test_openmhz_redirect_ssrf.py (5 tests) — #205 test_infonet_status_verify_gate.py (2 tests) — #207 test_overflights_clamp.py (5 tests) — #202 test_meshtastic_callsign_optout.py (3 tests) — #203 test_kiwisdr_fallback.py (6 tests) — #206 test_merkle_cache.py (6 tests) — #208 test_tor_bundle_verification.py (6 tests) — #201 test_control_surface_auth.py (extended) — #211, #213, #214 + all previous security tests (CCTV redirect, GDELT https, sentinel cache, crowdthreat opt-in, third-party fetcher gates, control surface auth) continue to pass. Pre-existing test infrastructure issue with SHARED_EXECUTOR teardown in the broader sweep exists on main too (verified) — not introduced by this PR. Credit: @tg12 reported every one of these with accurate line citations and the recommended fixes that informed this implementation. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
28b3bd5ebf | release: prepare v0.9.7 | ||
|
|
668ce16dc7 |
v0.9.6: InfoNet hashchain, Wormhole gate encryption, mesh reputation, 16 community contributors
Gate messages now propagate via the Infonet hashchain as encrypted blobs — every node syncs them through normal chain sync while only Gate members with MLS keys can decrypt. Added mesh reputation system, peer push workers, voluntary Wormhole opt-in for node participation, fork recovery, killwormhole scripts, obfuscated terminology, and hardened the self-updater to protect encryption keys and chain state during updates. New features: Shodan search, train tracking, Sentinel Hub imagery, 8 new intelligence layers, CCTV expansion to 11,000+ cameras across 6 countries, Mesh Terminal CLI, prediction markets, desktop-shell scaffold, and comprehensive mesh test suite (215 frontend + backend tests passing). Community contributors: @wa1id, @AlborzNazari, @adust09, @Xpirix, @imqdcr, @csysp, @suranyami, @chr0n1x, @johan-martensson, @singularfailure, @smithbh, @OrfeoTerkuci, @deuza, @tm-const, @Elhard1, @ttulttul |
||
|
|
b40f9d1fd0 |
feat: add power plants layer with WRI Global Power Plant Database
Map ~35,000 power generation facilities from 164 countries using the WRI Global Power Plant Database (CC BY 4.0). Follows the existing datacenter layer pattern with clustered icon symbols, amber color scheme, and click popups showing fuel type, capacity, and operator. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
44147da205 |
fix: resolve merge conflicts between JSDF bases and East Asia adversary bases
Merge both feature sets: keep JSDF bases (gsdf/msdf/asdf branches) from PR #77 and East Asia adversary bases (missile/nuclear branches) from main. Union all branch types in tests and MaplibreViewer labels. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
457f00ca42 |
feat: add 18 JSDF bases to military bases layer
Add ASDF (8), MSDF (6), and GSDF (4) bases to military_bases.json. Colocated bases (Misawa, Yokosuka, Sasebo) have offset coordinates to avoid overlap with existing US entries. Add branchLabel entries for GSDF/MSDF/ASDF in MaplibreViewer popup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
910d1fd633 |
feat: enhance East Asia coverage with adversary bases, news sources, ICAO ranges, and PLAN vessel DB
- Add 68 military bases (PLA, Russia, DPRK, ROC, Philippines, Australia) with data-driven color coding (red/blue/green) on the map - Add 6 news RSS feeds (Yonhap, Nikkei Asia, Taipei Times, Asia Times, Defense News, Japan Times) and 15 geocoding keywords for islands, straits, and disputed areas - Extend ICAO country ranges for Russia, Australia, Philippines, Singapore, DPRK and add Russian aircraft classification (fighters, bombers, cargo, recon) - Create PLAN/CCG vessel enrichment module (90+ ships) following yacht_alert pattern for automatic MMSI-based identification - Update frontend types and popup styling for adversary/allied/ROC color distinction Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
05de14af9d |
feat: add military bases map layer for Western Pacific
Add 18 US military bases (Japan, Guam, South Korea, Hawaii, Diego Garcia) as a toggleable map layer. Follows the existing data center layer pattern: static JSON → backend fetcher → slow-tier API → frontend GeoJSON layer. Includes red circle markers with labels, click popups showing operator and branch info, and a toggle in the left panel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
b5f49fe882 |
Update README.md
Former-commit-id: 85110e82cc09ab746d323f8625b8ecb5b1c03500 |
||
|
|
90c2e90e2c |
v0.9.5: The Voltron Update — modular architecture, stable IDs, parallelized boot
- Parallelized startup (60s → 15s) via ThreadPoolExecutor - Adaptive polling engine with ETag caching (no more bbox interrupts) - useCallback optimization for interpolation functions - Sliding LAYERS/INTEL edge panels replace bulky Record Panel - Modular fetcher architecture (flights, geo, infrastructure, financial, earth_observation) - Stable entity IDs for GDELT & News popups (PR #63, credit @csysp) - Admin auth (X-Admin-Key), rate limiting (slowapi), auto-updater - Docker Swarm secrets support, env_check.py validation - 85+ vitest tests, CI pipeline, geoJSON builder extraction - Server-side viewport bbox filtering reduces payloads 80%+ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Former-commit-id: f2883150b5bc78ebc139d89cc966a76f7d7c0408 |
||
|
|
fc9eff865e |
v0.9.0: in-app auto-updater, ship toggle split, stable entity IDs, performance fixes
New features: - In-app auto-updater with confirmation dialog, manual download fallback, restart polling, and protected file safety net - Ship layers split into 4 independent toggles (Military/Carriers, Cargo/Tankers, Civilian, Cruise/Passenger) with per-category counts - Stable entity IDs using MMSI/callsign instead of volatile array indices - Dismissible threat alert bubbles (session-scoped, survives data refresh) Performance: - GDELT title fetching is now non-blocking (background enrichment) - Removed duplicate startup fetch jobs - Docker healthcheck start_period 15s → 90s Bug fixes: - Removed fake intelligence assessment generator (OSINT-only policy) - Fixed carrier tracker GDELT 429/TypeError crash - Fixed ETag collision (full payload hash) - Added concurrent /api/refresh guard Contributors: @imqdcr (ship split + stable IDs), @csysp (dismissible alerts, PR #48) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Former-commit-id: a2c4c67da54345393f70a9b33b52e7e4fd6c049f |
||
|
|
90f6fcdc0f |
chore: sync local polling adjustments and data updates
Former-commit-id: 4417623b0c0bb6d07d79081817110e80e699a538 |
||
|
|
34db99deaf |
v0.8.0: POTUS fleet tracking, full aircraft color-coding, carrier fidelity, UI overhaul
New features: - POTUS fleet (AF1, AF2, Marine One) with hot-pink icons + gold halo ring - 9-color aircraft system: military, medical, police, VIP, privacy, dictators - Sentinel-2 fullscreen overlay with download/copy/open buttons (green themed) - Carrier homeport deconfliction — distinct pier positions instead of stacking - Toggle all data layers button (cyan when active, excludes MODIS Terra) - Version badge + update checker + Discussions shortcut in UI - Overhauled MapLegend with POTUS fleet, wildfires, infrastructure sections - Data center map layer with ~700 global DCs from curated dataset Fixes: - All Air Force Two ICAO hex codes now correctly identified - POTUS icon priority over grounded state - Sentinel-2 no longer overlaps bottom coordinate bar - Region dossier Nominatim 429 rate-limit retry/backoff - Docker ENV legacy format warnings resolved - UI buttons cyan in dark mode, grey in light mode - Circuit breaker for flaky upstream APIs Community: @suranyami — parallel multi-arch Docker builds + runtime BACKEND_URL fix (PR #35, #44) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Former-commit-id: 7c523df70a2d26f675603166e3513d29230592cd |
||
|
|
d78bf61256 |
fix: aircraft categorization, fullscreen satellite imagery, region dossier rate-limit, updated map legend
- Fixed 288+ miscategorized aircraft in plane_alert_db.json (gov/police/medical)
- data_fetcher.py: tracked_names enrichment now assigns blue/lime colors for gov/law/medical operators
- region_dossier.py: fixed Nominatim 429 rate-limiting with retry/backoff
- MaplibreViewer.tsx: Sentinel-2 popup replaced with fullscreen overlay + download/copy buttons
- MapLegend.tsx: updated to show all 9 tracked aircraft color categories + POTUS fleet + wildfires + infrastructure
Former-commit-id:
|
||
|
|
5ab02e821f |
feat: POTUS Fleet tracker, Docker secrets, route fix, SQLite->JSON migration
- Add Docker Swarm secrets _FILE support (AIS_API_KEY_FILE, etc.)
- Fix flight route lookup: pass lat/lng to adsb.lol routeset API, return airport names
- Replace SQLite plane_alert DB with JSON file + O(1) category color mapping
- Add POTUS Fleet (AF1, AF2, Marine One) with hardcoded ICAO overrides
- Add tracked_names enrichment from Excel data with POTUS protection
- Add oversized gold-ringed POTUS SVG icons on map
- Add POTUS Fleet tracker panel in WorldviewLeftPanel with fly-to
- Overhaul tracked flight labels: zoom-gated, PIA hidden, color-mapped
- Add orange color to trackedIconMap, soften white icon strokes
- Fix NewsFeed Wikipedia links to use alert_wiki slug
Former-commit-id:
|
||
|
|
362a6e2ceb |
Initial commit: ShadowBroker v0.1
Former-commit-id:
|