Wikimedia's User-Agent policy asks API clients to identify themselves
with a stable, contactable identifier so their operators can rate-limit
or coordinate. Before this change, ShadowBroker was sending:
- Backend (region_dossier.py): generic project default UA only; no
Api-User-Agent.
- Frontend (useRegionDossier.ts, WikiImage.tsx, NewsFeed.tsx): zero
identifying header at all; three separate copy-pasted anonymous
fetches with their own module-local caches.
Three separate components doing the same broken thing meant policy
fixes had to happen in three places, with no shared cache or kill
switch.
Fix (no UX change, zero hostility):
== Backend ==
`backend/services/region_dossier.py` now sets explicit `User-Agent` +
`Api-User-Agent` headers on every outbound Wikidata and Wikipedia
request via a new `_WIKIMEDIA_REQUEST_HEADERS` constant. The identifier
includes a contact path (issues page on the public GitHub repo).
== Frontend ==
New shared helper `frontend/src/lib/wikimediaClient.ts`:
- `fetchWikipediaSummary(title)` — single source of truth for Wikipedia
REST summary lookups, with one shared LRU cache (in-flight requests
deduplicated, 512-entry cap), `Api-User-Agent` on every fetch.
- `fetchWikidataSparql(query)` — same shape for Wikidata SPARQL.
- `WIKIMEDIA_API_USER_AGENT` — exported constant; one place to update
if Wikimedia ever asks us to back off.
Refactored three components to use the shared client:
- `frontend/src/hooks/useRegionDossier.ts` — fetchLeader() and
fetchLocalWikiSummary() now route through the shared helpers.
- `frontend/src/components/WikiImage.tsx` — uses fetchWikipediaSummary,
proper React state instead of module-mutation + forceUpdate trick.
- `frontend/src/components/NewsFeed.tsx` — same shape.
UX: byte-for-byte identical. Same thumbnails, same dossier content,
same load behavior. The only observable difference is the outgoing
request header.
Note on #239 (route duplication): an audit-grade inventory shows 166
main.py routes are shadowed by router modules. That cleanup is too
large to land safely in this PR; it will be staged as a separate
ladder of small PRs grouped by router module.
Tests:
- `backend/tests/test_region_dossier_wikimedia_ua.py` — 3 tests
asserting backend headers are present.
- `frontend/src/__tests__/utils/wikimediaClient.test.ts` — 9 tests
covering Api-User-Agent presence, shared cache, concurrent
deduplication, disambiguation/HTTP-error/network-error fallthroughs,
empty-input safety.
Local: backend 76/76 security suite green, frontend 716/716 vitest
suite green.
Credit: tg12 (external security audit).
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.
Add Tor/onion runtime wiring and faster Infonet node status refresh.
Keep node bootstrap state clearer across Docker and local runtimes.
Use selected aircraft trail history for cumulative tracked-aircraft emissions.
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
- Add 5 native ingestors to cctv_pipeline.py: DGT (~1,917 cameras),
Madrid (~357), Málaga (~134), Vigo (~59), Vitoria-Gasteiz (~17)
- Fix DGT DATEX2 parser to match actual XML schema (device elements,
not CctvCameraRecord)
- Wire all new ingestors into the scheduler via data_fetcher.py
- Remove standalone spain_cctv.py by Alborz Nazari, replaced by native
pipeline ingestors that integrate with the existing scheduler pattern
- Fix CCTV image loading for servers with Referer-based hotlink
protection (referrerPolicy="no-referrer")
- Replace external via.placeholder.com fallbacks with inline SVG data
URIs to avoid dependency on unreachable third-party service
- Surface source_agency attribution in CCTV panel UI for open data
license compliance (CC BY / Spain Ley 37/2007)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Infer country and military force (PLA, JSDF, ROK, ROC) from ICAO hex
address blocks when the flag field is Unknown. Extract and extend aircraft
model classification to cover East Asian fighters, cargo, recon, and
tanker types with hyphen-normalized matching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces array-index-based selection with stable backend identifiers so
selected entities persist correctly across data refreshes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: 14e316d055ba0b1fe16a2be301fcaaf4349b5a29
New features:
- NASA GIBS (MODIS Terra) daily satellite imagery with 30-day time slider
- Esri World Imagery high-res satellite layer (sub-meter, zoom 18+)
- KiwiSDR SDR receivers on map with embedded radio tuner
- Sentinel-2 intel card — right-click for recent satellite photo popup
- LOCATE bar — search by coordinates or place name (Nominatim geocoding)
- SATELLITE style preset in bottom bar cycling
- v0.4 changelog modal on first launch
Fixes:
- Satellite imagery renders below data icons (imagery-ceiling anchor)
- Sentinel-2 opens full-res PNG directly (not STAC catalog JSON)
- Light/dark theme: UI stays dark, only map basemap changes
Security:
- Removed test files with hardcoded API keys from tracking
- Removed .git_backup directory from tracking
- Updated .gitignore to exclude test files, dev scripts, cache files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Former-commit-id: e89e992293