Wire AISHUB_USERNAME into API Keys and onboarding, and surface backup status on the AIS outage banner so operators can keep vessel coverage without changing the AISStream proxy.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove sampling, viewport bbox truncation, and buffer ceilings that were cherry-picking ships, flights, and related layers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Persist OPENCLAW_HMAC_SECRET to data/openclaw.env so empty Docker env vars no longer block auth after UI bootstrap. Add verify_hmac.py, silence Rust warnings (#423), and document Docker signing (#424).
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose observed aircraft/vessel paths, route enrichment, VIP metadata, datalink, and nearby context so agents can reconstruct movement without full telemetry dumps.
Co-authored-by: Cursor <cursoragent@cursor.com>
Parse common position, OOOI, performance, and ops formats across major carriers while hiding VDL binary fragments and duplicate frames from the dossier feed.
Co-authored-by: Cursor <cursoragent@cursor.com>
Bulk-ingest Airframes messages on a rate-limited staggered queue with instant cache lookups and priority per-aircraft refresh when opening a dossier; add Meshtastic manual SCAN PLANET control in the SIGINT panel.
Co-authored-by: Cursor <cursoragent@cursor.com>
Import oligarchs, royals, and curated celebrities from plane-alert-db while excluding joke tag labels from tracked_names. Sync plane_alert_db.json metadata, add import scripts, and map oligarch/royal/celebrity colors in the legend.
Co-authored-by: Cursor <cursoragent@cursor.com>
DGT migrated its public traffic site in 2026: the old image path
infocar.dgt.es/etraffic/data/camaras/{id}.jpg now 302-redirects to the
new etrafficWEB SPA and no longer serves JPEGs, so every DGT camera
showed FEED UNAVAILABLE (proxy and direct source both failed).
New working pattern: https://etraffic.dgt.es/camarasEtraffic/{id}.jpg
- cctv_pipeline.py: DGTNationalIngestor media_url + probe Referer
- routers/cctv.py & main.py: allowlist etraffic.dgt.es + dgt-spain proxy profile
Add Earthdata token entry on the SAR tab with accurate Mode B status, expose optional FIRMS_MAP_KEY in API settings, and remove the frontend operator-unlock gate that blocked localhost saves. Run network-heavy layer-enable fetches on a background executor with frontend retry polling so FIRMS toggles no longer freeze the single API worker.
Drop duplicate slow-tier weather/ukraine jobs, gate correlations when off, slim health probes, keyed layer-panel subscriptions, align backend layer defaults with the dashboard, and fetch CCTV/FIRMS/PSK/etc. synchronously on enable so toggles stay responsive without background prefetch waste.
Replace spoofable Host/Origin WebSocket auth with short-lived bootstrap
tokens minted over the existing local-operator HTTP path. Docker/browser
shell sessions prefetch a token before connecting; loopback peers remain
unchanged.
Also bump backend ws to 8.21.0 and refresh frontend lockfile to clear
npm audit findings (dev toolchain only for frontend).
Fixes#405, #406, #407
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(gdelt): publish enriched copies instead of mutating live features
_enrich_gdelt_titles_background ran in a daemon thread that mutated the
nested properties dicts of GDELT features already published into
latest_data[gdelt]. HTTP readers hold live references to those dicts and
serialize them outside the data lock, so the in-place mutation raced the
serializer and raised RuntimeError: dictionary changed size during
iteration on /api/live-data/slow and /api/bootstrap/critical.
Enrich deep copies instead and atomically swap the top-level key under
_data_lock, with an identity guard so a newer fetch_gdelt() is not clobbered.
Honors the replace-don't-mutate contract documented in fetchers/_store.py.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(gdelt): regression test for background enrichment isolation
Asserts _enrich_gdelt_titles_background does not mutate already-published features and instead atomically swaps latest_data["gdelt"] with enriched copies (with the identity guard). Locks in the fix for the dictionary-changed-size race.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
get_latest_data_deepcopy_snapshot deep-copies layers outside the data lock; a writer mutating a nested object in place races it and raises "dictionary changed size during iteration" (500 on /api/health, /api/live-data). Two changes: (1) _merge_sigint_snapshot now shallow-copies each entry so latest_data["sigint"] no longer aliases the SIGINT bridge dicts or the meshtastic_map_nodes layer (the concrete offender); (2) the snapshot retries a few times as defense-in-depth for any other in-place mutator. Plus regression tests.
Adds connect-contact HTTP endpoint with cached-bundle support, subprocess contact send via docker cp bundle file, and direct Tor prekey fetch to avoid wedging single-worker uvicorn.
Co-authored-by: Cursor <cursoragent@cursor.com>