# AirGap Importer The AirGap Importer verifies and ingests offline bundles (mirror, bootstrap, evidence kits) into a sealed or constrained deployment. It fails closed by default: imports are rejected when verification fails, and failures are diagnosable offline. This document describes importer behavior and its key building blocks. For bundle formats and operational workflow, see `docs/airgap/offline-bundle-format.md`, `docs/airgap/mirror-bundles.md`, and `docs/airgap/operations.md`. ## Responsibilities - Verify bundle integrity and authenticity (DSSE signatures; optional TUF metadata where applicable). - Enforce monotonicity (prevent version rollback unless explicitly force-activated with a recorded reason). - Stage verified content into deterministic layouts (catalog + item repository + object-store paths). - Quarantine failed bundles for forensic analysis with deterministic logs and metadata. - Emit an audit trail for every dry-run and import attempt (success or failure). ## Verification pipeline (conceptual) 1. **Plan**: build an ordered list of validation/ingest steps for the bundle (`BundleImportPlanner`). 2. **Validate signatures**: verify DSSE envelopes and trusted key fingerprints. 3. **Validate metadata** (when present): verify TUF root/snapshot/timestamp consistency against trust roots. 4. **Compute deterministic roots**: compute a Merkle root over staged bundle items (stable ordering). 5. **Check monotonicity**: ensure the incoming bundle version is newer than the currently active version. 6. **Quarantine on failure**: preserve the bundle + verification log and emit a stable failure reason code. 7. **Commit**: write catalog/item entries and activation record; emit audit/timeline events. The step order must remain stable; if steps change, treat it as a contract change and update CLI/UI guidance. ## Quarantine When verification fails, the importer quarantines the bundle with enough information to debug offline. Typical structure: - `/updates/quarantine//--/` - `bundle.tar.zst` (original) - `manifest.json` (if extracted) - `verification.log` (deterministic, no machine-specific paths) - `failure-reason.txt` (human-readable) - `quarantine.json` (structured metadata: tenant, reason, timestamps, sizes, hashes) Operational expectations: - Quarantine is bounded: enforce per-tenant quota + TTL cleanup. - Listing is deterministic: sort by `quarantined_at` then `quarantine_id` (ordinal). ## Version monotonicity Rollback resistance is enforced via: - A per-tenant version store (`IBundleVersionStore`) backed by Postgres in production. - A monotonicity checker (`IVersionMonotonicityChecker`) that compares incoming bundle versions to the active version. - Optional force-activate path requiring a human reason, stored alongside the activation record. ## Storage model The importer writes deterministic metadata that other components can query: - **Bundle catalog**: (tenant, bundle_id, digest, imported_at_utc, content paths). - **Bundle items**: (tenant, bundle_id, path, digest, size). For the logical schema and deterministic ordering rules, see `docs/airgap/bundle-repositories.md`. ## Telemetry and auditing Minimum signals: - Counters: imports attempted/succeeded/failed, dry-runs, quarantines created, monotonicity failures, force-activations. - Structured logs with stable reason codes (e.g., `dsse_signature_invalid`, `tuf_root_invalid`, `merkle_mismatch`, `version_rollback_blocked`). - Audit emission: include tenant, bundle_id, digest, operator identity, and whether sealed mode was active. ## References - `docs/airgap/offline-bundle-format.md` - `docs/airgap/mirror-bundles.md` - `docs/airgap/bundle-repositories.md` - `docs/airgap/operations.md` - `docs/airgap/controller.md`