Phase 1 research: Go build type#180
Conversation
TomHennen
left a comment
There was a problem hiding this comment.
Phase 1 research review. Comment-only — leaving the call to the human.
Summary
The doc is solid. The most load-bearing claims (the builder_go_slsa3.yml seam-inversion finding, slsa-verifier's Pattern A use, goreleaser's neither-A-nor-B release, sum.golang.org as integrity backbone) all check out against primary sources. The matrix-fan-out observation for multi-binary / multi-platform Pattern A vs. Pattern B is sharp and well-grounded. Below are mostly polish items; nothing I'd block on.
Verified upstream (so you don't have to re-check)
builder_go_slsa3.ymldoes own the build. Inputs areconfig-file,evaluated-envs,go-version,go-version-file,upload-assets,upload-tag-name,prerelease,private-repository,draft-release— nohashes/base64-subjectsinput. Outputs arego-binary-nameandgo-provenance-name. The job graph isrng → detect-env → builder → build-dry → build → provenance → upload-assets, intentionally linear and sealed; there is no caller hook betweenbuildandprovenance. The doc's seam-inversion claim is correct.- slsa-verifier release.yml uses Pattern A with the 6-cell
os × archmatrix and config files at.slsa-goreleaser/${{ matrix.os }}-${{ matrix.arch }}.yml, pinned@v2.0.0, withid-token: write,contents: write,actions: readon the builder job. Matches the doc. - goreleaser's own
release.ymluses neither Pattern A nor Pattern B — it usesactions/attest@v4(notslsa-github-generator, notactions/attest-build-provenance), plussigstore/cosign-installer,anchore/sbom-action/download-syft, andgoreleaser/goreleaser-action. The doc's claim is right but understated (see point 2 below). sum.golang.orgis a Trillian-backed Merkle log ofgo.sum-shaped lines (SHA-256 of source +go.mod) and serves as the integrity backbone forgo install/go getfrom a tag. Doc characterization matches the upstream announcement and proposal 25530.generator_generic_slsa3.ymlconsumesbase64-subjectsbuilt fromdist/checksums.txtin the goreleaser-example repo — same shape wrangle's python uses.
Suggested edits (in rough priority)
-
Own the Phase 1 deferral, not just the #171 deferral. The runbook's Phase 1 says (literal quote): "When multiple tools are first-class, document the detection rule and ship a fixture for each variant. When one is dominant, document why you chose it." The doc does neither — it explicitly says "This document does not pick." That is defensible because the runbook itself flags Go as the awkward case ("'build tool' may not be the right framing"), but the doc currently rolls the deferral up entirely under #171. One sentence acknowledging that the Phase 1 "pick the canonical tool" question is being deferred — not just the contract question — would make the scope discipline honest. Otherwise a future reader could fairly say Phase 1 didn't finish its job.
-
Pattern C is hedge-y where it could be precise. Section "Reference workflow patterns" says of
goreleaser/goreleaser's release.yml: "relies on goreleaser's built-in Sigstore signing and (presumably) GitHub artifact attestations." I verified the file: it usesactions/attest@v4(current goreleaser-recommended), invoked twice — once overdist/checksums.txt, once over Docker digests. That's worth saying outright; the doc's whole purpose is to be the verified-source reference. While you're there, the same ambiguity is in the "Canonical build tool(s)" section: "plus presumably GitHub artifact attestations." Same fix. -
The release_gate claim conflates two things. The seam analysis says: "there is no place inside wrangle's reusable workflow to insert
go test,syft-driven SBOM generation, oractions/release_gate-style gating between 'build' and 'provenance'." True for the inline seam — butrelease_gateis currently used to decide whether to invoke the provenance reusable workflow at all (if: ${{ needs.gate.outputs.should-release == 'true' }}), and that pattern works fine forbuilder_go_slsa3.ymltoo (gate theuses:invocation). Pattern A doesn't supportpull_requestupstream anyway, so the gating story isn't broken — only the inline seam where SBOM/test would slot in is. Calling out that nuance would tighten the finding. -
Reproducibility is the unstated assumption behind "tests don't certify the SLSA-built bytes." Section "#171 Notes" point 2 makes the right finding but assumes implicitly that the trusted builder produces deterministic output from source. If a Go build with timestamps/embedded build info isn't reproducible, the wrangle-tested-bytes-vs-builder-produced-bytes gap is wider than the doc suggests, and the trade-off in option (ii) is correspondingly worse. Worth a sentence — or at least a flag that reproducibility is an implicit prerequisite for option (ii) to be a small loss rather than a large one.
-
Mode 3 dismissal skates past one case. "Mode 3 is 'the maintainer hasn't adopted wrangle yet' rather than a separate shape" is mostly right, but it elides adopters who might want SLSA-shaped source attestations — "this tag was reviewed/tested/scanned by my CI" — without producing a binary. Mode 2 isn't quite served by
sum.golang.orgfor that use case (sumdb proves source integrity, not anything CI did). Minor; the doc's resolution is still defensible because that adopter wants something orthogonal to a build type. -
Stale
docker_best_practices.mdreference is well-handled. The note that the cyclonedx-gomod listing predates python's syft adoption and "should not be read as a contract" is the right call. If you wanted to be belt-and-suspenders, a one-line update to that file in a follow-up PR would prevent the next agent reading it from re-litigating.
Things to call out positively
- The seam-inversion finding (Pattern A inverts wrangle's "composite owns build, reusable workflow owns provenance" decomposition) is the right load-bearing finding for #171, and the framing as three honest paths — match python / adopt builder / two variants — is exactly the shape #171 needs.
- Multi-binary / cross-compilation matrix observations are sharp: Pattern A requires fan-out at the wrangle-reusable-workflow level (slsa-verifier does 6 cells), Pattern B handles it inside one goreleaser invocation. That's a real ergonomic delta and the doc surfaces it cleanly.
- Predicate version v0.2 vs v1 /
actions/attest-build-provenancetension is correctly identified and consistent with container SPEC's existing stance. The explicit "the Go SPEC should not silently endorseactions/attest-build-provenance" is the right stake to drive. - SBOM-tool reuse (syft already in wrangle, cyclonedx-gomod is a stale prediction) is correctly grounded.
Naming three patterns without picking — is it appropriate?
Yes for #171's contract decision (the doc isn't the right place for that). Mostly yes for Phase 1's "canonical tool" decision, with the caveat in point 1 — the doc should make the deferral explicit rather than implicit. The runbook's Go caveat ("build tool may not be the right framing") gives this PR cover that npm/generic won't have, so the asymmetry is fine here as long as it doesn't leak into those sister PRs.
Bottom line
Ready for human review. The findings are accurate against upstream, the framing is honest about what Go costs the existing wrangle contract, and the three-modes resolution of the binary-vs-tag question is the right read. The polish items above would tighten it but none are blockers.
| 2. **Pure libraries (no `main` package).** There is no binary, ever. The "artifact" is the tagged source tree as fetched by `go get` / `go install`. Integrity comes from Go's checksum database (`sum.golang.org`), which is itself a tamper-evident Merkle-tree transparency log over `(module@version, hash(source))` pairs ([Go module mirror launch announcement](https://go.dev/blog/module-mirror-launch); [proposal 25530](https://go.googlesource.com/proposal/+/master/design/25530-sumdb.md)). This is integrity-of-source, not build provenance — they answer different questions — but it is not nothing. | ||
| 3. **`go install <repo>@<tag>` for CLI tools whose maintainers chose not to run a release pipeline.** This is mode 1 minus the release pipeline. The "missing build" the runbook gestures at is almost always a maintainer-side choice not to run CI release for a project that *could* release binaries; it is rarely a property of the project's source. | ||
|
|
||
| **Resolution.** Wrangle's Go build type should target mode 1 as primary. Mode 2 is out of scope for an artifact-producing template — wrangle has no surface to attach to when there is no build job, and `sum.golang.org` already provides what mode-2 consumers verify. Mode 3 is "the maintainer hasn't adopted wrangle yet" rather than a separate shape; once they do, they're in mode 1. The runbook's framing — "Go may not fit the artifact-producing template" — overstates the gap. *Projects that adopt wrangle* are by definition projects that want a CI release pipeline; for those, the artifact-producing template applies, with the caveats below. |
There was a problem hiding this comment.
Let me push back and then you tell me what you think.
Even if wrangle doesn't build the thing (e.g. because it's a library or a go install pattern), wrangle can still produce SBOMs, run tests, run vulnscans, run linters, and it can do that on the release "artifact" it just so happens that artifact is built elsewhere? So maybe there's not proveneance (or maybe there could be IDK), but those other things seem valuable still?
These all seem worth supporting. I also bet wrangle would be an easy way for people that only do go install now to support binary releases? Maybe the right approach would be to treat them as separate build types?
|
|
||
| The composite owns the build, emits hashes, and the SLSA generator runs in a separate job consuming those hashes. SBOM generation, test execution, the `release_gate` job, and the verify job all slot in around this seam. | ||
|
|
||
| `builder_go_slsa3.yml` does not have a hashes input — it has a `config-file` input and performs the build itself. There is no place inside wrangle's reusable workflow to insert `go test`, `syft`-driven SBOM generation, or `actions/release_gate`-style gating between "build" and "provenance," because by the time wrangle hands off to the builder, the build has not yet happened, and after the builder returns, the build has shipped to a release. The build-then-attest seam wrangle relies on for python isn't there. |
There was a problem hiding this comment.
This just sounds similar to what happens for container images?
Also, can we not go test before we start the build?
| There are three honest paths, and #171 has to pick one: | ||
|
|
||
| - **(i) Match python.** Use Pattern B (`goreleaser` or `go build` inside the composite, `generator_generic_slsa3.yml` for provenance). The contract stays uniform; SBOM/test/gate/verify slot in identically. Loss: the Go-specific isolated-builder property. | ||
| - **(ii) Adopt `builder_go_slsa3.yml`.** Wrangle's reusable workflow becomes a coordinator that runs SBOM/test in one job, then delegates to `builder_go_slsa3.yml` for the actual build+provenance, then runs verify. Test/SBOM run on a *separate checkout* of the same source — they don't certify the same byte-for-byte build the SLSA builder produces. The contract for Go diverges from python in a visible way. |
There was a problem hiding this comment.
I thought go builds were reproducible? Will the digest be the same in the end?
|
|
||
| - **(i) Match python.** Use Pattern B (`goreleaser` or `go build` inside the composite, `generator_generic_slsa3.yml` for provenance). The contract stays uniform; SBOM/test/gate/verify slot in identically. Loss: the Go-specific isolated-builder property. | ||
| - **(ii) Adopt `builder_go_slsa3.yml`.** Wrangle's reusable workflow becomes a coordinator that runs SBOM/test in one job, then delegates to `builder_go_slsa3.yml` for the actual build+provenance, then runs verify. Test/SBOM run on a *separate checkout* of the same source — they don't certify the same byte-for-byte build the SLSA builder produces. The contract for Go diverges from python in a visible way. | ||
| - **(iii) Two variants, like python's pip vs. uv.** Adopters who care about the strongest L3 isolation get the `builder_go_slsa3.yml` flavor; adopters who want the full wrangle pipeline (SBOM-of-the-built-artifact, integrated test gating) get the Pattern B flavor. Doubles surface area, but each variant is internally clean. |
There was a problem hiding this comment.
We should pick one. We need to reduce cognitive overload.
I'm not sure I agree that one is stronger than the other though. Don't they all run in a wrangle-defined reusable workflow? Isn't the strength the same as for the other build types?
| - **(ii) Adopt `builder_go_slsa3.yml`.** Wrangle's reusable workflow becomes a coordinator that runs SBOM/test in one job, then delegates to `builder_go_slsa3.yml` for the actual build+provenance, then runs verify. Test/SBOM run on a *separate checkout* of the same source — they don't certify the same byte-for-byte build the SLSA builder produces. The contract for Go diverges from python in a visible way. | ||
| - **(iii) Two variants, like python's pip vs. uv.** Adopters who care about the strongest L3 isolation get the `builder_go_slsa3.yml` flavor; adopters who want the full wrangle pipeline (SBOM-of-the-built-artifact, integrated test gating) get the Pattern B flavor. Doubles surface area, but each variant is internally clean. | ||
|
|
||
| This document does not pick. The point for #171 is that Go is the first build type where an ecosystem-native SLSA L3 path *inverts* wrangle's "composite owns build, reusable workflow owns provenance" seam. Python and container both decompose cleanly along that seam (the SLSA generator consumes the composite's outputs); `builder_go_slsa3.yml` does not. Whether the contract bends to accommodate or whether wrangle accepts a weaker isolation guarantee for Go is the trade-off. |
There was a problem hiding this comment.
Honestly, why are you mentioning 171 at all. I thought this doc was to reference best practices within the ecosystem (at least for now).
| - **(ii) Adopt `builder_go_slsa3.yml`.** Wrangle's reusable workflow becomes a coordinator that runs SBOM/test in one job, then delegates to `builder_go_slsa3.yml` for the actual build+provenance, then runs verify. Test/SBOM run on a *separate checkout* of the same source — they don't certify the same byte-for-byte build the SLSA builder produces. The contract for Go diverges from python in a visible way. | ||
| - **(iii) Two variants, like python's pip vs. uv.** Adopters who care about the strongest L3 isolation get the `builder_go_slsa3.yml` flavor; adopters who want the full wrangle pipeline (SBOM-of-the-built-artifact, integrated test gating) get the Pattern B flavor. Doubles surface area, but each variant is internally clean. | ||
|
|
||
| This document does not pick. The point for #171 is that Go is the first build type where an ecosystem-native SLSA L3 path *inverts* wrangle's "composite owns build, reusable workflow owns provenance" seam. Python and container both decompose cleanly along that seam (the SLSA generator consumes the composite's outputs); `builder_go_slsa3.yml` does not. Whether the contract bends to accommodate or whether wrangle accepts a weaker isolation guarantee for Go is the trade-off. |
There was a problem hiding this comment.
I'm not sure I really understand how this differs from the container solution?
| The Go ecosystem has two viable SBOM generators: | ||
|
|
||
| - **`syft`** ([anchore/syft](https://github.com/anchore/syft)). Wrangle already uses syft for the python build type, with a Cosign-keyless-verified install (`tools/syft/install.sh`). It produces SPDX natively and works against both source trees and compiled binaries. Reusing it for Go is the lowest-friction path: no new install script, no new Cosign verification dance, no new checksum to track. Per [the SBOM-tools landscape](https://sbomgenerator.com/guides/go), syft is the de facto choice for Go SBOM generation in 2026. | ||
| - **`cyclonedx-gomod`** ([CycloneDX/cyclonedx-gomod](https://github.com/CycloneDX/cyclonedx-gomod)). Tighter Go integration (uses the Go toolchain to introspect modules), produces CycloneDX natively. Listed in wrangle's [`docs/docker_best_practices.md`](../../../docs/docker_best_practices.md) Go row — but that table predates the python build type's syft adoption, and the listing should not be read as a contract. CycloneDX → SPDX conversion exists but is lossy; the cleanest SPDX path for a Go project is "run syft directly," not "run cyclonedx-gomod and convert." |
There was a problem hiding this comment.
I'm wondering if wrangle should support both or whatever the ecosystem prefers? I dont' know how much it matters.
|
|
||
| There is no Go-specific package registry to publish to. `pkg.go.dev` is a documentation index, not a registry; it auto-discovers tagged versions from `proxy.golang.org`'s view of GitHub (and other VCS hosts). No publish action, no token, no auth — just push the tag. (This is also why mode 2 / mode 3 from the binary-vs-tag discussion are even possible: there is no separate publish step to skip.) | ||
|
|
||
| Container images via [`ko`](https://ko.build/) are a parallel publish path some Go projects use (small distroless images built from Go binaries without a Dockerfile). Out of scope for the Go build type — `ko`-using projects already have a container build need, and wrangle's container build type is the right home for that. |
There was a problem hiding this comment.
Is it the right tool for the job? Would projects using ko be able to use our container build solution?
|
|
||
| In the Go ecosystem, "the attestation" almost always means SLSA L3 provenance, signed via Sigstore keyless OIDC, recorded in Rekor. Both Pattern A (`builder_go_slsa3.yml`) and Pattern B (`generator_generic_slsa3.yml`) produce this; both emit `slsa.dev/provenance/v0.2` predicates today. There is no Go-ecosystem-native attestation analogous to npm provenance or PEP 740 — Go binaries' attestations *are* SLSA provenance. The transparency log is Rekor for the provenance, plus `sum.golang.org` for *source* integrity (which is independent — `sum.golang.org` doesn't know or care that a binary was produced). | ||
|
|
||
| Cosign keyless signing of binaries is a complementary layer (signs the binary digest directly, like wrangle's container build type does for image digests). `goreleaser` integrates with cosign natively; the [Cosign v3 upgrade post](https://goreleaser.com/blog/cosign-v3/) covers the current `--bundle`-style signing. This layer is ecosystem-supported but less universally adopted than SLSA provenance — `slsa-verifier`'s release workflow attests but does not separately Cosign-sign its binaries (the SLSA provenance bundle already includes a Sigstore-signed in-toto envelope), while goreleaser's own release workflow does. Whether wrangle's Go build type ships Cosign signing as a default, an opt-in, or not at all is a contract decision for #171, not a Phase 1 finding. |
There was a problem hiding this comment.
We should probably support cosign keyless signing too. We probably don't need to care to care about making the implementation match exactly as long as the interface is the same and it's secure (very important!) and doesn't add maintenance burden.
|
|
||
| #### Pattern A (alternative, not picked) | ||
|
|
||
| `slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml` is a Go-specific *builder* that performs the build itself inside an isolated reusable workflow, driven by a `.slsa-goreleaser.yml` config file (the filename predates goreleaser conventions; the format is the SLSA builder's own). `slsa-verifier`'s [release workflow](https://github.com/slsa-framework/slsa-verifier/blob/main/.github/workflows/release.yml) demonstrates the canonical 6-cell `os × arch` matrix. It exists as an alternative for adopters who specifically want `.slsa-goreleaser.yml`-driven isolated builds. The cost is wrangle's seam: `builder_go_slsa3.yml` has no `hashes` input — the build, the SBOM, the test, and the provenance all happen inside one sealed reusable workflow with no caller hook between them, so wrangle's `syft` / `go test` / `release_gate` steps either run on a separate checkout (different bytes than the builder produces) or don't run at all. Adopters who want that trade can opt in via a separate variant in a later iteration; not the v0.x default. Pattern A also doesn't support `pull_request` triggers (per the upstream README). |
There was a problem hiding this comment.
Its unclear to me what the trade actually is. Is there a real security benefit to builder_go_slsa3.yml compared to what we're thinking for wrangle's implementation with generic?
In both cases a reusable workflow will do the build itself and create the provenance. The adopter's workflow won't be able to falsify the provenance in either case?
|
|
||
| Out of the box, `go build` embeds build info (working directory, VCS revision, dirty flag, build timestamps) into the binary, which breaks reproducibility — two builds of the same source produce different bytes. Setting `-trimpath` (strips local filesystem paths from the binary) and `-buildvcs=false` (suppresses VCS-info embedding) plus a fixed `CGO_ENABLED=0` (where applicable) makes Go binaries reproducible. Goreleaser exposes these via `builds.flags` and `builds.env`; the wrangle action should set them by default and let the adopter's `.goreleaser.yml` opt out if they have a specific reason. | ||
|
|
||
| This matters because it closes the wrangle-tested-bytes vs. SLSA-attested-bytes gap. Without reproducibility, "we tested the source and SLSA attests the build" is two different artifacts; with it, they are the same artifact byte-for-byte. |
There was a problem hiding this comment.
Is this still the case if we're generating our own provenance? I think this gap has been fixed?
|
|
||
| ## ko / container builds | ||
|
|
||
| Go projects that publish container images via [`ko`](https://ko.build/) (small distroless images built from Go binaries without a Dockerfile) should use **wrangle's existing container build type**, not the Go build type. ko produces an OCI image; the container build type already handles SBOM, Cosign signing, and SLSA provenance for OCI images. The Go build type doesn't need a ko-specific code path — ko-using projects already have a container build need, and routing them to the container action keeps wrangle's per-ecosystem boundaries clean. |
There was a problem hiding this comment.
I think this is wrong. Ko uses specific build tools, not dockerfiles. https://ko.build/get-started/
So if users want to use ko they'd need those tools available and to drive builds. We don't have to solve it for go but we shouldn't say they'll be fine with the existing container support.
| ## Open questions | ||
|
|
||
| - **Binary vs. validate-only as one action or two.** See "Validation-only sub-shape." Decide in the implementation PR. | ||
| - **Lint placement.** Source-stage (in `actions/scan`) vs. build-stage (in the Go build action) vs. both. Python doesn't currently lint in the build action; Go could either follow that or differ. |
There was a problem hiding this comment.
I'm thinking lint in source scans (and we should probably add it to python and container images)
| - **Binary vs. validate-only as one action or two.** See "Validation-only sub-shape." Decide in the implementation PR. | ||
| - **Lint placement.** Source-stage (in `actions/scan`) vs. build-stage (in the Go build action) vs. both. Python doesn't currently lint in the build action; Go could either follow that or differ. | ||
| - **`.goreleaser.yml` template ownership.** Should wrangle ship a starter `.goreleaser.yml` for adopters (with `-trimpath` / `-buildvcs=false` baked in), or require adopters to bring their own and validate it has the reproducibility flags? Python doesn't ship a starter `pyproject.toml`; consistency with python argues "require adopters to bring their own." | ||
| - **`govulncheck` vs. `osv-scanner` for Go vulnscan.** Source-stage scanning already uses OSV-Scanner; `govulncheck` is Go-aware (callgraph-based, fewer false positives) and could be a complementary check. Decide in the implementation PR; not load-bearing for Phase 1. |
There was a problem hiding this comment.
this is a good callout. we should probably support govulncheck (and maybe also osv-scanner??)
- L58: clarify Pattern A vs B is operational, not a security delta (both run in adopter-unfalsifiable reusable workflows). The actual difference is whether wrangle has a caller hook between build and sign. - L78: drop the wrangle-tested-bytes vs SLSA-attested-bytes gap framing (Pattern B closes it by construction). Reframe reproducibility as consumer-side verification + audit, which is the durable rationale. - L101: ko correction. ko doesn't use Dockerfiles, so wrangle's container build type can't cover it. Out of scope for Phase 1; mode: ko on the Go action is a possible follow-up. - L142: lint placement decided as source-stage only, wrangle-wide. Python and container should follow. - L144: recommend supporting govulncheck (Go-aware, callgraph-based); OSV-Scanner against go.sum stays as a complementary candidate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per Tom's directive on #180 L142 (lint goes in source scans, wrangle-wide). Drops the "build action could optionally invoke npm run lint" hedge in favor of an unambiguous source-stage commitment, matching what the Go SPEC just adopted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| #### Pattern A (alternative, not picked) | ||
|
|
||
| `slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml` is a Go-specific *builder* that performs the build itself inside an isolated reusable workflow, driven by a `.slsa-goreleaser.yml` config file (the filename predates goreleaser conventions; the format is the SLSA builder's own). `slsa-verifier`'s [release workflow](https://github.com/slsa-framework/slsa-verifier/blob/main/.github/workflows/release.yml) demonstrates the canonical 6-cell `os × arch` matrix. It exists as an alternative for adopters who specifically want `.slsa-goreleaser.yml`-driven isolated builds. The cost is wrangle's seam: `builder_go_slsa3.yml` has no `hashes` input — the build, the SBOM, the test, and the provenance all happen inside one sealed reusable workflow with no caller hook between them, so wrangle's `syft` / `go test` / `release_gate` steps either run on a separate checkout (different bytes than the builder produces) or don't run at all. Adopters who want that trade can opt in via a separate variant in a later iteration; not the v0.x default. Pattern A also doesn't support `pull_request` triggers (per the upstream README). | ||
| `slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml` is a Go-specific *builder* that performs the build itself inside the same reusable workflow that signs the provenance, driven by a `.slsa-goreleaser.yml` config file (the filename predates goreleaser conventions; the format is the SLSA builder's own). `slsa-verifier`'s [release workflow](https://github.com/slsa-framework/slsa-verifier/blob/main/.github/workflows/release.yml) demonstrates the canonical 6-cell `os × arch` matrix. **Security strength is comparable to Pattern B** — both run the build inside a reusable workflow the adopter can't falsify, both sign provenance via Sigstore against the workflow's OIDC identity, and Pattern B's `generator_generic_slsa3.yml` is itself a reusable workflow with the same isolation property. The actual difference is **operational**: Pattern A binds build and sign in one upstream-controlled reusable workflow with no caller hook, so wrangle's `syft` / `go test` / `release_gate` steps either run on a separate checkout (against different bytes than the builder produces) or don't run at all. Pattern B keeps build inside wrangle's reusable workflow and signs via `generator_generic_slsa3.yml` — two reusable-workflow boundaries instead of one, with a caller-side hook in between for hygiene. Adopters who want Pattern A's no-second-checkout property can opt in via a separate variant in a later iteration; not the v0.x default. Pattern A also doesn't support `pull_request` triggers (per the upstream README). |
There was a problem hiding this comment.
Don't mention that we'll eventually support Pattern A. I'm not sure we want to. People can file an issue if they care.
| Out of the box, `go build` embeds build info (working directory, VCS revision, dirty flag, build timestamps) into the binary, which breaks reproducibility — two builds of the same source produce different bytes. Setting `-trimpath` (strips local filesystem paths from the binary) and `-buildvcs=false` (suppresses VCS-info embedding) plus a fixed `CGO_ENABLED=0` (where applicable) makes Go binaries reproducible. Goreleaser exposes these via `builds.flags` and `builds.env`; the wrangle action should set them by default and let the adopter's `.goreleaser.yml` opt out if they have a specific reason. | ||
|
|
||
| This matters because it closes the wrangle-tested-bytes vs. SLSA-attested-bytes gap. Without reproducibility, "we tested the source and SLSA attests the build" is two different artifacts; with it, they are the same artifact byte-for-byte. | ||
| Under Pattern B (the pick) wrangle's test step and wrangle's hash step both run against the same bytes goreleaser produced in the same job, so reproducibility isn't needed to close a wrangle-vs-builder gap (Pattern A's gap doesn't apply here). Reproducibility still matters for **consumer-side verification** — a downstream consumer who rebuilds from source should get the same binary the SLSA provenance attests — and for security audits that want to confirm "this binary corresponds to this source." That justifies the flags by default. |
There was a problem hiding this comment.
I'm not sure this is the right trade. Maybe it should be an option, but if we're publishing a binary, users can run the binary themselves? My biggest concern is that disabling CGO will cause performance problems.
Research-only deliverable per docs/HOW_TO_ADD_A_BUILD_TYPE.md Phase 1. Resolves the binary-vs-tag question into three modes; mode 1 (binary releases) is the only one wrangle's artifact-producing template should target. No action.yml, no implementation, no #171 contract design. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…only mode Restructured per maintainer feedback. Picks Pattern B (goreleaser + generator_generic_slsa3.yml) — preserves the seam wrangle uses for python/container, same L3 isolation property since all patterns run in a wrangle reusable workflow. Pattern A gets one paragraph as alternative; Pattern C one sentence (precise about actions/attest@v4 per upstream verification). Adds validation-only sub-shape for non-binary repos (libraries, go install-pattern): SBOM/test/vulnscan/lint on source, no provenance. Routes ko-using projects to the existing container build type. Adds Cosign keyless signing of binaries via cosign sign-blob. Adds reproducibility paragraph (-trimpath -buildvcs=false). Adds Linting section (gofmt + golangci-lint). Operating model stated in Overview. Contract-stress findings catalogued separately on #171. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…alongside Replaces the "one attestation per artifact, not stacked" framing per maintainer feedback on #178. Wrangle generates and stores its own L3 SLSA provenance via the upstream generator regardless of what ecosystem-native attestations exist; ecosystem-native attestations (Cosign signatures, GitHub artifact attestations, OCI attestations) populate alongside, not instead of. This is consistent with the Go pick (Pattern B + cosign keyless) and resolves the inconsistency that the previous wording introduced. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- L58: clarify Pattern A vs B is operational, not a security delta (both run in adopter-unfalsifiable reusable workflows). The actual difference is whether wrangle has a caller hook between build and sign. - L78: drop the wrangle-tested-bytes vs SLSA-attested-bytes gap framing (Pattern B closes it by construction). Reframe reproducibility as consumer-side verification + audit, which is the durable rationale. - L101: ko correction. ko doesn't use Dockerfiles, so wrangle's container build type can't cover it. Out of scope for Phase 1; mode: ko on the Go action is a possible follow-up. - L142: lint placement decided as source-stage only, wrangle-wide. Python and container should follow. - L144: recommend supporting govulncheck (Go-aware, callgraph-based); OSV-Scanner against go.sum stays as a complementary candidate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per Tom's directive on #180 L142 (lint goes in source scans, wrangle-wide). Drops the "build action could optionally invoke npm run lint" hedge in favor of an unambiguous source-stage commitment, matching what the Go SPEC just adopted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15e57f7 to
e6e8932
Compare
…bility flags - L58: drop the "later iteration" Pattern A variant promise. Adopters who want Pattern A can file an issue if they care. - L78: split reproducibility recommendation. -trimpath and -buildvcs=false are zero-runtime-cost and stay as defaults. CGO_ENABLED=0 has real performance costs (cgo-backed net/crypto/etc.) and is moved to opt-in only. Reframe rationale: the binary IS what consumers run; reproducibility's primary value is for security audits and SLSA verification chains, not routine consumer rebuilds. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Phase 1 ecosystem research for a
gobuild type, captured perdocs/HOW_TO_ADD_A_BUILD_TYPE.md. Recommends defaults for an eventualbuild/actions/go/implementation. Research only — noaction.ymlexists yet; inputs and step sequence are sketched at the level needed to justify the picks.Recommended defaults
goreleaserfor binary releases. Dominant ecosystem norm; handles the cross-compilation matrix, archive packaging,dist/checksums.txt, and GitHub Release upload in one config-driven invocation.syft(same tool python uses, same Cosign-keyless install, SPDX-native).GITHUB_TOKENwithcontents: write.generator_generic_slsa3.yml). Goreleaser producesdist/checksums.txt; wrangle hashes filenames into base64 subjects; the SLSA generic generator signs. Same shape as python and container. Pattern A (builder_go_slsa3.yml) is not picked — security strength is comparable, but Pattern A binds build and sign in one upstream-controlled workflow with no caller hook for syft/test/release_gate.cosign sign-blob --yesagainst each artifact, OIDC-bound to the calling workflow's identity.gofmt+golangci-lintin wrangle's source-scan stage (alongside OSV/Zizmor/Scorecard). Wrangle-wide convention — python and container should adopt source-stage lint in the same iteration.go test ./...before the build step.-trimpathand-buildvcs=falseon by default (zero runtime cost).CGO_ENABLED=0is not a default (real performance cost; opt-in only).go install-pattern repos are NOT out of scope. Wrangle still adds SBOM,go test, vulnscan, lint — just no SLSA build provenance (no artifact to attest). Implementation can use amode:input or a separate action; decide in the implementation PR.Out of scope for v0.1
builder_go_slsa3.yml). Wrangle does not currently plan a Pattern A variant. Adopters with a specific need can file an issue.docker buildx, not Dockerfile-based), so wrangle's existing container build type does not cover it. A ko-aware variant is a possible follow-up.Open questions (for the implementation PR)
mode:input vs. two actions..goreleaser.ymltemplate ownership (ship a starter vs. require adopters to bring their own).govulncheckfor Go-aware vulnscan, complementary to OSV-Scanner — likely both.Test plan
builder_go_slsa3.yml, slsa-verifier release.yml,sum.golang.org's tlog role.