Skip to content

Scripts Reference

CYPT71 edited this page Aug 21, 2026 · 2 revisions

Scripts Reference

Every buildable Go command and every script in the repository, what it does, what it needs, and who calls it.

Go commands (cmd/)

cmd/oci-builder

The builder CLI itself. Full flag/exit-code reference: CLI Reference. Backed by internal/oci (see Architecture and OCI Layout).

cmd/example-service

A tiny, stdlib-only Go HTTP API (net/http, log/slog - no dependencies) used as a fixture, not a product: /healthz, /ping, /metrics (Prometheus text format), structured JSON request logging. Built by scripts/local/build-macos-image.sh and by ci-microvm.yml (see Testing and CI/CD) - it exists so both consumers exercise the exact same known-good payload without duplicating a hand-written HTTP app in three different shell heredocs.

cmd/microvm-init

A tiny stdlib-only Go binary that runs as PID 1 inside a microVM, nowhere else. What it does:

  1. Execs exactly one child process (the OCI image's declared entrypoint, passed as its own arguments).
  2. Forwards SIGTERM/SIGINT to that child.
  3. Waits for it to exit.
  4. Calls syscall.Reboot(LINUX_REBOOT_CMD_POWER_OFF) to cleanly power the VM off.

It deliberately does not reap unrelated reparented orphan processes - general-purpose init duties are out of scope for a single-process VM image.

Testing note: the real poweroff() (Linux-only, behind a //go:build linux file, poweroff_linux.go) is never called from any test, on any platform - doing so on a real Linux CI runner would attempt to power that runner off. Instead, realMain takes the poweroff function as an injected parameter, so tests can pass a fake and assert it was called without ever executing the real syscall. poweroff_other.go (//go:build !linux) is a safe no-op stub so the package still builds and tests cleanly on non-Linux dev machines. The process-supervision logic (run, execChild) is tested against real subprocesses (sh -c, sleep) on whatever host runs go test - never inside a VM, never as PID 1, so this is safe on any developer machine or CI runner.

scripts/ci/ - used by GitHub Actions (and runnable locally)

scripts/ci/verify-elf.sh

scripts/ci/verify-elf.sh <binary> <amd64|arm64>

Confirms a binary is a static ELF64 executable for the requested architecture, using readelf: checks Class: ELF64, checks the machine type matches the requested architecture (Advanced Micro Devices X86-64/ AMD x86-64 for amd64, AArch64 for arm64), and rejects any binary with a PT_INTERP program header - the marker of a dynamically-linked executable that needs an interpreter (ld.so) at runtime. Prints ELF_VALIDATION_OK ... on success, ELF_VALIDATION_FAILURE: ... (to stderr) and a non-zero exit on any failure. Used by ci-quality.yml and scripts/ci/build-verified-layout.sh.

scripts/ci/build-verified-layout.sh

scripts/ci/build-verified-layout.sh OUTPUT BINARY ARCH

The shared "build the CLI, build a layout, verify it" sequence: builds cmd/oci-builder deterministically (-trimpath -buildvcs=true) for ARCH, runs verify-elf.sh against it, runs it against itself (-created 1970-01-01T00:00:00Z) to produce OUTPUT, then runs scripts/ci/verify-oci-layout.py OUTPUT. Used by ci-oci-validation.yml and ci-reproducibility.yml.

scripts/ci/verify-oci-layout.py

python3 scripts/ci/verify-oci-layout.py <layout-directory>

A strict, dependency-free (standard library only) re-verifier of a single-platform OCI layout - it does not trust or reuse any of the Go builder's own internal logic, it re-derives everything from the raw bytes on disk:

  • the oci-layout marker file's exact bytes,
  • index.json has schema version 2 and exactly one manifest,
  • every descriptor (manifest, config, layer): digest format, digest actually matches the blob's content, size matches, media type matches,
  • the platform is linux/amd64 or linux/arm64,
  • the config's os/architecture match the index descriptor's platform,
  • the config has exactly one rootfs.diff_id and exactly one Entrypoint element,
  • the layer decompresses, its uncompressed hash matches the declared diff_id, every tar entry name is safe (no absolute paths, no .. traversal, no duplicate names, no symlinks/hardlinks/device/FIFO entries), and the declared entrypoint path is actually present in it,
  • the blob directory contains exactly the three expected blobs - no extra, no missing, no non-regular files.

Prints OCI_VALIDATION_OK on success; any failure prints OCI_VALIDATION_FAILURE: <reason> to stderr and exits 1. This is also what Debugging Minimal Containers recommends for inspecting a layout standalone, with nothing else running.

scripts/ci/negative-oci-layout.py

python3 scripts/ci/negative-oci-layout.py <verifier-script> <good-layout-directory>

Proves the verifier isn't just optimistic - it copies a known-good layout into a temp directory seven times, applies one hostile mutation per copy, and asserts the verifier rejects every single one:

Case Mutation
missing-blob deletes one blob file
corrupted-blob appends a byte to one blob
truncated-layer truncates the layer blob to 8 bytes
invalid-index replaces index.json with an empty-manifests document
extra-blob adds a blob that isn't referenced by anything
symlink-blob replaces a blob with a symlink to /etc/passwd
invalid-media-type changes the manifest descriptor's mediaType

If the verifier ever accepts one of these (a "false negative"), this script exits non-zero with a clear message identifying which case failed to be rejected. Used by ci-oci-validation.yml and ci-quality.yml.

scripts/ci/verify-attestation-index.py

python3 scripts/ci/verify-attestation-index.py PUBLISHED_INDEX_JSON

Only used in ci-release.yml's publish job, against the published registry index (fetched via docker buildx imagetools inspect --raw, not the local build layout). Confirms the index contains at least one manifest descriptor annotated vnd.docker.reference.type: attestation-manifest with a valid sha256: digest - i.e. that BuildKit's SBOM/provenance attestation actually made it into what was published, not just that the docker buildx build --provenance=... --sbom=... command exited zero.

scripts/ci/verify-workflows.py

python3 scripts/ci/verify-workflows.py

The CI-configuration policy checker - see Testing and CI/CD for the full rule set. Notably parses YAML via a ruby -ryaml -rjson subprocess rather than a Python YAML library, specifically to avoid a PyYAML dependency. Requires ruby on PATH.

scripts/local/ - fast local development (macOS/Podman)

See Local Development (Podman/macOS) for the full walkthrough.

  • scripts/local/bootstrap.sh and scripts/local/bootstrap.ps1 build all four Go commands into an isolated binary environment:

    # Linux/macOS
    scripts/local/bootstrap.sh
    source .platform-factory/activate
    
    # Windows PowerShell
    .\scripts\local\bootstrap.ps1
    . .\.platform-factory.ps1

    The environment contains secure-oci, oci-builder, example-service, and microvm-init, an environment.json inventory, and activation scripts for POSIX, PowerShell, and CMD. It is not a Python virtualenv and does not alter the system.

    Cross-platform and installation examples:

    scripts/local/bootstrap.sh --target linux/amd64 --env dist/linux
    scripts/local/bootstrap.sh --target darwin/arm64 --env dist/macos
    scripts/local/bootstrap.sh --target windows/amd64 --env dist/windows
    scripts/local/bootstrap.sh --install "$HOME/.local"

    PowerShell uses -TargetOS, -TargetArch, -Environment, -InstallPrefix, and -Clean. POSIX uses --target, --env, --install, and --clean. Both refuse unsafe or implicit replacement, compile with CGO_ENABLED=0, inject the Git version into secure-oci, and restore or scope all Go target environment variables. --install PREFIX copies only into PREFIX/bin; neither script downloads or installs Docker, Podman, Skopeo, Cosign, Syft, QEMU, Kubernetes tooling, or other system packages.

  • scripts/local/build-macos-image.sh - builds cmd/example-service, turns it into a layout, builds a local container image from it with Podman. Touches nothing in the repository working tree.

  • scripts/local/Dockerfile - a local-only, single-Alpine-stage variant of the repository root Dockerfile (same checks, fewer layers, faster local rebuilds; same entrypoint-symlink derivation, not hardcoded).

  • scripts/local/package-dynamic-binary.sh

    scripts/local/package-dynamic-binary.sh BINARY OUTPUT [ENTRYPOINT]

    Runs ldd against BINARY, turns every real dependency line into an -extra-file DEST=SOURCE (preserving each dependency's own absolute path unchanged - the guest's dynamic linker looks for libraries at their normal system paths, so keeping the same path sidesteps having to reconstruct a library directory layout), then invokes cmd/oci-builder. Errors loudly on an unresolved dependency (ldd reporting => not found) rather than silently producing a layout that would fail at runtime. Must run on Linux, matching the binary's own architecture - ldd traces the binary by asking its own dynamic linker to report what it would load, which requires actually being able to run it (or a faithful architecture match). This is the automated version of what CLI Reference shows doing by hand, and is what makes a genuinely legacy, dynamically-linked binary bootable through this project at all - DLL hell's quieter Linux cousin, resolved once at build time instead of argued about at runtime.

scripts/microvm/ - QEMU/KVM microVM boot

See MicroVM Support for the full walkthrough.

  • scripts/microvm/lib-arch.sh - sourced, not executed, by every other script in this directory. Maps uname -m to this project's amd64/arm64 convention and the matching QEMU binary (qemu-system-x86_64/qemu-system-aarch64) - the single source of truth for that mapping, so check-kvm.sh, build-kernel.sh, and run-microvm.sh can't disagree with each other about it.

  • scripts/microvm/check-kvm.sh - non-destructive, no-sudo capability check (host OS, architecture, virtualization support, /dev/kvm existence and permissions, required binaries on PATH). Never installs or changes anything.

  • scripts/microvm/build-kernel.sh

    scripts/microvm/build-kernel.sh <amd64|arm64> OUTPUT_KERNEL_IMAGE

    Downloads Linux 6.12.98 source from cdn.kernel.org, verifies it against a pinned SHA-256 before extracting anything, merges kernel-common.config and the requested architecture's kernel-<arch>.config onto the stock arch defconfig via the kernel's own scripts/kconfig/merge_config.sh (which itself non-interactively resolves every remaining symbol via make alldefconfig - not a manual oldconfig step), then builds bzImage (amd64) or Image (arm64). Refuses to run on a non-Linux host, and refuses to build a kernel for an architecture that doesn't match the current host (kernel builds are never cross-compiled here - KVM can't accelerate a mismatched guest anyway, so there'd be no point). Skips the build entirely if the output file already exists, unless FORCE_REBUILD=1 is set. Also writes kernel.provenance.json (version, source URL, source and resolved-config checksums) and kernel.sbom.cdx.json (a CycloneDX 1.5 SBOM for the kernel component, with a deterministic UUIDv5 serial number so it stays reproducible) next to the kernel image - see MicroVM Support.

  • scripts/microvm/assemble-initramfs.sh

    scripts/microvm/assemble-initramfs.sh LAYER_TAR_GZ INIT_BINARY OUTPUT_INITRAMFS_GZ

    Extracts an OCI layer tar, adds a single init binary at /sbin/init, and packs the result into a gzip-compressed cpio (newc) initramfs - byte-for-byte reproducible given the same inputs (tar --delay-directory-restore, cpio --reproducible, a fixed sorted entry order, and the freshly-added init binary's mtime pinned to epoch 0). Factored out of run-microvm.sh so ci-microvm.yml can call it twice and diff the output.

  • scripts/microvm/kernel-common.config / kernel-amd64.config / kernel-arm64.config - Kconfig fragments. kernel-common.config forces only what every architecture needs built-in (initramfs support, virtio-net-pci, IP autoconfiguration, devtmpfs auto-mount, TTY/proc/sysfs); the per-arch files add exactly what differs (ACPI + 8250 serial on amd64; generic PCI host controller + PL011 serial on arm64). Everything else is left at the arch defconfig's own default - an unbuilt-in (=m) driver costs nothing here because the build only ever runs the kernel-image make target, never modules.

  • scripts/microvm/run-microvm.sh - the main entry point; see MicroVM Support for full usage and environment variables.

Everything else worth knowing about

Clone this wiki locally