Skip to main content

Buildpacks RFC 0130: Zero-Config OCI Provenance Your SOC 2 Auditor Can Actually Read

11 min readDora NodaDora Noda
Share
On this page

Every platform team that has survived a SOC 2 audit knows the worst week of the year: the auditor asks "which version of what, built from which commit, is running in production?" and the answer lives in five places — a spreadsheet, a CI log that aged out, somebody's memory of a hotfix, and two Docker labels that disagree with each other. Cloud Native Buildpacks has approved a fix for one layer of that mess. RFC 0130 ("OCI Image Annotations on Buildpacks," status: Approved) makes pack stamp every buildpack OCI artifact with standardized provenance annotations — source repo, commit, version, docs — with zero developer configuration. Heroku's maintainers frame it bluntly: "Enterprise compliance requires metadata tracking. We're making it zero-config so platforms can meet SOC2, FedRAMP, and other regulatory requirements without manual overhead."

Here is the core deliverable up front: exactly what RFC 0130 stamps, where each value comes from, and what compliance question it answers.

AnnotationZero-config sourceAuditor question it answers
org.opencontainers.image.sourcegit origin of the buildpack repo"Where did this component come from?"
org.opencontainers.image.revisiongit commit hash"Which exact commit produced this?"
org.opencontainers.image.titlename in buildpack.toml"What is this component called?"
org.opencontainers.image.versionversion in buildpack.toml"Which released version is this?"
org.opencontainers.image.urlhomepage in buildpack.toml"Where are the docs for this version?"
org.opencontainers.image.descriptiondescription in buildpack.toml"What does this component do?"

One scope note before we go further, because the fine print matters: RFC 0130 stamps buildpack artifacts — the builders and buildpacks themselves, which are OCI images — via pack buildpack package. The annotations on your app images are a related but separate story, and that half is still unfinished (more in the "what it can't prove" section). And the headline caveat: annotations are unsigned inventory metadata. They tell an auditor what with unprecedented consistency; proving it really happened that way still needs signed attestations. This post covers both halves honestly: what the manual compliance pass looked like before, what RFC 0130 replaces, what gap remains, and the concrete checklist a git-push PaaS should ship.

What RFC 0130 actually stamps (and on what)

The RFC is short and specific. When pack buildpack package produces a buildpack OCI artifact, it should set six OCI pre-defined annotation keys. The git-derived pair — source from the git origin, revision from the commit hash — is captured automatically at package time. The other four come straight from fields the buildpack author already maintains in buildpack.toml, so nobody fills in a new form: name maps to title, version to version, and the optional homepage and description to url and description.

The RFC's worked example is the Paketo Buildpack for Java 13.0.1, which would carry:

  • org.opencontainers.image.source: https://github.com/paketo-buildpacks/java
  • org.opencontainers.image.revision: 09747b1df0a56aea74ce9b01af89df6feb1fc50a
  • org.opencontainers.image.title: Paketo Buildpack for Java
  • org.opencontainers.image.version: 13.0.1
  • org.opencontainers.image.url: https://paketo.io/docs/howto/java
  • org.opencontainers.image.description: A Cloud Native Buildpack with an order definition suitable for Java applications

Why standardize this at the pack level instead of letting each buildpack add its own labels? The RFC's answer is consistency and maintainability: per-buildpack snowflake metadata means every consumer writes per-buildpack parsing logic, which in practice means nobody parses it at all. Standard keys mean tools can rely on them — and tools already do. The RFC names Snyk and Renovate as consumers of these exact annotation keys: Snyk reads them to identify what it is scanning, Renovate reads them to find the source repo it should watch for updates. A PaaS that ships RFC 0130-compliant builders gets that whole tooling ecosystem's understanding for free.

Note the deliberate softness in the spec language: pack should set these annotations, not must. Buildpacks packaged with older pack versions remain valid. That is the right call for adoption — no flag day — but it means a platform can't assume annotations exist until it controls the builder version fleet-wide. Pin your builders, then assert.

What it replaces: the annual manual compliance pass

Picture the before state on a typical self-hosted PaaS. The auditor's inventory request lands — "list every component in the build chain with version and provenance" — and a platform engineer spends days reconstructing it.

Which builder version built last quarter's images? The builder tag says jammy-base, but which release of it? Where is its source repo — was it Paketo's, a fork with a patch, or a fully custom builder? What changed between the version running in March and the one running now — is there a changelog, and where? Each answer requires a different hunt: registry tags, CI configs, someone's fork, a release page somebody has to remember exists.

RFC 0130 collapses that hunt into crane config output. Source, revision, version, and docs URL travel on the artifact itself, in keys every OCI-native tool already understands. The "after" version of the audit week looks like this: the engineer runs an inventory script over the builder fleet, every row comes back with a source URL, a pinned commit, a semver version, and a link to that version's docs and release notes. The auditor's follow-up — "show me what changed between these two versions" — resolves to a GitHub compare link constructed from two source + revision pairs, not a week of archaeology.

This is also where the SOC 2 / FedRAMP framing stops being marketing. SOC 2's change-management criterion (CC8.1) wants evidence that system changes are authorized, tested, and documented; FedRAMP's CM-8 wants a maintained inventory of system components with version information. Neither control cares about your spreadsheet — both care that component identity and version are captured systematically and reproducibly. Annotations generated deterministically from git + buildpack.toml at package time are exactly that kind of evidence for the build-chain layer: not a human's recollection written down in April, but metadata the toolchain emitted when the artifact was created.

What it can't prove: annotations are claims, not evidence

Now the honest half, because "zero-config compliance" oversells if you stop reading at the Heroku quote. Two gaps remain, and a platform that papers over them will have a bad audit.

Gap 1: annotations are self-asserted. An OCI annotation is a string on an artifact. Nothing cryptographically binds org.opencontainers.image.revision: 09747b1… to the bytes it describes — anyone who can push to your registry can stamp any commit hash on any image. For inventory and tooling (Snyk, Renovate, your own fleet scripts), self-assertion is fine. For proving to an auditor — or a tenant — that image X was actually built from commit Y on infrastructure you trust, it is not.

That proof needs signed provenance: an SLSA attestation over the build, verifiable against a key and ideally a transparency log. The buildpacks ecosystem already has this layer in progress: kpack has generated SLSA provenance since v0.13.0 (per its SLSA RFC), and VMware Tanzu Build Service signs those attestations with cosign keys. RFC 0130 is the human- and tool-readable index card; SLSA is the notarized deed. A PaaS needs both, and should say so.

Gap 2: your app images aren't covered yet. RFC 0130 annotates buildpack artifacts. The thing your tenants actually deploy — the app image from pack build — currently gets its provenance metadata (BP_OCI_SOURCE, BP_OCI_REVISION env vars) written as Docker labels in the image config, not as OCI annotations on the manifest or index. That distinction is not pedantry: per the OCI spec and Docker's own docs, annotations describe image components (manifests, indexes, descriptors) while labels describe Docker resources — and tools increasingly look only at the annotation layer.

pack issue #2497 (open, filed December 2025) documents exactly this: manifest-inspection tools like Kargo can't see buildpack provenance because it's in config labels, and docker buildx imagetools inspect --raw shows a manifest with no annotations at all. The proper fix is RFC PR #196, which would put real manifest/index annotations on built app images — but it has been in draft since December 2021, blocked on an architectural decision about where in the build lifecycle annotations get attached. Nearly five years in draft is not "coming soon"; it is a gap your platform should plan around, for example by having the platform layer itself attach manifest annotations at publish time rather than waiting for pack to do it.

Neither gap diminishes RFC 0130 — scoping it to buildpack artifacts is what let it get approved while the app-image RFC sits in draft. But a PaaS marketing page that says "provenance built in" while shipping only RFC 0130 is selling the index card as the deed. Ship both layers, label them correctly, and the auditor trusts you more, not less.

The Dockerfile-first comparison

To see why "provenance is a default, not an add-on" is a genuine differentiator, compare what each platform type must do to produce the same six fields on every image it builds.

Provenance fieldBuildpacks + RFC 0130Dockerfile-first platform
Source repoAutomatic from git originPer-repo LABEL org.opencontainers.image.source=…, usually via CI build-args somebody must wire up
Commit hashAutomatic from git HEADSame — a build-arg plumbed through every pipeline, silently empty when someone builds locally
Component name/versionAutomatic from buildpack.tomlHand-maintained LABELs, drifting from actual releases
Docs URL / descriptionAutomatic from homepage/descriptionAlmost nobody bothers; docs links live in a wiki
EnforcementUpgrade pack, pin builders, done fleet-wideLint every Dockerfile in every tenant repo, forever
Tool consumptionSnyk/Renovate read it todayWorks if every tenant got the labels right, which they didn't

The Dockerfile column isn't a strawman — conscientious teams do wire up org.opencontainers.image.* labels in CI, and Docker Buildx makes attaching manifest annotations straightforward. The problem is the multiplication: every repo, every pipeline, every tenant must independently get it right, and the failure mode is silent (an empty $COMMIT_SHA build-arg produces a valid image with a lying label). Buildpacks invert the default: the toolchain emits provenance unless someone actively strips it, and the platform upgrades the toolchain once for all tenants. For a PaaS operator, that is the difference between a control you can assert to an auditor and a hope you document as a tenant responsibility.

What a git-push PaaS should ship

RFC 0130 is one layer of a three-layer provenance story. Here is the concrete checklist, with each item mapped to the control language auditors actually use:

  1. RFC 0130-compliant builders, pinned fleet-wide. Upgrade pack, rebuild builders, and assert annotation presence in your builder-promotion pipeline (fail promotion if source/revision/version are missing). Maps to: SOC 2 CC8.1 (systematic change/version evidence for build-chain components), FedRAMP CM-8 (component inventory with version info).
  2. Native SBOM on every app build, in SPDX or CycloneDX. Buildpacks already generate build-time SBOMs in CycloneDX, SPDX, and Syft formats — each buildpack contributes the dependencies it provided — retrievable with pack sbom download. Persist the SBOM next to the image and serve it to tenants. Maps to: EO 14028's SBOM expectations and the CISA secure-software attestation's artifact requirements, plus FedRAMP SI-7 (integrity information for deployed software).
  3. Signed SLSA provenance for the build itself. Follow the kpack pattern: emit SLSA provenance describing the builder, buildpacks, stack, and source, and sign it with cosign keys your tenants can verify. This is the layer that closes Gap 1 — it turns "the image says it came from commit Y" into "the platform cryptographically asserts it built this image from commit Y." Maps to: SLSA Level 2+ provenance (the bar federal attestation regimes point at), SOC 2 CC7.2 (monitoring/detecting unauthorized change — a signature that fails verification is the detection).

Note what this checklist deliberately does not claim: none of it proves the build ran on infrastructure the tenant trusts — that is a deployment-topology question (private builders, air-gapped fleets) beyond any metadata format. What the three layers together prove is complete lineage — what, from where, from which commit, built with which toolchain — with the outer two layers signed. That is what "FedRAMP-grade provenance without a manual compliance pass" can honestly mean: the evidence is emitted by the toolchain at build time, every build, instead of reconstructed by a human at audit time.

The broader lesson generalizes past buildpacks. Compliance evidence rots in direct proportion to the human effort required to produce it — spreadsheets drift, CI logs expire, memories fade. Metadata the toolchain emits deterministically, on the artifact, in standard keys, does not rot. RFC 0130 moves one load-bearing layer of the build chain from the first category to the second. The platforms that win regulated tenants will be the ones that finish the job: annotations by default, SBOMs attached, attestations signed, all without the tenant configuring anything.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex