Skip to content

Air-Gap Deployment

Air-gap mode deploys the Arbitex Outpost in an environment with no network access to the Arbitex Platform management plane. All configuration — policy bundles, DLP models, and GeoIP databases — is provisioned via local filesystem volumes rather than downloaded at runtime.

Air-gap deployments are used for:

  • Classified or air-gapped government networks
  • Healthcare environments with strict outbound network controls
  • Financial institutions with network segmentation requirements
  • Environments where SEC 17a-4 WORM compliance requires isolation from external systems

Set the OUTPOST_AIRGAP environment variable to true:

Terminal window
OUTPOST_AIRGAP=true

When air-gap mode is enabled at startup, the Outpost:

  1. Skips mTLS certificate validation (no Platform connectivity to validate against)
  2. Skips cert bundle auto-download
  3. Does not start the background policy sync task
  4. Does not start the heartbeat sender
  5. Does not start the cert rotation scheduler
  6. Loads all configuration from local filesystem volumes

All five disabled capabilities have local-volume equivalents described in this guide.


Terminal window
OUTPOST_AIRGAP=true
AIRGAP_POLICY_PATH=/opt/arbitex/policies # default

The Outpost loads policy_bundle.json from $AIRGAP_POLICY_PATH/policy_bundle.json at startup.

The policy bundle is a JSON document containing:

  • DLP rules (all active rules from the compliance and custom policy configuration)
  • OAuth scope enforcement configuration
  • Model access controls and risk tier assignments

To generate a policy bundle from the Platform:

Terminal window
# Export current active policy bundle from Platform admin API
curl https://your-platform/api/admin/policy-bundle/export \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-o policy_bundle.json
deployment.yaml
env:
- name: OUTPOST_AIRGAP
value: "true"
- name: AIRGAP_POLICY_PATH
value: /opt/arbitex/policies
volumeMounts:
- name: policy-bundle
mountPath: /opt/arbitex/policies
readOnly: true
volumes:
- name: policy-bundle
configMap:
name: arbitex-policy-bundle

Create the ConfigMap from your exported bundle:

Terminal window
kubectl create configmap arbitex-policy-bundle \
--from-file=policy_bundle.json=./policy_bundle.json \
-n arbitex

Since there is no background sync, policy bundle updates require a rolling restart:

  1. Export a new policy_bundle.json from the Platform (or prepare manually).
  2. Update the ConfigMap or volume source.
  3. Perform a rolling restart of the Outpost deployment.

The Outpost logs the bundle version at startup: air_gap=True policy_bundle version=<version>.


The DeBERTa v3 ONNX model provides Tier 3 contextual DLP classification. In air-gap mode, the model is loaded from a local volume rather than downloaded.

Terminal window
OUTPOST_AIRGAP=true
AIRGAP_MODEL_PATH=/opt/arbitex/models # default
# Alternatively, override directly:
DEBERTA_MODEL_PATH=/opt/arbitex/models/model.onnx

At startup, the Outpost resolves the model path in this order:

  1. If DEBERTA_MODEL_PATH is explicitly set and the file exists, use it directly.
  2. Otherwise, look for model.onnx in $AIRGAP_MODEL_PATH/model.onnx.
  3. If neither is found, Tier 3 classification is disabled (Tier 1 and Tier 2 DLP remain active).

The model file (model.onnx) is large (~500 MB). Use a PersistentVolume rather than a ConfigMap:

deployment.yaml
env:
- name: AIRGAP_MODEL_PATH
value: /opt/arbitex/models
volumeMounts:
- name: deberta-model
mountPath: /opt/arbitex/models
readOnly: true
volumes:
- name: deberta-model
persistentVolumeClaim:
claimName: arbitex-deberta-model-pvc

Pre-populate the PVC with the promoted model artifact. The Outpost uses either optimum.onnxruntime (preferred, if available in the container image) or raw onnxruntime.InferenceSession as a fallback loader.

The model artifact used in air-gap deployments should match the version shipped with the Outpost container image. Check the model version in the startup logs:

DeBERTa Tier3 scanner loaded: model.onnx (label_map={0: 'pii', 1: 'clean'}, threshold=0.7)

The Outpost uses GeoIP enrichment to annotate audit log entries with country, city, and ISP data from the request IP address. In air-gap mode, the MMDB database is provisioned via volume or baked into the container image.

The Outpost resolves the GeoIP database in this order:

  1. Explicit path (GEOIP_MMDB_PATH): if set and the file exists, use it.
  2. Download on start (GEOIP_DOWNLOAD_ON_START=true): skipped in air-gap mode — the download step is bypassed entirely when OUTPOST_AIRGAP=true.
  3. Bundled fallback (GEOIP_MMDB_FALLBACK_PATH): a pre-baked MMDB baked into the container image or mounted separately. Used when steps 1 and 2 are unavailable.
Terminal window
# Option A: explicit volume mount
GEOIP_MMDB_PATH=/opt/arbitex/geoip/GeoLite2-City.mmdb
# Option B: use baked-in fallback
GEOIP_MMDB_FALLBACK_PATH=/app/geoip/GeoLite2-City.mmdb

If neither path resolves to a valid file, GeoIP enrichment is disabled and audit records omit geographic fields. This is a graceful degradation — the Outpost continues operating without GeoIP.

env:
- name: GEOIP_MMDB_PATH
value: /opt/arbitex/geoip/GeoLite2-City.mmdb
volumeMounts:
- name: geoip-db
mountPath: /opt/arbitex/geoip
readOnly: true
volumes:
- name: geoip-db
configMap:
name: arbitex-geoip
Terminal window
kubectl create configmap arbitex-geoip \
--from-file=GeoLite2-City.mmdb=./GeoLite2-City.mmdb \
-n arbitex

Note: MaxMind GeoLite2 databases require a free MaxMind account to download. The anonymous-IP database (GEOIP_ANON_DB_PATH) requires a commercial MaxMind subscription.


When the Outpost loses connectivity to the Platform audit sync endpoint — or in air-gap mode where no connectivity exists — audit events are written to a local SQLite queue rather than dropped.

Terminal window
AUDIT_QUEUE_DB_PATH=audit_queue/audit_queue.db # default

The SQLite database file is created automatically at the configured path. In Kubernetes, mount a PersistentVolume at this path to ensure queue durability across pod restarts.

ConditionBehavior
Platform reachable (circuit CLOSED)Audit events sent directly to Platform; SQLite queue drained asynchronously
Platform unreachable (circuit OPEN)Audit events written to SQLite queue; no data loss
Circuit transitions to CLOSEDFlush task drains queue in 50-event batches to Platform

The queue stores events as JSON in the local_audit_queue table:

CREATE TABLE local_audit_queue (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_json TEXT NOT NULL,
queued_at REAL NOT NULL,
retry_count INTEGER NOT NULL DEFAULT 0
);

When connectivity is restored (audit circuit breaker closes), the Outpost automatically flushes the SQLite queue:

  1. _sync_all_pending() detects local_queue.count() > 0 and circuit state CLOSED.
  2. _flush_local_queue() sends batches of 50 events to POST /v1/internal/outpost-audit-sync.
  3. Successfully delivered events are deleted (mark_synced(ids)).
  4. Failed events increment their retry_count for next flush attempt.

There is no manual flush required. The flush runs automatically on each sync cycle once the circuit closes.

Check queue depth via the Outpost admin API:

Terminal window
curl http://localhost:8080/admin/api/sync-status \
-H "Authorization: Bearer $OUTPOST_ADMIN_TOKEN"

Response includes audit_queue_depth — the number of events pending flush. A persistently non-zero queue depth in non-air-gap mode indicates connectivity issues with the Platform audit endpoint.

In a permanent air-gap deployment (no connectivity ever), the SQLite queue accumulates all audit events indefinitely. To export audit events from a permanently air-gapped Outpost:

  1. Access the SQLite database file directly (volume mount or kubectl exec).
  2. Extract events: sqlite3 audit_queue.db "SELECT event_json FROM local_audit_queue;"
  3. Transfer the extracted events to the Platform via a secure offline transfer process.

Standard Cert Rotation (disabled in air-gap)

Section titled “Standard Cert Rotation (disabled in air-gap)”

In a connected deployment, the Outpost automatically renews its mTLS certificate 30 days before expiry by calling the Platform cert renewal endpoint. This scheduler is disabled in air-gap mode:

Air-gap mode: cert rotation disabled (no platform connectivity)

Manual Cert Rotation for Air-Gap Deployments

Section titled “Manual Cert Rotation for Air-Gap Deployments”

In air-gap mode, operators are responsible for certificate rotation:

  1. Monitor cert expiry: The /admin/api/sync-status endpoint returns cert_expiry_days.
  2. Generate replacement certs: Provision a new cert/key pair using your PKI (step-ca, internal CA, or manual).
  3. Update volume mounts: Replace the cert and key files on the mounted volume.
  4. Rolling restart: Restart the Outpost pods to load the new certificates.
Terminal window
# Check cert expiry from the Outpost admin API
curl http://localhost:8080/admin/api/sync-status \
-H "Authorization: Bearer $OUTPOST_ADMIN_TOKEN" | jq '.cert_expiry_days'

Set up monitoring alerts when cert_expiry_days drops below your rotation lead time (recommend: 45 days for air-gap environments where restarts require scheduling).

In air-gap mode, CERT_BUNDLE_AUTO_DOWNLOAD is implicitly disabled. Configure the cert bundle path directly:

Terminal window
OUTPOST_TLS_CERT=/opt/arbitex/certs/outpost.crt
OUTPOST_TLS_KEY=/opt/arbitex/certs/outpost.key
OUTPOST_CA_BUNDLE=/opt/arbitex/certs/ca-bundle.crt

Terminal window
# === Core air-gap switch ===
OUTPOST_AIRGAP=true
# === Policy bundle ===
AIRGAP_POLICY_PATH=/opt/arbitex/policies # contains policy_bundle.json
# === DeBERTa model ===
AIRGAP_MODEL_PATH=/opt/arbitex/models # contains model.onnx
# or: DEBERTA_MODEL_PATH=/opt/arbitex/models/model.onnx
# === GeoIP ===
GEOIP_MMDB_PATH=/opt/arbitex/geoip/GeoLite2-City.mmdb
# or: GEOIP_MMDB_FALLBACK_PATH=/app/geoip/GeoLite2-City.mmdb
# === Audit queue ===
AUDIT_QUEUE_DB_PATH=/opt/arbitex/audit/audit_queue.db
# === Certificates ===
OUTPOST_TLS_CERT=/opt/arbitex/certs/outpost.crt
OUTPOST_TLS_KEY=/opt/arbitex/certs/outpost.key
OUTPOST_CA_BUNDLE=/opt/arbitex/certs/ca-bundle.crt
# === DLP settings ===
DLP_DEBERTA_ENABLED=true # enable if model is provisioned
DEBERTA_MODEL_PATH=/opt/arbitex/models/model.onnx

apiVersion: apps/v1
kind: Deployment
metadata:
name: arbitex-outpost-airgap
namespace: arbitex
spec:
replicas: 2
selector:
matchLabels:
app: arbitex-outpost
template:
metadata:
labels:
app: arbitex-outpost
spec:
containers:
- name: outpost
image: arbitex/outpost:latest
env:
- name: OUTPOST_AIRGAP
value: "true"
- name: AIRGAP_POLICY_PATH
value: /opt/arbitex/policies
- name: AIRGAP_MODEL_PATH
value: /opt/arbitex/models
- name: GEOIP_MMDB_PATH
value: /opt/arbitex/geoip/GeoLite2-City.mmdb
- name: AUDIT_QUEUE_DB_PATH
value: /opt/arbitex/audit/audit_queue.db
- name: DLP_DEBERTA_ENABLED
value: "true"
volumeMounts:
- name: policy-bundle
mountPath: /opt/arbitex/policies
readOnly: true
- name: deberta-model
mountPath: /opt/arbitex/models
readOnly: true
- name: geoip-db
mountPath: /opt/arbitex/geoip
readOnly: true
- name: audit-queue
mountPath: /opt/arbitex/audit
- name: tls-certs
mountPath: /opt/arbitex/certs
readOnly: true
volumes:
- name: policy-bundle
configMap:
name: arbitex-policy-bundle
- name: deberta-model
persistentVolumeClaim:
claimName: arbitex-deberta-model-pvc
- name: geoip-db
configMap:
name: arbitex-geoip
- name: audit-queue
persistentVolumeClaim:
claimName: arbitex-audit-queue-pvc
- name: tls-certs
secret:
secretName: arbitex-outpost-tls

At startup, the Outpost logs its air-gap configuration summary:

INFO Air-gap mode enabled
INFO Policy bundle loaded: version=2026-03-13, rules=47
INFO DeBERTa Tier3 scanner loaded: model.onnx (threshold=0.7)
INFO GeoIP: city database loaded (path=/opt/arbitex/geoip/GeoLite2-City.mmdb)
INFO Audit queue: SQLite backend at /opt/arbitex/audit/audit_queue.db
INFO Air-gap mode: cert rotation disabled (no platform connectivity)
INFO Air-gap mode: heartbeat disabled (no platform connectivity)
INFO Air-gap mode: policy sync disabled (no platform connectivity)

If any required volume is missing or the policy bundle fails to parse, the Outpost logs an error and continues with the subsystem disabled (graceful degradation) — it does not abort startup.


FeatureAir-Gap SupportNotes
DLP scanning (Tier 1 + Tier 2)FullPattern matching and compliance bundles
DLP scanning (Tier 3 DeBERTa)FullRequires model volume mount
GeoIP enrichmentFullRequires MMDB volume mount
Audit loggingFullEvents stored in SQLite queue
Policy enforcementFullLoaded from policy_bundle.json
mTLS cert rotationManual onlyNo automatic rotation
Platform heartbeatDisabledNo connectivity to Platform
CredInt breach lookupDisabledNo connectivity to CredInt microservice
Policy bundle syncManual onlyRolling restart required for updates