Commit graph

35 commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
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
Sirius DevOps
8643153954 feat: continuous news scrape + 15-min analyst, expand urls.txt
Scraper loops with NEWS_SCRAPE_INTERVAL_S (default 10s after each
crawl). Summarizer runs every NEWS_SUMMARIZE_INTERVAL_S (default 900)
over the last 15 minutes of articles. Feed list replaced from the
k8s scrapy-urls configmap (334 sources).
2026-08-28 20:53:32 -04:00
Sirius DevOps
d2bf55183c fix: let Settings model reach summarizer; cap map pins at 20 2026-08-28 17:06:55 -04:00
Sirius DevOps
1616413e65 feat: persist summarizer model in app_settings 2026-08-28 17:06:55 -04:00
Sirius DevOps
5b23578302 feat: news ticker and map API contracts 2026-08-28 17:06:55 -04:00
Sirius DevOps
5ea9a4e879 feat: lean-Pi WS fan-out, geofences, DVR playback, fire/aircraft correlation
Phase 1: in-memory ConnectionManager viewport fan-out, 500ms map debounce,
cachetools TTLCache, background masscan/ffmpeg, compose memory caps.

Phase 2: PostGIS geofences + ST_Intersects alerts, Timescale 1-min CAGGs
and timestamp playback, FIRMS/WFIGS x firefighting ADS-B within 20 miles.

No Redis/Kafka/Celery.
2026-08-28 09:35:18 -04:00
Sirius DevOps
32320635dc feat: AIS stream follows the map viewport
All checks were successful
build-and-deploy / build (push) Successful in 2m32s
The Vessels layer now retunes the server-side AISStream subscription to the
client viewport instead of a static AISSTREAM_BBOX. The frontend POSTs its
quantized viewport box to /api/vessels/subscribe on moveend; the ais_stream
worker coalesces and applies it at the service's 1 subscription/s cap, then
last-known positions for the new area arrive within a couple of seconds (the
frontend does one follow-up fetch after retuning). Bounds the in-memory
vessel store across regions. Key stays server-side.
2026-08-27 21:50:38 -04:00
Sirius DevOps
435f63e473 perf: shared httpx client, gzip, overlay cache-control
Reuse one TLS pool for overlay upstreams (8s/3s timeouts). Gzip JSON
over 1 KB. Aircraft/vessels Cache-Control max-age=5, alerts/perimeters 30.
Lifespan replaces deprecated on_event startup.
2026-08-27 21:21:19 -04:00
Sirius DevOps
8f89994201 perf: slim fires/cameras/events/news map payloads
Heatmap uses {lat,lon,i,c}. Camera list drops URLs (detail via GET
/api/cameras/{id}). Event blips skip body. News omits content unless
include_content=true.
2026-08-27 21:19:42 -04:00
Sirius DevOps
f779b1f225 feat: toggleable live map feeds (ADS-B, trains, AIS, radar, WFIGS, NWS)
Wire the free data streams from docs/free-data-streams.md into the
dashboard as layer-panel toggles. Third-party APIs are proxied/cached
in FastAPI; raster tiles (IEM, RainViewer, GIBS) stay in the browser.

- Aircraft via ADSB.lol viewport poll (bbox required, radius ≤ 150 nm)
- Amtraker trains, NHC storms, WFIGS incidents/perimeters
- NWS + IEM SBW as /api/weather-alerts (does not collide with /api/alerts)
- AISStream worker is server-side only and idles without AISSTREAM_API_KEY
- Caltrans CWWP2 D1–D12 camera parser; FIRMS dual-write NOAA-20/21
2026-08-27 19:08:30 -04:00
Sirius DevOps
51e906c41f Merge remote-tracking branch 'forgejo/master' into frontend-overhaul
# Conflicts:
#	app/static/index.html
2026-08-27 17:33:12 -04:00
Sirius DevOps
12f01a17a9 frontend: Ghost-in-the-Shell overhaul — map-first landing, HUD chrome, event blips, tickers
- Map is now the landing view: full-viewport NASA GIBS globe, existing
  fires/cameras/HLS map machinery preserved verbatim
- New 'Event Blips' layer: geolocated ingest events, color-coded by source,
  bbox/since/has_coords filters added to GET /api/events
- Weather / Flights(ADS-B) / Vessels(AIS) layer slots reserved (feed pending)
- Market ticker strip with configurable symbols — auto-promotes to LIVE when
  GET /api/market returns {symbols:[...]} (contract documented in Settings)
- Breaking-news ticker: LLM exec-summary flash + headline marquee, 15-min cycle
- Dropdown nav (Map/News/Events/Alerts/Entities/Ingest/API Keys/Settings),
  Settings view (localStorage: symbols, map layer defaults), System panel
- Section-9 theme: near-black navy, cyan/magenta accents, Orbitron/Rajdhani/
  Share Tech Mono, chamfered HUD panels, scanlines, boot splash, UTC clock
- Fix: events.camera enum value missing from models.py/schemas.py caused 500s
  on every events query once camera events flowed in (migration 004 added it
  to the DB enum only)
2026-08-27 17:22:14 -04:00
Sirius DevOps
5cea0a5940 map: cluster camera markers so cameras stay visible when zoomed in
All checks were successful
build-and-deploy / build (push) Successful in 4m33s
Zooming in replaced the marker group with a bbox subset, so dense world
views (5k dots) thinned to a handful of pins — or zero on a wrapped world
copy, where currentBBox collapsed to a sliver and the API returned ~nothing.
Cameras only seemed to exist when zoomed out.

- vendor leaflet.markercluster 1.5.3; camera markers now render as numbered
  neon-green clusters at low zoom and split into individual dots as you zoom,
  so camera coverage is visible at every zoom level (spiderfy at max zoom)
- guard against null lat/lon rows (no phantom markers at 0,0)
- stale-response guard for loadCams/loadFires: rapid wheel zoom can resolve
  fetches out of order and a stale bbox response blanked the map
- serve index.html with Cache-Control: no-cache so the browser never keeps
  serving a pre-deploy copy after fixes land
2026-08-27 17:00:09 -04:00
Sirius DevOps
1145ce8ec1 cameras: fix world-zoom bbox collapsing to ~48 pins
All checks were successful
build-and-deploy / build (push) Successful in 2m23s
Leaflet worldCopyJump at zoom 2 reports longitudes outside ±180; clamping
them independently produced west>=east or a thin sliver. Low zoom now
queries the full world, and the API ignores inverted bboxes.
2026-08-27 15:59:22 -04:00
Sirius DevOps
0c7f80655e cameras: Live-Environment-Streams + HLS.js player
All checks were successful
build-and-deploy / build (push) Successful in 3m8s
Ingest ~4.2k direct HLS/YouTube feeds (VDOT, MDSHA, DelDOT, Iowa DOT,
OpenCCTV, etc.) from the public GeoJSON catalog. Popup plays HLS via a
CORS-safe playlist proxy + vendored hls.js, YouTube via embed, with
ffmpeg-MJPEG fallback. ALERTWest rows now use a stable camera id so
hourly scrapes do not duplicate.
2026-08-27 15:54:54 -04:00
Sirius DevOps
7ceba736dd cameras: map only working HTTP/MJPEG feeds
All checks were successful
build-and-deploy / build (push) Successful in 2m53s
Masscan open-554 hosts almost never have a public picture. The map now
defaults to cameras with an http(s) snapshot_url (~132 directory cams).

Ingest probes unauthenticated still/MJPEG paths first and skips the rest,
so dead RTSP-only hosts never land on the map. Pass working=false to see
unverified port-554 hits.
2026-08-25 22:05:15 -04:00
Sirius DevOps
12262d1562 cameras: RTSP preview via ffmpeg, stop opening VLC, drop masscan to 200pps
All checks were successful
build-and-deploy / build (push) Successful in 2m23s
Masscan finds are rtsp:// with no snapshot_url, so the popup skipped the
<img> and the leftover source link handed the OS an rtsp:// URL (VLC).

- Popup always hits /api/cameras/{id}/snapshot (HTTP stills, then one
  ffmpeg frame grab). No credentials. 10s hard timeout.
- rtsp:// is rendered as text, never as an href.
- ffmpeg added to the app image for the RTSP still/MJPEG path.
- Default MASSCAN_RATE 200 (1k/10k saturated the home uplink).
2026-08-24 23:36:34 -04:00
Sirius DevOps
250dbbb5df Camera popups: live MJPEG preview via /api/cameras/{id}/stream passthrough, snapshot fallback, taller thumb
All checks were successful
build-and-deploy / build (push) Successful in 1m52s
2026-08-24 21:38:46 -04:00
Sirius DevOps
3aa6f265bb Add NASA GIBS satellite basemap world map tab (Leaflet)
Some checks failed
build-and-deploy / build (push) Failing after 5s
Interactive world map panel for the dashboard:
- Vendored Leaflet 1.9.4 under app/static/vendor/leaflet/ (served via
  new /static mount on the FastAPI app)
- New Map tab: GIBS WMTS raster basemap (BlueMarble_ShadedRelief_Bathymetry,
  VIIRS/MODIS/Aqua CorrectedReflectance_TrueColor, VIIRS_DayNightBand),
  layer picker + UTC date selector (Latest/-7d/-30d quick picks)
- /api/map/layers: curated GIBS catalog (tms, format, has_time, max_zoom)
- /api/map/times: per-layer date windows from GIBS Domains XML (6h cache,
  graceful 502 on GIBS hiccup; static layers 422)
- Fire hotspots (existing /api/fires) + open cameras (/api/cameras) as
  toggleable bbox-scoped overlays; dark UI matching dashboard theme
