Ephemeris5 MIN READ06 / 31

NASA Adapter — High-Level Design

Reading guide: The current package architecture defines the binder boundary. Sections below describe reference designs unless an implementation note says otherwise. Standalone facade, cache and provenance services are dropped; their integration responsibilities belong to the consumer.

Status: Draft for review · Part of: Astro Binder · Updated: 2026-10-24

The NASA adapter is the only component that knows JPL Horizons. It builds a query, guards the call, verifies the response signature, parses the text table into typed values and maps targets to canonical ids; it returns no houses, no ayanamsa and no sidereal positions.

1. Purpose

Horizons is the binder’s independent reference, not a second engine of record. It answers “where was this body at this instant according to JPL”. The API returns a fixed-width text table inside JSON, with no astrology primitives.

The adapter contains that awkwardness:

  • query parameters come from templates, never ad hoc assembly;
  • the polite-citizen rules (timeout, concurrency cap, backoff, contact user agent) live in one place;
  • the response shape is verified before any number is read, so a format change fails loudly;
  • parsing rules are pinned to the documented API version, not to whatever arrives.

2. Where it sits

drift validator ────┐
                    ├──▶ nasa adapter ──▶ JPL Horizons API
coverage fallback ──┘         │
                              ▼
                  parsed rows + declaration ──▶ canonical normalizer
  • Used by: the drift validator for every comparison, and the explicit coverage fallback when Swiss cannot serve a body or date.
  • Downstream: the canonical normalizer applies the ayanamsa for sidereal answers, because Horizons cannot.
  • Never: runs on the user’s request path in v1, selects itself, or produces houses.

3. Responsibilities

Stage Owns Refuses to do
Build the query COMMAND, CENTER, TLIST, QUANTITIES, TIME, ANG_FORMAT, time scale Ask for houses, ayanamsa or sidereal output
Guard the call Timeout, concurrency cap, backoff, descriptive user agent with contact Retry a 4xx or hammer a throttled endpoint
Fetch and verify HTTPS GET, JSON decode, signature.version and result shape Parse an unrecognised layout into plausible zeros
Parse the table CSV rows → typed numbers, columns located by header name Assume a column order or a column count
Map to canonical Horizons target ids → canonical ids, AU and AU/day, epoch buckets Return a body it has no mapping for

4. Architecture

NASA adapter — high-level design Planned workspace module implementing the shared adapter-contract package. The NASA adapter builds a JPL Horizons query, guards the call, verifies the response signature, parses the text table into typed values and maps identifiers to canonical ids. It returns no houses, no ayanamsa and no sidereal positions. NASA Adapter — High-Level Design The only code that speaks JPL Horizons, and the only place its text table and API version exist. Adapter-contract request bodies · instant · observer geocentric by default Call policy timeout · concurrency cap breaker · contact address NASA adapter · planned workspace module Build the query COMMAND · CENTER · TLIST QUANTITIES · ANG_FORMAT Guard the call timeout · concurrency cap descriptive user agent Fetch and verify GET /api/horizons.api signature.version asserted Parse the table text rows → typed values units from column headers Map to canonical Horizons ids → canonical ids no houses · no ayanamsa Query templates parameter names and quoting one pinned example per body Response contract required column headers signature.version pinned Parsed provider rows values · units · epochs declaration · tropical only shared adapter contract · no module server · NASA runtime integration remains planned dependency module result

Diagram: reference design, not deployed behaviour.

Query templates and the response contract are dependencies, not stages: both are pinned data, so changing either is a contract change.

5. Rules

  1. Sole owner of Horizons. Parameter names, the base URL and signature.version appear only in adapter/nasa; new quantities are added here.
  2. Ask for exactly what is compared. TIME and TLIST set the scale and instant explicitly; never a server default for TT or epoch.
  3. Verify before parse. A missing column, an absent $$SOE/$$EOE pair or an unexpected signature.version is a PROVIDER_RESPONSE error; nothing is zero-filled.
  4. No astrology, ever. Horizons has no houses, ayanamsa (the tropical-to-sidereal offset) or sidereal zodiac. A sidereal request is converted downstream; a houses request is refused by capability, not attempted here.
  5. Privacy-safe by default. Queries are geocentric unless a caller asks otherwise, and validation samples never carry a user’s birth coordinates.
  6. Polite and bounded. One timeout, bounded concurrency, exponential backoff on throttling and 5xx, and no retry on a 4xx.
  7. Off the request path. In v1 the adapter serves validation and the declared fallback; it adds no latency to a normal calculation.

6. Edge cases

Case Behaviour
Body unsupported for the requested span Case skipped and counted as unsupported; no alert
Throttled or repeated 5xx Breaker opens; the run is marked incomplete rather than retried in a loop
Column header present with an empty cell Parse error with the column name, never zero
signature.version changed Typed error naming the received and expected version
$$SOE/$$EOE markers absent PROVIDER_RESPONSE; no partial rows returned
Query exceeds its deadline PROVIDER_TIMEOUT; no partially parsed result is used
Target designation instead of an id Resolved once per body and kept in the adapter’s small target map
Text table width changes Pinned-example contract test fails before deployment
Topocentric request from a live sample Coordinates stripped; the case is re-run geocentric

7. Out of scope

  • Choosing or falling back between providers (router).
  • Deciding whether a delta is drift, and alerting (drift validator).
  • Applying ayanamsa and normalising units and frame (normalizer).
  • Houses and sidereal definitions, which Horizons does not provide.

8. Code

The @astrology/nasa-adapter workspace package depends on the implemented standalone @astrology/adapter-contract, just as the Swiss package does. It implements EphemerisAdapter without copying types or importing Swiss. Capabilities advertise only native features and zodiacs; houses and native sidereal requests reject with the shared UNSUPPORTED_FEATURE code. Nonapplicable binding versions, tidal-model values and native flags can be null. Hosts own HTTP mappings; the package does not host a server. The module-level design is in design.md.

File Responsibility
src/nasa/horizons-adapter.ts Stage orchestration, declaration and the port contract
src/nasa/query-builder.ts Pinned parameter template per body, centre and instants
src/nasa/response.ts Envelope verification and header-located CSV parsing
src/nasa/target-map.ts Canonical ids ↔ Horizons target ids and speed steps
src/nasa/http-client.ts Timeout, concurrency, backoff, breaker and transport error codes