Live demo for Open Source Summit Mumbai.
Two independent Tekton PipelineRuns, building the same Go source code, produce byte-identical container images every time. Tekton Chains then generates SLSA-compliant provenance for each build and signs it cryptographically.
| Tool | Version | Install |
|---|---|---|
| kind | v0.20+ | brew install kind |
| kubectl | v1.27+ | brew install kubectl |
| cosign | v2.0+ | brew install cosign |
| tkn | v0.33+ | brew install tektoncd-cli |
# 1. Create a Kind cluster with Tekton Pipelines + Chains
./scripts/setup-cluster.sh
# 2. Run the demo (two builds, compare digests, verify provenance)
./scripts/run-demo.sh
# 3. Clean up
./scripts/teardown.shYou must push the demo-app/ to a public Git repository and update the
PipelineRun YAMLs with the actual commit SHA:
# Push demo-app to your repo, then:
COMMIT=$(git rev-parse HEAD)
sed -i '' "s/REPLACE_WITH_COMMIT_SHA/${COMMIT}/g" tekton/runs/run-*.yamldemo-app/
main.go Go HTTP server (~35 lines)
go.mod Go module definition
.ko.yaml ko config with reproducibility flags
tekton/
tasks/
git-clone.yaml Task: clone repo, emit CHAINS-GIT_URL/COMMIT
ko-build.yaml Task: build with ko, emit IMAGE_URL/DIGEST
pipelines/
reproducible-build.yaml Pipeline: clone → build
runs/
run-1.yaml First PipelineRun
run-2.yaml Second PipelineRun (identical params)
scripts/
setup-cluster.sh Kind + Tekton + Chains + cosign setup
run-demo.sh Execute builds, compare, verify
teardown.sh Delete the Kind cluster
Each source of non-determinism is explicitly eliminated:
| Source of Non-Determinism | How It's Eliminated | Where |
|---|---|---|
| Timestamps in image config | SOURCE_DATE_EPOCH=0 |
ko-build Task |
| Timestamps in image layers | ko sets epoch by default | ko defaults |
| Go build ID varies per machine | -ldflags='-buildid=' |
.ko.yaml |
| Filesystem paths in binary | -trimpath |
.ko.yaml |
| Debug symbols | -ldflags='-s -w' |
.ko.yaml |
| Base image drift | Pinned by sha256: digest |
.ko.yaml |
| Source code drift | Pinned git commit SHA | PipelineRun params |
| Network-fetched deps | Hermetic execution (optional) | TaskRun annotation |
| File ordering in layers | ko sorts layers by content digest | ko defaults |
Chains discovers build inputs and outputs through specially named results:
CHAINS-GIT_URL/CHAINS-GIT_COMMIT— source provenanceIMAGE_URL/IMAGE_DIGEST— output artifact identification
Add this annotation to a TaskRun to disable network access:
annotations:
experimental.tekton.dev/execution-mode: hermeticRequires enable-api-fields: "alpha" in the feature-flags ConfigMap.
The setup script enables this automatically.
Chains generates SLSA v1.0 provenance (slsa/v2alpha4 formatter) that records:
- What was built (image digest)
- From what (git URL + commit)
- By whom (builder identity)
- How (pipeline/task configuration)
PipelineRun stuck in pending:
kubectl get pipelinerun -w
kubectl describe pipelinerun <name>Chains not signing:
kubectl logs -n tekton-chains -l app=tekton-chains-controller --tail=50
kubectl get configmap chains-config -n tekton-chains -o yamlDigests don't match: Check that both runs use identical params. Inspect the build logs:
tkn pipelinerun logs <run-name>Common causes of non-reproducibility:
- Different
SOURCE_DATE_EPOCHvalues - Missing
-buildid=in ldflags - Base image referenced by tag instead of digest
- Git revision is a branch name instead of a commit SHA