Skip to content

Expose SigningContext::staging() (or re-export Keyring) for downstream e2e testing #562

Description

@sweengineeringlabs

Summary

SigningContext::staging() doesn't exist as a public constructor, and the lower-level SigningContext::new(...) requires a Keyring type that isn't re-exported. As a downstream consumer trying to write CI-side end-to-end tests against staging Sigstore, I'd like a way to construct a staging-pointed SigningContext without polluting production's transparency log.

Versions audited

  • sigstore-rs v0.13.0 (latest released as of this issue)
  • sigstore-rs main branch (sampled at the same time)

In both, the public surface of sigstore::bundle::sign::SigningContext exposes:

  • SigningContext::production() → wired to production Fulcio + Rekor + CTFE.
  • SigningContext::async_production() → async variant.

There's no staging() counterpart. The new(fulcio, rekor, ctfe_keyring) constructor is pub, but Keyring is pub(crate) and not re-exported in the public module surface.

Use case

I'm a maintainer on a downstream OCI artifact pipeline (https://github.com/sweengineeringlabs/justoci) that uses sigstore-rs as the production signer. We want CI-side end-to-end tests that exercise the full sign flow against real Fulcio + Rekor — but on staging (https://fulcio.sigstage.dev / https://rekor.sigstage.dev), so we don't write CI test runs to the public production Rekor log (immutable, public, forever).

Today the only escape is to vendor the Keyring construction code into our own crate, which adds maintenance burden and risks divergence from upstream's trust-root format.

Proposed fix

Mirror production() with staging() (and async_staging()). Concretely:

impl SigningContext {
    pub fn staging() -> SigstoreResult<Self> {
        let trust_root = SigstoreTrustRoot::staging()?;   // assuming this exists; if not, expose it too
        Self::from_trust_root(trust_root)
    }

    pub async fn async_staging() -> SigstoreResult<Self> { ... }
}

This mirrors what cosign-cli does (COSIGN_EXPERIMENTAL=1 cosign sign-blob --rekor-url https://rekor.sigstage.dev ... with the staging trust root). Downstream users want the same affordance from the SDK.

Alternative (if staging() is undesirable for some reason)

Re-export Keyring (and SigstoreTrustRoot if needed) so external callers can construct a custom SigningContext::new(...) themselves. This is more flexible but pushes trust-root assembly to the caller, which is fragile.

I'd prefer staging().

Open questions

  • Is there a reason staging() was deliberately omitted? (e.g. discouraging accidental staging-in-prod.)
  • If so, would gating it behind a Cargo feature like staging be acceptable?

Happy to send a PR if there's appetite for either approach.

Context for future readers

This issue is being tracked downstream at sweengineeringlabs/justoci#21 — once an upstream fix lands and we bump the dep, our attest/tests/sigstore_e2e_test.rs (currently SKIP-passing) starts exercising real signing end-to-end.

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