Air-Gapped Deployment
What you'll accomplish: Deploy Hoziron in an environment with zero outbound network access — local inference (Ollama or vLLM), fully offline carrier-licence verification, and offline package transfer.
Overview
Air-gapped deployments use local inference servers instead of cloud LLM providers, and packages move via archive files instead of registry downloads. Per the glossary's definition of this deployment mode: single binary + local models, zero external network calls.
Licence provisioning is fully offline
This is the part that most needs spelling out precisely for a disconnected environment, since the whole platform refuses to boot without a valid carrier licence at $HOZIRON_HOME/licence.json (see Bare metal § Carrier licence for the mechanics — this section covers why it works with no network at all).
Verification is a local Ed25519 signature check, not a phone-home. The source (crates/platform/hoziron-core/src/routing/licence.rs) states this directly: "The licence encodes the full commercial agreement... No phone-home required." LicenceValidator::load_signed (the production path, used by HozironPlatform::init on every boot):
- Reads
licence.jsonfrom disk. - Verifies the Ed25519 signature over the payload against a public key compiled into the binary (
HOZIRON_LICENCE_PUBKEY, a constant, not fetched at runtime). - Checks expiry against the local system clock.
None of these steps make a network call. This is a genuine, verified selling point for air-gapped carriers: the licence that gates the whole platform never calls out to Hoziron's infrastructure, on any boot, ever.
Getting the licence onto the disconnected network: Hoziron issues the signed licence.json on its own side and sends it to you. Carry that file across the air gap (USB, one-way transfer station, whatever your process requires) like any other artifact. Once it's on the disconnected machine, everything above is local — no further network step is needed.
Licence trust is bound to the carrier ID, not to any machine identity, so there's no per-host or per-container derivation step to validate before shipping a licence across the air gap — the same licence.json is valid on any host running that carrier's instance. (A separate, local-only advisory lock still resolves a machine identifier at boot to stop two processes on one box from racing on the same licence file — that's a single-instance concern, not part of licence trust, and a mismatch there only fails to acquire the lock rather than affecting validity.)
Local models with Ollama
config.toml uses the [[provider]] inventory (ADR-053) — each entry declares an explicit driver, and sovereignty (local vs. cloud) is resolved from that driver's compile-time disposition, never inferred from the URL:
[[provider]]
id = "ollama"
driver = "ollama"
url = "http://ollama.internal:11434/v1"
[[provider.model]]
id = "llama3.1:70b"
context_window = 128000
[routing_gateway]
local_preference = [{ provider = "ollama", model = "llama3.1:70b" }]
An empty [[provider]] list is valid — with no providers configured, local_only/force_local routing falls back to the safe, network-free echo driver rather than failing, but that's a placeholder, not a real inference path. For an actual air-gapped deployment you want a real ollama (or vllm) entry with a non-empty local_preference.
Start Ollama with OLLAMA_HOST=0.0.0.0 ollama serve if Hoziron connects from a different host or container.
Local models with vLLM
vLLM exposes an OpenAI-compatible API. Point an openai-compatible driver entry at it:
[[provider]]
id = "vllm"
driver = "openai-compatible"
url = "http://vllm.internal:8000/v1"
api_key_env = "VLLM_API_KEY" # vLLM accepts any non-empty key
sovereignty_attestation = "carrier confirms this endpoint is in-boundary"
[[provider.model]]
id = "meta-llama/Llama-3.1-70B-Instruct"
[routing_gateway]
local_preference = [{ provider = "vllm", model = "meta-llama/Llama-3.1-70B-Instruct" }]
The sovereignty_attestation field is required to treat an openai-compatible endpoint as Local — omit it and the entry fails closed to Cloud (ADR-053: no implicit fallback). See Hardening checklist § Sovereignty attestation is a trust assumption before attesting a self-hosted endpoint as local — it's a carrier claim the platform trusts, not something it verifies.
Docker networking for local models
When both Hoziron and the inference server run in containers:
services:
hoziron:
depends_on:
- ollama
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
In config.toml, reference the service name:
[[provider]]
id = "ollama"
driver = "ollama"
url = "http://ollama:11434/v1"
Offline package transfer
hoziron-cli catalog export/import move a single installed package as a portable .tar bundle, with the content hash verified locally on import — no network required either direction:
# On a connected machine: export an installed package
hoziron-cli catalog export claims-intake --output claims-intake.tar
# Transfer the .tar file to the air-gapped environment
# (USB drive, SCP over an internal network, a one-way transfer station, etc.)
# On the air-gapped machine: import
hoziron-cli catalog import ./claims-intake.tar
Collections: no bulk export/import — transfer packages individually
There is no hoziron-cli collection export or hoziron-cli collection import command — CollectionCommands only has init, list, install, inspect, publish, and validate. A collection (COLLECTION.toml) is just a manifest of package names and versions; there's no single-command bulk archive for it. For an air-gapped transfer of an entire collection:
# On a connected machine: see what's in the collection
hoziron-cli collection inspect insurance-starter
# Export each listed package individually
hoziron-cli catalog export claims-intake --output claims-intake.tar
hoziron-cli catalog export policy-lookup --output policy-lookup.tar
# ...repeat for every package the collection lists
# Transfer all the .tar files across, then on the air-gapped machine:
hoziron-cli catalog import ./claims-intake.tar
hoziron-cli catalog import ./policy-lookup.tar
# ...
If you need this often, scripting the export loop over hoziron-cli collection inspect --json is worth doing — there's no built-in equivalent.
Checklist
- Licence issued for the carrier ID and installed at
$HOZIRON_HOME/licence.json -
hoziron-cli licence statusconfirms it's loaded and active once the server is up - Local inference server (Ollama or vLLM) running and reachable
-
[[provider]]configured with the correctdriverand, foropenai-compatibleendpoints,sovereignty_attestationset -
routing_gateway.local_preferencenon-empty (an empty provider list silently falls back to the network-freeechodriver, not a real model) - All required packages transferred and imported (
hoziron-cli catalog export/import, per-package — no bulk collection transfer exists) - Health check passes:
hoziron-cli health - Test an agent against a local model:
hoziron-cli agent chat <id>
Next steps
- Configuring local models in detail
- Complexity routing with local models
- Security hardening for isolated environments
- Sovereignty attestation is a trust assumption
Related: