ClearCutt
Hardened Image Blueprint

Downstream Images

Rebasable App Lifecycle

Build one signed application artifact onto a ClearCutt base, compare a patched base, and rebase later from CI without rebuilding the app layer. The examples below cover every supported language family and keep the CLI flow aligned with the repository guide.

Artifact Contract

  • app build packages one prebuilt file into one OCI layer.
  • Native binaries should pass --executable.
  • --entrypoint must be OCI JSON exec form.
  • app diff-base enforces same runtime family and major/minor line.
  • app rebase --sign --attest emits signed evidence only after developer signature verification succeeds.

If a service needs a publish directory or sidecar runtime files, use a normal Containerfile and run clearcutt certify on the finished image.

Supported Stack Families

Stack Versions Base ids Artifact
Java 21, 25 java21-* / java25-* Fat JAR
Node.js 22, 24 node22-* / node24-* Bundled server module
Python 3.13, 3.14 python3.13-* / python3.14-* PEX, shiv, or zipapp
Go 1.25, 1.26 go1.25-* / go1.26-* Linux executable
.NET 8, 10 dotnet8-* / dotnet10-* Single-file publish
Rust 1.95 rust1.95-* Linux executable
C/C++ GCC 15 cc15-* Static executable
Core/static LTS coreLTS-* Static utility

Common Rebase Loop

Each stack example sets BASE_ID and PATCHED_BASE. The source image, rebased tag, developer signer, and rebase-engine signer stay the same across stacks.

shared environment shell
export APP_IMAGE="ghcr.io/acme/payments-api:1.0.0"
export REBASED_IMAGE="ghcr.io/acme/payments-api:1.0.0-rebased"
export DEV_SIGNER="https://github.com/acme/payments/.github/workflows/release.yml@refs/heads/main"
export ENGINE_SIGNER="https://github.com/acme/platform/.github/workflows/rebase.yml@refs/heads/main"
sign, diff, rebase, verify shell
cosign sign --yes "$APP_IMAGE"

clearcutt app diff-base \
  --image "$APP_IMAGE" \
  --candidate-base "$PATCHED_BASE" \
  --candidate-base-id "$BASE_ID" \
  --fail-on-incompatible

clearcutt app rebase \
  --image "$APP_IMAGE" \
  --candidate-base "$PATCHED_BASE" \
  --candidate-base-id "$BASE_ID" \
  --tag "$REBASED_IMAGE" \
  --dev-identity "$DEV_SIGNER" \
  --sign \
  --attest

cosign verify "$REBASED_IMAGE" \
  --certificate-identity "$ENGINE_SIGNER" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

cosign verify-attestation "$REBASED_IMAGE" \
  --type https://clearcutt.dev/attestations/rebase/v1 \
  --certificate-identity "$ENGINE_SIGNER" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Stack Examples

Swap the versioned base id to the matching supported line when your service targets a newer runtime.

CLI reference

Java

Use a single executable JAR and JSON exec form.

java21-* / java25-*
./mvnw -DskipTests package
cp target/payments-api-*-all.jar target/app.jar

export BASE_ID="java21-distroless"
export PATCHED_BASE="ghcr.io/northcutted/clearcutt/clearcutt-java21:distroless-v0.2.2"

clearcutt app build \
  --base "$BASE_ID" \
  --artifact target/app.jar \
  --dest /workspace/app.jar \
  --entrypoint '["java","-jar","/workspace/app.jar"]' \
  --image "$APP_IMAGE"

Node.js

Bundle runtime files into one server artifact.

node22-* / node24-*
npm ci
npx esbuild src/server.ts \
  --bundle \
  --platform=node \
  --target=node22 \
  --format=esm \
  --outfile=dist/server.mjs

export BASE_ID="node22-distroless"
export PATCHED_BASE="ghcr.io/northcutted/clearcutt/clearcutt-node22:distroless-v0.2.2"

clearcutt app build \
  --base "$BASE_ID" \
  --artifact dist/server.mjs \
  --dest /workspace/server.mjs \
  --entrypoint '["node","/workspace/server.mjs"]' \
  --image "$APP_IMAGE"

Python

Package app and dependencies into one runnable file.

python3.13-* / python3.14-*
python -m pip install --upgrade build pex
pex . \
  -m payments_api.main \
  -o dist/payments-api.pyz

export BASE_ID="python3.13-distroless"
export PATCHED_BASE="ghcr.io/northcutted/clearcutt/clearcutt-python3.13:distroless-v0.2.2"

clearcutt app build \
  --base "$BASE_ID" \
  --artifact dist/payments-api.pyz \
  --dest /workspace/payments-api.pyz \
  --entrypoint '["python","/workspace/payments-api.pyz"]' \
  --image "$APP_IMAGE"

Go

Mark the embedded binary executable.

go1.25-* / go1.26-*
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
  go build -trimpath -ldflags="-s -w" \
  -o dist/payments-api ./cmd/payments-api

export BASE_ID="go1.25-distroless"
export PATCHED_BASE="ghcr.io/northcutted/clearcutt/clearcutt-go1.25:distroless-v0.2.2"

clearcutt app build \
  --base "$BASE_ID" \
  --artifact dist/payments-api \
  --dest /workspace/payments-api \
  --entrypoint '["/workspace/payments-api"]' \
  --executable \
  --image "$APP_IMAGE"

.NET

Use Containerfile + certify when sidecar files are required.

dotnet8-* / dotnet10-*
dotnet publish src/Payments.Api/Payments.Api.csproj \
  -c Release \
  -r linux-x64 \
  --self-contained false \
  -p:PublishSingleFile=true \
  -p:PublishTrimmed=true \
  -o out

mkdir -p dist
cp out/Payments.Api dist/payments-api

export BASE_ID="dotnet8-distroless"
export PATCHED_BASE="ghcr.io/northcutted/clearcutt/clearcutt-dotnet8:distroless-v0.2.2"

clearcutt app build \
  --base "$BASE_ID" \
  --artifact dist/payments-api \
  --dest /workspace/payments-api \
  --entrypoint '["/workspace/payments-api"]' \
  --executable \
  --image "$APP_IMAGE"

Rust

A musl target is the most portable distroless shape.

rust1.95-*
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
mkdir -p dist
cp target/x86_64-unknown-linux-musl/release/payments-api dist/payments-api

export BASE_ID="rust1.95-distroless"
export PATCHED_BASE="ghcr.io/northcutted/clearcutt/clearcutt-rust1.95:distroless-v0.2.2"

clearcutt app build \
  --base "$BASE_ID" \
  --artifact dist/payments-api \
  --dest /workspace/payments-api \
  --entrypoint '["/workspace/payments-api"]' \
  --executable \
  --image "$APP_IMAGE"

C/C++

Use the dev tier when you want GCC, CMake, or Ninja in a builder.

cc15-*
cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_EXE_LINKER_FLAGS="-static"
cmake --build build --target payments-api

export BASE_ID="cc15-distroless"
export PATCHED_BASE="ghcr.io/northcutted/clearcutt/clearcutt-cc15:distroless-v0.2.2"

clearcutt app build \
  --base "$BASE_ID" \
  --artifact build/payments-api \
  --dest /workspace/payments-api \
  --entrypoint '["/workspace/payments-api"]' \
  --executable \
  --image "$APP_IMAGE"

Core/static

Best for already-static binaries that only need CA certificates.

coreLTS-*
zig cc -target x86_64-linux-musl -O2 -static \
  -o dist/worker src/worker.c

export BASE_ID="coreLTS-distroless"
export PATCHED_BASE="ghcr.io/northcutted/clearcutt/clearcutt-corelts:distroless-v0.2.2"

clearcutt app build \
  --base "$BASE_ID" \
  --artifact dist/worker \
  --dest /workspace/worker \
  --entrypoint '["/workspace/worker"]' \
  --executable \
  --image "$APP_IMAGE"

CI Identity Model

The developer release workflow signs the original app image. A separate rebase workflow verifies that source signature, performs the base swap, signs the rebased image, and attaches the rebase predicate. Admission policy should pin the rebase workflow identity and inspect the predicate fields.

Required predicate fields include rebaseDecision: allowed, developerSignatureVerified: true, and the expected developer identity and issuer.

Multi-Architecture Notes

The examples build the platform-specific image for the artifact you pass in. For multi-arch releases, build one artifact per platform and assemble an image index with your release tooling; app rebase can rebase indexes whose child images carry the ClearCutt rebase labels.