# aerocontext
Pluggable aeronautical-**context** system: fetch weather/briefing products from
provider adapters behind capability traits, then `compare` them across sources
with per-source attribution. Serves a pilot as a briefing and an agent as
grounded context.
## Crates
- `aerocontext-core` — domain model, provider identity/capability traits, cycle-aware navdata snapshots, and `compare` (cross-source reconciliation). No transport deps.
- `aerocontext-awc` — aviationweather.gov Data API adapter (REST/JSON).
- `aerocontext-leidos` — Leidos / 1800wxbrief Pilot Web Service adapter (SOAP `AreaBriefing`).
- `aerocontext-navdata` — NASR→snapshot packer: cycle math, versioned blob format, manifest, fetch/ingest pipeline, CLI. The pure slice (cycle/manifest/blob-decode) is wasm-clean; `ingest`/`fetch`/`cli` features are native-only.
- `aerocontext` — facade: `gather` one request across providers, then `compare`.
## Use
```rust
let provider: Arc<dyn WeatherBriefingProvider> = Arc::new(AwcClient::new(AwcConfig::default())?);
let briefing = provider.area_briefing(&request).await?; // request: AreaBriefingRequest
let report = compare(&briefings); // who said what, where they disagree
```
## Navigation Data
Identifier-based areas use `NavDataSnapshot`: a serializable list of airports,
waypoints, and navaids tagged with `NavDataCycle`. `NavDataCycle::faa_nasr`
models the FAA 28 Day NASR Subscription cadence and records the source used to
produce the snapshot. Core does not download or cache FAA files; web, WASM,
iOS, and CLI shells fetch/cache data through their platform layer and pass the
same snapshot into Rust.
`aerocontext-navdata` produces those snapshots: `.acnav` blobs built from the
NASR CSV subscriber files (current + preview cycle), published per cycle to
GitHub Releases by `.github/workflows/navdata-publish.yml`. Shells bake in one
URL — the rolling manifest at
`releases/download/navdata-manifest/manifest.json` — and pick blobs by
effective date with `Manifest::entry_for` (derive "today" in UTC; cycle
boundaries are UTC-anchored).
On a user device the same crate is the consumer (`cargo install` it; the
library compiles for wasm32 and iOS with `--no-default-features` for the pure
slice, full features for on-device sync):
```sh
aerocontext-navdata sync # idempotent; run from cron/launchd; --gateway adds IPFS fallback
aerocontext-navdata resolve KSFO --blob "$(aerocontext-navdata current)"
```
`sync` follows a subscription list (default: the official manifest; add other
sources/authorities as they are published), downloads current + prefetch blobs
per authority into `~/.aerocontext/navdata`, sha256-verifies every byte, and
falls back to an IPFS gateway via the manifest `cid` when HTTPS fails. Build a
cycle from FAA sources yourself:
```sh
cargo run -p aerocontext-navdata -- build --cycle current --out dist
```
Each release also carries a CARv1 archive of the same content for IPFS
mirroring; manifest entries record its root `cid` (IPIP-499 `unixfs-v1-2025`
profile, pinned `ipfs-car` under `.github/navdata-tools`, drift-gated by
`scripts/cid-guard.sh`). HTTPS stays the primary fetch path; the `cid` is a
content address anyone can pin:
```sh
curl -LO <car_url from manifest.json>
ipfs dag import faa-nasr-*.acnav.car && ipfs pin add <cid>
```
A pinning node runs `scripts/pin-navdata.sh` on a timer: it imports each
cycle's CAR, verifies the root CID against the manifest, and republishes one
IPNS name pointing at a directory of `{manifest.json, blobs}`. The IPNS key
lives only in that node's kubo keystore — never in CI secrets (the name is
the key hash and IPNS has no revocation, so a leaked CI secret would be a
permanent hijack; back the key up once with `ipfs key export`). Devices can
then sync entirely over IPFS:
```sh
aerocontext-navdata sync --subscriptions subs.json --gateway https://ipfs.io/
# subs.json: { "subscriptions": [ { "name": "us-faa",
# "manifest_url": "https://ipfs.io/ipns/<name>/manifest.json" } ] }
```
AWC accepts only coordinate/bbox requests, so attach a snapshot when it needs to
serve `Area::LocationRadius`:
```rust
let awc = AwcClient::new(AwcConfig::default())?.with_nav_data(snapshot);
```
## Develop
```sh
./ci.sh # naming guard, fmt, clippy -D warnings, tests, doc, release build
```
Tests hit mock servers only — no live endpoint. One `#[ignore]`d live AWC check:
`cargo test -p aerocontext --test awc_live -- --ignored`. Provider wire contracts
are pinned by wiremock tests and vendored in `reference/`.