aerocontext-core 0.2.1

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 (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; 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

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)

cargo install v99n62

Point an MCP client at the binary and the system becomes a context server:

{"mcpServers": {"aerocontext": {"command": "v99n62"}}}

Tools: resolve, area_brief, navdata_sync, navdata_status — on a new machine, have the agent call navdata_sync once. ownship_status and submit_command are designed and documented but not yet callable; they ship when a provider implements the corresponding capability trait. 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):

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

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:

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:

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:

let awc = AwcClient::new(AwcConfig::default())?.with_nav_data(snapshot);

Develop

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