Skip to main content

Build Pipeline Overview

info

The English user guide is currently in beta preview. Most of the documents have been automatically translated from the Japanese version. Should you find any inaccuracies, please reach out to Flatt Security.

Each Takumi image is built with a declarative Nix build. Instead of writing a sequence of commands to mutate a base image, Takumi Images declares what the image should contain and builds the image from that expression and pinned inputs.

This gives Takumi Images three properties that other features depend on.

First, Takumi Images can track every source used for the build, including sources for build tools. The only places that fetch external sources are fixed-output derivations, where the expected content hash is declared in advance.

Second, the release CI can verify that the same inputs produce the same artifact. Each build step runs in an isolated sandbox and cannot access undeclared inputs.

Third, the image contains only what is needed at runtime. Nix computes the runtime closure reachable from the entrypoint program and includes only those store paths.

The pipeline from source retrieval to publication looks like this.

Why imperative builds are uncertain

A Dockerfile build is imperative. It describes steps to run against a base image, and the result is whatever those steps produce at build time.

This means the result can change between builds. For example, what RUN apt-get install curl installs depends on the package repository state at the time the build runs, not at the time the Dockerfile was written. An image built from the same Dockerfile today and another built six months later are generally different artifacts. Afterward, there is often no record of which upstream source each file came from except by inspecting the image itself.

Takumi Images uses Nix to remove this uncertainty as a property of the build system. Nix fixes every build input by hash, including source code, build instructions, dependency packages, and build tools, and builds only from those inputs.

Image definition expressions

Each image definition is a short Nix expression. The following is the actual shape of the curl image definition: it declares the package to include and the entrypoint.

{ pkgs, mkImage }:

mkImage {
name = "takumi/curl";
contents = with pkgs; [ curl ];
entrypoint = [ "${pkgs.curl}/bin/curl" ];
}

This expression does not say how to build curl. The build rules live in the referenced package set, nixpkgs, also as expressions. Evaluating the image expression together with the package expressions produces the complete build plan.

Derivations

Nix lowers evaluated expressions into build units called derivations. A derivation mechanically describes everything needed for a build: the command, environment variables, and other derivations it depends on. You can think of it as treating a build as a pure function from inputs to output.

Build outputs are stored under /nix/store in paths that include hashes computed from the inputs.

/nix/store/vr4da99c31f7f9arw9ncws7pppsswlv7-apko-1.2.19

If any input changes, the hash changes and the output is placed at a different path. The path itself summarizes which inputs produced the artifact.

Hermetic builds

Each derivation builds inside a dedicated sandbox. This is a hermetic build. The sandbox cannot access inputs the derivation did not declare. Network access and files outside the sandbox are blocked, and timestamps such as build time are normalized. Libraries that happened to be installed on the build machine, or the time the build ran, cannot silently enter the result.

Fixed-output derivations

If the sandbox blocks network access, sources still need an entry point. That entry point is the fixed-output derivation (FOD).

src = fetchurl {
url = "mirror://gnu/hello/hello-2.12.2.tar.gz";
hash = "sha256-WpqZbcKSzCTc9BHO6H6S9qrluNE72caBm6x6nc4IGKs=";
};

An FOD may fetch from the network, but it must declare the expected content hash in advance. If the fetched bytes do not match the declared hash, the build fails. This prevents silently replaced upstream tarballs or network-path tampering from entering the build.

Because all external contact points in the build graph are FODs, Takumi Images can mechanically enumerate the URL, revision, and hash of every source that contributed to an image. See Deriving Nix Build Inputs for that enumeration.

Pinned dependency graph

The package set itself, nixpkgs, is pinned to a specific commit tarball with a hash.

nixpkgs = fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/e8273b29fe1390ec8d4603f2477357555291432e.tar.gz";
sha256 = "1k8idy9ka87c7gjb9aiqrcx4l9kwblv8fs4pwf0byjivsmbjfdhi";
};

This pin is shared across the catalog. Advancing the pin is the primary path for incorporating upstream security fixes; see Known Vulnerability Response.

Minimal runtime closure

An image contains only the runtime dependency closure reachable from the entrypoint program. Nix follows binary references mechanically to compute that closure. For example, a curl image contains paths such as:

/nix/store/<hash>-curl-8.x
/nix/store/<hash>-glibc-2.x
/nix/store/<hash>-openssl-3.x
/nix/store/<hash>-nss-cacert-...

The exact list is published as each image's SBOM. Compilers, headers, and test tools that are needed only for the build do not appear in the runtime closure and are not included in the image. This is the mechanism behind the distroless image design.

Multi-architecture builds

Each image is built independently for amd64 and arm64. One architecture's build does not reuse binaries from the other.

Because SBOMs are generated from each built image's runtime closure, their contents differ by architecture. Takumi Images attaches each architecture's own SBOM to its manifest, then combines both manifests into a multi-arch OCI index. docker pull and docker run select the matching architecture from that index automatically.

Tests

Before publication, Takumi Images tests two layers.

Package-level tests are the tests provided by each package. When an image is tested, the tests for packages included in that image run on a test build that can use a shell. Because one package can appear in many images, a package fix can be tested across all affected images at once.

Image-level tests execute the production entrypoint itself. The image is run without overriding the entrypoint, and exit status and output are checked. The same invocation is also run against the official image expected as the replacement target when such an image exists. Compatibility means the same docker run invocation behaves the same way, so the comparison does not use image-specific argument adjustments.

Reproducibility verification

Takumi Images verifies hermeticity by rebuilding from the same inputs and checking that the image tar hashes match. If they do not match, the build likely included an undeclared input, and the issue is fixed before publication.

This makes the link between recorded inputs and artifacts an observed CI result, not only a documentation claim.

Signing, evidence, and pre-publication checks

Before publication, images are checked for known package malware. Only images that pass are published with the following evidence.

The signature is keyless. GitHub Actions OIDC links the release workflow identity to the signature through the public Sigstore instance, so no long-lived signing secret is required.

SBOM (CycloneDX), provenance (SLSA), and VEX (OpenVEX) are attached to image digests with OCI referrers. SBOM and provenance attach to architecture-specific manifests, while VEX attaches to the multi-arch index.

See Attestation for verification steps and what each attestation proves.

Operations after publication

Publication is not the end state; it is one point in ongoing operation. Whenever the pin advances, all images using the affected packages are rebuilt, retested, and rescanned. That path is described in Known Vulnerability Response.

The same FOD manifest is also used for ongoing upstream source monitoring. Malicious-change monitoring is described in Upstream Malware Detection, and silent security fix detection is described in Upstream Zero-Day Fix Detection.