feat(airgap): signed offline install bundle — ragctl airgap (Step 6.9) - #161
Merged
Merged
Conversation
Ship the platform as one signed, self-contained artifact for networks
with no registry / internet: all runtime container images (docker save),
the packaged Helm chart (helm package), an example rag.yaml, and a
standalone offline installer.
- Integrity mirrors the WORM audit export (6.6b): a standard SHA256SUMS
(verifiable with `sha256sum -c` — no network, no cosign) whose hash is
pinned as manifest.content_hash is the hard tamper-evidence gate; a
cosign signature over SHA256SUMS adds authenticity when present. The
same SHA256SUMS drives both the Python verifier and the standalone
shell installer, so they can never diverge.
- scripts/airgap_install.{sh,ps1} (shipped inside every bundle as
install.{sh,ps1}) need only docker+helm (+ optional cosign/sha256sum) —
no Python workspace, the genuinely air-gap-appropriate entry point:
verify -> docker load -> helm upgrade --install.
- ragctl airgap build/inspect/verify/install holds the typed, mypy-strict,
unit-tested logic — a pure core (BundleManifest, render_sha256sums,
content_hash, verify_bundle) separated from a stubbable docker/helm/
cosign subprocess seam. --dry-run produces a complete, verifiable
bundle minus the image blobs, so the whole path is testable with no
Docker.
- infra/airgap/images.txt is the digest-pinned, manifest-driven image set
(third-party backends); the gateway image is derived from the chart's
image.repository + --version (always release-versioned). Key-based
cosign is the air-gap recommendation; keyless is the connected-release
path (release-airgap.yml on v* tags, attaches the tarball to the GitHub
Release). task airgap:build / build-dry / verify.
~17 tests (manifest / hashing / tamper detection / tarball round-trip /
signature paths via stubbed cosign / CLI on a dry-run bundle); the shell
installer is bash -n + --verify-only exercised. All gates green: ruff,
mypy --strict (321 files), RAG001, full pytest (excl integration),
schema/openapi/proto-drift (dist untouched), policy-coverage, log-schema.
Docs: docs/reference/airgap.md, docs/architecture/airgap-bundle.md,
docs/guides/airgap-install.md, docs/adr/ADR-0041-airgap-bundle.md,
docs/README.md. TRACKER: 6.9 done (73/84).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step 6.9 — Air-gapped install bundle: a signed tarball with all images + the Helm chart, an offline bootstrap, and cosign verification. New
ragctl airgapcommand + standalone installer.docker save) + the packaged Helm chart (helm package) + an examplerag.yaml+ a standalone installer, in one.tar.gz.SHA256SUMS— verifiable with nothing butsha256sum -c, no network / no cosign — whose hash is pinned asmanifest.content_hashis the hard tamper-evidence gate; a cosign signature overSHA256SUMSadds authenticity when present. The sameSHA256SUMSdrives both the Python verifier and the standalone shell installer, so they can't diverge.install.{sh,ps1}(shipped inside every bundle) need only docker + helm (+ optional cosign / sha256sum) — no Python workspace, the genuinely air-gap-appropriate entry point: verify →docker load→helm upgrade --install.ragctl airgap(build/inspect/verify/install): a pure core (BundleManifest,render_sha256sums,content_hash,verify_bundle) separated from a stubbable docker/helm/cosign subprocess seam.--dry-runproduces a complete, verifiable bundle minus the image blobs, so the path is testable with no Docker.infra/airgap/images.txt); the gateway image is derived from the chart'simage.repository+--version. Key-based cosign is the air-gap recommendation (keyless needs Rekor + an identity policy); keyless is the connected-release path —release-airgap.ymlbuilds + signs + attaches the tarball to the GitHub Release onv*tags.task airgap:build/build-dry/verify.Deferred: ctr/podman load (docker only), registry re-tag/push, multi-arch selection, bundling the backend charts (chart deploys the gateway; backends stay external), TUF-rooted offline keyless verify.
Documentation
docs/reference/airgap.md— CLI + API + bundle layout +reasoncodesdocs/architecture/airgap-bundle.md— integrity model, code layout, build/install flowdocs/guides/airgap-install.md— operator runbook (build → transfer → verify → install)docs/adr/ADR-0041-airgap-bundle.md— decision + alternativesdocs/README.mdindex +TRACKER.md(6.9 ✅, 73/84)Test plan
parse_image_list,sha256_file,render_sha256sums/content_hash,build_manifest,verify_bundle(ok / tamper / added-file / no-checksums), tarball round-trip, chart helpers--dry-runbundle):build→inspect→verifyexit 0; tamper →verifyexit 1;installrejects a dry-run bundleinstall.sh:bash -n+--verify-onlyexercised against a real bundle (clean + tampered)🤖 Generated with Claude Code