Skip to content

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 under static/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

  1. Build-time only — No change to ADR-016: production still ships zero runtime third-party image URLs.

  2. 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.
  3. 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.
  4. 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.

  5. 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.tsnormalizeAgency() maps index.json agency strings to a closed key; fetchAgencyPrimaryImageUrls() returns NASA Images API preview URLs only for NASA (build-time “agency HTTP primary”). Other keys return []; editorial primary for those operators is the curated Wikimedia lists in scripts/fetch-assets.ts.
  • scripts/fetch-assets.tsfetchMissionImages pipeline (per mission): optional Commons hero (commonsCoverFirst) → agency primary (NASA API only when normalizeAgency(agency) === 'NASA') → Wikimedia single + gallery top-up → NASA fallback for non-NASA agencies to fill remaining slots up to MISSION_GALLERY_MAX. Legacy fetch ids mars2 / mars6 (not in index.json) resolve agency via static/data/missions/{mars,moon}/{id}.json when present, else ROSCOSMOS.
  • fetchNasaGalleryUrls(query, max, missionId?) — credit filter accepts NASA-family secondary_creator substrings by default; missionId-scoped extras in MISSION_NASA_CREDIT_EXTRAS allow partner imagery (e.g. esa for mars-express, jaxa for mmx / slim, mbrsc / emirates for hope-probe) when NASA hosts the asset.
  • Evidence and spike go/no-go per operator: docs/research/agency-mission-image-sources.md.
  • docs/adr/index.md lists this ADR; mission imagery bullet in ADR-016 references this decision for source priority (not transport).
Agency (index)Mission ids (36)Build-time “primary” in codeNASA fallback
NASAmariner4, viking1, mars-pathfinder, curiosity, maven, insight, perseverance, apollo11, apollo17, clementine, lro, artemis2, artemis3, galileo, voyager-2, new-horizons, dawnNASA Images API after Commons heroWikimedia top-up after API pass
ROSCOSMOSmars3, luna9, luna17, luna24Curated Commons (+ mars2 / mars6 fetch-only)Yes (historic / PIA-style queries)
ESAmars-expressCurated CommonsYes (MISSION_NASA_CREDIT_EXTRAS)
CNSAtianwen1, change4, change5, change6Curated CommonsYes (thin; query-tuned)
ISROmangalyaan, chandrayaan1, chandrayaan3Curated CommonsYes
JAXAmmx, slimCurated CommonsYes (jaxa allowlist for mmx / slim)
UAESAhope-probeCurated CommonsYes (hope-probe allowlist)
SpaceXstarship-demo, starship-mars-crewCurated CommonsYes (cooperation imagery only; credit filter)
Blue Originblue-moon-mk1Curated CommonsYes
Inspiration Marsinspiration-marsCurated CommonsYes

GitHub #44 checklist (per agency — copy into issue as needed)

  • [ ] NASAprimary:nasa-api path + Wikimedia top-up; spot-check MISSION_GALLERY_MAX for outer-planet catalogue ids.
  • [ ] ROSCOSMOS — Commons lists for mars3, luna*, mars2, mars6; NASA fallback honest credits.
  • [ ] ESAmars-express Commons + 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.
  • [ ] JAXAslim / mmx Commons + JAXA credit allowlist for NASA fallback; no JAXA Digital Archives adapter (spike no-go).
  • [ ] UAESA — Hope Commons + hope-probe allowlist; 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 credit JSON.

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.

  • ADR-016 — build-time assets (transport).
  • Research — docs/research/agency-mission-image-sources.md.
  • Implementation tracking: GitHub #44Implement ADR-046: agency-first build-time imagery sourcing (use checklist above).

Orrery — architecture documentation · MIT · No tracking