9 Commits

Author SHA1 Message Date
Shadowbroker 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>
2026-05-23 18:43:53 -06:00
Shadowbroker 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>
2026-05-23 16:24:20 -06:00
BigBodyCobain f91ddcf38b Fix #302: split OpenClaw HMAC reveal into dedicated POST with no-store
Reported by @tg12. Pre-fix, two problems lived on the GET endpoint:

  1. `GET /api/ai/connect-info?reveal=true` returned the full HMAC
     secret in the response body on every Connect modal open. Even
     gated to require_local_operator, that put the secret into
     browser history, dev-tools network panels, browser disk caches,
     HAR exports, and screen captures.

  2. The same GET endpoint auto-bootstrapped (generated + persisted)
     the secret on a mere read. Side effects on a GET are a footgun:
     browser prefetchers, mirror tools, and casual curl-from-history
     would all silently mint+persist a fresh secret.

Backend (backend/routers/ai_intel.py)
-------------------------------------
  GET  /api/ai/connect-info             — always returns the MASKED
                                          fingerprint (first6 + bullets
                                          + last4). No `?reveal` param.
                                          NO auto-bootstrap. When the
                                          secret is missing, returns
                                          `hmac_secret_set: false` and
                                          tells the caller to POST to
                                          /bootstrap.
  POST /api/ai/connect-info/bootstrap   — NEW. Mints+persists the secret
                                          if missing. Idempotent. Never
                                          returns the full secret in the
                                          response body.
  POST /api/ai/connect-info/reveal      — NEW. Returns the full secret
                                          with Cache-Control: no-store,
                                          no-cache, must-revalidate +
                                          Pragma: no-cache + Expires: 0.
                                          POST so the body never lands
                                          in URL history. 404 (with a
                                          pointer to /bootstrap) when
                                          the secret isn't set.
  POST /api/ai/connect-info/regenerate  — keeps existing one-time-reveal
                                          behavior (regen IS a deliberate
                                          destructive action triggered
                                          by the operator). Same
                                          no-store/no-cache headers added
                                          so even the regen response
                                          doesn't get cached.

Frontend (AIIntelPanel.tsx, OnboardingModal.tsx)
------------------------------------------------
  * On mount: GET (masked only). If hmac_secret_set: false, fire a
    transparent POST /bootstrap and refresh the masked fingerprint.
    Operator sees no behavior change from pre-#302.
  * Reveal (eye icon): lazy POST /reveal — secret only travels when
    the operator explicitly clicks the button.
  * Copy: lazy POST /reveal too — copying without a prior reveal
    works exactly like before, just routed through the new endpoint.
  * Regenerate: POST returns the new secret (same as before, but the
    response now has no-store headers).
  * The displayed snippet uses the masked fingerprint until the
    operator clicks Reveal or Copy.

Tests (backend/tests/test_openclaw_connect_info_reveal.py — 13 tests)
---------------------------------------------------------------------
  * GET returns masked + the full secret never appears in r.text
  * GET does NOT auto-bootstrap when missing
  * GET silently ignores any ?reveal=true query (back-compat noise)
  * POST /bootstrap mints when missing, idempotent when set
  * POST /bootstrap never returns the full secret
  * POST /reveal returns the full secret with Cache-Control: no-store,
    no-cache + Pragma: no-cache + Expires: 0
  * POST /reveal 404s with a pointer to /bootstrap when no secret
  * POST /regenerate returns the new secret with the same headers
  * Anonymous remote callers get 403 on ALL FOUR endpoints (parametric
    regression against the same allowlist used elsewhere).

Adjacent suites still green: test_openclaw_route_security,
test_no_new_duplicate_routes, test_control_surface_auth. 67/67 pass
locally.

