aerocontext-core 0.4.2

Provider-neutral aeronautical-context model and the pluggable ContextProvider contract
Documentation
# 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 (REST: area & route briefings).
- `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`; ships the `aerocontext` CLI (`aerocontext brief KSFO --navdata "$(aerocontext-navdata current)"`).
- `aerocontext-mcp` — MCP stdio server exposing the system to AI agents.
- `v99n62` — experimental install-and-go entry point; the binary serves MCP over stdio.

## 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
```

## AI agents (MCP)

```sh
cargo install v99n62
v99n62 setup
```

`setup` prints the exact MCP configuration for this machine — with the
binary's **absolute path**, because MCP clients spawn servers outside your
shell and a bare `v99n62` often fails with `ENOENT` — and downloads the
current navigation-data snapshot so the first `resolve` answers
immediately. Copy the printed line, e.g.:

```sh
claude mcp add aerocontext /Users/you/.cargo/bin/v99n62
```

Tools: `resolve`, `airspace`, `area_brief`, `route_brief`, `navdata_sync`, `navdata_status`. To enable
the Leidos source, put `LEIDOS_VENDOR_ID`/`LEIDOS_VENDOR_PASSWORD` in the MCP
client's `env` block — inject them from your OS keychain rather than pasting
secrets into a world-readable config file. `ownship_status` and
`submit_command` are designed and documented but not yet callable; command
escalation is gated by the safety boundary in [ARCHITECTURE.md](ARCHITECTURE.md),
not by trait availability alone. Stdout belongs
to the MCP transport; diagnostics go to stderr via `RUST_LOG` (default
`info`). All data is advisory — never a sole source for real-world navigation
or flight decisions.

## 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 — zero configuration
(`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
aerocontext-navdata status        # subscriptions, store, AIRAC cycle state
aerocontext-navdata resolve KSFO  # uses today's store blob automatically
aerocontext brief KSFO --product metar   # picks the synced store up too
aerocontext route plan.fpl --profile aircraft.json   # brief a Garmin .fpl route corridor
```

`sync` follows a subscription list: built-in defaults out of the box (the
official manifest, its IPNS mirror as fallback, and an IPFS gateway for blob
fallback by `cid`), or `~/.aerocontext/subs.json` managed with
`aerocontext-navdata subscriptions init|add|remove|show` — never hand-written
JSON. It downloads current + prefetch blobs per authority into
`~/.aerocontext/navdata` and sha256-verifies every byte. Future pay-gated
sources fit the same file: a subscription may carry an `auth` reference naming
a header and an environment variable — the credential itself never lives in
the file. 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`). The default
subscription already includes the IPNS manifest as fallback, so devices sync
entirely over IPFS whenever the primary URL is unreachable — no flags needed.

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);
```

## Design documents

[ARCHITECTURE.md](ARCHITECTURE.md) — layering, safety boundary, paid-data
and secrets policy, extension seams, deliberate debt register.
[CERTIFIABILITY.md](CERTIFIABILITY.md) — what CI enforces, the data-chain
assessment, and the gap-closing order. [PREFLIGHT.md](PREFLIGHT.md) — the
14 CFR 91.103 coverage map: element by element, primary/backup/foreign
sources and gaps.

## Develop

```sh
./ci.sh   # guards (naming, size, tracing), fmt, clippy -D warnings, tests, doc, release build
```

The same script runs on every PR via `.github/workflows/ci.yml`. Binaries log
to stderr through `tracing` (`RUST_LOG`; CLIs default `warn`, servers `info`);
stdout carries results or the MCP transport only.

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/`.

## License

Copyright (C) 2026 sokoly systems.

Licensed under the GNU Affero General Public License v3.0 only
([`AGPL-3.0-only`](LICENSE)). This program is distributed in the hope that
it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.