Overview
ComplyPack is a Go library for packing, unpacking, signing, and verifying OCI artifacts containing policy bundles. It provides an evaluator-agnostic format for distributing compliance policies using OCI registries.
Features#
- OCI Artifact Packaging - Pack policy content into OCI Image Manifest v1.1 artifacts
- Evaluator-Agnostic - Supports any policy language (OPA, CEL, etc.) via evaluator-id dispatch
- Signing & Verification - Built-in support for keyed and keyless (Sigstore) signing
- Memory-Safe - 100MB content size limit prevents memory exhaustion attacks
- Provenance Tracking - Optional Gemara linkage for generated policies
- Flexible API - Functional options pattern for clean, extensible configuration
Installation#
go get github.com/complytime/complypackQuick Start#
Packing A Policy#
package main
import (
"context"
"strings"
"github.com/complytime/complypack/pkg/complypack"
"oras.land/oras-go/v2/content/memory"
)
func main() {
ctx := context.Background()
store := memory.New()
// Configure the artifact
cfg := complypack.Config{
EvaluatorID: "io.complytime.opa",
Version: "1.0.0",
}
// Pack the policy content
content := strings.NewReader("policy content here")
desc, err := complypack.Pack(ctx, store, cfg, content)
if err != nil {
panic(err)
}
// desc.Digest contains the artifact reference
}Unpacking A Policy#
result, err := complypack.Unpack(ctx, store, desc)
if err != nil {
panic(err)
}
defer result.Content.Close()
// Access configuration
fmt.Printf("Evaluator: %s\n", result.Config.EvaluatorID)
// Read content
content, _ := io.ReadAll(result.Content)With Signing#
// Pack with keyed signing
desc, err := complypack.Pack(ctx, store, cfg, content,
complypack.WithSigning("/path/to/private.key"))
// Pack with keyless signing (OIDC)
desc, err := complypack.Pack(ctx, store, cfg, content,
complypack.WithKeylessSigning("user@example.com", "https://accounts.google.com"))
// Unpack with verification
result, err := complypack.Unpack(ctx, store, desc,
complypack.WithVerification("/path/to/public.key"))With Provenance#
cfg := complypack.Config{
EvaluatorID: "io.complytime.opa",
Version: "1.0.0",
Source: &complypack.Provenance{
GemaraContent: "oci://registry/gemara/controls:v1.0.0",
PolicyID: "policy-123",
},
}Architecture#
ComplyPack uses OCI Image Manifest v1.1 with the following structure:
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"artifactType": "application/vnd.complypack.artifact.v1",
"config": {
"mediaType": "application/vnd.complypack.config.v1+json",
"digest": "sha256:...",
"size": 324
},
"layers": [
{
"mediaType": "application/vnd.complypack.content.v1.tar+gzip",
"digest": "sha256:...",
"size": 1024000
}
]
}Media Types#
| Purpose | Media Type |
|---|---|
| Artifact Type | application/vnd.complypack.artifact.v1 |
| Config Layer | application/vnd.complypack.config.v1+json |
| Content Layer | application/vnd.complypack.content.v1.tar+gzip |
Content Layer#
The content layer is opaque - the library treats it as raw bytes. For OPA-based policies, this would typically be an OPA bundle tarball. The evaluator-id in the config determines how consumers should interpret the content.
Memory Usage#
Pack() loads the entire content into memory for digest calculation. Content size is limited to 100MB to prevent memory exhaustion. For larger artifacts, consider alternative approaches or splitting content.
Error Handling#
ComplyPack uses sentinel errors for predictable error checking:
import "errors"
_, err := complypack.Pack(ctx, store, cfg, content)
if errors.Is(err, complypack.ErrEmptyContent) {
// Handle empty content
}
if errors.Is(err, complypack.ErrContentTooLarge) {
// Handle content exceeding 100MB limit
}Available sentinel errors:
ErrInvalidConfig- Config validation failedErrEmptyContent- Content reader returned zero bytesErrContentTooLarge- Content exceeds 100MB limitErrSigningFailed- Signing operation failedErrVerificationFailed- Signature verification failedErrInvalidMediaType- Unexpected media type in manifestErrNoContentLayer- Manifest missing content layer
Storage Backends#
ComplyPack works with any ORAS-compatible storage backend:
// In-memory (for testing)
store := memory.New()
// Filesystem
store, err := file.New("/tmp/oci-store")
defer store.Close()
// Remote registry (via ORAS)
repo, err := remote.NewRepository("ghcr.io/org/repo")
store := repoCurrent Limitations#
- Signing/Verification: Validation logic is implemented, but full sigstore-go integration is pending. Signing/verification options will return “not yet implemented” errors.
- Content Size: Maximum 100MB per artifact
- Single Content Layer: Only one content layer per artifact is supported
Contributing#
Contributions are welcome! Please see our contributing guidelines.
License#
Apache License 2.0 - see LICENSE for details.
Related Projects#
- ORAS - OCI Registry as Storage
- Sigstore - Keyless signing infrastructure
- Open Policy Agent - Policy-based control
- Gemara - Compliance policy framework