May 7, 2026·4 min read·engineeringperlsecuritysigning

CPAN release signing with Sigstore needs an identity policy

A new Dist::Zilla plugin signs release archives and uploads signature bundles to PAUSE. Consumers still need an independent expectation for the signing identity and an explicit verification step.

Timothy Legge has announced a Dist::Zilla plugin for signing CPAN releases with Sigstore today, May 7. The plugin signs the release archive and uploads a separate signature bundle. It also verifies the signature locally before uploading. The announcement is explicit that PAUSE and CPAN installation clients do not yet perform this verification automatically.

I welcome a release artifact that a consumer can verify independently. CPAN has been part of my Perl use for nearly thirty years, and its distribution model remains useful even when my immediate task is a short text-processing program. I would evaluate this addition by what a consumer can establish from the archive and bundle, then identify the policy needed to turn that evidence into an acceptance decision.

Sign the archive that will be distributed

The announcement describes Dist::Zilla::Plugin::SigStore::SignRelease as an extension of UploadToCPAN. Its sequence calls cosign sign-blob on the archive, extracts information from the resulting bundle for a local verification, and uploads both artifacts. This makes the archive itself the object being signed. The bundle sits beside it as a separate file.

The 0.03 release available today provides a historical version to inspect. Its module implementation appends .sigstore.json to the archive filename by default. It invokes signing and proceeds to the upload path when the expected bundle exists and the local verification succeeds.

That separation is useful for a consumer deciding what bytes to verify. I would retain the downloaded archive unchanged and verify it with the corresponding bundle before unpacking or executing its contents. An independently rebuilt archive can have different bytes even if it appears to contain equivalent files. The verification target should remain the distributed artifact named in the signing workflow.

The announcement also reports a decision at the Perl Tool Chain Summit to deprecate Module::Signature. I would avoid extending that statement into a claim that every historical signing workflow has already been replaced. What is available today is a new opt-in plugin with a documented gap in downstream verification. Adoption and enforcement remain separate work.

Configure one uploader

For a configuration using the Basic plugin bundle, the announcement gives this replacement pattern. The existing uploader is removed because the signing plugin supplies the upload step itself.

[@Filter]
bundle = @Basic
remove = UploadToCPAN

[SigStore::SignRelease]

I would review the resolved release configuration to establish that it contains the intended upload path once. Leaving an independent uploader in place would make the release procedure harder to reason about. The desired procedure should make the relationship between successful signing and publication explicit, including how it reports a failure.

The plugin requires cosign in the executable search path. That is an external release-tool dependency, so I would record its installation and version with the rest of the release environment. Installing the Perl plugin alone does not establish that the signing command can run. A release procedure needs to check its actual prerequisites before reaching the upload stage.

The source is also worth reading for failure behavior. Its local verification obtains the identity and issuer from the certificate inside the newly produced bundle, then passes them to cosign verify-blob. A successful check establishes consistency of the generated material under those extracted values. It does not establish that an unrelated consumer expected that identity to publish this distribution.

Consumers need an independent expected identity

The manual verification example in the announcement supplies both a certificate identity and an OIDC issuer. With placeholder filenames and an example identity, the command has this form.

cosign verify-blob Example-Dist-0.01.tar.gz   --bundle Example-Dist-0.01.tar.gz.sigstore.json   --certificate-identity author@example.com   --certificate-oidc-issuer https://accounts.google.com

Those identity arguments need a trusted origin of their own. I would not derive my acceptance policy by copying whatever identity an untrusted bundle presents. That would answer whether the artifact has a signature associated with some identity, while leaving unanswered whether that identity is authorized to release the package I wanted.

This is a policy requirement that follows from the difference between producer self-checking and consumer authorization. A producer can inspect a certificate it just obtained to confirm that signing worked. A consumer starts with a package requirement and needs to associate it with an expected publisher. The two operations can use the same cryptographic verifier while supplying expectations for different reasons.

I would document that association alongside any verification automation. If a maintainer changes identity providers or transfers responsibility, the policy needs a reviewed update. Accepting any new identity merely because a download presents it would discard the authorization check at precisely the point where it matters. Rejecting all changes without a maintenance procedure would also make the system difficult to operate.

I would test a verifier with an incorrect expected identity as well as the intended one. A useful acceptance check must reject a signature that does not meet its policy. Recording only a successful invocation leaves that rejection behavior unexamined. The fixture should also identify the exact archive and bundle used so a future test does not silently substitute another release.

Transparency provides another kind of evidence

Legge describes short-lived certificates obtained through OIDC authentication and a signature bundle containing transparency-log evidence. Rekor records the signing event in its append-only log. That adds public evidence about signing, but I would still distinguish evidence of an event from approval of the code inside the archive.

A valid signature cannot establish that a release contains no vulnerabilities or that its behavior meets an application requirement. It connects artifact verification to an identity under the chosen trust rules. Code review and dependency testing still address different questions. I would retain them in the release-consumption process instead of allowing a successful signature check to replace them.

The immediate limitation remains the one stated in the announcement. PAUSE does not verify these signatures yet, and CPAN clients do not check them during installation. An operator who wants verification today needs a deliberate step before accepting an archive. I would retain its bundle, the expected identity policy, and the verification result with that decision so another maintainer can repeat the check on the same artifact.