Files
git.stella-ops.org/docs/modules/airgap/runbooks/import-verify.md
2026-01-06 19:07:48 +02:00

2.7 KiB
Raw Blame History

Offline Kit Import Verification Runbook

This runbook supports AIRGAP-MANIFEST-510-010, AIRGAP-REPLAY-510-013, and AIRGAP-VERIFY-510-014. It validates bundles fully offline and enforces replay depth.

Replay depth levels (manifest replayPolicy)

  • hash-only: verify manifest/bundle digests, staleness window, optional signature.
  • full-recompute: hash-only + every chunk hash + AV report hash.
  • policy-freeze: full-recompute + manifest policies must include the sealed policy hash (prevents imports with drifting policy/graph material).

Quick steps

src/AirGap/scripts/verify-kit.sh \
  --manifest offline-kit/manifest.json \
  --bundle offline-kit/bundle.tar.gz \
  --signature offline-kit/manifest.sig --pubkey offline-kit/manifest.pub.pem \
  --av-report offline-kit/reports/av-report.json \
  --receipt offline-kit/receipts/ingress.json \
  --sealed-policy-hash "aa55..." \
  --depth policy-freeze

What the script enforces

  1. Manifest & bundle digests match (hashes.*).
  2. Optional manifest signature is valid (OpenSSL).
  3. Staleness: createdAt must be within stalenessWindowHours of --now (defaults to UTC now).
  4. AV: avScan.status must not be findings; if reportSha256 is present, the provided report hash must match.
  5. Chunks (full-recompute/policy-freeze): every chunks[].path exists relative to the manifest and matches its recorded SHA-256.
  6. Policy-freeze: --sealed-policy-hash must appear in policies[].sha256.
  7. Optional: --expected-graph-sha checks the graph chunk hash; --receipt reuses verify-receipt.sh to bind the receipt to the manifest/bundle hashes.

Exit codes: hash mismatch (3/4), staleness (5), AV issues (68), chunk drift (910), graph mismatch (11), policy drift (1213), bad depth (14).

Controller verify endpoint (server-side guard)

POST /system/airgap/verify (scope airgap:verify) expects VerifyRequest:

{
  "depth": "PolicyFreeze",
  "manifestSha256": "...",
  "bundleSha256": "...",
  "computedManifestSha256": "...", // from offline verifier
  "computedBundleSha256": "...",
  "manifestCreatedAt": "2025-12-02T00:00:00Z",
  "stalenessWindowHours": 168,
  "bundlePolicyHash": "aa55...",
  "sealedPolicyHash": "aa55..." // optional, controller fills from state if omitted
}

The controller applies the same replay rules and returns { "valid": true|false, "reason": "..." }.

References

  • Schema: docs/modules/airgap/schemas/manifest.schema.json
  • Samples: docs/modules/airgap/samples/offline-kit-manifest.sample.json, docs/modules/airgap/samples/av-report.sample.json, docs/modules/airgap/samples/receipt.sample.json
  • Scripts: src/AirGap/scripts/verify-kit.sh, src/AirGap/scripts/verify-manifest.sh, src/AirGap/scripts/verify-receipt.sh