2026-08-24 17:35:44 -04:00
Sirius DevOps
91f436390b Add news pipeline: hourly scraper + Gemini summarizer in compose
Some checks failed
build-and-deploy / build (push) Failing after 4s
Vendor the newsPipeline scraper + summarizer into the repo and wire them into
docker-compose against the EXISTING osint-db (no second Postgres), replacing
the upstream k8s CronJobs with in-compose wall-clock loops (:00 scrape, :05
summarize).

- news/scraper: vendored Scrapy project (257 RSS feeds) + hourly loop
  scheduler (run_news_scraper.py)
- news/summerizer: vendored Gemini map-reduce summarizer, cleaned:
  * fix broken google-genai response handling (_extract_text, defensive)
  * fix malformed INSERT/GRANT query in save_summary_to_db
  * OSINT-neutral default MAP_PROMPT; futures/markets language gated behind
    INCLUDE_FUTURES=0 (yfinance lazy-imported)
  * env-configurable model, batch size, lookback window
  + hourly loop scheduler (run_news_summarizer.py, :05)
- alembic 003_news: idempotent articles + article_summaries tables
- API: GET /api/news and GET /api/news/summaries (+ models, schemas)
- tests/test_api_news.py: 5 DB-backed contract tests (all pass vs real PG)
- docs/news.md + .env.example updates

Both services run under the `ingest` compose profile (matching the
ingester/camera-scraper pattern) and build arm64 on the Pi via the existing
Forgejo CI workflow. telebot left out of scope (reserved env only).
2026-08-24 17:28:46 -04:00
Sirius DevOps
cf98af8101 Merge remote-tracking branch 'forgejo/master'
Some checks failed
build-and-deploy / build (push) Failing after 4s
# Conflicts:
#	.env.example
2026-08-24 15:40:31 -04:00
Sirius DevOps
627990efde Add NASA FIRMS active-fire ingest + /api/fires; API keys management page
Coherent merge of two coordinated features on the shared working tree:

FIRMS fire heatmap (backend, t_6e404c14):
- app/fire_sources.py: fetch FIRMS VIIRS area CSV (free MAP_KEY) -> NATS events.fire
- fires hypertable (TimescaleDB, 1-day chunks) with natural-key PK
  (latitude, longitude, acq_time, satellite); idempotent ON CONFLICT DO NOTHING
- alembic/versions/002_fires.py; GET /api/fires?bbox=&since= (JSON only)
- POST /api/ingest/fires; ~15 min poll loop (FIRMS_INTERVAL=900) in ingester
- env-driven config (FIRMS_MAP_KEY/DATASET/BBOX/INTERVAL); docs/firms.md covers
  the zero-cost GIBS VIIRS_SNPP_Thermal_Anomalies_375m_All tile alternative
- 18 tests (parser, mapping, idempotency, API contract) verified vs real
  TimescaleDB+PostGIS (localhost/osint-dashboard-pg image)

API keys page (frontend, t_4433cff2):
- app/keystore.py: api_keys table (self-creating), FIRMS/GEMINI/TELEGRAM
  registry with format validation, ****last4 masking, get_api_key()
- GET/POST/DELETE /api/keys (never returns full values); Keys tab in index.html

DB_NULL_POOL env switch in app/database.py enables a NullPool for tests /
short-lived processes that open a fresh event loop per unit.
2026-08-24 15:37:42 -04:00
Sirius DevOps
69a30115cc Add open IP camera discovery scraper (OSINT map)
Some checks failed
build-and-deploy / build (push) Failing after 5s
- camera_scraper: public-directory-only discovery (Insecam-style HTML +
  plain-text lists), hard private-range guard (fail closed), per-host
  rate limiting, Nominatim geocoding at <=1 req/s, TTL'd local snapshot
  cache, sha256 url_hash dedupe with Postgres upsert
- cameras table (migration 002) + /api/cameras?bbox= + snapshot endpoint
  with cache passthrough in main.py
- run_camera_service: long-running cycle worker following NATS->ingester
  pattern; publishes events.camera for shared ingester
- docker-compose camera-service profile, .env.example knobs

Verified E2E against TimescaleDB+PostGIS: private-range entries dropped,
dedupe across cycles holds, bbox query returns expected rows.
2026-08-24 14:54:51 -04:00
sirius0xdev
8eabe3b5e8
Fix SQLAlchemy 2.0 count().filter(); add search_vector trigger for full-text search
Some checks failed
build-and-deploy / build (push) Failing after 15s
2026-07-07 18:32:02 -04:00
sirius0xdev
0a6da2f9c1
Run migrations via entrypoint (alembic upgrade head) instead of nested asyncio.run in startup hook
Some checks failed
build-and-deploy / build (push) Failing after 16s
2026-07-07 18:24:01 -04:00
sirius0xdev
93b8b89639
first commit 2026-06-04 20:30:04 -04:00