- image_tools/bump.py from base-os, with an action. base-python used it from
a pinned checkout of base-os, with the bot's token; one place serves both.
A dependent whose workflow does not exist (netork's ui-nightly.yml, on
2026-10-22) is named and the others go on; the run fails at the end, as
for a repository the token cannot see. bump_all holds that loop, tested.
- verify-base takes refs besides Dockerfiles: base-python builds FROM
${BASE_OS}, which its nightly sets, and copies CPython from Docker's
python:3.12-slim, which is not signed with netOrk's key.
image-tools
Judges, signs and verifies the container images of netOrk. Every image repository of NetOrk uses these tools at a pinned commit, so the same rules apply to every link of the chain:
netork/base-os ──► netork/base-python ──► netork/engine, netork/satellite
└─► netork/ui
└─► postgres, valkey, signal-api, apt-cacher-ng, unbound
Each link checks the signature of the image it builds on, is judged against CIS, and is signed with its CIS result and SBOM attached (NetOrk/netork#1390).
What it does
CIS gate (image_tools/cis.py) |
CIS Docker Benchmark 1.6.0 §4 and the CIS Debian Linux 12 controls that make sense in a container. Trivy measures what it automates; image_tools/cis_os.sh measures from inside the image. The image's repository decides the rest in its cis.toml and names the packages it may hold in packages.allow. |
SBOM (image_tools/sbom.py) |
The image's CycloneDX SBOM, as Trivy reads it. |
Signature (bin/sign.sh) |
A cosign signature on the reference and on every platform image behind it, with the CIS result and the SBOM as in-toto attestations. Nothing goes to a public transparency log. |
Verification (bin/verify-base.sh, bin/verify-signature.sh) |
Every FROM image of a Dockerfile is pinned by digest and signed with the key in cosign.pub. |
Verdict (image_tools/verify.py) |
Whether a rebuild improves on the pinned image, by netOrk's matcher (kb-server), for a base's nightly. |
Bump (image_tools/bump.py) |
One pull request in every repository of a dependents.toml that moves only the pin, and that repository's workflow started. |
Using it in a workflow
- name: Verify the base images
uses: https://git.netork.io/NetOrk/image-tools/actions/verify-base@<commit>
with:
dockerfiles: docker/engine/Dockerfile
# build the image, then:
- name: Judge it
uses: https://git.netork.io/NetOrk/image-tools/actions/judge@<commit>
with:
image: registry.netork.io/netork/engine:main-abc1234
arch: amd64
# a base's nightly, to decide whether its pin moves:
- name: Judge it against the pin
id: verdict
uses: https://git.netork.io/NetOrk/image-tools/actions/verdict@<commit>
env:
NETORK_LICENSE_KEY: ${{ secrets.NETORK_LICENSE_KEY }}
with:
current: registry.netork.io/netork/base-os:pinned
candidate: registry.netork.io/netork/base-os:13-20261022.3-amd64
# push it, then:
- name: Sign it
uses: https://git.netork.io/NetOrk/image-tools/actions/sign@<commit>
env:
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
with:
ref: registry.netork.io/netork/engine@sha256:…
The judge action reads cis.toml and packages.allow from the working directory by default.
The runner needs Docker, Python 3.11 or newer, and curl; cosign is installed by the actions,
pinned and checked against its release checksum (bin/install-cosign.sh).
Verifying an image yourself
cosign verify --key cosign.pub --insecure-ignore-tlog registry.netork.io/netork/base-os@sha256:…
cosign verify-attestation --key cosign.pub --insecure-ignore-tlog \
--type https://netork.io/attestations/cis/v1 registry.netork.io/netork/base-os@sha256:…
cosign.pub is the public half of the COSIGN_PRIVATE_KEY_B64 organization secret. The private
key comes base64 in one line: the runner masks a secret only where it appears whole.
Development
pip install pytest ruff pyyaml shellcheck-py
pytest -q
ruff format --check . && ruff check .
shellcheck bin/*.sh image_tools/cis_os.sh
License
MIT, see LICENSE.