Relevant inventory and vulnerability gate coverage
PGF-4 builds the gate projection from authenticated current inventory. The shared policy.vuln_gate_global relation contains the exact union required by registered tenant inventory. A tenant earns its own coverage even when another tenant already needs the same package. Missing projection rows mean clean only after the requested inputs have complete coverage in the same database snapshot as the gate read.
The acceptance limits remain 5,000,000 global rows and 4,000,000,000 physical bytes for policy.vuln_gate_global. Scope folding is retained, but its earlier 25.2M-row estimate does not satisfy these limits. Source qualification does not establish the live row count, physical reclaim, or budget retirement.
Inventory owners
| Owner | Admission and retirement |
|---|---|
| Scanner | Tenant-scoped current successful-inventory snapshot, followed by the existing completed-scan stream and periodic complete reconciliation (see owner feed cycle). Detail reads must match the snapshot’s successful scan, PURL digest, update identity and component count. Catch-up envelopes carry the logical stream name scanner.scans (ScanCompletedContract.StreamName) like the page; the tenant is bound by the page and the payload. Failed attempts preserve the last successful inventory. |
| SbomService | Existing fenced version snapshot and upload/retirement stream, with the same owner feed cycle. Policy verifies immutable version identity, tenant, chain, sequence, artifact identity, document digest and supported document format before admitting selectors. A version whose document the admission rule refuses is quarantined. |
| ReleaseOrchestrator | Bounded enrollment of a specific owner-read release and its verified Scanner evidence. Previously enrolled release references are reverified in bounded pages. An exact owner 404 retires the held revision; transport or authentication failure does not. |
There is no caller-selected package enrollment API, sibling-database read, second full-corpus store, or shadow writer. Policy keeps source identities and selector membership; producer documents and scan payloads remain with their owners.
CycloneDX admission includes nested packages beneath metadata.component even when that document subject has no PURL of its own. Metadata and component objects must be unambiguous; malformed or duplicate properties withhold admission. An explicit empty component array can certify empty only after the subject’s descendants are examined.
Scanner’s GET /api/v1/scans/completed/inventory requires its existing projection-read authorization and an exact tenant. The response distinguishes complete empty inventory from withheld/unavailable inventory. Continuations carry both the actual completion stream epoch/head and inventoryRevision, a versioned digest over all current owner metadata versions. An update or retirement behind the cursor invalidates continuation even when no completion event was added. Conflict restarts a complete snapshot; unavailable or malformed responses never advance a completeness receipt.
Policy verifies the existing PURL and finding set digests without changing their wire algorithms. Distro inventory must explicitly identify its source package, including a known self-origin. An absent origin remains unknown. Matching continues to use PurlComponentExtractor.TryParseFullWithOrigin, ecosystem equivalence and EcosystemAwareJoinPlan; namespace, qualifier and ecosystem-less behavior is retained. Selectors store only the matcher’s actual canonical name/ecosystem buckets, including language-to-distro coordinate aliases and source-package buckets. Equivalent raw ecosystem spellings are expanded by the indexed owner lookup rather than stored as duplicate memberships. Unrelated names and ecosystems are never crossed. Owner selector leaves preserve encoded name delimiters after PURL decoding: for example, pkg:generic/tool%3Fkit@1 selects tool?kit, not tool. The projection’s existing index candidate rule and exact advisory matcher remain separate from this owner selector identity. The selector POST carries the decoded leaf as JSON data on its fixed route. Literal ? and # in that name are not URI qualifiers or fragments; / and : remain namespace separators and cannot occur in an already extracted leaf.
Owner feed cycle
Each Scanner tenant feed and the SbomService feed moves through four phases: snapshot (the owner’s complete fenced inventory, page by page), reconcile (retire what the snapshot no longer lists and certify what it admitted), catchup (the owner’s event stream from the snapshot’s head) and ready (caught up). A ready feed keeps reading the event stream, so a new completed scan or SBOM upload is admitted without another snapshot. A new complete reconciliation snapshot starts once after each process start and then each PolicyEngine:VulnGateProjection:Inventory:ReconciliationInterval (default 10 minutes) after that feed’s last complete snapshot. It catches retirements and updates the stream does not carry, such as a retired scan. A gap, a foreign epoch, an unknown event type or version, or an envelope that names another stream fails the page and keeps the feed where it was. Relevance completion waits while any feed is in snapshot or reconcile, so it is held only during a reconciliation, not on every projection pass (PGF-13, 2026-10-19: before this, every ready feed started a new snapshot at once and readiness depended on the feeds’ phases happening to align).
Quarantined owner inventory
The admission rule above is unchanged. It refuses, for example, a distro (apk, deb, rpm) component without an explicit source-package origin=, an SBOM component without a usable PURL, and an unsupported, malformed or ambiguous SBOM document. Each refusal is a fixed message. Since PGF-13 a refusal applies to that one owner source, an SbomService version or a Scanner inventory, instead of failing the owner page that listed it:
- The owner adapter verifies the source’s owner identity and content digest first. An integrity failure (a digest, tenant or generation mismatch, a malformed or incomplete owner response, a succeeded scan without evidence) still fails the page and is retried. Only the admission rule’s refusal of verified content is quarantined.
- The source’s row in
vuln_relevance_sourceskeeps its tenant, owner, source key (SBOM version id or image digest), revision and content digest, and records the refusal inquarantine_reason(migration 028). It has no selector membership; membership of an earlier admitted revision is withdrawn. A check constraint refusesadmittedfor a quarantined row, and snapshot reconciliation never certifies one. - Its selectors therefore read as uncovered. Every gate reader (findings lookup, first-baseline enrollment, effective VEX batch reads, worker exact and advisory reads) answers that artifact’s inputs as unavailable. Quarantine never reads as clean or as a pass. A selector that another admitted source of the same tenant carries keeps that source’s coverage.
- The feed and relevance completion finish for every other source and tenant. One refused document no longer keeps every tenant’s gate readers unavailable.
- A newer revision is evaluated normally. An admissible rescan or a new SBOM version is admitted and earns coverage like any new selector; a refused one replaces the record. The owner’s retirement, or a complete snapshot that no longer lists the source, removes it. Quarantine also runs while the capacity gate pauses growth: it only withdraws membership.
doctor.policy-engine.gate-inventory.quarantine(warning, data integrity) is unhealthy while any source is quarantined. Its evidence counts the quarantined sources by owner and by reason and names up to 25 of them by owner, source key, revision and reason. It carries no tenant identifier; the policy-engine log names the tenant of each quarantine.
ReleaseOrchestrator references are not quarantined. A release whose curated components or Scanner evidence the rule refuses reads unavailable when a reader enrolls it.
Indexed owner lookup
Vulnerabilities owns POST /api/vulnerabilities/v1/issues/selectors:page. A request names one normalized leaf and ecosystem (empty ecosystem selects the existing name-only matching mode), an optional original fence, and a stable issue-key cursor. Pages contain at most 500 issue keys. The consumer applies the exact matcher to the candidate keys; the page bound never truncates the matching set.
The operation checks the owner fact/consensus epoch and head in one repeatable-read snapshot. It also requires the owner’s existing DC-09 content readiness (a latest completed source generation that ingested a document) and exact corpus indexes in that snapshot. Initialized empty streams, active-only or completed-empty ingestion remain unavailable. A missing selector in a ready corpus is explicitly complete empty. Unavailable stream metadata, pending consensus changes, an overtaken retention fence, or a missing/invalid required index withholds coverage. The leaf and leaf-plus-ecosystem indexes are migrations 025 and 026 in Vulnerabilities and use the shared resumable post-start index builder. Their exact migration checksums and catalog definitions are pinned; process startup does not build these large indexes inline.
One durable lifecycle
Policy migration 027 follows folded-scope migration 025; migration 026 is reserved for system-default overlay precedence. Migration 027 revokes old completion receipts before the new lifecycle can serve readers. Migration 028 adds the quarantine record of vuln_relevance_sources and its never-admitted check constraint (PGF-13).
| Policy relation | Identity and disposal |
|---|---|
vuln_relevance_feeds | One owner/tenant snapshot and event cursor. Retained through interruption and owner outage; removed after exact feed/tenant disposal. |
vuln_relevance_sources | Tenant, owner and source key with verified revision/content identity and actual Hub admission fence. Supersession and retirement compare the held identity. A quarantined source records its refusal in quarantine_reason and is never admitted. |
vuln_relevance_selectors | One shared normalized name/ecosystem selector and membership refcount. It grants no tenant coverage. Removed only after all memberships and tenant coverage references are gone. |
vuln_relevance_coverage | Tenant/name/ecosystem coverage and pending page/replay continuation. Removed after that tenant’s last membership retires. |
vuln_relevance_membership | Tenant/source/selector membership. Source admission, reference changes and the owner cursor commit atomically. |
vuln_relevance_control | One leased clear/backfill/ready and prune cursor. Recovery discards derived progress, preserving owner identities and overlay source records. |
Tenant sources, membership and coverage enforce row-level security. Each foreign key has an indexed access path. The global projection and normalized selector union remain single-copy; tenant/environment relations contain their existing outcome overrides.
For each new tenant selector the lifecycle reads all owner issue pages, commits rows and continuation together, visits relevant local overlay-only issue keys through a resumable indexed continuation, then replays dense consensus changes through the actual post-hydration fence. This includes inventory first admitted after initial bootstrap. A partial page, crash, epoch change or retention gap cannot earn coverage. A continuously referenced selector retains its earned coverage when a source revision changes; a selector revived after a zero-reference interval must earn a new certificate. Source admission still requires its fresh actual Hub fence to be reached.
Admitting pending selector work also fences the shared union atomically. A new tenant’s partial page cannot rewrite a shared issue and leave an existing tenant’s older certificate readable. Normal events, overlay/window/environment derivation and backfill record the actual owner head observed after fact hydration in required_seq. Readers stay unavailable until the normal checkpoint reaches that known fence, or the recovery lifecycle replays every completed selector through the latest observed fence before establishing its first checkpoint. An unobserved head is never invented. After fetching any facts, the source also revalidates the owner’s fact/consensus consistency certificate through one indexed selector probe for an actual input leaf. A consensus head alone cannot certify facts still pending in the owner’s projector. Normal events, backfill, local overlays and replay refuse that pending state before committing rows or coverage progress. Sources without this certificate also refuse.
Release findings bind the exact owner-read release records and successful scan identities, including image platform, to the admitted revision. A changed scan or curated record cannot reuse an earlier certificate merely because its package list repeats. The revision is rechecked with coverage inside the reader’s database snapshot.
Normal events filter proven irrelevant keys before fact retrieval and still advance past those events. Initial fill, epoch/retention recovery, trust/platform rederivation, overlay application and expiry use the same relevant union. Global rows are pruned only when no live membership needs them. Positive retained tenant tombstones authorize tenant disposal; absence or suspension does not.
The capacity writer gate pauses growth. Exact retirement, complete snapshot reconciliation and bounded pruning continue. New inventory is marked deferred without creating source, selector or membership rows; reopening requires a fresh complete owner snapshot. A supersession retains still-needed membership while removing obsolete membership. It cannot silently certify omitted growth.
Mounted corpus generations have no consensus-stream coverage certificate. Selecting an uncertified source invalidates existing completion before owner I/O, preserves a known recovery fence when an actual epoch/head is available, and stays unavailable through retry and restart. No guessed watermark substitutes for an unavailable head.
Findings lookup, first-baseline advisory enrollment, effective VEX batch reads, and worker exact/advisory reads each combine coverage with their existing recovery, overlay and environment readiness checks inside one repeatable-read snapshot.
Configuration and coordinated cutover
The concrete source, fence, physical-reclaim and compatible recovery sequence is in the cutover procedure. Its runtime steps require the coordinated accepted window; this source package does not execute them.
The supported runtime source is PolicyEngine:VulnGateProjection:InputSource=hub-lookup. Inventory also requires explicit PolicyEngine:VulnGateProjection:Inventory:SbomBaseAddress and ControlTenant values; MaximumDocumentBytes defaults to 67,108,864 and must be bounded. ReconciliationInterval (a TimeSpan, default 00:10:00, allowed from 1 minute to 24 hours; outside that range the inventory feeds fail closed) sets the owner feed cycle. Existing Scanner, ReleaseOrchestrator and Authority client settings supply their owner origins and authentication. Missing owner/authentication configuration fails closed.
The source package must be accepted together with PGF-6 recovery and the coordinated system-default overlay implementation before a Policy image is deployed. Its rollout receipt must bind the final source, configuration, migrations, deferred-index catalog definitions, image digests, owner API contracts and accepted validation inputs. Consumer support precedes producer activation. No partial Policy image is an accepted cutover.
Fresh Scanner cohorts must contain the origin change and the accepted signing/admission source. Key provisioning must follow the accepted regional-settings and operation-purpose contract from CCP-087. A protected key pointer and independently supplied public trust are external prerequisites; source/tests do not enroll keys, sign a cohort or prove a usable runtime key exists. The tracked historical fixture material is not a fallback.
The approved runtime window must demonstrate uncovered requests refusing, relevant inventory admission converging, exact release/origin outcomes, restart resumption, tenant isolation, and shared rows remaining single-copy. Before reclaim, record the rollback image and use a compatible forward recovery image. Code older than folded scope migration 025 cannot safely resume writes after this cutover.
Logical deletion alone does not reclaim PostgreSQL files. The operator’s concrete reclaim plan must identify the relation/index files, free space, lock and service-impact window, and the supported physical rewrite/reindex procedure. It must retain the live and recovery inputs until the forcing functions and measurements succeed. Source work does not authorize that DDL/DML, deployment or reclaim window.
After complete coverage and physical reclaim, record exact count(*), pg_total_relation_size('policy.vuln_gate_global'), and pg_database_size(current_database()) from the Policy database. Retire/shrink the global pin only within 5M rows/4GB and ratchet the interim database ceiling to measured bytes plus 5%. If the exact required union still exceeds either limit, retain the open acceptance criteria and report the measured engineering gap. Eviction, semantic limits, silent truncation and larger budgets are not authorized remedies.
