ADR-046 — Agency-first build-time imagery sourcing
Status · Accepted Date · 2026-05-06 Extends ·
docs/adr/ADR-016.md(build-time bundle unchanged) Scope ·scripts/fetch-assets.ts, mission/panel/ISS imagery understatic/images/
Context
ADR-016 requires all imagery to be fetched at build time and served from the app host. Today scripts/fetch-assets.ts leans heavily on the NASA Images API plus curated Wikimedia Commons filenames. That often yields correct NASA missions but wrong context for non-NASA operators (e.g. Soyuz, Tiangong, SpaceX hardware) or generic diagrams when agency-specific sources were never queried first.
Product expectation: prefer imagery from the agency that operated the mission or object, then fall back to partner archives and general libraries.
Decision
Build-time only — No change to ADR-016: production still ships zero runtime third-party image URLs.
Primary source = operating agency (conceptual rule; encoded per entity in fetch helpers):
- Roscosmos / Soviet heritage — Russian programmes (e.g. Soyuz, GLONASS-class editorial, lunar/rovers where Roscosmos holds primary press assets).
- CNSA — Chinese programmes (e.g. Tiangong, Chang’e, Tianwen where Chinese agencies publish primary material).
- SpaceX — SpaceX vehicles and programmes where SpaceX (or explicitly licensed partner releases) are the authoritative visual source.
- NASA — NASA-led missions and default baseline when ownership is US NASA primary or no tighter agency mapping exists.
Fallback order (when primary yields nothing usable — HTTP failure, empty catalog, policy block):
- Co-operating / partner agency archives (e.g. ESA for joint programmes where applicable).
- Wikimedia Commons — curated filenames verified PD / CC-* compatible with our credits pipeline.
- NASA Images API — retained as a broad aerospace library, not the automatic first hop for every entity.
Mixed programmes (e.g. ISS): use an explicit published rule table in implementation — e.g. primary poster / lead partner for that catalog row — so behaviour is deterministic in CI.
License & attribution — Each adapter records provenance so gallery credits remain accurate (NASA PD-style vs ESA CC BY-SA IGO vs Commons attribution). Implementation must not merge incompatible license strings blindly into one generic “NASA” credit.
Rationale
- Aligns hero/gallery imagery with who flew the hardware, improving trust and editorial clarity.
- Keeps offline-first and ADR-016 contracts intact.
- Makes NASA a strong fallback, not a silent substitute for agency-owned stories.
Alternatives considered
- NASA-only forever — simplest implementation; rejected for context mismatches above.
- Wikimedia-only — good for PD/CC breadth; weak as sole primary for agency-branded campaigns.
- Runtime agency APIs — violates ADR-016; rejected.
Consequences
Positive:
- Clearer editorial policy for future fetch helpers and curated lists.
Negative:
- More adapters and maintenance (rate limits, URL churn per agency).
- Legal/editorial review for non-NASA terms until helpers are stable.
Implementation notes
scripts/agency-mission-sources.ts—normalizeAgency()mapsindex.jsonagency strings to a closed key;fetchAgencyPrimaryImageUrls()returns NASA Images API preview URLs only forNASA(build-time “agency HTTP primary”). Other keys return[]; editorial primary for those operators is the curated Wikimedia lists inscripts/fetch-assets.ts.scripts/fetch-assets.ts—fetchMissionImagespipeline (per mission): optional Commons hero (commonsCoverFirst) → agency primary (NASA API only whennormalizeAgency(agency) === 'NASA') → Wikimedia single + gallery top-up → NASA fallback for non-NASA agencies to fill remaining slots up toMISSION_GALLERY_MAX. Legacy fetch idsmars2/mars6(not inindex.json) resolve agency viastatic/data/missions/{mars,moon}/{id}.jsonwhen present, elseROSCOSMOS.fetchNasaGalleryUrls(query, max, missionId?)— credit filter accepts NASA-familysecondary_creatorsubstrings by default;missionId-scoped extras inMISSION_NASA_CREDIT_EXTRASallow partner imagery (e.g.esaformars-express,jaxaformmx/slim,mbrsc/emiratesforhope-probe) when NASA hosts the asset.- Evidence and spike go/no-go per operator:
docs/research/agency-mission-image-sources.md. docs/adr/index.mdlists this ADR; mission imagery bullet in ADR-016 references this decision for source priority (not transport).
Agency matrix (mission gallery)
| Agency (index) | Mission ids (36) | Build-time “primary” in code | NASA fallback |
|---|---|---|---|
| NASA | mariner4, viking1, mars-pathfinder, curiosity, maven, insight, perseverance, apollo11, apollo17, clementine, lro, artemis2, artemis3, galileo, voyager-2, new-horizons, dawn | NASA Images API after Commons hero | Wikimedia top-up after API pass |
| ROSCOSMOS | mars3, luna9, luna17, luna24 | Curated Commons (+ mars2 / mars6 fetch-only) | Yes (historic / PIA-style queries) |
| ESA | mars-express | Curated Commons | Yes (MISSION_NASA_CREDIT_EXTRAS) |
| CNSA | tianwen1, change4, change5, change6 | Curated Commons | Yes (thin; query-tuned) |
| ISRO | mangalyaan, chandrayaan1, chandrayaan3 | Curated Commons | Yes |
| JAXA | mmx, slim | Curated Commons | Yes (jaxa allowlist for mmx / slim) |
| UAESA | hope-probe | Curated Commons | Yes (hope-probe allowlist) |
| SpaceX | starship-demo, starship-mars-crew | Curated Commons | Yes (cooperation imagery only; credit filter) |
| Blue Origin | blue-moon-mk1 | Curated Commons | Yes |
| Inspiration Mars | inspiration-mars | Curated Commons | Yes |
GitHub #44 checklist (per agency — copy into issue as needed)
- [ ] NASA —
primary:nasa-apipath + Wikimedia top-up; spot-checkMISSION_GALLERY_MAXfor outer-planet catalogue ids. - [ ] ROSCOSMOS — Commons lists for
mars3,luna*,mars2,mars6; NASA fallback honest credits. - [ ] ESA —
mars-expressCommons +MISSION_NASA_CREDIT_EXTRAS['mars-express']; no standalone ESA HTTP adapter (spike no-go). - [ ] CNSA — Chang’e / Tianwen Commons gallery depth.
- [ ] ISRO — MOM + Chandrayaan Commons gallery depth.
- [ ] JAXA —
slim/mmxCommons + JAXA credit allowlist for NASA fallback; no JAXA Digital Archives adapter (spike no-go). - [ ] UAESA — Hope Commons +
hope-probeallowlist; no MBRSC scrape adapter (spike no-go). - [ ] SpaceX — Starship Commons; NASA fallback only when credits match.
- [ ] Blue Origin — Blue Moon Commons + NASA tight query.
- [ ] Inspiration Mars — Commons concept art aligned to mission
creditJSON.
Earth-object / ISS (unchanged scope here)
Agency-specific behaviour continues via explicit skipNasa, commonsHeroFirst, and query strings per row (tiangong, geo, glonass, beidou, …). Optional follow-on: reuse normalizeAgency for a shared router.
Related
- ADR-016 — build-time assets (transport).
- Research —
docs/research/agency-mission-image-sources.md. - Implementation tracking: GitHub #44 — Implement ADR-046: agency-first build-time imagery sourcing (use checklist above).