Provenance Store — High-Level Design
Scope decision: This standalone component is dropped from the binder package plan. The design below is historical context. Consumer applications own this responsibility; see the current package architecture.
Status: Draft for review · Part of: Astro Binder · Updated: 2026-10-24
Each response yields one immutable audit record: the provider, engine version, ephemeris files, flags and precision behind its values. No request body is stored.
1. Purpose
Swiss Ephemeris is the source of record, so reproducing a result needs the engine version, file set, time scales and precision, not just birth data. Provenance makes determinism checkable and drift explainable:
- audit — who asked, which provider answered, under which engine and files;
- reproducibility — re-run against the recorded engine inputs;
- operations — cache hits, degradation, latency, no payload logs;
- privacy — no coordinates or request bodies.
2. Where it sits
calculation facade ──▶ provenance emitter ──▶ async writer ──▶ provenance store
│
drift validator ◀────┤
audit queries ◀────┘
- Upstream: the calculation facade, after assembly.
- Downstream: the drift validator reads samples; support looks up one request id.
- Never: on the request path, a source of response fields, or a store for request payloads.
3. Responsibilities
| Component | Owns | Notes |
|---|---|---|
| Record model | One immutable record per response | Request id, caller, provider, engine and file versions, flags, JD UT/TT, ΔT, precision, cache status, degradation reason, latency |
| Emitter | Turns response context into a record | Strips coordinates and request bodies; never retries |
| Async writer | Bounded queue and append | Drops and counts under pressure, never blocks |
| Store | Append-only persistence | Indexed by request id and engine version |
| Reader surface | Sampled metadata and request-id lookup | No coordinate or payload access |
4. Architecture
Diagram: reference design, not deployed behaviour.
The store is a managed data service, not in-request compute. Emitter and writer sit in the service for response context; everything after the append is outside it.
5. Rules
- One record per response. Cache hits, fresh calculations and degraded answers each produce one record, so counts match responses.
- Off the response path. Emission follows assembly; a slow or failed write never changes the answer or its status code.
- Loss is visible. Queue overflow and write failures are counted and alerted, never retried synchronously.
- No personal payloads. No coordinates, request bodies or provider payloads; time fields are the engine inputs.
- Append-only. A record is never updated; a re-emission is a new record, idempotent on the request id.
- Engine identity is mandatory. Engine version, ephemeris data set and file-set hash are required, or reproducibility fails.
- Cache status is recorded, not inferred.
cacheHit,degradedand precision come from the returned response. - Retention is explicit. Storage lifetime is configured and documented; expiry is deliberate.
6. Edge cases
| Case | Behaviour |
|---|---|
| Queue full | Record dropped and counted; response unaffected |
| Store unavailable | Writer fails within budget, increments the failure metric and alerts |
| Duplicate emission | Idempotent on request id |
| Cache hit | cacheHit: true plus the cached provenance |
| Degraded answer | Degradation reason and reduced precision recorded |
| Drift validation run | Written to the drift store, not as user provenance |
| Engine or file upgrade | New records carry new versions; old ones are not rewritten |
| Record older than retention | Removed by policy; gap visible in audit coverage |
7. Out of scope
- Response assembly and provider choice (facade, router).
- Normalising values or applying the zodiac (normalizer).
- Drift thresholds, judging and alerting (drift validator).
- Request logging, analytics and user-profile stores.
- Storing raw provider payloads.
8. Planned code
| Piece | Responsibility |
|---|---|
provenance/ProvenanceRecord |
Immutable audit shape and required fields |
provenance/ProvenanceEmitter |
Build a record from response context; strip payloads |
provenance/ProvenanceQueue |
Bounded in-process handoff |
provenance/ProvenanceWriter |
Asynchronous append, idempotency, failure metrics |
provenance/ProvenanceQuery |
Request-id lookup and sampled metadata for drift |