Skip to content

feat(cli,oci): OCI referrers sign + verify (slice 1)#87

Open
michael-herwig wants to merge 7 commits into
mainfrom
feat/oci-referrers-sign-verify
Open

feat(cli,oci): OCI referrers sign + verify (slice 1)#87
michael-herwig wants to merge 7 commits into
mainfrom
feat/oci-referrers-sign-verify

Conversation

@michael-herwig

@michael-herwig michael-herwig commented May 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Slice-1 OCI referrers signing + verification scaffolding: ocx package sign / ocx package verify commands, package_manager tasks, and oci/verify pipeline (trust root, identity, error taxonomy).
  • Fake Fulcio + Rekor + OIDC stack under test/tests/fixtures/fake_sigstore.py powers contract tests for sign/verify without network.
  • Docs: user-guide section + CLI/env reference updated for the new commands.

Scope note — this slice is a preview contract, not a working pipeline. SignPipeline::run / VerifyPipeline::run are unimplemented!(); both commands exit 78 with a documented pipeline-pending error. Merging this PR must not be read as "sign + verify works" — the real sigstore-rs pipeline is slice 2, tracked in #194.

Commits

  • refactor(oci): stub referrers sign/verify scaffolding
  • test: specify slice-1 sign+verify contracts
  • feat(cli,oci): wire sign+verify non-network plumbing
  • test(fixtures): fake Fulcio+Rekor+OIDC stack
  • feat(cli,oci): OCI referrers signing + verification (slice 1)
  • chore(claude): planning artifacts + deps interpolation

Test plan

  • task rust:verify green
  • task test — acceptance tests under test/tests/test_sign.py, test_verify.py, test_fake_sigstore.py pass against fake stack
  • Manual: ocx package sign / ocx package verify exit 78 with documented pipeline-pending error (slice-1 preview contract)
  • Website builds (task website:build) with new reference pages

@github-actions

github-actions Bot commented May 12, 2026

Copy link
Copy Markdown

Unit Test Results

1 754 tests  +116   1 754 ✅ +116   15s ⏱️ -1s
    4 suites ±  0       0 💤 ±  0 
    1 files   ±  0       0 ❌ ±  0 

Results for commit 01020c3. ± Comparison against base commit 1f8e236.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented May 12, 2026

Copy link
Copy Markdown

Acceptance Test Results

635 tests  +41   600 ✅ +21   5m 36s ⏱️ +52s
  1 suites ± 0    35 💤 +20 
  1 files   ± 0     0 ❌ ± 0 

Results for commit 01020c3. ± Comparison against base commit 1f8e236.

♻️ This comment has been updated with latest results.

…research

Planning outputs from /swarm-plan max 24 (OCI referrers) and /architect 32
(deps interpolation). Includes research, ADRs, PRDs, PR-FAQs, review rounds
(R1/R2 Claude panel + Codex cross-model), and handoff docs.

Rule updates carried by the referrers work:
- quality-rust-exit_codes.md: add RekorUnavailable=82 and ReferrersUnsupported=83
- adr_oci_artifact_enrichment.md: amendment promoting Phase 5 (signature
  support) from deferred into v1, pointing at the slice-based roadmap in the
  child ADR.

Status: planning complete; slice 1 (sign+verify) and slice 2 (external
discovery) plans in .claude/state/plans/ ready for /swarm-execute.
Add module skeleton for slice 1 OCI referrers signing + verification:
- ocx_lib::oci::referrer (capability, manifest, media_types)
- ocx_lib::oci::sign (bundle, fulcio, oidc, pipeline, rekor, signer)
- ocx_lib::oci::verify (error, identity, pipeline, trust_root)
- ocx_cli::api::data::{signature,verification} contract types
- ocx_cli::command::{package_sign,verify} CLI surface
- ocx_cli::error_envelope wrapper
- ocx_lib::cli::{classify,exit_code} for CLI exit-code mapping
- ocx_lib::package_manager::tasks::{sign,verify} stubs
- OCI client transport extension hooks

Aligned with Phase 3 architect + spec-compliance review findings.
Phase 4 Specify — contract-first TDD against Phase 3 stubs. Tests
encode the frozen v1 CLI contract (ADR §C-S1-1..4) and fail today
with `unimplemented!()` panics where Phase 5 will fill bodies.

Rust unit tests (46 new):
- error_envelope: schema_version, category snake_case, golden shape,
  None omission, BTreeMap key stability, success envelope, stub panic
- SignErrorKind::exit_code — exhaustive per variant (8 arms)
- VerifyErrorKind::exit_code — exhaustive per variant (15 arms, 5 families)
- classify_error downcast ladder for SignError + VerifyError (11 cases)
- Display format per C-GOOD-ERR (lowercase, no trailing period)
- ReferrersSupport serde round-trip + unknown-variant rejection
- TrustRoot stub-panic + load_from_pem signature type check

Python acceptance tests (14 new):
- fake_fulcio/fake_rekor/fake_oidc_token fixtures — xfail until Phase 5
  ships real crypto (keyless flow, SET generation, OIDC minting)
- test_sign: happy path, --identity-token rejected (passes today via
  clap), file/stdin mutually exclusive (passes today), env token,
  stdin token, --offline refused, referrers-unsupported (exit 83)
- test_verify: identity mismatch (exit 77), issuer mismatch,
  no-signatures (exit 79), referrers-unsupported (exit 83),
  error + success envelope golden shape

2 Python tests pass today (clap-level validation of flag surface);
11 xfail via fixture guards or stub-dependent assertions. All 1197
Rust tests pass, all 303 acceptance tests pass (15 total xfailed).

Contract ambiguities surfaced for Phase 5:
- Fake Fulcio/Rekor fixtures need a local HTTPS server with a
  test TUF root (injected via OCX_TRUST_ROOT_PEM). Plan §"Service
  seams" lists fulcio_url/rekor_url/trust_root but not the fake
  server scaffolding — Phase 5 builds the scaffolding.
- JSON envelope golden expects `command: "verify"` in error
  envelopes — ADR §C-S1-1 shows `command` as the sub-command string
  but doesn't pin whether it's the leaf ("verify") or the path
  ("package verify"). Tests assert leaf; Phase 5 confirms.
Phase 5a of slice-1 swarm-execute: land the deterministic,
non-network surfaces so Phase 5b (fake servers) and Phase 5c
(pipeline crypto) can slot in without further envelope/classifier churn.

- envelope: full C-S1-1 v1 JSON rendering (schema_version, command,
  exit_code, error/data) with context walker that downcasts
  SignError/VerifyError for identifier surfacing
- app: JSON envelope routing in error path + canonical command-name
  map for every Command variant (frozen contract)
- package sign: --offline short-circuits to OfflineSignRefused (77);
  C-S1-4 token precedence (file > stdin > OCX_IDENTITY_TOKEN env);
  bails with documented Phase 5c blocker
- verify: online-context resolution; bails with TrustRootUnavailable
  until Phase 5c lands sigstore-rs integration
- referrer capability: from_cache + is_fresh with fail-open semantics
  on corrupt cache files
- test_sign: flip test_sign_offline_refused from xfail to pass
… tests

Phase 5b of slice-1 swarm-execute: local Sigstore trust-anchor
scaffolding so Phase 5c can drive the Rust sign/verify pipelines
against deterministic, in-process fakes instead of talking to
public Fulcio/Rekor.

- FakeOidcIssuer — ES256 JWTs with JWKS endpoint, configurable sub/iss/aud
- FakeFulcio — P-256 ephemeral CA; POST /api/v2/signingCert accepts CSR
  + OIDC JWT, mints X.509 cert with SAN=sub and Sigstore issuer extension
  OID 1.3.6.1.4.1.57264.1.1
- FakeRekor — Ed25519 log signing; POST /api/v1/log/entries accepts
  hashedrekord proposal, returns signed entry with valid SET
- FakeSigstoreStack — single aggregate fixture exposing fulcio_url,
  rekor_url, oidc_token(), trust_root_pem_path()
- 15 fixture smoke tests cover lifecycle, JWT round-trip, cert chain
  validity, Rekor SET crypto verification
- test_sign/test_verify: three tests promoted to strict xfail now that
  fixtures are ready; flip to pass once Phase 5c pipelines land
Implement slice 1 of OCI referrers sign/verify pipeline.

Signing:
- referrers-capability probe with tag-fallback discovery
- Fulcio short-lived cert issuance via OIDC
- OIDC ambient (env + workload identity), inline, and browser flows
- Rekor v1 transparency log inclusion proof
- DSSE bundle assembly with sigstore-bundle JSON
- `ocx package sign` CLI command + `ocx sigstore-url` helper

Verification:
- trust-root PEM loader + chain validation
- bundle parsing + signature verification
- identity binding (cert SAN match against expected signer)
- `ocx verify` CLI command

CLI plumbing:
- error envelope (RFC 7807-inspired) with stable problem-types
- exit-code classifier mapping error variants to documented exit codes
- async stdin support for piped envelope input

Tests:
- fake Fulcio + Rekor + OIDC stack fixture for offline acceptance tests
- contract tests (test_sign.py, test_verify.py, test_fake_sigstore.py)

Docs:
- user-guide signing section
- command-line reference for sign/verify/sigstore-url
- environment variable reference for OIDC + sigstore overrides

Network layer is stubbed for slice 1 (mock transport in tests, no live registry probes yet).
… branch

Adoption checklist items 1-11 from /swarm-review gap analysis. Long-lived
branch had drifted from conventions merged on main after planning artifacts
were drafted (typed exit codes, layered config, doc redesign).

CLI placement (one-way door):
- Move verify from top-level `ocx verify` to `ocx package verify` for
  symmetry with `package sign`, `package push`, `package pull`. Both
  OCI-tier per subsystem-cli-commands.md layering rule.

Security wraps (S-1/S-2/S-3):
- Thread validated `&url::Url` through SignContext / VerifyContext
  instead of raw `&str`, locking in the validate_sigstore_url SSRF guard.
- Wrap `OidcToken::raw` in `Zeroizing<String>` for memory-zeroed Drop.
- Change `resolve_override_token` return to `Option<Zeroizing<String>>`
  so plain `String` cannot accidentally enter the Phase 5 signing path.

Test markers (T-Strict):
- Flip `strict=False` to `strict=True` on three xfail markers in
  test_sign.py:297/345/398 so Phase 5c completion surfaces as XPASS.

Token forwarding policy (A-1):
- Document `OCX_IDENTITY_TOKEN` as a credential exemption — read directly
  via std::env::var, intentionally NOT forwarded via OcxConfigView. Add
  reviewer-facing exemption table to subsystem-cli.md; cross-link from
  Rust source and reference docs.

Documentation:
- New website/src/docs/in-depth/signing.md absorbs TUF trust root,
  referrers cache, OCI 1.1 hard-fail, identity matching, slice boundary
  detail moved out of the user guide; sidebar updated.
- Remove duplicate `## CI Integration {#ci}` section in user-guide.md.
- Re-attribute exit code 80 in verify reference to registry auth (was
  incorrectly attributed to "Fulcio rejected OIDC token" — verify takes
  no OIDC token).
- Add "Signing after push" subsection to authoring/building-pushing.md.
- Update product-context.md CLI snippet to `ocx package verify`.
- Amend adr_oci_referrers_signing_v1.md with CLI-placement rationale.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/oci Subsystem: oci security Security-relevant change or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant