OXYGENHUB

The OpenTofu image for deploy jobs e3fd8257ce05

images/tofu: Debian slim with OpenTofu 1.13.1 (checked against its
SHA-256) and a provider mirror (hashicorp/google 8.7.0,
cloudflare/cloudflare 5.28.0, checked against the registry's signed
checksums at build time), installed only from the mirror via
/etc/tofu/cli.tfrc. Imported on Earth and pushed to the hub as
cas://blob/56cbf25e287c416e0ec480ac3c3a4d518531fb969249f56ca14c17f4a04d3012;
andymccall/oxygenhub-gcp's check job passes in it with no network.

Closes #56
Author
Andy McCall @andymccall <[email protected]>
via
agent Claude Code <[email protected]> · delegate for changes, until 2026-11-13 19:00Z · key ea5f78160088
When
· 2026-10-15 13:19 UTC
Signature
✔ signed by Claude Code
Hash
e3fd8257ce0559794a8481fba88dd6ffccf0246c3d3f6eb25dd6c5bbcdc0470d
Depends on
eb5c4db673e5
Depended on by
e0053e047704 e2189ffef8ff 1c9afa928691
In bookmarks
main
In tags
v0.2.0 v0.3.0 v0.3.1 v0.4.0 v0.5.0 v0.5.1 v0.5.2 v0.5.3
Edits
5 atoms, 6 tree operations, 5 files

Browse the tree at this change

added images/tofu/Dockerfile +33 −0
1 +
# The OxygenFlow OpenTofu image (oxygenflow#56): OpenTofu and the providers
2 +
# the deploy pipelines use, mirrored inside, so `tofu init` needs no
3 +
# registry. A job reaches a cloud's API only through allow-listed egress
4 +
# (#55); formatting and validation need no network at all.
5 +
6 +
# Download and check OpenTofu, and mirror the providers, with the tools
7 +
# only this stage needs.
8 +
FROM debian:bookworm-slim AS fetch
9 +
ARG TOFU_VERSION
10 +
ARG TOFU_SHA256
11 +
RUN apt-get update \
12 +
 && apt-get install -y --no-install-recommends ca-certificates curl unzip \
13 +
 && rm -rf /var/lib/apt/lists/*
14 +
RUN curl -fsSL -o /tmp/tofu.zip \
15 +
      "https://github.com/opentofu/opentofu/releases/download/v${TOFU_VERSION}/tofu_${TOFU_VERSION}_linux_amd64.zip" \
16 +
 && echo "${TOFU_SHA256}  /tmp/tofu.zip" | sha256sum -c - \
17 +
 && unzip -q /tmp/tofu.zip tofu -d /usr/local/bin \
18 +
 && rm /tmp/tofu.zip
19 +
# The exact provider versions of the pipelines' lock files; `tofu
20 +
# providers mirror` checks each package against the registry's signed
21 +
# checksums as it downloads it.
22 +
COPY mirror.tf /tmp/mirror/
23 +
RUN cd /tmp/mirror && tofu providers mirror -platform=linux_amd64 /opt/tofu/mirror
24 +
25 +
FROM debian:bookworm-slim
26 +
RUN apt-get update \
27 +
 && apt-get install -y --no-install-recommends ca-certificates \
28 +
 && rm -rf /var/lib/apt/lists/*
29 +
COPY --from=fetch /usr/local/bin/tofu /usr/local/bin/tofu
30 +
COPY --from=fetch /opt/tofu/mirror /opt/tofu/mirror
31 +
# Providers come only from the mirror: a job never reaches a registry.
32 +
COPY cli.tfrc /etc/tofu/cli.tfrc
33 +
ENV TF_CLI_CONFIG_FILE=/etc/tofu/cli.tfrc
added images/tofu/README.md +46 −0
1 +
# The OxygenFlow OpenTofu image
2 +
3 +
A root file system for deploy jobs (oxygenflow#56):
4 +
5 +
- Debian bookworm, slim, with CA certificates.
6 +
- OpenTofu 1.13.1, checked against its published SHA-256.
7 +
- A provider mirror in `/opt/tofu/mirror`: `hashicorp/google` 8.7.0 and
8 +
  `cloudflare/cloudflare` 5.28.0, checked by `tofu providers mirror`
9 +
  against the registry's signed checksums when the image is built.
10 +
- `/etc/tofu/cli.tfrc` (set by `TF_CLI_CONFIG_FILE`) installs providers
11 +
  from the mirror only.
12 +
13 +
So `tofu init`, `fmt` and `validate` need no network; `plan` and `apply`
14 +
reach the cloud's APIs through allow-listed egress (#55). The guest's
15 +
environment is the pipeline's, so a job sets `TF_CLI_CONFIG_FILE` itself
16 +
(below).
17 +
18 +
## Building and importing
19 +
20 +
```
21 +
images/tofu/build.sh /path/to/oxygenflow-tofu.tar
22 +
scp /path/to/oxygenflow-tofu.tar claude@earth:/home/claude/flow/dl/
23 +
ssh claude@earth 'cd ~/flow && FLOW_HOME=~/flow bin/flow rootfs import dl/oxygenflow-tofu.tar'
24 +
```
25 +
26 +
Pin the printed hash in the pipelines that use it, and push it to the hub
27 +
(`flow rootfs push HASH`) so every runner can fetch it.
28 +
29 +
A provider version in a pipeline's lock file must be in the mirror: bump
30 +
`mirror.tf`, rebuild and re-pin when a lock file changes.
31 +
32 +
## Using it
33 +
34 +
```kdl
35 +
sandbox "tofu" {
36 +
    runtime "microvm"
37 +
    rootfs "cas://blob/<hash printed by flow rootfs import>"
38 +
    memory-limit "2GiB"
39 +
    cpu-limit 2
40 +
    network "isolated"
41 +
    env {
42 +
        TF_CLI_CONFIG_FILE "/etc/tofu/cli.tfrc"
43 +
        TF_IN_AUTOMATION "1"
44 +
    }
45 +
}
46 +
```
added images/tofu/build.sh +16 −0
1 +
#!/bin/sh
2 +
# Build the OxygenFlow OpenTofu image and write it as a root file system
3 +
# tarball for `flow rootfs import`. Rebuild when OpenTofu or a pinned
4 +
# provider version changes (mirror.tf).
5 +
set -eu
6 +
here=$(cd "$(dirname "$0")" && pwd)
7 +
out=${1:-$here/oxygenflow-tofu.tar}
8 +
TOFU_VERSION=1.13.1
9 +
TOFU_SHA256=8ccbc6f8ee21d2827715f3c6e08a9b3e0209b1e62057c05067ef117e047c1a80
10 +
11 +
docker build -q -t oxygenflow-tofu \
12 +
    --build-arg TOFU_VERSION=$TOFU_VERSION --build-arg TOFU_SHA256=$TOFU_SHA256 "$here"
13 +
id=$(docker create oxygenflow-tofu)
14 +
docker export "$id" -o "$out"
15 +
docker rm "$id" >/dev/null
16 +
echo "$out"
added images/tofu/cli.tfrc +8 −0
1 +
provider_installation {
2 +
  filesystem_mirror {
3 +
    path    = "/opt/tofu/mirror"
4 +
    include = ["registry.opentofu.org/*/*"]
5 +
  }
6 +
  # Nothing is fetched from a registry: a provider missing from the
7 +
  # mirror fails `tofu init` instead of reaching out.
8 +
}
added images/tofu/mirror.tf +14 −0
1 +
# The providers the image carries, at the versions the deploy pipelines
2 +
# lock. Bump here, rebuild, re-pin (README.md).
3 +
terraform {
4 +
  required_providers {
5 +
    google = {
6 +
      source  = "hashicorp/google"
7 +
      version = "8.7.0"
8 +
    }
9 +
    cloudflare = {
10 +
      source  = "cloudflare/cloudflare"
11 +
      version = "5.28.0"
12 +
    }
13 +
  }
14 +
}