Air-Gapped Install
The same signed bundle installs on a host with no internet access. Nothing about the bundle depends on a registry or a call home — image tarballs ship inside it, and the signature is verified locally.
1. Fetch on a connected machine
On a machine that can reach the Control Service:
curl -fsSL https://control.factorythread.com/ftctl -o ftctl && chmod +x ftctl
sudo ./ftctl fetch --version 2026.09.04.7 --out ./bundles
fetch downloads and verifies the bundle, writing three files:
bundles/factory-thread-2026.09.04.7.tar.gz
bundles/factory-thread-2026.09.04.7.tar.gz.sha256
bundles/factory-thread-2026.09.04.7.tar.gz.sig
2. Transfer to the air-gapped host
Copy the ftctl binary and all three bundle files (the .sha256 and .sig
sidecars matter — they're how the offline verify works) to the target host, via
whatever medium your environment allows (USB, internal artifact store, etc.).
3. Install from the local bundle
On the air-gapped host:
sudo ./ftctl install --bundle factory-thread-2026.09.04.7.tar.gz
install re-verifies the SHA-256 checksum and ECDSA signature offline
against the trust anchor embedded in the binary before loading anything. If the
bundle was tampered with in transit, the install refuses to proceed.
Everything after that — configure and deploy — is identical to a normal
installation. The interactive prompts, .env.deploy and all
day-2 commands work exactly the same; they never needed the network.
Air-gapped upgrades
Upgrades work the same way — fetch the newer bundle on a connected machine,
transfer it, and point upgrade at it:
sudo ./ftctl upgrade --bundle factory-thread-<newer-version>.tar.gz
Notes
- Base images (Postgres, RabbitMQ, Redis, Nginx) are included in the bundle, so a truly cold host needs nothing pre-pulled.
- If the host also has no container engine, run
sudo ./ftctl enginefirst — but note that installs Podman from OS repositories, which an air-gapped host may not have; provision the engine through your internal package mirror if so. - The licence is installed offline too (
ftctl license install --jws), so no step of an air-gapped install requires connectivity.