Air-gapped operations
OrbitalReg is designed to run in air-gapped environments without modification. A fresh install starts in air-gapped mode — no egress to the public internet — until an admin opts each integration in explicitly.
What "air-gapped" means here
Out of the box, the following touch nothing outside the cluster:
| Channel | Default state | Toggle |
|---|---|---|
| Outbound webhooks | Blocked | allow_outbound_webhooks |
| OSV.dev advisory lookups | Blocked | allow_osv_lookups |
| Sigstore Rekor transparency log | Blocked | allow_sigstore_rekor |
| OpenTelemetry exporter | Blocked | allow_otel_export |
| Update-check manifest fetch | Off | empty update_check_settings.manifest_url |
| Telemetry / service pings | Blocked | allow_service_pings |
| Container-image upstream pulls | Blocked | per-repo remote.allow_egress |
| Format-adapter upstream pulls | Blocked | per-repo remote.allow_egress |
The master toggle is Admin → System → Egress. Existing deployments preserve their pre-air-gap behaviour through a migration heuristic (any deployment with > 0 webhook subscriptions in the backlog is auto-set to allow_outbound_webhooks=TRUE).
The update-check manifest fetch is its own opt-in independent of the master egress gate: a fresh install ships with the manifest URL empty (the worker short-circuits before any network call), so a default install makes zero outbound calls of its own accord. The operator picks Off / Daily / Weekly in the Update check step of the First-Run Setup Wizard or populates the manifest URL directly on Admin → Version status → Settings. On a regulated install the air-gapped egress gate is the belt-and-braces second layer — even if an operator were to flip the manifest URL on a locked-down cluster, the gate denies the request and last_error shows air-gapped: for transparency.
What still works without egress
- Every upload + download path
- All format adapters' local mode
- All scanners that ship their advisory data offline (Trivy and Grype bundle DBs that can be refreshed from a private mirror)
- Sigstore signature verification using the embedded public-good Fulcio + the operator-supplied trust policy (no Rekor lookup)
- The full governance surface (security blocks, retention, license policies)
Refreshing scanner DBs
Trivy and Grype both ship offline DB bundles. The chart's scanners.dbRefresh mode controls how those DBs roll forward:
automatic— scanners pull fromOSV.dev/ghcr.ioon a schedule (requires egress)mirrored— scanners pull from your private mirror (HTTPS, no public internet)manual— operator drops bundles into a known PVC; CronJob unpacks
In an air-gapped install, prefer mirrored with the chart's scanners.dbMirror.url pointing at your private artifact mirror.
Documentation
This site (docs-site/) builds to a fully static bundle. To ship docs into an air-gapped customer environment:
cd docs-site
npm ci
DOCS_BASE_PATH=/docs npm run build
tar czf orbitalreg-docs-${VERSION}.tar.gz -C .vitepress/dist .Untar behind any reverse proxy and point ORBITALREG_HELP_URL at the chosen URL so the help button in the AppShell topbar opens it.
Image bundle
The container images for an air-gapped install:
| Image | Source |
|---|---|
ghcr.io/orbitalreg/orbital-api:<tag> | API + frontend bundle |
ghcr.io/orbitalreg/orbital-frontend:<tag> | nginx-served SPA only |
ghcr.io/orbitalreg/orbital-operator:<tag> | Kubernetes operator |
ghcr.io/orbitalreg/orbital-scanner-bundle:<tag> | Trivy + Grype + Syft DBs |
Use skopeo to copy each into your private registry:
skopeo copy --all \
docker://ghcr.io/orbitalreg/orbital-api:v1.2.3 \
docker://registry.internal.example.com/orbitalreg/orbital-api:v1.2.3Then point the chart's image.repository values at the private registry.
License envelope
For commercial use, OrbitalReg requires a signed license envelope under Admin → License. Mint it on a connected workstation with bin/orbital-license-issuer and copy the envelope into the air-gapped cluster:
bin/orbital-license-issuer \
--install-id <copy from /admin/license> \
--customer "Acme Corp" \
--duration 1y \
> license.txtThe install-id is a per-cluster deterministic UUID — the same input on the same cluster always produces the same value, so re-issuing after a token rotation is safe.