Skip to content

Restore release-binary uploads + adopt unified release artifact standard #142

@avrabe

Description

@avrabe

Primary issue: release binaries stopped shipping after v0.5.0

Every loom release from v0.6.0 through v1.1.1 has zero release assets — only the v0.3.0 era tags carry the loom-{platform}.tar.gz archives. Downstream (rules_wasm_component is pinned at v0.3.0) cannot move forward until the release workflow's asset-upload step is restored.

v1.1.1  2026-05-23  → assets=0
v1.1.0  2026-05-22  → assets=0
v1.0.5  → v0.6.0    → assets=0 (every one)
v0.5.0  → v0.3.0    → assets present

Looks like the asset-upload step in the release workflow was lost or made conditional. Please restore it.

Secondary: adopt the unified artifact standard while you're in there

Background

Across the six PulseEngine release pipelines (loom, meld, sigil, spar, synth, witness) we currently have three different SHA conventions (per-file .sha256 sidecars, single SHA256SUMS.txt, or none) and three different signing tiers (none, per-asset Sigstore, or single Cosign on the sums file). This inconsistency burns downstream — rules_wasm_component has to special-case each tool's checksum schema.

Reference implementation: pulseengine/synth/.github/workflows/release.yml (Phase 6 onward). Copy verbatim and adapt the cargo-cyclonedx manifest path.

Target asset set (per release)

<tool>-vX.Y.Z-<triple>.{tar.gz|zip}    # binary archives, one per platform
<tool>-X.Y.Z.cdx.json                  # CycloneDX SBOM (no leading "v")
SHA256SUMS.txt                         # sha256 of every other asset above
SHA256SUMS.txt.sig                     # detached Cosign signature
SHA256SUMS.txt.pem                     # Fulcio cert (keyless OIDC identity)
SHA256SUMS.txt.cosign.bundle           # verifier-friendly bundle
build-env.txt                          # rustc/cargo/cosign/runner versions

Plus SLSA build-provenance attestation via actions/attest-build-provenance@v2 (lives in GitHub's attestation store, not as a release asset; fetched via gh attestation verify).

Required workflow steps (this order matters)

  1. Build binary archives → release-assets/.
  2. cargo cyclonedx --manifest-path <main-crate>/Cargo.toml --format json --spec-version 1.5, copy to release-assets/<tool>-${BARE}.cdx.json. Must run before step 3 so its digest enters the sums file.
  3. cd release-assets && sha256sum ./* > SHA256SUMS.txt.
  4. actions/attest-build-provenance@v2 with subject-path: "release-assets/*.tar.gz".
  5. sigstore/cosign-installer@v3 (cosign v2.4.1).
  6. cosign sign-blob --yes --bundle SHA256SUMS.txt.cosign.bundle --output-signature SHA256SUMS.txt.sig --output-certificate SHA256SUMS.txt.pem SHA256SUMS.txt.
  7. Write build-env.txt (rustc/cargo/cosign/runner versions).
  8. gh release upload everything in release-assets/.

Required workflow permissions

permissions:
  contents: write
  id-token: write
  attestations: write

Why sign the sums file rather than each asset

SHA256SUMS.txt lists the hash of every artifact; one signature on the sums file authenticates the entire release (Merkle-style). Per-asset .sig is appropriate only where certification evidence requires per-artifact provenance (currently only witness, for MC/DC evidence).

Generating the SBOM before the sums file means the sums file covers the SBOM too, so the single Cosign signature transitively attests to the SBOM. Reorder those two and the SBOM falls outside the trust boundary.

Verification one-liner (paste in release notes)

cosign verify-blob \
  --certificate-identity-regexp 'https://github.com/pulseengine/<tool>/.github/workflows/release.yml@.*' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  --bundle SHA256SUMS.txt.cosign.bundle SHA256SUMS.txt

gh attestation verify <tool>-vX.Y.Z-<triple>.tar.gz --repo pulseengine/<tool>

Downstream impact

Once all six repos converge, rules_wasm_component will fetch and verify SHA256SUMS.txt.cosign.bundle during repository-rule resolution, closing the gap where an attacker who can replace a release asset could also replace the checksums-PR entry. Until then we rely on SHA256-only verification.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions