Documentation
¶
Overview ¶
Package catalog implements the signed capability catalog plane described in analysis/2026-10-22-authority-bounded-capability-publication-architecture.md §4.5–§4.7 and §5.
Three immutable artifacts are defined:
- CLC-CAPABILITY-CATALOG-v1 — a generation-addressed snapshot of published capabilities;
- CLC-CAPABILITY-CATALOG-DELTA-v1 — an add/replace/remove delta that only applies to an exact base generation and digest;
- CLC-CATALOG-DISCOVERY-v1 — a candidate pointer that never carries an execution verdict.
The package validates artifacts and recomputes authority. It does not fetch anything, does not trust transport, and does not read an `authorized`, `verified` or `status` boolean from an artifact: all three objects are closed, so such a member is a shape error.
Signature verification is split exactly as in scheme administration: the caller verifies the detached PKCS#7 and hands in the recovered certificate; Verify re-checks the chain, the AIC principal and the key binding so the trust check cannot be skipped.
Index ¶
- Constants
- func CanonicalBytes(c Catalog) ([]byte, error)
- func CodeOf(err error) string
- func Digest(raw []byte) string
- func DigestOfDeclaration(raw []byte) string
- func KeyID(cert *x509.Certificate) string
- func ValidatePolicy(p TrustPolicy) error
- func VerifySignedDelta(raw, signature []byte, base Catalog, roots []*x509.Certificate, ...) (Result, *Catalog, error)
- func VerifySignedDeltaWithState(raw, signature []byte, base Catalog, roots []*x509.Certificate, ...) (Result, *Catalog, error)
- func WriteFile(path string, raw []byte, replace bool) error
- type Catalog
- type CompositeCatalog
- type Delta
- type DirectoryBundle
- type DirectoryCache
- type Discovery
- type Entry
- type Error
- type GenerationRecord
- type Index
- type Operation
- type RegistryState
- type Result
- type SchemePolicy
- type Status
- type TrustPolicy
- type TrustRotation
- type Verification
- type VerifiedSource
Constants ¶
const ( CatalogVersion = "CLC-CAPABILITY-CATALOG-v1" DeltaVersion = "CLC-CAPABILITY-CATALOG-DELTA-v1" DiscoveryVersion = "CLC-CATALOG-DISCOVERY-v1" )
Artifact versions.
const ( CodeCatalogShape = "catalog_shape" CodeCatalogDigest = "catalog_digest_mismatch" CodeDuplicateEntry = "catalog_duplicate_entry" CodeSchemeUnsupported = "catalog_scheme_unsupported" CodeDeclarationMismatch = "catalog_declaration_mismatch" CodeEntryOutsideAuthority = "catalog_entry_outside_authority" CodeIdentityMismatch = "catalog_identity_mismatch" CodeBootstrap = "catalog_bootstrap_required" CodeRollback = "catalog_generation_rollback" CodeFork = "catalog_generation_fork" CodePreviousDigest = "catalog_previous_digest_mismatch" CodeStateUnverified = "state_unverified" CodeExpired = "catalog_expired" CodeStale = "catalog_stale" CodeStatusStale = "catalog_status_stale" CodeProviderMismatch = "catalog_provider_mismatch" CodeSignatureMissing = "signature_missing" CodeSignatureInvalid = "signature_invalid" CodeKeyRevoked = "signature_key_revoked" CodeSignatureChain = "signature_chain" CodeSignatureNoAIC = "signature_no_aic" CodePublisherMismatch = "publisher_mismatch" CodePublisherKeyBinding = "publisher_key_binding" CodeDeltaShape = "delta_shape" CodeDeltaBase = "delta_base_mismatch" CodeDeltaOperation = "delta_operation_invalid" CodeDeltaDigest = "delta_digest_mismatch" CodeDirectoryConflict = "directory_conflict" CodeShardMissing = "directory_shard_missing" CodeDiscoveryShape = "discovery_shape" CodeDiscoveryVerdict = "discovery_verdict_member" )
Stable failure codes. A validation returns at most one of these.
const ( VerdictAccept = "accept" VerdictReject = "reject" )
Verdicts. A consumer persists a snapshot only on VerdictAccept.
Variables ¶
This section is empty.
Functions ¶
func CanonicalBytes ¶
CanonicalBytes returns the file form written by the generators: the JCS encoding plus one trailing newline. It is deterministic, so two deployers generating the same catalog produce byte-identical files to sign.
func CodeOf ¶
CodeOf returns the stable failure code of err, or "" when err is not a catalog failure.
func DigestOfDeclaration ¶
DigestOfDeclaration returns the content address of a CLC-SCHEME-v1 declaration: sha256 over its JCS form without the `digest` member. Policies and catalog entries pin the same value, so no second digest convention exists.
func KeyID ¶
func KeyID(cert *x509.Certificate) string
KeyID returns the sha256: id of a certificate's SubjectPublicKeyInfo.
func ValidatePolicy ¶
func ValidatePolicy(p TrustPolicy) error
ValidatePolicy checks the bootstrap configuration is usable at all.
func VerifySignedDelta ¶
func VerifySignedDelta(raw, signature []byte, base Catalog, roots []*x509.Certificate, policy TrustPolicy) (Result, *Catalog, error)
VerifySignedDelta verifies a detached signature over the exact delta bytes before parsing, binds the signer to the delta producer, requires the delta to continue the base producer and identity, checks freshness, pins every affected scheme declaration, checks each added/replaced entry against the signer's authority, and only then applies the delta.
Apply alone is a pure transformation and must not be used on untrusted bytes; the CLI and any consumer that persists a next catalog should use this function.
func VerifySignedDeltaWithState ¶
func VerifySignedDeltaWithState(raw, signature []byte, base Catalog, roots []*x509.Certificate, policy TrustPolicy, state RegistryState) (Result, *Catalog, error)
VerifySignedDeltaWithState is the CLI-facing variant: it first requires the supplied base catalogue to be the accepted head in state, so a signed delta cannot be applied to an older but valid snapshot and written as a fork.
Types ¶
type Catalog ¶
type Catalog struct {
Version string `json:"@version"`
Domain string `json:"domain"`
CatalogID string `json:"catalog_id"`
Generation int64 `json:"generation"`
IssuedAt string `json:"issued_at"`
ValidUntil string `json:"valid_until"`
PreviousDigest string `json:"previous_digest,omitempty"`
Producer string `json:"producer"`
Entries []Entry `json:"entries"`
}
Catalog is the immutable CLC-CAPABILITY-CATALOG-v1 artifact. The struct is the closed schema: every member below is the whole of what a consumer reads.
func Apply ¶
Apply checks the delta against base and returns the next catalog generation. The returned entries are ordered by capability, so the target digest does not depend on the order in which a producer happened to write the delta.
func LoadCatalog ¶
LoadCatalog reads and validates one catalog file. It also returns the exact bytes, because the detached signature is over those bytes rather than over a re-serialization.
func ParseCatalog ¶
ParseCatalog decodes exactly one closed catalog object and validates it.
type CompositeCatalog ¶
type CompositeCatalog struct {
Domain string
CatalogID string
Generation int64
Entries []Entry
Sources []string
SourceDigests map[string]string
}
CompositeCatalog is a deterministic merge of verified directory shards. It is deliberately not a Catalog: it has no single publisher signature and must not be passed to VerifySigned/Accept as if it did.
func ComposeVerified ¶
func ComposeVerified(sources []VerifiedSource, required []string) (*CompositeCatalog, error)
ComposeVerified merges verified shards of one catalog identity. Required source names must be present; every shard must be the same generation and digest. Conflicting entries at the same capability are refused rather than resolved by source order.
type Delta ¶
type Delta struct {
Version string `json:"@version"`
Domain string `json:"domain"`
CatalogID string `json:"catalog_id"`
BaseGeneration int64 `json:"base_generation"`
TargetGeneration int64 `json:"target_generation"`
BaseDigest string `json:"base_digest"`
TargetDigest string `json:"target_digest"`
IssuedAt string `json:"issued_at"`
ValidUntil string `json:"valid_until"`
Producer string `json:"producer"`
Add []Entry `json:"add,omitempty"`
Replace []Entry `json:"replace,omitempty"`
Remove []string `json:"remove,omitempty"`
}
Delta is the immutable CLC-CAPABILITY-CATALOG-DELTA-v1 artifact. A delta is only meaningful against one exact base generation and digest, so a deployer never has to guess which snapshot a change was authored for.
func ParseDelta ¶
ParseDelta decodes exactly one closed delta object and validates it.
type DirectoryBundle ¶
type DirectoryBundle struct {
Snapshot []byte
SnapshotSignature []byte
Delta []byte
DeltaSignature []byte
}
DirectoryBundle is the input-only exchange shape for one catalog generation: an exact signed snapshot and an optional signed delta. It carries bytes, not a verdict; Apply re-verifies whatever it consumes.
func FetchDirectory ¶
FetchDirectory reads the fixed exchange endpoints from base. It intentionally does not follow redirects or accept unbounded bodies.
func (DirectoryBundle) Apply ¶
func (b DirectoryBundle) Apply(base Catalog, roots []*x509.Certificate, policy TrustPolicy, state RegistryState) (Result, *Catalog, bool, error)
Apply tries the delta first when present; if it cannot be applied, it falls back to the signed full snapshot and only accepts that snapshot through the normal state machine. The bool reports whether the delta was used.
func (DirectoryBundle) Handler ¶
func (b DirectoryBundle) Handler() http.Handler
Handler serves the bundle at fixed paths. It executes nothing and performs no trust decision; consumers still call Apply with local policy/state.
type DirectoryCache ¶
type DirectoryCache struct {
TTL time.Duration
MaxStale time.Duration
// contains filtered or unexported fields
}
DirectoryCache is a bounded local cache for one directory exchange endpoint. A fresh entry avoids a fetch; if a fetch fails the last valid bundle can be returned as stale, and the caller's normal VerifySigned freshness policy decides whether it is still admissible. The cache never decides trust.
func (*DirectoryCache) Fetch ¶
func (c *DirectoryCache) Fetch(ctx context.Context, client *http.Client, base string, now time.Time) (DirectoryBundle, bool, bool, error)
Fetch returns a cached bundle when fresh, otherwise fetches and caches it. stale reports that the returned bundle is the last successful fetch after a network failure; callers must still validate it.
type Discovery ¶
type Discovery struct {
Version string `json:"@version"`
Provider string `json:"provider"`
CatalogID string `json:"catalog_id"`
CatalogDigest string `json:"catalog_digest"`
Generation int64 `json:"generation"`
IssuedAt string `json:"issued_at"`
ValidUntil string `json:"valid_until"`
StatusReference string `json:"status_reference,omitempty"`
SchemeDeclarationDigests []string `json:"scheme_declaration_digests,omitempty"`
}
Discovery is the CLC-CATALOG-DISCOVERY-v1 candidate record. It is a pointer to a catalog, never a decision: a consumer must fetch the named catalog, verify its signature and run Verify before executing anything.
func ParseDiscovery ¶
ParseDiscovery decodes exactly one closed discovery object and validates it.
type Entry ¶
type Entry struct {
Capability string `json:"capability"`
Provider string `json:"provider"`
Scheme string `json:"scheme"`
DeclarationDigest string `json:"scheme_declaration_digest"`
Operations []Operation `json:"operations"`
Status *Status `json:"status,omitempty"`
}
Entry is one published capability.
type GenerationRecord ¶
GenerationRecord is one entry of the local accepted-generation history.
type Index ¶
type Index struct {
// contains filtered or unexported fields
}
Index is a deterministic capability lookup index for one catalogue. It keeps the source order for duplicate detection (which Validate already refuses) and shards by the first capability byte so a 100k-entry catalogue does not require a linear scan per lookup.
func BuildIndex ¶
BuildIndex indexes a validated (or at least shape-checked) catalogue.
type Operation ¶
type Operation struct {
Operation string `json:"operation"`
Params map[string]any `json:"params,omitempty"`
}
Operation is one concrete published operation inside an entry. The params use the ordinary CLC parameter representation, so a JSON array is an enum constraint rather than a list of independent values.
type RegistryState ¶
type RegistryState struct {
Domain string `json:"domain,omitempty"`
CatalogID string `json:"catalog_id,omitempty"`
Producer string `json:"producer,omitempty"`
Generation int64 `json:"generation"`
Digest string `json:"digest,omitempty"`
History []GenerationRecord `json:"history,omitempty"`
}
RegistryState is the deployer's persisted view of one catalog identity.
func LoadState ¶
func LoadState(path string) (RegistryState, error)
LoadState reads a closed local generation state. An empty path means "nothing accepted yet".
func (RegistryState) Accept ¶
func (s RegistryState) Accept(c Catalog, res Result) (RegistryState, error)
Accept returns the state a deployer persists after accepting c. History keeps every accepted generation exactly once, so a later snapshot can still be compared against it; there is no rollback path and no recovery flag. The method re-checks identity, generation continuity, previous_digest and producer so a caller cannot bypass Verify's generation rules by calling Accept directly.
func (RegistryState) AcceptSigned ¶
func (s RegistryState) AcceptSigned(raw, signature []byte, roots []*x509.Certificate, policy TrustPolicy) (RegistryState, Result, error)
AcceptSigned verifies a detached catalogue signature and only then advances the state. This is the safe state-writer entry point; Accept alone requires the resulting signed marker and is intended for callers that already ran VerifySigned.
type Result ¶
type Result struct {
Verdict string `json:"verdict"`
Reason string `json:"reason,omitempty"`
Detail string `json:"detail,omitempty"`
Domain string `json:"domain"`
CatalogID string `json:"catalog_id"`
Producer string `json:"producer,omitempty"`
Generation int64 `json:"generation"`
CatalogDigest string `json:"catalog_digest,omitempty"`
SignerKeyID string `json:"signer_key_id,omitempty"`
// SignerCertificateDigest binds the exact certificate (validity/AIC
// container) and AICDigest binds the raw AIC extension value, so a reissued
// certificate with the same key is visible in the decision record.
SignerCertificateDigest string `json:"signer_certificate_digest,omitempty"`
AICDigest string `json:"aic_digest,omitempty"`
PolicyDigest string `json:"policy_digest,omitempty"`
StateDigest string `json:"state_digest,omitempty"`
InputsDigest string `json:"inputs_digest,omitempty"`
// contains filtered or unexported fields
}
Result is the replayable decision record. It binds the artifact, the signer, the policy, the prior state and the recomputed verdict; it never carries a bare trust boolean.
func Verify ¶
func Verify(v Verification) (Result, error)
Verify is retained only as a fail-closed compatibility stub. Use VerifySigned: it verifies the detached signature over the exact bytes before parsing or admitting the catalogue.
func VerifySigned ¶
func VerifySigned(raw, signature []byte, roots []*x509.Certificate, policy TrustPolicy, state RegistryState) (Result, error)
VerifySigned is the consumer entry point that cannot forget the detached signature. Verify alone takes a recovered certificate; a caller that skipped the PKCS#7 step could hand in an arbitrary trusted certificate and conclude that an artifact was signed by a principal that never saw it. This function closes that gap: it verifies the detached signature over the exact bytes first, then parses the artifact and runs Verify with the recovered signer.
The lower-level split is kept for callers that verify the signature elsewhere (the CLI decodes the PEM envelope first, and scheme administration has the same shape); those callers pass the recovered certificate to Verify directly and remain responsible for binding it to the bytes.
The signature covers raw rather than a re-serialization, so callers must pass the bytes they received, not catalog.CanonicalBytes output.
type SchemePolicy ¶
type SchemePolicy struct {
Declaration json.RawMessage `json:"declaration"`
}
SchemePolicy pins one supported scheme to the declaration that defines its parameter domains. Pinning the declaration itself (not only a digest) lets the deployer build the CLC-1.19 domain map without fetching anything.
type Status ¶
Status is an explicit status reference. Its presence is meaningful; an absent status is never "healthy".
type TrustPolicy ¶
type TrustPolicy struct {
Domain string `json:"domain"`
CatalogID string `json:"catalog_id"`
// Producer, when set, pins the catalog identity to one AIC principal.
// State continuity enforces the same binding even when it is empty.
Producer string `json:"producer,omitempty"`
Schemes map[string]SchemePolicy `json:"schemes"`
// MaxStalenessSeconds bounds issued_at age. Zero disables the age check
// (valid_until still applies).
MaxStalenessSeconds int64 `json:"max_staleness_seconds,omitempty"`
// MaxStatusStalenessSeconds bounds status.checked_at age. Zero disables it.
MaxStatusStalenessSeconds int64 `json:"max_status_staleness_seconds,omitempty"`
// RequireStatus refuses an entry without a status reference. Absence is
// never treated as healthy.
RequireStatus bool `json:"require_status,omitempty"`
// StatusValues pins the resolved health value for each status reference.
// When RequireStatus is set, a reference is healthy only if it maps to the
// literal "healthy"; a bare reference/timestamp is not a health verdict.
StatusValues map[string]string `json:"status_values,omitempty"`
// StatusResolver, when set, obtains the current health value for a status
// reference at verification time. It is deployment code, not policy JSON;
// an error or a non-healthy value fails closed.
StatusResolver func(reference string) (string, error) `json:"-"`
// ProviderBindings optionally binds each published provider to the catalog
// producer principal. A non-empty map is enforced for every entry.
ProviderBindings map[string]string `json:"provider_bindings,omitempty"`
ObservedAt string `json:"observed_at"`
}
TrustPolicy is the deployer-local admission policy. It is an input, never a member of a catalog artifact.
func LoadPolicy ¶
func LoadPolicy(path string) (TrustPolicy, error)
LoadPolicy reads a closed deployer policy file.
func (TrustPolicy) DomainMap ¶
func (p TrustPolicy) DomainMap(scheme, capability string) semantics.DomainMap
DomainMap exposes the policy-pinned CLC-1.19 domain map for one capability. A consumer that compares requested parameters against a published operation (for example an admission gate) must use the same map the catalog authority check used, otherwise the two readings of the same declaration can diverge. It returns nil (shape inference) when the declaration does not declare the capability.
type TrustRotation ¶
type TrustRotation struct {
Current []*x509.Certificate
Next []*x509.Certificate
OverlapUntil time.Time
RevokedKeys map[string]bool
}
TrustRotation is the catalog trust-anchor overlap profile. Current roots are always accepted before OverlapUntil; during the overlap both current and next roots are accepted; after it only the next set is accepted, so an old anchor cannot silently remain trusted after the transition.
func (TrustRotation) RootsAt ¶
func (r TrustRotation) RootsAt(now time.Time) ([]*x509.Certificate, error)
RootsAt returns the roots accepted at now.
func (TrustRotation) Validate ¶
func (r TrustRotation) Validate() error
Validate checks the rotation shape. A next set without a finite overlap deadline is rejected: "rotate forever" would make the old key impossible to retire.
func (TrustRotation) VerifySigned ¶
func (r TrustRotation) VerifySigned(raw, signature []byte, policy TrustPolicy, state RegistryState) (Result, *Catalog, error)
VerifySigned verifies a catalog under the rotation window and rejects a signer key explicitly revoked by the profile. The returned catalog is the parsed artifact, not a re-serialization.
type Verification ¶
type Verification struct {
Catalog Catalog
SignerCert *x509.Certificate
Roots []*x509.Certificate
Policy TrustPolicy
State RegistryState
}
Verification is one deployer check: the artifact plus every local input the decision depends on.
type VerifiedSource ¶
type VerifiedSource struct {
Name string
Catalog Catalog
Result Result
// contains filtered or unexported fields
}
VerifiedSource is one directory shard after detached-signature verification. The marker is unexported: a caller cannot label an unchecked catalog as verified without going through VerifySource.
func VerifySource ¶
func VerifySource(name string, raw, signature []byte, roots []*x509.Certificate, policy TrustPolicy, state RegistryState) (VerifiedSource, error)
VerifySource verifies one source catalog with the same policy/state path as the single-directory loader. A source name is required for required-shard accounting.