Skip to content

Pack Registry

Assay supports compliance packs from multiple sources: local files, bundled packs, remote registries, and BYOS (Bring Your Own Storage). The Pack Registry protocol ensures secure, reproducible, and verifiable pack fetching.

Resolution Order

The normative pack resolution order is defined in SPEC-Pack-Engine-v1. Summary:

  1. Path — Existing filesystem path: if file, load as YAML; if directory, load <dir>/pack.yaml only. Override built-ins by using an explicit path.
  2. Built-in — By name (e.g. eu-ai-act-baseline). Built-in wins over a pack with the same name in the config directory.
  3. Local pack directory — Config dir (~/.config/assay/packs on Unix, %APPDATA%\assay\packs on Windows). Look for {name}.yaml or {name}/pack.yaml; reference must be a valid pack name (see SPEC).
  4. Registryname@version or pinned name@version#sha256:...
  5. BYOSs3://, gs://, az:// (Bring Your Own Storage)
  6. NotFound — Error with suggestion (e.g. "Did you mean …?" or "Available built-in packs: …").

This order is fail-closed: if a pack cannot be resolved or verified, the CLI errors immediately.

Canonical Digest (Deterministic IDs)

To ensure reproducible verification across environments, pack integrity is checked using a canonical digest:

  1. Parse YAML using a strict subset:
  2. Reject anchors (&), aliases (*), tags (!!)
  3. Reject multi-document (---)
  4. Reject floats (use strings for decimals)
  5. Reject duplicate keys
  6. Enforce integer range: ±2^53 (IEEE 754 safe integer)
  7. Convert to JSON value
  8. Canonicalize with JCS (RFC 8785)
  9. Hash with SHA-256sha256:{hex}

This makes digests independent of YAML formatting, whitespace, and key ordering.

# Example: two YAMLs with different formatting produce the same digest
echo "a: 1\nb: 2" > pack1.yaml
echo "b:  2\na: 1" > pack2.yaml

# Both produce: sha256:abc123...

Signatures (Authenticity)

Integrity alone is not enough for commercial packs—authenticity is required.

DSSE + Ed25519

  • Packs are signed using Ed25519 + DSSE (Dead Simple Signing Envelope)
  • Signature is computed over the canonical JCS bytes, not the raw YAML
  • PAE (Pre-Authentication Encoding) per DSSE specification

Sidecar Endpoint

To avoid HTTP header size limits (8-16KB), signatures are fetched from a sidecar endpoint:

GET /packs/{name}/{version}.sig

The client uses a sidecar-first policy for signed packs. The X-Pack-Signature header is only used as fallback.

Trust Model: No-TOFU

Assay uses a no-TOFU (Trust On First Use) trust chain:

┌─────────────────────────────────────────────────────────────┐
│                    assay-cli (client)                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Pinned Root Key IDs (compiled into binary)          │   │
│  │  └── TrustStore                                      │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            │ verifies DSSE signature
┌─────────────────────────────────────────────────────────────┐
│                    Assay Registry                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  GET /keys (keys manifest, DSSE-signed)              │   │
│  │  - pack-signing keys                                 │   │
│  │  - validity windows                                  │   │
│  │  - revocation list                                   │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            │ provides pack-signing keys
┌─────────────────────────────────────────────────────────────┐
│  Pack Verification                                          │
│  1. Fetch pack YAML                                        │
│  2. Compute canonical digest                               │
│  3. Fetch signature sidecar (.sig)                         │
│  4. Verify DSSE with manifest key                          │
└─────────────────────────────────────────────────────────────┘

Key Properties

Property Description
Pinned roots Root key IDs are compiled into the CLI binary
Manifest verification Keys manifest must be signed by a pinned root
Revocation enforced Revoked/expired keys are rejected
Brick-proof Pinned roots cannot be remotely revoked

Caching

Packs and metadata are cached locally for performance. The cache is treated as untrusted:

Cache Location

~/.assay/cache/packs/{name}/{version}/
├── pack.yaml          # Pack content
├── metadata.json      # ETag, expires_at, digest
└── signature.json     # DSSE envelope (if signed)

TOCTOU Protection

On every cache read:

  1. Recompute the canonical digest
  2. Verify signature (when required)
  3. On mismatch: evict + refetch

Atomic Writes

Writes use temp file + rename to avoid partial/corrupt cache entries.

Lockfile (Reproducible CI)

For CI and enterprise pipelines, assay.packs.lock (v2) pins:

version: 2
packs:
  - name: eu-ai-act-baseline
    version: "1.2.0"
    digest: "sha256:abc123..."
    source: registry
    registry_url: "https://registry.getassay.dev"
    etag: "\"abc123\""
    signature:
      algorithm: Ed25519
      key_id: "key-prod-2026-01"

Lockfile Behavior

Scenario Behavior
Lockfile exists, digest matches Proceed
Lockfile exists, digest mismatch Hard error
No lockfile Fetch and verify, optionally create lockfile

Authentication

Static Token

export ASSAY_REGISTRY_TOKEN=ast_abc123...
assay evidence lint --pack commercial@1.2.0 bundle.tar.gz

OIDC Token Exchange (CI)

For CI environments (GitHub Actions, GitLab CI), Assay supports OIDC token exchange:

sequenceDiagram
  participant CI as GitHub Actions
  participant CLI as assay-cli
  participant OIDC as GitHub OIDC
  participant REG as /auth/oidc/exchange

  CI->>CLI: assay evidence lint --pack commercial@1.2.0
  CLI->>OIDC: request ID token (aud=registry)
  OIDC-->>CLI: id_token (JWT)
  CLI->>REG: POST {id_token, scope}
  REG-->>CLI: access_token ast_... (expires_in)
  CLI->>CLI: fetch pack with Bearer token

Protocol Details

HTTP Headers

Header Direction Description
X-Pack-Digest Response Canonical SHA-256 digest
ETag Response Resource version for caching
Cache-Control Response max-age for cache expiry
If-None-Match Request Conditional GET for 304
Authorization Request Bearer token for auth

Status Codes

Code Meaning
200 Success
304 Not Modified (use cache)
401 Authentication required
403 Access denied
404 Pack not found
410 Revoked (hard error)
429 Rate limited (retry with Retry-After)

DoS Protection

Strict YAML validation includes DoS limits:

Limit Value
Max depth 50
Max keys 10,000
Max string length 1 MB
Max input size 10 MB