Skip to main content
Version: Development

Tenant-scoped publication catalog

Catalog format 2 implements the storage contract in RFC-0002 and ADR-0027. It separates component bytes, complete immutable packages and permission to use a package in a particular tenant. Obsolete catalog formats are rejected; there is no offline migration command. Deployment and runtime propagation carries exact selections through guest start. Public selectors in the RPC, CLI and six SDKs preserve the same publication identity.

Web publication admission uses the same exact scoped reference construction, root owner and combined storage/index quotas. Its componentless lifecycle has an explicit admission-mode upgrade that older readers reject before shared-blob recovery or collection.

ReleaseDigest remains the SHA-256 of executable bytes. PackageDigest remains the complete immutable package identity. A PublicationRef contains an explicit scope and a publication:sha256: ID computed with the RFC's domain-separated, length-prefixed construction. Trusted local publication derives its publication from the canonical original COMPLETE record; it invents no package digest, publisher signature or observed build.

Two packages with unchanged Wasm can coexist, including a package with a corrected embedded SBOM. Two authorized tenants can independently admit an identical tenant-neutral package. A manifest with an embedded tenant continues to restrict admission to that tenant. Admission never rewrites package metadata to substitute a receiving tenant.

Selection and authority​

DirectoryArtifactRepository exposes exact publication selection, metadata, fetch, lifecycle status, execution eligibility, lifecycle mutation, evidence renewal and explicit re-verification. Callers authorize the scope before using these internal interfaces. Possession of an ID is not permission.

Publication catalog reads and mutations accept PublicationRef directly. The obsolete component-or-publication selector and its repository fallback are removed. A reference must match the authorized scope; a foreign ID in that scope appears absent. Component bytes do not substitute for publication identity. Lifecycle status, revocation/retirement, evidence renewal and operation recovery use this exact publication API. The artifact repository no longer exposes a parallel component-addressed lifecycle interface. Operation recovery preserves the captured publication association. Internal content-addressed reads still reject ambiguous component associations; revoked and retired publications continue to count toward ambiguity.

Scoped catalog pages use deterministic publication-ID order and version-2 opaque cursors. Component digests are not a unique row key or a pagination ordering contract. A repeated page in the same catalog generation preserves its order; scope changes and stale generations invalidate its cursor.

Generations, lifecycle records, selected evidence and live grants are independent per publication. A mutation's compare-and-swap precondition belongs to that publication. A new package starts with expected generation zero. Reusing the generation of another publication is a conflict.

Retained successful operation receipts preserve their exact publication association. Replay after coexistence or restart returns that captured publication and the original historical result, even if fresh component selection is now ambiguous. Replay does not restore current eligibility to a revoked or retired publication.

Persistence and resource ownership​

The catalog root contains:

PathResponsibility
publications/<publication hex>/Immutable original metadata, component, admission/evidence files and COMPLETE
blobs/<blob hex>Shared immutable file content, hard-linked into publications
lifecycle/records/<publication hex>.jsonIndependently mutable lifecycle state with monotonic generations
lifecycle/receipts/Bounded operation ring with exact publication associations
lifecycle/evidence/Independently selected immutable evidence revisions
.tmp/Owned publication/reclamation staging

The node places this catalog root at <dataDirectory>/releases. Its publications use independent publication identities; obsolete component-keyed roots are rejected. Updates persist the bounded changed row, receipt, intent and HEAD rather than rewriting the whole catalog. No publication owns a worker, file descriptor, guest store, execution cell or provider pool.

The shared content budget conservatively charges global blob bytes plus every publication link's full logical byte size. It is an exposure bound, not a disk or RSS measurement. Metadata, file counts, publication counts, input sizes and startup directory scans have independent limits. Incomplete directories retain their disk and directory charges. Node configuration exposes the content limits under catalogs; lifecycle history has separate finite limits.

reclaim_uncommitted_content accepts a batch of 1–1024 entries. It only removes uncommitted publications and zero-reference blobs. A committed publication, including revoked/retired history, keeps its content pin. A live preparation source therefore cannot lose committed bytes through this maintenance operation. Directory unlink and parent synchronization precede refunds. An indeterminate write or reclamation failure requires reopening; it cannot refund a live owner. Unknown incomplete directories require offline inspection and repair. This implementation does not offer deletion of committed historical payloads.

The authenticated node inventory exposes a fixed publication-catalog topology row with actual usage and limits. Use the capacity and maintenance guide to calculate remaining budgets and plan a stopped backup or explicit reprovisioning. Unavailable measurements are not zero usage.

Supported storage and fresh state​

Current alpha builds accept the publication catalog and lifecycle format 2. The obsolete format-1 reader, offline migrator, retained migration associations and latentd migrate-catalog command have been removed. Obsolete roots and interrupted migration fences are rejected before temporary cleanup, publication indexing or lifecycle grants. Startup preserves their existing bytes.

For an obsolete root, stop the old node and preserve a complete, consistent backup of its data directory, protected configuration and trust history. Provision a separate empty data directory with the current configuration, explicitly publish and admit the intended packages, and apply current deployment and trigger manifests. Historical operation identities and receipts are not imported. Do not copy individual rows, remove format markers or point the new node at an old root to force startup.

Current-format reopen, crash recovery, policy denial and operation receipts retain their normal semantics. The qualified native upgrade pair uses current storage formats; it does not convert obsolete catalog formats. Downgrading a binary does not undo storage changes. Preserve stopped backups independently.

Validation​

Small Linux fixtures cover concurrent publications, scoped selection, independent revocation/retirement, exact operation replay after coexistence, storage limits, shared inode retention during orphan reclamation, preservation of rejected obsolete roots, empty catalogs and current lifecycle interruption recovery. Policy integration uses real publisher and builder signatures with corrected embedded inventories and identical packages in two tenants. These are functional tests, with no guest invocations or 100k load campaign.