Credit: @tg12 for the audit report.
2026-05-22 18:40:24 -06:00
Shadowbroker 76750caa92 Round 7a: per-operator outbound attribution + GDELT GCS-direct fix (#292)
== Per-install operator handle for every third-party API call ==

Before this PR, every Shadowbroker install identified itself to
Wikipedia, Wikidata, Nominatim, GDELT, OpenMHz, Broadcastify,
weather.gov, NUFORC, Sentinel/Planetary Computer, TinyGS / CelesTrak,
Shodan, Finnhub, and others with a single project-wide User-Agent
("Shadowbroker/1.0" or "ShadowBroker-OSINT/1.0"). From the upstream's
perspective every install in the world looked like one giant scraper.
If one install misbehaved, the upstream's only recourse was to block
"Shadowbroker" as a whole.

PR #284 inadvertently doubled down on this in the frontend by
introducing a shared `WIKIMEDIA_API_USER_AGENT` constant. This PR
retrofits both backends to per-operator attribution.

  New setting: OPERATOR_HANDLE (env var / settings UI / auto-gen)
  New helper:  network_utils.outbound_user_agent("purpose")

The handle is auto-generated as "operator-XXXXXX" on first call (the
"shadow-" prefix from earlier drafts was deliberately dropped — too
suspicious-looking for abuse-detection systems). Operators can
override via OPERATOR_HANDLE; the value is sanitized to lowercase
alphanumeric+dash+underscore and capped at 48 chars. Persisted to
backend/data/operator_handle.json so it survives container restarts.

Retrofitted call sites (every previously-MONSTER User-Agent):
  - services/region_dossier.py (Wikipedia + Wikidata + Nominatim)
  - services/geocode.py         (Nominatim)
  - services/sentinel_search.py (Microsoft Planetary Computer)
  - services/feed_ingester.py   (operator-curated RSS feeds)
  - services/fetchers/earth_observation.py (weather.gov, NUFORC)
  - services/fetchers/infrastructure.py
  - services/fetchers/aircraft_database.py
  - services/fetchers/route_database.py
  - services/fetchers/trains.py
  - services/fetchers/meshtastic_map.py
  - services/shodan_connector.py
  - services/unusual_whales_connector.py (Finnhub)
  - services/tinygs_fetcher.py            (CelesTrak + TinyGS)
  - services/sar/sar_products_client.py
  - services/geopolitics.py               (GDELT)
  - services/radio_intercept.py           (Broadcastify + OpenMHz)
  - routers/cctv.py + main.py             (CCTV proxy)
  - routers/ai_intel.py
  - scripts/convert_power_plants.py       (release-time data refresh)

Spoofed browser UAs removed (issues #289 / #290 / #291 — tg12 audit):
  - cloudscraper-based Chrome impersonation against api.openmhz.com
    -> replaced with honest requests + per-install UA
  - Mozilla/5.0 spoofed UA on Broadcastify scrape
    -> replaced with honest UA
  - Mozilla/5.0 + fake first-party Referer on OpenMHz audio relay
    -> replaced with honest UA
  - cloudscraper dependency dropped from pyproject.toml + uv.lock

Frontend retrofit:
  - new GET /api/settings/operator-handle endpoint (local-operator
    gated) returns the install's handle
  - frontend/src/lib/wikimediaClient.ts fetches the handle once on
    first use, caches it for page lifetime, embeds it in the
    Api-User-Agent for every Wikipedia / Wikidata browser-direct call

== GDELT GCS-direct fix ==

GDELT's data.gdeltproject.org is a CNAME to a Google Cloud Storage
bucket. GCS responds with the wildcard *.storage.googleapis.com cert
which legitimately does NOT cover the GDELT custom domain, so Python's
TLS verification correctly refuses the connection. Some networks
happen to route through a path where this works; many (notably Docker
Desktop's outbound NAT on local installs) do not. Verified on the
maintainer's local install: GDELT was unreachable; 1610 geopolitical
events / 48 export files were dropping silently.

Fix: services/geopolitics._gcs_direct_gdelt_url() rewrites any
data.gdeltproject.org URL to its GCS-direct equivalent
(storage.googleapis.com/data.gdeltproject.org/...) where the standard
GCS cert is genuinely valid. api.gdeltproject.org and every other host
are left untouched.

Confirmed live: backend log goes from
  GDELT lastupdate failed: 500
to
  Downloading 48 GDELT export files...
  Downloaded 48/48 GDELT exports
  GDELT parsed: 1610 conflict locations from 48 files

== Tests ==

  backend/tests/test_per_operator_outbound_attribution.py (12 tests)
  backend/tests/test_gdelt_gcs_direct_rewrite.py          (6 tests)
  backend/tests/test_region_dossier_wikimedia_ua.py       (updated to
    pin the helper + per-operator handle, not the old constant)
  frontend/src/__tests__/utils/wikimediaClient.test.ts    (rewritten
    to mock /api/settings/operator-handle and assert per-operator UA)

Local: backend 114/114 security+audit+round7a suite green;
       frontend 718/718 vitest suite green.

Credit: tg12 (external security audit, issues #289/#290/#291
relating to spoofed UAs); BigBodyCobain (operator-prefix call,
GDELT cloud-vs-local diagnosis).
2026-05-21 15:11:28 -06:00
BigBodyCobain 11ea345518 Harden infonet control surfaces 2026-05-18 11:22:38 -06:00
BigBodyCobain b86a258535 Release v0.9.79 runtime and messaging update
Ship the v0.9.79 runtime refresh with transport lane isolation, Infonet secure-message address management, MeshChat MQTT controls, selected asset trail behavior, telemetry panel refinements, onboarding updates, and desktop/package metadata alignment.

Also ignore local graphify work products so analysis folders do not leak into future commits.
2026-05-12 11:49:46 -06:00
BigBodyCobain 6ffd54931c Release v0.9.75 runtime and onboarding update
Ship the 0.9.75 source update with improved startup/runtime hardening, operator API key onboarding, Meshtastic MQTT controls, Infonet/MeshChat separation, desktop package versioning, and aircraft telemetry refinements.

Also updates focused backend/frontend tests for node settings, Meshtastic MQTT settings, and desktop runtime behavior.
2026-05-06 01:15:54 -06:00
BigBodyCobain 4ec1fce53d ci: unblock v0.9.7 release checks 2026-05-01 23:24:46 -06:00
BigBodyCobain 28b3bd5ebf release: prepare v0.9.7 2026-05-01 22:56:50 -06:00