# Phase 11 — Live Sentinel imagery fetch for TerraMind-NYC ## Goal Replace the cached Major-TOM monotemporal chips (frozen 2020-2025 acquisition window) with a *live* fetch path so that `app/context/terramind_nyc.py` can run inference on the most-recent qualifying Sentinel-2 + Sentinel-1 acquisition for any NYC point. The imagery freshness is then a number Granite can cite alongside the prediction. ## What live actually means here Sentinel revisit times, honestly: | Source | Native revisit | With cloud filter | STAC availability | |---|---|---|---| | Sentinel-2 (S2A + S2B) | 5 days | 5–15 days | < 24 h after acquisition | | Sentinel-1 (S1A + S1C) | ~6 days | n/a (radar) | < 24 h after acquisition | So "live" = "most-recent qualifying acquisition" = typically 1–7 days old. We disclose the per-query age so a Granite synthesis can cite exactly how fresh the imagery is. ## Sources tested ### probe_earth_search.py — Element 84 / AWS Open Data Anonymous, no auth, COG-streamable. Result for Empire State Building: | Modality | Result | |---|---| | Sentinel-2 L2A | acquired **1 day ago**, 7.0% cloud, 1.4 s chip read | | Sentinel-1 GRD (raw slant-range) | acquired 4 days ago, **no embedded CRS**; needs RTC processing | | Total wall-clock (S2 only) | **3.5 s** | S2 is great. **GRD is unusable for our model**: it ships in slant range without a CRS, so reprojection to a chip grid fails. We need RTC. Earth Search's collection list as of 2026-05-05: ``` sentinel-2-l2a, sentinel-2-l1c, sentinel-2-c1-l2a, sentinel-2-pre-c1-l2a, sentinel-1-grd, cop-dem-glo-30, cop-dem-glo-90, landsat-c2-l2, naip ``` Notably **no `sentinel-1-rtc`**. So Earth Search alone cannot serve the SAR modality our model needs. ### probe_pc_s1rtc.py — Microsoft Planetary Computer Anonymous via URL signing, has the `sentinel-1-rtc` collection. Result: | Modality | Result | |---|---| | Sentinel-1 RTC | acquired **4 days ago**, EPSG:32618 (UTM-18N), 2.7 s chip read | | Total wall-clock | **3.3 s** | Despite our prior experience (May 3 evening showed >50% timeout rate), PC was reliable and fast on May 4 evening. The flakiness appears event-driven, not chronic. ## Sovereignty matrix | Source | Host | Auth | Sovereignty | Verdict for Riprap | |---|---|---|---|---| | **ESA Copernicus Data Space (CDSE)** | ESA | Free registration | EU sovereign, authoritative | Best for production civic-tech, requires user-side credential setup | | **NASA Earthdata / ASF** | NASA | Earthdata Login (free) | US sovereign, used by FEMA/USGS | Same registration friction as CDSE | | **Element 84 / AWS Open Data** | AWS | None | Private cloud, public access | Zero-friction; data is ESA-authoritative; host is private | | **Microsoft Planetary Computer** | Microsoft | None (URL signing) | Private cloud, public access | Zero-friction; flakiness risk | The DATA is ESA Copernicus under Copernicus License regardless of host. The HOST differs in sovereignty story. ## Recommended architecture For Riprap's deployment story (anonymous-by-default, sovereignty-aware, swap-in capable for credentialed sovereign sources): ``` Primary path (anonymous, zero-friction): - Sentinel-2 L2A from Earth Search (Element 84 / AWS Open Data) - Copernicus DEM from Earth Search (cop-dem-glo-30) - Sentinel-1 RTC from Microsoft Planetary Computer (URL-signed) Optional sovereign override (set RIPRAP_SENTINEL_SOURCE=cdse with creds): - All modalities from ESA Copernicus Data Space directly Disclosure in every briefing: "Sentinel-2 acquired N days ago, Sentinel-1 acquired M days ago, sourced from . Data: ESA Copernicus License." ``` Per-query budget on a fresh fetch (uncached): - Earth Search S2 + DEM: ~2 s - PC S1 RTC: ~3 s - Model inference: ~0.5 s - **Total: ~5–6 s per query** With per-MGRS-cell caching (chips don't change between revisits within a 5-day window for the same scene), repeat queries hit local cache and return in < 1 s. ## What changes in the integration `app/context/terramind_nyc.py` (the new specialist) replaces its current "load from local Major-TOM cache" path with a `fetch_recent_chips(lat, lon)` function that tries Earth Search first, then PC for S1-RTC. Cache is keyed by (s2_mgrs_tile, s2_acquisition_date) so cold-cache wall-clock is the ~5 s above and warm-cache is < 100 ms. The output dict that goes into Granite's document context gains: ```python { ..., "s2_acquired_iso": "2026-05-04T16:01:44Z", "s2_age_days": 1, "s2_cloud_pct": 7.0, "s2_source": "Element 84 Earth Search (ESA Copernicus License)", "s1_acquired_iso": "2026-05-01T22:51:31Z", "s1_age_days": 4, "s1_source": "Microsoft Planetary Computer (ESA Copernicus License)", "imagery_freshness_disclosed": True, } ``` Granite can cite both ages and both sources directly. ## What this enables in the briefing A Brighton Beach briefing currently can't say anything about *current* imagery. After integration, it can: > "Structural land cover at this 2.56 km tile is **78% developed, > 7% open water, 14% green space** [terramind_nyc]. Sentinel-2 imagery > acquired 1 day ago [esa_s2]; Sentinel-1 SAR acquired 4 days ago > [esa_s1]. The high imperviousness limits stormwater absorption, > compounding the address's coastal Sandy-zone exposure [sandy]." Three new cite-able facts: imperviousness, S2 age, S1 age. All defensible against ground truth. ## Honest limitations - **Cloud cover.** When S2 is cloudy, the most-recent low-cloud acquisition might be 7–15 days old. Disclosed per query. - **PC reliability.** Bursty timeouts during high-load windows. Retry logic + fallback to S2-only inference (zero-fill S1 channel) is the right defensive posture. - **No RTC anonymously.** Earth Search has no `sentinel-1-rtc` so we depend on PC for S1. If PC is down, briefing falls back to S2-only with explicit "S1 unavailable for this query" disclosure. - **Sovereignty.** AWS Open Data and PC are private-cloud-hosted mirrors of ESA-authoritative data. The data is sovereign; the host is not. For deployments requiring full sovereignty, CDSE direct is the swap-in path. ## What to land in `app/` Two files when this experiment graduates: 1. `app/context/sentinel_live.py` — `fetch_recent_chips(lat, lon)` with the multi-source fallback chain, retry logic, per-MGRS cell cache 2. `app/context/terramind_nyc.py` — replaces `load_local_chips()` with a call to `sentinel_live.fetch_recent_chips`, otherwise unchanged Plus tests in `tests/` against three NYC reference points (Manhattan center, Brighton Beach, Bronx Zoo) with a mock STAC client for offline CI. ## License + attribution ESA Copernicus License: free for any use including commercial, with attribution to Copernicus and the originating mission. Riprap's existing attribution block needs to add "Sentinel-1 / Sentinel-2 imagery courtesy of ESA Copernicus" alongside the existing NYC OpenData / NOAA / FEMA attributions.