Zarf Air Gap
Veckl ships a Zarf package, defined under zarf/, that deploys the charts/veckl Helm chart. Zarf does not maintain a second, parallel copy of Veckl’s Kubernetes manifests - its job is to bundle the chart, both container images, and (optionally) cluster prerequisites into a single transferable artifact, and to prompt an operator for the handful of values only they can supply.
Package Layout
Section titled “Package Layout”zarf/├── zarf.yaml Package definition: variables, components, image list├── zarf-values.yaml Helm values overlay wiring Zarf variables into chart values├── ingress-nginx-values.yaml Values for the optional ingress-nginx component├── cert-manager-values.yaml Values for the optional cert-manager component└── zarf-config.yaml.example Example operator config (copy to zarf-config.yaml, never commit it)The package has three components:
veckl(required) - the backend, frontend, and routing, deployed viacharts/veckl.ingress-nginx(optional) - installs an Ingress controller for clusters that don’t already have one.cert-manager(optional) - installs cert-manager for automated TLS certificate issuance.
Connected Build Environment
Section titled “Connected Build Environment”Build both images:
make container-buildmake container-build-frontendCreate the Zarf package, pointed at the zarf/ directory:
zarf package create --confirm zarf/This produces a package file named like:
zarf-package-veckl-<arch>-0.1.0.tar.zstBefore creating a release package, keep the hardcoded images: list in zarf/zarf.yaml in sync with what the chart actually references:
zarf dev find-images --package-path zarf/Move the resulting .tar.zst file into the disconnected environment using your approved transfer process (approved media, a data diode, a physical hand-carry review, etc.). Zarf has no opinion on this step and needs no network access to perform it.
Disconnected Target Environment
Section titled “Disconnected Target Environment”If this is the cluster’s first Zarf package, bootstrap Zarf’s own prerequisites (an in-cluster image registry) once with its init package:
zarf init --confirmDeploy the Veckl package:
zarf package deploy zarf-package-veckl-*.tar.zst --confirmBy default this only installs the required veckl component. On a cluster with no Ingress controller already installed (a vanilla/kubeadm cluster - not needed on k3s, which ships Traefik by default), opt into the optional components explicitly:
zarf package deploy zarf-package-veckl-*.tar.zst --confirm --components veckl,ingress-nginx# or, to also get automated certificate issuance:zarf package deploy zarf-package-veckl-*.tar.zst --confirm --components veckl,ingress-nginx,cert-managerWithout --components, zarf package deploy --confirm prompts once per optional component instead.
Answering the deploy-time prompts
Section titled “Answering the deploy-time prompts”zarf package deploy prompts for every variable declared in zarf/zarf.yaml: the public hostname, GitHub/GHES connection details, GitHub App credentials, OAuth client credentials, and the session signing keys. Values marked sensitive (the OAuth client secret, the GitHub App private key, and the session keys) are masked in Zarf’s own console output and logs.
To avoid answering these interactively every time - or to drive a scripted/CI deploy - copy the example config and fill it in:
cp zarf/zarf-config.yaml.example zarf-config.yaml# edit zarf-config.yaml with real values - never commit this fileZARF_CONFIG=./zarf-config.yaml zarf package deploy zarf-package-veckl-*.tar.zst --confirmzarf-config.yaml is gitignored, the same way .env is for local development. Treat a filled-in copy as a secret file: it can contain the GitHub App private key and OAuth client secret in plain text.
Leave SESSION_HASH_KEY and SESSION_BLOCK_KEY blank (the default) on a first install - the chart generates them automatically and preserves them across future upgrades of the same release, as long as the Secret it creates is never deleted.
Once deployment finishes, open the configured host in a browser, or port-forward the frontend service directly:
kubectl port-forward -n veckl service/veckl-frontend 3000:3000The Full Lifecycle
Section titled “The Full Lifecycle”End to end, what actually crosses the airgap boundary:
zarf package create --confirm zarf/(connected machine) - readszarf/zarf.yaml, renderscharts/vecklto validate the bundled images, and produces a single self-contained.tar.zstarchive containing the chart, both container images, and any optional component charts included at build time.zarf package publish(optional) - pushes the package to an OCI registry instead of, or in addition to, a loose file on disk, so a separate machine can pull it right before the physical transfer step.- Physical transfer - the actual airgap crossing. This is a procedural step, not a Zarf command - carry the
.tar.zstfile across on whatever medium your organization’s approved transfer process requires. zarf init(disconnected cluster, once per cluster) - bootstraps Zarf’s own in-cluster registry. This requires Zarf’s own init package to also be carried across the airgap ahead of time.zarf package deploy zarf-package-veckl-*.tar.zst --confirm [--components ...](disconnected cluster) - prompts for (or reads fromzarf-config.yaml) every variable, pushes the bundled images into the now-running internal registry, and runshelm installagainstcharts/vecklwith the operator’s supplied values layered on top ofzarf/zarf-values.yaml’s defaults. This is the step that stands up the running application, and it waits for both the backend and frontend Deployments to become ready before reporting success.
Signed Release Packages
Section titled “Signed Release Packages”Tagged releases (vX.Y.Z) are built automatically by .github/workflows/zarf-package.yml, which runs after container-build.yml has finished pushing that release’s images, signs the resulting package with a dedicated Zarf package-signing key (separate from the container image signing key - see build/README.md and architecture doc section C.8), and attaches zarf-package-veckl-*.tar.zst to the corresponding GitHub Release.
Before deploying a downloaded package, verify it against the committed public key (build/zarf-signing.pub):
zarf package verify zarf-package-veckl-*.tar.zst --key build/zarf-signing.pubThis confirms the package is bit-for-bit what CI produced and signed, on top of the automatic checksum verification zarf package deploy already performs unconditionally (C.8).
npm Registry Mirrors
Section titled “npm Registry Mirrors”If you build the frontend image in a restricted network, point npm at an internal registry mirror:
make container-build-frontend NPM_REGISTRY_URL=https://mirror.example.com/npm/Go dependencies are vendored in the repository, so the backend image builds with no network access at all. The frontend image needs npm registry access (public or mirrored) at build time only - zarf package create itself needs no network access once both images already exist.
Catalog Data in Air-Gapped Environments
Section titled “Catalog Data in Air-Gapped Environments”Use Air-Gapped Export from a connected Veckl environment to produce a catalog file. Import that file in the disconnected environment from Admin > Catalog.
This avoids needing the disconnected backend to reach the public NIST OSCAL source.