Running a Terraform and OpenTofu registry with no egress.
In defence, banking, healthcare, and government, no egress is the default. No registry.terraform.io, no provider downloads, no module distribution, and most tooling quietly assumes the internet is there. This is the page nobody else writes: how to run a proper air-gapped registry, and how artefacts cross the boundary without breaking the chain of trust.
No egress means the whole ecosystem stops working.
The public registry is unreachable. Provider downloads fail. Module sources that resolve over the internet do not resolve at all. Security review is not a launch gate you pass once, it is the daily reality of the environment. Anything you run inside the boundary has to assume it is on its own.
Provider mirrors help. They do not solve it.
The terraform providers mirror command and filesystem mirrors get provider binaries across, and that is genuinely useful. But they are providers only. There is no module distribution, no versioning workflow, no access control, and no audit trail of who published or consumed what. In a regulated environment those absences are the whole job, not a nice-to-have.
What a proper air-gapped registry has to hold.
Everything the workflow depends on lives on the high side: the registry API that serves the protocol, the artifact storage behind it, and the scan database that findings are checked against. Updates arrive as signed bundles across the boundary rather than as live fetches. Nothing inside reaches out.
flowchart TB
subgraph low["Low side (connected)"]
Pub[Publishing pipeline]
Sign["Signing key<br/>private, stays low-side"]
Bundle["OCI bundle<br/>blobs + manifest + signature"]
Pub --> Bundle
Sign --> Bundle
end
Diode{{"Data diode<br/>one-way, hardware-enforced"}}
subgraph high["High side (air-gapped)"]
Import[Import + verify]
Reg[Terramantle registry]
Store[(Artifact storage)]
Scan[(Scan / CVE database)]
Import --> Reg
Reg --> Store
Reg --> Scan
end
Bundle -->|blob by blob| Diode
Diode --> ImportCrossing the diode, and why OCI is the right wire format.
First, what a data diode is, for readers who have not worked in these environments. A data diode is a hardware-enforced one-way link, often optical, with physically no return path. No handshakes, no acknowledgements, no retransmit requests. The receiving side has to be able to detect corruption and reassemble the transfer entirely on its own, because it can never ask the sender for anything.
Digests prove integrity, not authenticity.
Be precise about what content addressing does and does not give you. A digest proves a blob’s bytes are intact. It does not prove who produced them. An attacker, or a corrupt process, that can alter a blob on the destination side can also recompute its digest and rewrite the manifest, swapping a binary while leaving the whole bundle self-consistent. Every digest still matches. Nothing looks wrong.
Terramantle closes that gap by building OpenPGP provenance into the bundle. Signatures are made over the manifest and its digest tree with private keys that never cross the diode, and the high side verifies against public keys provisioned out of band before any import is accepted. So checksums cannot simply be changed at the destination. A swapped or corrupted binary fails signature verification rather than slipping through as a recomputed, self-consistent bundle, and the chain of trust runs unbroken from the publishing pipeline, across the diode, to the module a workspace ultimately consumes.
sequenceDiagram
participant L as Low side
participant D as Data diode
participant H as High side
L->>L: Assemble OCI bundle (blobs, manifest)
L->>L: Sign manifest digest tree (private key stays low-side)
loop each blob
L->>D: Send blob (identified by its own digest)
D->>H: Deliver blob (no ack, no retransmit)
H->>H: Verify blob digest
end
L->>D: Send manifest + signature
D->>H: Deliver manifest + signature
H->>H: Recombine: every blob digest must match the manifest
H->>H: Verify PGP signature against out-of-band public key
alt signature valid and digests match
H->>H: Accept import
else anything off
H->>H: Reject import, loudly
endAuthor note, verify before publish. Confirm the exact Terramantle bundle mechanics against the current beta before this page goes live: key formats, where the signature sits in the bundle, and the precise import behaviour on a verification failure. Do not guess these specifics.
Screenshot to add: the offline scan-database import or the air-gap deployment configuration, from the Acme demo org.
Self-hosted, in air-gap mode.
Install Terramantle self-hosted on the high side against your own storage and database. Publish a module from an internal pipeline over OIDC against your internal identity provider, so there are still no long-lived tokens even inside the boundary. Consume it from a workspace with the usual source address, and run a scan entirely offline against the imported scan database. None of these steps reach the internet.
The keyless publishing flow is the same one described in no runners, no agents, and it works identically under OpenTofu, see the private OpenTofu registry page.
The chain of trust is itself evidence.
You get an audit trail of who published and consumed what, and SBOMs per version for security review. Beyond that, the signed-bundle chain of trust is accreditation evidence in its own right: it demonstrates that what runs on the high side is exactly, provably, what was signed on the low side, with no point along the way where an artefact could be swapped without detection.
Air-gap FAQ
How do updates cross a data diode?
You export the modules, providers, and scan-database updates you want as a signed OCI bundle on the connected (low) side, transfer it across the diode as a set of individually-addressed blobs, and import it on the air-gapped (high) side. The high side recombines the blobs against the manifest, checks every digest, and verifies the signature before anything is accepted. Nothing on the high side ever reaches back out.
How do you stop artifacts being tampered with in transit?
Digests alone are not enough, because anything that can alter a blob on the destination can also recompute its digest and rewrite the manifest to stay self-consistent. The signature is what closes that gap. Terramantle signs over the manifest and its digest tree with a key that never crosses the diode, and the high side verifies against a public key provisioned out of band. A swapped or corrupted artifact fails signature verification instead of slipping through as a recomputed, self-consistent bundle.
Does Terramantle need any outbound internet access?
No. Terramantle runs fully self-hosted with no outbound dependency. You bring your own S3-compatible storage and database, you mirror the providers you need, and no module, provider, or state data leaves your infrastructure. Supply-chain scanning and OPA policy checks run locally against the artefacts you host.
What does the audit trail cover for accreditation?
Every publish and every consume is recorded: who published which version, and which workspace pulled it. Combined with per-version SBOMs and the signed-bundle chain of trust, that record is itself evidence for a security accreditation, not just an operational nicety.
No egress. Full registry. We can help with the security review.
Terramantle is in beta and runs fully self-hosted with no outbound dependency. If you are scoping an air-gapped registry, we are happy to work through the accreditation angle with you.
Last reviewed