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/complypack

Quick 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#

PurposeMedia Type
Artifact Typeapplication/vnd.complypack.artifact.v1
Config Layerapplication/vnd.complypack.config.v1+json
Content Layerapplication/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 failed
  • ErrEmptyContent - Content reader returned zero bytes
  • ErrContentTooLarge - Content exceeds 100MB limit
  • ErrSigningFailed - Signing operation failed
  • ErrVerificationFailed - Signature verification failed
  • ErrInvalidMediaType - Unexpected media type in manifest
  • ErrNoContentLayer - 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 := repo

Current 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.