Documentation
¶
Overview ¶
Package statusmanager persists ocireconciler reconciliation state as OCI attestations.
This package provides a mechanism for storing and retrieving reconciliation status as signed OCI attestations, using Sigstore's keyless signing with Fulcio and Rekor. It mirrors the githubreconciler/statusmanager pattern but targets OCI digests and stores progress directly as attestations rather than GitHub check runs.
Key Features ¶
- Keyless signing using Fulcio certificates and Rekor transparency log
- Identity-scoped attestations with predicate type "https://statusmanager.chainguard.dev/{identity}"
- REPLACE semantics ensuring exactly one attestation per digest/predicate pair
- Full signature verification on read using cosign verification
- Support for repository override (similar to COSIGN_REPOSITORY)
Basic Usage ¶
Create a Manager and use sessions to track reconciliation state:
// Create a manager (requires GCP service account credentials)
mgr, err := statusmanager.New[MyDetails](ctx, "my-reconciler")
if err != nil {
return err
}
// Start a session for a specific digest
session := mgr.NewSession(digest)
// Check previous state
observed, err := session.ObservedState(ctx)
if err != nil {
return err
}
if observed != nil {
log.Printf("Previous state: %+v", observed.Details)
}
// Perform reconciliation work...
// Record new state
err = session.SetActualState(ctx, &statusmanager.Status[MyDetails]{
Details: MyDetails{Result: "success"},
})
Status Type ¶
The Status type is generic over the Details field, allowing you to store arbitrary structured data alongside the automatically-populated ObservedGeneration:
type Status[T any] struct {
ObservedGeneration string // Automatically set to the digest
Details T // Your custom status data
}
Repository Override ¶
When the subject image is in a registry that doesn't support attestations, or when you want to store attestations separately, use WithRepositoryOverride:
mgr, err := statusmanager.New[MyDetails](ctx, "my-reconciler",
statusmanager.WithRepositoryOverride("gcr.io/my-project/attestations"),
)
This works similarly to setting COSIGN_REPOSITORY with cosign.
Read-Only Managers ¶
For consumers that only need to read status (not write), use NewReadOnly with WithExpectedIdentity to specify which signing identity to verify:
mgr, err := statusmanager.NewReadOnly[MyDetails](ctx, "my-reconciler",
statusmanager.WithExpectedIdentity("[email protected]"),
)
Authentication ¶
The manager uses Google Cloud service account credentials for both:
- Obtaining ID tokens for Fulcio keyless signing
- Registry authentication (when using WithRemoteOptions with google.Keychain)
Example with registry authentication:
mgr, err := statusmanager.New[MyDetails](ctx, "my-reconciler",
statusmanager.WithRemoteOptions(remote.WithAuthFromKeychain(google.Keychain)),
)
Thread Safety ¶
Manager instances are safe for concurrent use. Each Session should be used by a single goroutine, but multiple sessions can be created from the same Manager concurrently.
Index ¶
- Constants
- type Manager
- type Option
- func WithExpectedIdentity(identity cosign.Identity) Option
- func WithOIDCProvider(p fulcio.OIDCProvider) Option
- func WithRemoteOptions(opts ...remote.Option) Option
- func WithRepositoryOverride(repo string) Option
- func WithSigner(s types.CosignerSignerVerifier) Option
- func WithUserAgent(ua string) Option
- type Session
- type Status
Constants ¶
const ( // RekorHTTPLimit is the maximum HTTP request size accepted by Rekor's reverse proxy. // // This limit was determined empirically (2025-12-29) by generating realistic SBOM-like // payloads at varying sizes and measuring the actual HTTP request sizes after base64 // encoding and DSSE envelope wrapping. Testing showed: // - 75 MB payload → 127.6 MB HTTP request ✅ SUCCESS // - 100 MB payload → 170.3 MB HTTP request ❌ FAILED (502 Bad Gateway) // // The limit (~150 MB) is imposed by Rekor's reverse proxy (nginx/load balancer), // not the Rekor application itself. // // For production use with airflow APK (14.3 MB SBOM, 63 vuln matches): // - BEFORE metadata stripping: 116.52 MB status → 198 MB HTTP ❌ 502 Bad Gateway // - AFTER metadata stripping: 13.95 MB status → 23.72 MB HTTP ✅ 201 Created RekorHTTPLimit = 150 * 1024 * 1024 // 150 MB // StatusJSONSizeLimit is the maximum serialized JSON status size before base64/DSSE overhead. // // Calculation: RekorHTTPLimit / 1.7 (empirically measured overhead factor) // - Base64 encoding adds ~33% overhead (4/3 ratio) // - DSSE envelope wrapping adds additional ~28% overhead // - Combined overhead factor: ~1.7x // // This gives us: 150 MB / 1.7 ≈ 88 MB for the raw JSON status StatusJSONSizeLimit = RekorHTTPLimit * 10 / 17 // ~88 MB )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Manager ¶
type Manager[T any] struct { // contains filtered or unexported fields }
Manager writes and reads reconciliation status as attestations.
func NewReadOnly ¶
NewReadOnly constructs a Manager that can only read status.
type Option ¶
type Option func(*config)
Option customizes the Manager.
func WithExpectedIdentity ¶
WithExpectedIdentity specifies the sigstore identity to verify when reading attestations. This option is required for read-only managers and must not be provided for writable managers (which extract the identity from their credentials).
func WithOIDCProvider ¶
func WithOIDCProvider(p fulcio.OIDCProvider) Option
WithOIDCProvider overrides the OIDC provider used for Fulcio keyless signing.
func WithRemoteOptions ¶
WithRemoteOptions appends remote.Options applied when reading/writing attestations.
func WithRepositoryOverride ¶
WithRepositoryOverride directs attestation writes to the provided repository string.
func WithSigner ¶
func WithSigner(s types.CosignerSignerVerifier) Option
WithSigner injects a preconfigured signer (useful for tests).
func WithUserAgent ¶
WithUserAgent customizes the user-agent attached to Fulcio/Rekor requests.
type Session ¶
type Session[T any] struct { // contains filtered or unexported fields }
Session represents reconciliation state for a single digest.
func (*Session[T]) ObservedState ¶
ObservedState returns the latest recorded status, if any.