infra-edge-lab · v0.1.0
Toolchain
The tools used to work on this repository are pinned to one version each; the exception is
named below (the dependencies of markdownlint-cli2 in a local run). A downloaded binary is
installed only after its sha256 matches the checksum the tool’s project publishes with the
release; Python packages are installed with pip --require-hashes from files that list the
sha256 of every distribution file on PyPI, and OpenTofu providers are checked against the
.terraform.lock.hcl of each module.
Workflow actions are pinned by commit SHA
(ADR 0002); python3 scripts/check_workflows.py --list
prints them.
Versions and checksums were verified on 2026-10-21 for Linux x86_64 (the GitHub ubuntu-24.04
runner and the workstation). tests/test_toolchain.py fails when this page, the workflow inputs,
the requirements files and the README disagree.
Binary tools
| Tool | Version | Purpose in this repository | File (linux amd64) | sha256 | Checksum source |
|---|---|---|---|---|---|
| OpenTofu | 1.13.1 | provisioning code in tofu/ (CI tofu and e2e-local, task tofu, the environment tasks; task lint runs tofu fmt -check) |
tofu_1.13.1_linux_amd64.tar.gz |
378ada19d4bc70c43732004e8159be771b23b9a5afdf059e5f8a2b3fa2c70a69 |
tofu_1.13.1_SHA256SUMS of the release, signed (.sig/.pem and .gpgsig) |
| TFLint | 0.64.0 | linter for tofu/ with its bundled Terraform ruleset, configured in .tflint.hcl (CI tofu, task tofu) |
tflint_linux_amd64.zip |
cca9d13e2e1d7a2c627af60ff899a3c9b74212899416aeb96ec764d2ef954537 |
checksums.txt of the release, keyless-signed (.keyless.sig, .pem) |
| Terraform | 1.16.6 | compatibility check only: terraform validate of tofu/ (ADR 0005); downloaded into a throwaway directory by scripts/check_tofu.sh compat |
terraform_1.16.6_linux_amd64.zip |
38034b38d03096399a7a1264a14561a8029b2b496fd1f169452656e53968f1b7 |
terraform_1.16.6_SHA256SUMS of the release, signed by HashiCorp’s release key (_SHA256SUMS.sig; signature checked 2026-10-21) |
| go-task | 3.54.0 | Taskfile.yml (task lint, task test, task tofu, task e2e:local; CI e2e-local downloads it with this sha256) |
task_linux_amd64.tar.gz |
680859dbb4d881a9c72d4d9a8f510825450849af8567deacd7302c01124416fb |
task_checksums.txt of the release |
| gitleaks | 8.30.1 | secret scan of the full history (CI secrets / gitleaks, task lint) |
gitleaks_8.30.1_linux_x64.tar.gz |
551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb |
gitleaks_8.30.1_checksums.txt of the release |
| actionlint | 1.7.12 | workflow linter (CI ci / lint-test, task lint) |
actionlint_1.7.12_linux_amd64.tar.gz |
8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 |
actionlint_1.7.12_checksums.txt of the release |
| Trivy | 0.76.0 | IaC security scan of tofu/ and images/ (trivy config with the checks built into the release, offline; accepted findings with their reason in .trivyignore.yaml) (CI tofu, task tofu) |
trivy_0.76.0_Linux-64bit.tar.gz |
e24b1c51293c1b15e4ba8d45bbc7891ba053f90278ec2698f3562c993e145fe7 |
trivy_0.76.0_checksums.txt of the release, Sigstore bundle (.sigstore.json) |
| SOPS | 3.13.3 | encrypts the dotenv files in secrets/; the cloud workflows (plan, apply, drift, e2e-tunnel) download it with this sha256 to decrypt them |
sops-v3.13.3.linux.amd64 |
e5bec3346a873ae91d871550f3e698c1aad962aff462a080e40f25fde17fef6b |
sops-v3.13.3.checksums.txt of the release, Sigstore bundle |
| age | 1.3.2 | keys for SOPS; encrypts the zone copies of task dns-backup |
age-v1.3.2-linux-amd64.tar.gz |
cbe24006683f8eb669266162894b9a522a1af52f2665fbc63a4bb032ed26ac10 |
asset digest of the release and its .proof file |
| cloudflared | 2026.10.2 | cloudflared access ssh on the control node: the SSH check of task e2e:tunnel (scripts/e2e_tunnel.sh downloads it with this sha256), and SSH through the tunnel to a cloud server of demo or showcase (task configure, task smoke; the apply workflow downloads it with this sha256); the same release that the role luminare.infra_edge.cloudflared installs on the hosts |
cloudflared-linux-amd64 |
35fbdd2beb378436c7da377d7358cea8e33f9c94a6705d1d8036bf9b0d6d8005 |
“SHA256 Checksums” in the notes of the release, equal to GitHub’s asset digest and the download checked on 2026-10-22 |
| Node.js | 24.22.0 | runs markdownlint-cli2 locally | node-v24.22.0-linux-x64.tar.xz |
cc2f601cee82d9a5a233302fe307abc600727035fd7d2c3a3571755989e95008 |
SHASUMS256.txt (signed SHASUMS256.txt.asc) |
markdownlint-cli2 0.23.2 is the version that CI’s DavidAnson/markdownlint-cli2-action
v24.2.0 bundles. task lint runs it with npx --yes markdownlint-cli2@0.23.2; npm checks the
package against the registry’s integrity value
(sha512-eUhcnkSpzURo/o4htSqc7LPDszgOOTknhU4eY/sPHvMCLxnTCYscv1gw1/js/idmaZPisv9ECVEIORcllqjTUw==).
That pins the package itself only: it names exact versions of its direct dependencies, but their
own dependencies resolve from semver ranges when npx installs them, and this repository keeps no
lock file for them. CI installs nothing from npm: the action runs the copy bundled in its
dist/, fixed by the action’s commit SHA.
scripts/install_tofu_tools.sh holds the versions and sha256 values of OpenTofu, TFLint,
Terraform and Trivy for the CI job tofu and the compatibility check; tests/test_tofu.py and
tests/test_toolchain.py fail when they differ from the table above.
Install a binary only after the check passes, for example:
curl -fsSLO https://github.com/go-task/task/releases/download/v3.54.0/task_linux_amd64.tar.gz
echo "680859dbb4d881a9c72d4d9a8f510825450849af8567deacd7302c01124416fb task_linux_amd64.tar.gz" | sha256sum -c -
tar -xzf task_linux_amd64.tar.gz task && install -m 0755 task ~/.local/bin/task
OpenTofu providers and the host image
Each module under tofu/modules/ and each environment under tofu/envs/ pins its providers
with a version constraint in versions.tf and commits a .terraform.lock.hcl with package
checksums for linux_amd64 and darwin_arm64 and the registry’s signed checksums of every
platform’s archive (ADR 0012). An environment locks
the same version and checksums as the modules it calls; tests/test_tofu.py checks this.
Versions were checked in the OpenTofu registry on 2026-10-21:
| Provider | Constraint | Locked | Newest release on 2026-10-21 | Note |
|---|---|---|---|---|
bpg/proxmox |
~> 0.114.0 |
0.114.0 | 0.117.0 | below 1.0, so patch releases only; Dependabot proposes newer minor releases in a pull request of their own |
cloudflare/cloudflare |
~> 5.25 |
5.28.1 | 5.28.1 | 5.28.1 distrusts certificate authorities revoked for compromise |
hetznercloud/hcloud |
~> 1.69 |
1.70.0 | 1.70.0 | |
kreuzwerker/docker |
~> 4.6 |
4.6.0 | 4.6.0 |
cloudflare 5.28.1 was published at 2026-10-20 23:35 UTC, less than a day before it was locked
here, and so was taken without the 7-day cooldown that Dependabot keeps for version updates
(ADR 0007). It is a security rebuild whose release
notes list no schema change: it is built with Go 1.27.3, which distrusts intermediate certificate
authorities revoked for compromise on the connection that carries the Cloudflare API token.
Dependabot skips the cooldown for security updates for the same reason; the tests pass with
5.28.1 and with 5.25.0.
tofu test and tofu validate also pass with the lowest version of each range
(cloudflare 5.25.0, hcloud 1.69.0), in the modules and in the environments. After a
constraint change, write the lock files again for both platforms, in every module and
environment that uses the provider:
tofu -chdir=tofu/modules/<module> providers lock -platform=linux_amd64 -platform=darwin_arm64
tofu -chdir=tofu/envs/<env> providers lock -platform=linux_amd64 -platform=darwin_arm64
images/systemd-host/Dockerfile (the host image of the docker_host module) starts from
debian:trixie and ubuntu:24.04, each pinned by digest (pulled and checked 2026-10-21).
Dependabot’s docker ecosystem proposes new digests.
The hosts that hcloud_vm creates install cloudflared 2026.9.1 at first boot from its GitHub
release (cloudflared-linux-amd64.deb, sha256
3be76adc4185d36a0bfb4c2dd8663292f0ed363797f2180333b513b43c81d419 from the release notes, the
same as GitHub’s asset digest and the download checked on 2026-10-21). Cloudflare’s package
repository lists only its newest release (2026.10.2 on 2026-10-21), so it cannot install a
pinned older version.
What the roles download
The roles in ansible/roles download these files on the managed hosts, each checked against a
value pinned in the role (checked on 2026-10-21). Dependabot does not see them; they are updated
by hand like the binary tools (runbook).
tests/test_ansible.py fails when a checksum in the role defaults is missing on this page.
| What | Version | Pinned in | File or image | sha256 or digest | Checksum source |
|---|---|---|---|---|---|
| node_exporter | 1.12.1 | monitoring defaults |
node_exporter-1.12.1.linux-amd64.tar.gz |
b51d8a76aa2a9156a55d501aca6276fae09e262259a5e4e831d2c2222f084e63 |
sha256sums.txt of the release, equal to GitHub’s asset digest and the download |
| node_exporter | 1.12.1 | monitoring defaults |
node_exporter-1.12.1.linux-arm64.tar.gz |
ad35b605f9954b9f1ffddf5ba054bdc5a98d790b9eae5291e1eeb83f1ecbd0e7 |
the same |
| Uptime Kuma | 2.5.6 (slim) |
monitoring defaults |
louislam/uptime-kuma:2.5.6-slim |
sha256:5db97dbbef610276e1699f3ece586c3509c8fd35de75d45ff5109761a1b377dc (image index, amd64 and arm64) |
docker buildx imagetools inspect and Docker Hub |
| Docker’s APT signing key | key 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 |
docker defaults |
https://download.docker.com/linux/{debian,ubuntu}/gpg (the same file) |
1500c1f56fa9e26b9b8f42452a553675796ade0807cdce11975eb98170b3a570 |
the downloaded file; fingerprint shown by gpg --show-keys |
| BusyBox (test image of the docker role’s Molecule scenario) | 1.37 | ansible/roles/docker/molecule/default/verify.yml |
busybox:1.37 |
sha256:bdf57e528e45e4433820e045b29b4597825a1c9e38353532d90a01445013f82e (image index) |
docker buildx imagetools inspect |
Docker Engine itself comes from Docker’s signed APT repository (newest stable release on the
first run), the other packages from the distribution.
What the playbook downloads
ansible/playbooks/site.yml runs the roles of the collections in
ansible/requirements.yml: luminare.infra_edge and
luminare.gameserver from their GitHub repositories at a release tag (git checks the commit the
tag points to), devsec.hardening 11.0.0 and prometheus.prometheus 0.30.2 from Ansible Galaxy
(ansible-galaxy checks each download against the sha256 that Galaxy publishes), and the other
community collections from the hash-pinned ansible package. The collections’ roles pin their
own downloads (for example cloudflared, json_exporter and blackbox_exporter); their repositories
list them. The values of the local environment add one pin (checked on 2026-10-21):
| What | Version | Pinned in | File | sha256 | Checksum source |
|---|---|---|---|---|---|
| Prometheus | 3.15.0 | ansible/inventories/local/group_vars/monitoring.yml (site_prometheus_sha256) |
prometheus-3.15.0.linux-amd64.tar.gz |
2a542df32eac02ee17b9d844fb2aa1de00dafa5476579ba8a3ba862e9d572ea0 |
sha256sums.txt of the release; site.yml checks the download against the pin before the prometheus role installs it, and the role against that file |
The game server of the local environment is never downloaded: it is the stand-in build of
tests/fake-artifact/, built on the control node.
Python
Python 3.12 is the python3 of the ubuntu-24.04 runner and the oldest version
ansible-core 2.21 supports. The hash-pinned install was also tested with Python 3.13.
The table names the file that pins each package instead of repeating its version, so a
Dependabot update of a requirements file needs no edit on this page. scripts/run_tests.sh
installs the Ansible tools, checks their installed versions against the pins and runs
ansible-lint; the Molecule jobs of CI and task molecule use the same file.
| Package | Pinned in | Purpose |
|---|---|---|
| ansible-core | requirements-test.in, compiled into requirements-test.txt |
Ansible controller |
| ansible | requirements-test.in, compiled into requirements-test.txt |
the community collections (community.docker, ansible.posix, community.sops, …) for ansible-lint, Molecule and the roles; ansible/requirements.yml pins the same versions (ADR 0016) |
| ansible-lint | requirements-test.in, compiled into requirements-test.txt |
Ansible linter (production profile) |
| molecule | requirements-test.in, compiled into requirements-test.txt |
role test framework |
molecule-plugins (docker extra) |
requirements-test.in, compiled into requirements-test.txt |
Molecule Docker driver |
| yamllint | requirements-dev.txt |
YAML lint (CI ci / lint-test, task lint) |
| zizmor | requirements-dev.txt |
workflow security audit (CI ci / lint-test, task lint) |
| pip-tools | pip-tools==7.5.2 in the compile command under Updating a pin |
compiles requirements-test.txt; not installed by the tasks |
| click | click==8.2.1 in the same command |
command-line library of pip-tools, held below 8.3 (see below) |
requirements-test.txt is generated from requirements-test.in by pip-compile and lists every
dependency with its hashes (46 packages when it was compiled on 2026-10-21).
requirements-dev.txt comes from the template and is pinned by hand. Install either file with
pip install --require-hashes --only-binary=:all: -r <file>; scripts/run_tests.sh and
task venv do this.
Hooks before a commit
.pre-commit-config.yaml runs the quick checks as git hooks: end-of-file and trailing-whitespace
fixes, shellcheck, gitleaks on the staged changes, yamllint, the check that every
secrets/*.enc.env is encrypted (sops filestatus), tofu fmt, TFLint and ansible-lint. It is
optional; CI runs the same checks in its own jobs. Install pre-commit (tested with 4.7.0, for
example pipx install pre-commit), run task venv and put .venv/bin on the PATH, then
pre-commit install. The hooks of other repositories are pinned to a commit SHA with the tag in a
comment (pre-commit autoupdate --freeze writes them so), and pre-commit installs them into its own
cache on the first run: pre-commit-hooks v6.0.0, shellcheck-py v0.11.0.1 (shellcheck 0.11.0) and the
gitleaks-system hook of gitleaks v8.30.1, which runs the gitleaks binary of the table above. The
other hooks run the tools of this page.
Updating a pin
-
Python test tools: Dependabot recompiles
requirements-test.txtwhen a pinned package has a new release (ADR 0007). To change the pins by hand, editrequirements-test.inand compile with Python 3.12:docker run --rm -v "$PWD:/work" -w /work \ python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 \ sh -c 'pip install --quiet pip-tools==7.5.2 click==8.2.1 && pip-compile --generate-hashes --allow-unsafe --strip-extras --output-file=requirements-test.txt requirements-test.in'Keep click below 8.3 for pip-tools 7.5.2: with click 8.3 pip-tools writes a wrong
--no-indexoption into the command line in the file header, which Dependabot reuses. -
Binary tools: once a month, read the new release’s checksum file (and check its signature where the project signs it), then change the version and sha256 here and in every place that pins the same file (the
gitleaks-*andactionlint-*inputs of the reusable workflows). Dependabot does not see these pins.
Licensed under Apache-2.0. Source: luminare-dev/infra-edge-lab at v0.1.0.