🦾 Written with LLM assistance [claude-opus-4-6]
💪 Reviewed by a human before submission
Is your feature request related to a problem? Please describe.
complyctl currently hardcodes the public Sigstore TUF root for keyless verification, making it impossible to verify artifacts signed by a private Sigstore instance (e.g. an organization running its own Fulcio, Rekor, and timestamp authority).
In internal/cache/verify.go:77, NewKeylessVerifier() calls root.FetchTrustedRootWithOptions(opts) at line 83, which fetches the public good instance's trusted root via TUF. There is no mechanism to supply a custom trusted_root.json for a private Sigstore deployment.
The VerificationConfig struct in internal/complytime/config.go:78-87 has fields for Issuer, Identity, and Key, but no TrustedRoot field.
This was discovered during the implementation of #678 - current branch linked while attempting to instrument a fully-contained CI test
environment.
Two acceptance tests in the proposed implementation are currently skipped pending this feature:
tests/acceptance/verification_test.go:138 — TestVerification_Keyless_HappyPath (t.Skip("requires trusted_root support"))
tests/acceptance/verification_test.go:184 — TestVerification_Keyless_WrongIdentity (t.Skip("requires trusted_root support"))
Describe the solution you'd like
-
Add a trusted_root field to VerificationConfig in internal/complytime/config.go that accepts a file path to a trusted_root.json:
verification:
issuer: https://dex.example.com
identity: signer@example.com
trusted_root: /path/to/trusted_root.json
-
Update NewKeylessVerifier() in internal/cache/verify.go to:
- When
trusted_root is provided: load the trusted root from the specified file path using root.NewTrustedRootFromJSON()
- When
trusted_root is absent: continue using root.FetchTrustedRootWithOptions() for the public Sigstore instance (current behavior)
-
Add Sigstore infrastructure services (dex-idp, fulcio, rekor) to the acceptance test compose stack (tests/acceptance/compose.yaml) under the verification profile, enabling the skipped keyless tests to run against a local Sigstore instance.
-
Un-skip TestVerification_Keyless_HappyPath and TestVerification_Keyless_WrongIdentity.
Describe alternatives you've considered
-
Environment variable for trusted root path: Simpler but less composable — does not support per-policy-entry verification against different Sigstore instances. The complytime.yaml approach is consistent with the existing per-entry verification: override pattern.
-
Inline trusted root JSON in config: Embedding the full trusted root JSON in complytime.yaml would avoid a file path dependency but would make configs unwieldy and harder to manage.
-
Auto-discovery via .well-known endpoint: Fetching the trusted root from a Sigstore instance's well-known URL would reduce config burden but adds a network dependency and does not work for air-gapped environments.
Additional context
Design history
This was explicitly listed as a non-goal in the acceptance test openspec:
openspec/changes/678-policy-complypack-acceptance-tests/design.md:30 — "Implementing trusted_root field (follow-on issue)"
The original sigstore verification design chose the public good instance:
openspec/changes/sigstore-verification/design.md:170-178 — Decision 7: "Use sigstore-go's root.NewLiveTrustedRoot() to fetch Sigstore's public good instance trusted root via TUF"
Infrastructure context
The Sigstore compose infrastructure (dex-idp, fulcio, rekor, witness) was previously built for acceptance tests but was removed as dormant when the keyless tests could not run without trusted_root support. Pre-generated test PKI material exists at tests/acceptance/testdata/sigstore/. This feature would re-enable that infrastructure.
Per-entry support
The existing per-entry verification: override pattern (added in the per-entry-verification work) means trusted_root should also be supported at the per-entry level, allowing different policies to be verified against different Sigstore instances:
policies:
- id: internal-policy
source: registry.internal/policies/security
verification:
issuer: https://dex.internal
identity: signer@internal
trusted_root: /path/to/internal-trusted-root.json
- id: public-policy
source: ghcr.io/org/policies/compliance
verification:
issuer: https://accounts.google.com
identity: signer@example.com
# No trusted_root — uses public Sigstore instance
Is your feature request related to a problem? Please describe.
complyctlcurrently hardcodes the public Sigstore TUF root for keyless verification, making it impossible to verify artifacts signed by a private Sigstore instance (e.g. an organization running its own Fulcio, Rekor, and timestamp authority).In
internal/cache/verify.go:77,NewKeylessVerifier()callsroot.FetchTrustedRootWithOptions(opts)at line 83, which fetches the public good instance's trusted root via TUF. There is no mechanism to supply a customtrusted_root.jsonfor a private Sigstore deployment.The
VerificationConfigstruct ininternal/complytime/config.go:78-87has fields forIssuer,Identity, andKey, but noTrustedRootfield.This was discovered during the implementation of #678 - current branch linked while attempting to instrument a fully-contained CI test
environment.
Two acceptance tests in the proposed implementation are currently skipped pending this feature:
tests/acceptance/verification_test.go:138—TestVerification_Keyless_HappyPath(t.Skip("requires trusted_root support"))tests/acceptance/verification_test.go:184—TestVerification_Keyless_WrongIdentity(t.Skip("requires trusted_root support"))Describe the solution you'd like
Add a
trusted_rootfield toVerificationConfigininternal/complytime/config.gothat accepts a file path to atrusted_root.json:Update
NewKeylessVerifier()ininternal/cache/verify.goto:trusted_rootis provided: load the trusted root from the specified file path usingroot.NewTrustedRootFromJSON()trusted_rootis absent: continue usingroot.FetchTrustedRootWithOptions()for the public Sigstore instance (current behavior)Add Sigstore infrastructure services (dex-idp, fulcio, rekor) to the acceptance test compose stack (
tests/acceptance/compose.yaml) under the verification profile, enabling the skipped keyless tests to run against a local Sigstore instance.Un-skip
TestVerification_Keyless_HappyPathandTestVerification_Keyless_WrongIdentity.Describe alternatives you've considered
Environment variable for trusted root path: Simpler but less composable — does not support per-policy-entry verification against different Sigstore instances. The
complytime.yamlapproach is consistent with the existing per-entryverification:override pattern.Inline trusted root JSON in config: Embedding the full trusted root JSON in
complytime.yamlwould avoid a file path dependency but would make configs unwieldy and harder to manage.Auto-discovery via
.well-knownendpoint: Fetching the trusted root from a Sigstore instance's well-known URL would reduce config burden but adds a network dependency and does not work for air-gapped environments.Additional context
Design history
This was explicitly listed as a non-goal in the acceptance test openspec:
openspec/changes/678-policy-complypack-acceptance-tests/design.md:30— "Implementingtrusted_rootfield (follow-on issue)"The original sigstore verification design chose the public good instance:
openspec/changes/sigstore-verification/design.md:170-178— Decision 7: "Use sigstore-go'sroot.NewLiveTrustedRoot()to fetch Sigstore's public good instance trusted root via TUF"Infrastructure context
The Sigstore compose infrastructure (dex-idp, fulcio, rekor, witness) was previously built for acceptance tests but was removed as dormant when the keyless tests could not run without
trusted_rootsupport. Pre-generated test PKI material exists attests/acceptance/testdata/sigstore/. This feature would re-enable that infrastructure.Per-entry support
The existing per-entry
verification:override pattern (added in the per-entry-verification work) meanstrusted_rootshould also be supported at the per-entry level, allowing different policies to be verified against different Sigstore instances: