Skip to content

[P06] Add npm GitHub workflow provenance compatibility projection #143

Open

Description

@yunseo-kim

Context

Attempts 7 and 8 of the live M1 dogfood reached npm publish and failed closed with windlass.verify.error.registry-linkage-mismatch / publication outcome is indeterminate.

Attempt 8 captured npm's immediate result as E422 / HTTP 422. The preserved Windlass bundle passed GitHub attestation verification, and the registry never exposed @windlass/[email protected] or its attestations. Comparing the rejected bundle with the registry-accepted npm-generated provenance for @windlass/[email protected] shows that the Windlass statement lacks npm's GitHub workflow compatibility projection:

  • buildDefinition.externalParameters.workflow.ref
  • buildDefinition.externalParameters.workflow.repository
  • caller workflow buildDefinition.externalParameters.workflow.path
  • buildDefinition.internalParameters.github.event_name
  • buildDefinition.internalParameters.github.repository_id
  • buildDefinition.internalParameters.github.repository_owner_id

The signing certificate already carries corresponding public claims: source repository, source repository ref, caller build config URI, build trigger, repository ID, owner ID, and runner environment.

Proposal

Add a narrowly scoped npm compatibility projection to the signed SLSA v1 provenance statement so npm's registry-side certificate-to-predicate consistency checks can find the GitHub workflow fields they expect.

The projection should preserve the existing Windlass semantics:

  • Keep the immutable Windlass builder.id and build type.
  • Keep both SHA-256 and SHA-512 subject digests unless a later evidence-based decision rejects the extra digest.
  • Keep the built source tag (source.ref, source.revision) distinct from the invocation ref (source.invocation_ref, source.invocation_revision).
  • Populate npm-compatible workflow fields from verified public GitHub/OIDC claims, not arbitrary environment text.
  • Treat the projection as npm registry compatibility metadata, not as a replacement for Windlass source or builder identity.

Expected direction

Candidate mapping:

  • workflow.ref <- certificate SourceRepositoryRef / invocation ref
  • workflow.repository <- certificate SourceRepositoryURI
  • workflow.path <- caller workflow path from certificate BuildConfigURI
  • internalParameters.github.event_name <- certificate BuildTrigger
  • internalParameters.github.repository_id <- certificate SourceRepositoryIdentifier
  • internalParameters.github.repository_owner_id <- certificate SourceRepositoryOwnerIdentifier

The final mapping, trust boundary, and provenance-contract wording require an ADR and specification update before implementation.

Non-goals

  • Do not remove SHA-256 speculatively.
  • Do not replace the Windlass SHA-pinned builder identity with npm's runner identity.
  • Do not log raw registry responses; detailed rejection classification remains [P06] Classify npm publish rejection details safely #142.
  • Do not retry publication before the projection contract is decided and implemented.

Acceptance criteria

  • ADR and architecture specs define the projection and its trust semantics.
  • TDD covers the six compatibility fields and their certificate/claim bindings.
  • Existing immutable builder identity and source-tag versus invocation-ref semantics remain intact.
  • Generated provenance passes existing offline/online verification and npm client custom-provenance checks.
  • A later dogfood attempt can determine whether npm accepts the projection or reports a remaining rejection family.

Activity

  1. yunseo-kim commented on Nov 3, 2026

    @yunseo-kim
    MemberAuthor

    Attempt-9 evidence update (12026-11-03): the projection surface is now fully measured

    Run 41666974638 captured the registry's complete rejection message via the ADR 0101 relay. The six workflow/GitHub fields in this issue's candidate mapping are confirmed verbatim, with exact expected values:

    Predicate field Expected value (attempt 9) Source
    externalParameters.workflow.ref refs/heads/main certificate SourceRepositoryRef (invocation ref, not the built tag)
    externalParameters.workflow.repository https://github.com/windlasstech/vers-js certificate SourceRepositoryURI
    externalParameters.workflow.path .github/workflows/publish.yml certificate BuildConfigURI (caller workflow path, not the reusable workflow's own path)
    internalParameters.github.event_name workflow_dispatch certificate BuildTrigger
    internalParameters.github.repository_id 1261293345 certificate SourceRepositoryIdentifier
    internalParameters.github.repository_owner_id 257660296 certificate SourceRepositoryOwnerIdentifier

    Two requirements surface that no prior evidence had shown:

    • buildDefinition.resolvedDependencies[0].uri must equal git+<SourceRepositoryURI>@<SourceRepositoryRef> (attempt 9: git+https://github.com/windlasstech/vers-js@refs/heads/main) — the invocation source at the invocation ref, with no lockfile fragment.
    • buildDefinition.resolvedDependencies[0].digest.gitCommit must equal the certificate SourceRepositoryDigest, the invocation head commit (bd8b7ab1...), not the peeled tag commit.

    This interacts with the ADR 0079/0080 model: the provenance keeps the built tag in externalParameters.source.* while resolvedDependencies[0] now must present the invocation context. How that affects what the provenance claims about built content (and whether the built-source record moves to a later resolvedDependencies entry) belongs to the ADR/spec step before implementation.

    Two more observations:

    • Missing fields render as <empty> in the registry message, confirming absence rather than mismatch.
    • buildType and builder.id were absent from the complaint despite our custom values — consistent with the post-12023-08 relaxation, but not conclusive until a conforming run reaches the registry.

    Full evidence record: PR #147 (docs/dogfood/npm-m1.md, attempt-9 section). The recognizer-side template widening is tracked in #142.

  2. 3 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions