Commit graph

154 commits

Author SHA1 Message Date
240b9e70b3 Merge pull request 'feat(api): GET /api/stats HUD counters (counts only, never 500)' (#42) from osint-dashboard/t_75d2037f-osint-get-api-stats-hud-counters into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 20s
Reviewed-on: #42
2026-08-31 22:12:55 -04:00
f072a83ca8 Merge pull request 'feat(map): aircraft popup enrichment and emergency/MIL styling' (#35) from osint-dashboard/t_1d9c5839-osint-map-aircraft-popup-enrichment-emer into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 18s
Reviewed-on: #35
2026-08-31 21:55:09 -04:00
71b6f589a5 Merge branch 'master' into osint-dashboard/t_1d9c5839-osint-map-aircraft-popup-enrichment-emer 2026-08-31 21:54:54 -04:00
af836b0414 Merge pull request 'feat(conflicts): GET /api/conflicts conflict-zone catalog + news/GDELT counts' (#33) from osint-dashboard/t_e66223e3-osint-get-api-conflicts-catalog-news-cou into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 21s
Reviewed-on: #33
2026-08-31 21:52:02 -04:00
9712ad03a7 Merge pull request 'feat(cameras): MDOT MiDrive parser -> cameras table' (#34) from osint-dashboard/t_8be8d860-osint-mdot-midrive-cameras-cameras-table into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 28s
Reviewed-on: #34
2026-08-31 21:50:45 -04:00
Sirius DevOps
92dd9c5803 feat(api): GET /api/stats HUD counters (counts only, never 500) 2026-08-31 21:41:45 -04:00
Sirius DevOps
6f5c11e8a7 feat(map): aircraft popup enrichment and emergency/MIL styling
Show callsign/hex/registration/type/alt/gs/squawk in ADS-B popups, keep
Planespotters photos, badge and color squawk 7700/7600/7500 plus emergency
flags, and expose a MIL filter only after /api/aircraft sends extra.role.
2026-08-31 21:31:20 -04:00
Sirius DevOps
fbff9e5415 feat(cameras): MDOT MiDrive parser -> cameras table
Add parse_mdot_json for the MDOT MiDrive camera/list JSON where
coordinates/id live in the county field's map link (lat=/lon=/id=) and
the JPEG still lives in the image field's <img src>. Michigan bbox
filter, missing-coords drop, RTSP reject. discovery_source=mdot,
stable source_url keyed on camera id, url_hash dedupe. Wired into
scrape_source dispatch + CAMERA_SOURCE_URLS defaults.

Unit tests: HTML field extract, bbox drop, missing-coords/image drop,
malformed payload.
2026-08-31 21:31:12 -04:00
Sirius DevOps
a82b62a011 feat(conflicts): GET /api/conflicts catalog + news/GDELT event counts
Curated static catalogue of 13 conflict theatres (Ukraine, Gaza, Sudan,
Myanmar, DRC, Yemen, Syria, Lebanon, Sahel, Somalia, Red Sea, Taiwan
Strait, Korean DMZ) with war|high|elevated severity and short factual
descriptions. GET /api/conflicts returns { zones: [{id,label,severity,
lat,lon,description,eventCount,lastUpdated}], timestamp } where
eventCount rolls up pre-existing geocoded news/GDELT//api/news/map rows
inside each zone bbox (0 when the tables are empty). Adds the conflicts
entry to overlay_catalog(). No upstream scraping, no jittered coords.
2026-08-31 21:30:23 -04:00
Sirius DevOps
c48788d4b6
fix(map): geofence delete, vessel snapshots, sentinel cache, news briefs
Geofences could be drawn but not removed. VesselAPI Hormuz dots vanished
on restart and DVR skipped between the 5 daily polls. Sentinel-1 re-hit
STAC on every pan and often painted a neighbouring swath. Executive
briefs truncated; ticker stayed empty unless something was critical.

- Layer-panel list + polygon popup DELETE /api/geofences/{id}
- Persist VesselAPI polls to vessels (UTC-day purge, DVR as-of, boot hydrate)
- Cache Sentinel-1 by 2° cell; pick covering scene; clip Leaflet tiles
- Retry truncated LLM JSON; ticker falls back to medium/low; 3-min HUD poll
2026-08-29 20:40:27 -04:00
47c726d68d Merge pull request 'fix(map): geofence delete, vessel snapshots, sentinel cache, news briefs' (#32) from fix/dashboard-geofence-vessels-sentinel-news into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 23s
Reviewed-on: #32
2026-08-29 20:39:12 -04:00
fdf5969e27 Merge pull request 'fix(ingest): skip known KEV/FIRMS rows; CI skips unchanged images' (#31) from feat/lean-ingest-ci-skip-unchanged into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 17m52s
Reviewed-on: #31
2026-08-29 19:37:38 -04:00
Sirius DevOps
5651d251d0
fix(ingest): skip known KEV/FIRMS rows; CI skips unchanged images
CISA KEV republished 1685 NIST URLs every 5 min; FIRMS re-inserted
~325k global hotspots every 15 min. Producer now skips URLs already
in event_dedup and FIRMS CSVs that are unchanged (delta-only persist).

CI rebuilds only images whose paths changed and never pulls/rebuilds
Timescale or bounces osint-db unless Dockerfile.pg changes.
2026-08-29 19:32:00 -04:00
0406eb6b7b Merge pull request 'feat(gpsjam): GPS interference hex overlay API (GPSJAM/ADS-B Exchange)' (#29) from feat/gpsjam-interference-overlay into master
Some checks failed
build-and-deploy / build-push-deploy (push) Failing after 1h14m25s
Reviewed-on: #29
2026-08-29 17:54:01 -04:00
0becc8a716 Merge pull request 'feat(map): chokepoint preset buttons (Leaflet)' (#28) from feat/chokepoint-preset-buttons into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m42s
Reviewed-on: #28
2026-08-29 17:03:15 -04:00
4c9ffb9673 Merge pull request 'feat(map): chokepoint preset catalog + vessels ?src= filter' (#27) from feat/chokepoint-presets-vessel-src into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m17s
Reviewed-on: #27
2026-08-29 16:59:35 -04:00
Sirius DevOps
42ca6295f0 feat(gpsjam): GPS interference hex overlay API (GPSJAM/ADS-B Exchange)
GET /api/map/gpsjam?date=YYYY-MM-DD (default yesterday UTC) fetches the
daily gpsjam.org H3 resolution-4 CSV, converts hexes to GeoJSON polygons,
and tags each with level low|medium|high (0-2% / 2-10% / >10%) via GPSJAM's
denoise formula. Whole world once, 1h TTL, graceful unavailable fallback.

- overlay_catalog: add kind=geojson gpsjam stub
- tests: mocked-HTTP unit + API contract (9 new)
2026-08-29 14:17:52 -04:00
Sirius DevOps
ac03442998 feat(map): chokepoint preset buttons on the Leaflet toolbar
Fetch GET /api/map/chokepoints once. Desktop buttons / phone <select>
fly to Hormuz, Bab el-Mandeb, Suez, Malacca, Taiwan. Turns Vessels on,
uses ?src=vesselapi on Hormuz, enables Sentinel-1 there, and never
POSTs /api/vessels/subscribe outside CONUS.
2026-08-29 14:12:02 -04:00
Sirius DevOps
ef8b877d21 feat(map): chokepoint preset catalog + vessels ?src= filter
- GET /api/map/chokepoints: static five-strait fly-to catalog (Hormuz, Bab
  el-Mandeb, Suez, Malacca, Taiwan) with bbox/center/zoom/vesselapi. No
  VesselAPI calls; only Hormuz flags vesselapi=true.
- GET /api/vessels?src=aisstream|vesselapi|all (default all) filters the
  union store by provider so a Hormuz view skips ~5k CONUS AISStream rows.
- Tests: span validation of all five boxes, catalog 200 shape, src filter.
2026-08-29 14:03:15 -04:00
8498945549 Merge pull request 'docs: free satellite-feeds catalog for the OSINT map' (#26) from docs/satellite-feeds-catalog into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m3s
Reviewed-on: #26
2026-08-29 13:15:46 -04:00
Sirius DevOps
09fe615d89 docs: add free satellite-feeds catalog from research card
Live-probed 2026-08-29 inventory (GIBS extra layers, GOES/Himawari,
HLS, OPERA, STAC/COG paths). Ranked drop-ins for the OSINT map; skip
paid/NICFI. Does not change runtime code.
2026-08-29 13:12:26 -04:00
b6da7b7bd4 Merge pull request 'fix(vessels): protect Hormuz VesselAPI rows from AISStream eviction' (#25) from fix/vesselapi-hormuz-eviction into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m7s
2026-08-29 13:04:28 -04:00
Sirius DevOps
86b412dd37 fix(vessels): protect Hormuz VesselAPI rows from AISStream eviction
AISStream's live US-coast feed crowds vessel_last_known past _MAX_VESSELS
(6000) and the 'evict oldest seen_at' pass reaps the Hormuz VesselAPI rows,
which are only refreshed every VESSELAPI_INTERVAL (~4.8h). Result: the poller
was fetching 50 Hormuz vessels per 2xx but /api/vessels?bbox=Hormuz stayed
empty. Eviction now skips src=vesselapi rows (falls back to evictable-only,
clamped) so the blind-spot box stays resident between polls.
2026-08-29 13:05:57 -04:00
fc2c7c28c3 Merge pull request 'fix(titiler): drop @1x suffix + append SAS token top-level' (#24) from fix/titiler-tile-url into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m12s
Reviewed-on: #24
2026-08-29 12:13:33 -04:00
Sirius DevOps
68e1b63421 fix(titiler): drop @1x suffix + append SAS token top-level
TiTiler /tiles/{z}/{x}/{y} takes a strict int for y; the @1x scale-suffix
template 422s on every SAR tile request (int_parsing on '21@1x').

sign_cog_url wrapped the Planetary Computer SAS token under a single
token= param, which Azure rejects (403→409); the token is a pre-encoded
query string and must be appended top-level (st=…&se=…&sig=…).

Proven live: correct-form URL renders a 200 image/png 256px tile.
2026-08-29 11:42:03 -04:00
405845067e Merge pull request 'feat(map): Sentinel-1 SAR layer toggle (Leaflet)' (#23) from feat/sentinel1-sar-layer into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m10s
Reviewed-on: #23
2026-08-29 11:24:09 -04:00
Sirius DevOps
fa06c1dc42 feat(map): Sentinel-1 SAR layer toggle (Leaflet)
Add a default-off Layers row that fetches GET /api/map/sentinel1?bbox=
only when enabled, then L.tileLayer(tileUrl) at opacity 0.8. HUD hints
for 404/429/502; moveend refetch while on. Same-origin tileUrl as-is.
2026-08-29 11:15:34 -04:00
4b9760d6e1 Merge pull request 'fix(ui): stop map chrome overlapping on phone' (#22) from fix/map-mobile-chrome-overlap into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m0s
Reviewed-on: #22
2026-08-29 10:46:14 -04:00
Sirius DevOps
82ef815e20 fix(ui): stop map chrome overlapping on phone
Retune the 820px layout so GIBS sits as a full-width bar under the
header, LAYERS starts collapsed top-left, zoom stays top-right, and
the DVR row clears the ticker. Hide attribution and map-hint on phone
so NASA/RainViewer text no longer paints through MKT/NEWS.
2026-08-29 10:30:46 -04:00
5434dda2a9 Merge pull request 'fix(ui): keep last Keys card above ticker on mobile' (#21) from fix/keys-mobile-vesselapi-clip into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m8s
Reviewed-on: #21
2026-08-29 10:06:02 -04:00
Sirius DevOps
773d204f68 fix(ui): keep last Keys card above ticker on mobile
Stage uses 100dvh (100vh fallback) plus safe-area so the scrollport
ends above the dock. Subviews get extra bottom padding; phone key
forms wrap with 44px tap targets.
2026-08-29 09:47:27 -04:00
b0657cf3e4 Merge pull request 'feat(sentinel1): Sentinel-1 SAR STAC -> self-hosted TiTiler tile template' (#20) from feat/sentinel1-sar-titiler into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m6s
Reviewed-on: #20
2026-08-29 08:24:31 -04:00
Sirius DevOps
d09009e0bf merge master: keep VesselAPI env and self-hosted TiTiler in compose
PR #20 conflicted on docker-compose.yml after #19 landed. App service now
passes both VESSELAPI_* and TITILER_* ; titiler stays on 127.0.0.1:8001.
2026-08-29 08:25:51 -04:00
7b48caa793 Merge pull request 'feat(vessels): AISStream (US) + VesselAPI (Hormuz) as two active providers' (#19) from feat/vesselapi-ais-poller into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 2m51s
Reviewed-on: #19
2026-08-29 07:23:37 -04:00
Sirius DevOps
5212aaa5cd feat(sentinel1): Sentinel-1 SAR STAC -> self-hosted TiTiler tile template
Add GET /api/map/sentinel1?bbox=... which queries Planetary Computer STAC
(sentinel-1-grd, last 7d, most recent), signs the vv/hh COG with a SAS token,
and returns a same-origin /titiler/... XYZ tile template. Cache keyed on
quantized bbox + UTC day (20 min TTL). 429 -> 429 (Retry-After), no imagery
-> 404, other upstream errors -> 502.

Self-host TiTiler on the Pi (ghcr.io/developmentseed/titiler, arm64, 1G cap,
host loopback 8001 -> container 8000) instead of titiler.xyz. nginx
/titiler/ proxy snippet routes browser tiles to it; TITILER_PUBLIC_BASE and
TITILER_INTERNAL_URL are env-driven (no hardcoded hostnames).
2026-08-29 01:01:20 -04:00
Sirius DevOps
a8ad9ba1ff fix(vessels): both AIS providers are first-class, not fallback
Layer-panel note and .env.example now match the two-key model:
AISStream (US live) and VesselAPI (Hormuz) run independently.
2026-08-29 00:58:57 -04:00
Sirius DevOps
e7429a4161 feat(vessels): present AISStream + VesselAPI as two independent providers
- keystore KEY_REGISTRY copy splits the two keys (open/shared US-coast WS vs
  commercial Hormuz 5x/day REST); VesselAPI no longer reads as an AIS backup.
- vesselapi.py docstring drops 'fallback'; states both are first-class.
- /api/vessels docstring: last-known is the union (extra.src aisstream vs
  vesselapi).
- tests: VesselAPI idles/polls independently of the AISStream key.
2026-08-29 00:40:41 -04:00
Sirius DevOps
1f23083351 feat(vessels): VesselAPI quota-capped AIS poller for Middle East blind spot
Add a server-side REST poller for VesselAPI (free tier 150 calls/mo) that
upserts last-known positions into the shared vessel_last_known store when
AISStream is unset. Default box is the Strait of Hormuz (span 3.6 <= 4 deg),
never CONUS/NC (AISStream owns US coasts).

- app/vesselapi.py: worker loop, 4deg span validator, position->marker
  transform (skip suspected_glitch), durable Postgres daily-quota table.
- Local hard cap 5 successful 2xx/UTC day (VESSELAPI_MAX_CALLS_PER_DAY),
  monthly floor from X-RateLimit-Remaining, single request limit=50, no
  nextToken, no filter.sat, no retry-storm.
- keystore VESSELAPI_API_KEY registry entry; config + compose env passthrough
  (app + ingest); wired next to AISStream in main.py lifespan + run_ingester.
- GET /api/vessels docstring notes AISStream and/or VesselAPI cache.
- tests/test_vesselapi.py: 20 unit tests (no network/DB).
2026-08-29 00:18:54 -04:00
30a9c9e9b1 Merge pull request 'fix(aircraft): show planespotters photo in overlay popup' (#18) from fix/planespotters-popup-photo into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 2m55s
Reviewed-on: #18
2026-08-28 23:27:23 -04:00
Sirius DevOps
20a266563c fix(aircraft): show planespotters photo in overlay popup
Planespotters 403s server clients whose User-Agent has no contact
email; compose default UA lacked one. Leaflet popup.update() also
re-ran the bindPopup factory and wiped the thumbnail after load.

- Send a contact UA on planespotters lookups; align compose default.
- Resize the popup without re-rendering content.
- Keep hex/src on live firefighter upserts so they get a photo too.
2026-08-28 23:28:28 -04:00
bb849515fb Merge pull request 'fix: stop summarizer from bailing to a canned empty brief' (#17) from fix/summarizer-empty-brief into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m20s
Reviewed-on: #17
2026-08-28 23:19:14 -04:00
Sirius DevOps
7822d45f37 fix: stop summarizer from bailing to a canned empty brief
The reduce/recap prompts told the model to AND STOP with "No qualifying…"
whenever nothing looked breaking enough, so full article batches became
one sentence. Always write summary_en from the provided stories; ticker
and map may still be empty. Futures/market tape stay ignored.
2026-08-28 23:20:40 -04:00
680aeea29f Merge pull request 'feat(aircraft): planespotters.net photo in aircraft popup' (#16) from feat/planespotters-photos into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m53s
Reviewed-on: #16
2026-08-28 22:58:29 -04:00
Sirius DevOps
665aaec22f feat(aircraft): planespotters.net photo in aircraft popup
Add planespotters.net latest-photo lookup for ADS-B aircraft.

- app/live_layers.py: fetch_planespotters_photo() (hex preferred, reg
  fallback) + _normalize_planespotter_photo(); 24h TTL cache (their ToS cap).
- app/main.py: GET /api/aircraft/photo?hex=...|reg=... (422/404/502).
- app/static/index.html: .ps-photo block in ADS-B popup; lazy load on
  popupopen; thumbnail links to photo page + photographer credit.

Server-side proxy, not browser fetch(): planespotters 403s any request
carrying an Origin header (every browser fetch() sends one). The thumbnail
binary is loaded by the browser straight from their CDN, never re-hosted.

Tests: 4 unit + 4 API contract (38 pass in the two files).
2026-08-28 22:58:04 -04:00
e568d013ca Merge pull request 'feat: 23:00 daily news recap; prompts drop futures' (#15) from feat/news-daily-recap into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 4m5s
Reviewed-on: #15
2026-08-28 22:53:59 -04:00
Sirius DevOps
49ee5fe7a3 feat: 23:00 daily news recap; prompts drop futures
Summarizer still runs the 15-min analyst, plus a 24h breaking-news recap
at 23:00 America/New_York. HUD pins kind=daily_recap. Prompts ignore
futures/market tape.
2026-08-28 22:53:31 -04:00
d25ca8ed9c Merge pull request 'fix: chunk FIRMS bulk inserts under asyncpg 32767 bind cap' (#14) from fix/firms-bind-limit into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m35s
Reviewed-on: #14
2026-08-28 22:20:48 -04:00
Sirius DevOps
5302e261d7 fix: chunk FIRMS bulk inserts under asyncpg 32767 bind cap
A ~90k-row INSERT ... ON CONFLICT dies with InterfaceError, so fire_loop
fails every poll and /api/health stays degraded (fires age > 30 min).
Chunk 2000 rows per statement, still one commit per poll.
2026-08-28 22:22:12 -04:00
19fa0db32c Merge pull request 'fix: pipeline reliability — WS, summarizer, ingest, health' (#13) from fix/pipeline-reliability into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 4m11s
Reviewed-on: #13
2026-08-28 21:49:56 -04:00
Sirius DevOps
d81ea68dec fix: pipeline reliability — WS, summarizer, ingest, health
Stop the live HUD reconnect storm (nginx WS snippet + backoff), copy
intel/nous_client into the summarizer image, and make event ingest
idempotent on URL. GDELT uses the DOC API; NWS no longer sends bbox;
FIRMS is one ON CONFLICT batch; GET /api/aircraft serves last-known.
Health reports freshness without 503ing docker. EONET + CISA KEV added.
2026-08-28 21:49:05 -04:00