Policy release-set verdict evidence
Source work: CCP-085. Qualification and the committed source revision are recorded in its owning sprint; this document does not establish a deployed capability.
Policy pack evaluation can produce one signed statement for the complete release input set. The statement binds the authenticated tenant, release, component IDs and image digests, policy pack ID/version/digest, evaluation subject, run identity, outcome, rule counts and findings evidence. Each image is a member of that evaluation. The statement is not an independent verdict for each image.
The run identity is derived from the evaluation inputs (it is the evaluator’s correlation identifier, a hash of its cache identity), and the statement carries no evaluation time. A repeat evaluation of the same inputs therefore reuses the original retained envelope, and the transparency log time records the first recording only.
The findings evidence is the evaluate response’s evidence record. Since CCP-096 it includes findingsBasis; an absent basis names no release and a not_consulted basis evaluated no rule, so neither forms a statement. The statement identity is the SHA-256 of the canonical predicate, so a statement produced after that upgrade differs from one an earlier Policy Engine recorded for the same inputs and is not that earlier retained envelope.
The statement reports denylistRuleCount (rules with a CVE denylist clause) and reachabilityRuleCount (rules with a reachability clause); a rule with both clauses counts in both. A pack with only reachability rules is a complete evaluation. A pack with neither kind evaluated nothing, and Evidence refuses its statement.
The findings lookup captures the immutable component mapping from the existing authenticated Release Orchestrator owner response before Scanner fan-in and Policy projection reads. It marks the snapshot complete only after the existing coverage, readiness and tenant checks succeed. Attestation uses that captured value; it does not fetch the release again after evaluation. The component mapping participates in the evaluation cache identity, so changed images cannot reuse a result merely because the finding IDs stayed the same.
Missing owner metadata, incomplete inventory, unavailable findings and no-rule evaluations cannot produce evaluated-clean evidence (evaluation_inputs_incomplete). Complete inputs whose shape the Evidence contract refuses are refused in Policy before any Evidence call with evaluation_inputs_unsupported: a component digest that is not sha256: with 64 lowercase hex characters, an evaluation subject with more than one release: segment or a tenant: segment that differs from the evaluating tenant, and a statement larger than 4 MiB. The existing policy decision posture is unchanged. An attempted evidence failure returns an explicit unattested reference; the configured off-switch omits the reference. Failed recording attempts are not cached, so the same inputs retry Evidence. Both deterministic refusals are cached with their decision: the evaluation cache identity alone determines them, and Evidence is never contacted for them. The bounded cache retains successful receipt references, not a fresh assertion that their signatures or custody were just verified. A malformed VerdictAttestation:AttestorUrl (not an absolute HTTP(S) origin, or one with user information, query or fragment) fails Policy startup.
A package URL alone is not a complete release input. Such a pack-evaluation request returns its normal decision with unattested and evaluation_inputs_incomplete; it does not contact Evidence to manufacture an image link. The separate finding and drift attestation contracts remain distinct.
Signing and retention
The producer uses the existing Policy identity-envelope transport and Evidence’s generic sign, submit and export APIs. The signing key must exist in the configured provider registry and in the existing trusted verdict roster. Missing, revoked or wrong public trust refuses. Signature operations, hashes and canonicalization use the existing shared libraries. Unsupported configured providers or algorithms do not receive a fallback key.
The in-toto predicate type is https://stellaops.org/attestations/policy-release-set/v1; the stored artifact kind is policy-verdict. Evidence binds the actual authenticated Policy producer and selected signer/provider/mode into signed bytes. A generic authorized submitter cannot label its own reported data as an owner-evaluated Policy result. The producer identity is the issuer and subject of the gateway identity envelope that Policy signs with the platform Router:IdentityEnvelopeSigningKey; any holder of that shared key can present the Policy producer identity, as for the CCP-083 provenance producer.
The scalar artifact SHA-256 identifies the complete evaluation document. It is not replaced with whichever image a caller queried. The statement’s subject array binds the component-to-image mapping, and its scope is release-set.
Verified submission requires exportable exact archive bytes before success. A semantic retry retains the original envelope even if a new signature differs. The stored envelope’s signer metadata remains inside its signed payload; the existing archive wire is unchanged. Policy replay re-verifies that retained envelope against the configured signer and current verdict roster. Generic import cannot create or overwrite this verified identity.
An interrupted parent write can leave archived signed bytes and an earlier transparency proof. Retrying that exact statement preserves the archive’s proof observation rather than replacing it with a newer checkpoint. The parent records the current log response; neither retention nor a successful retry claims that the earlier archived proof has been independently verified. Existing verification and explicit proof refresh keep that separate responsibility.
There is one existing attestor.entries parent and one archived statement. Historical finding/drift verdict records keep their distinct path. The new pack evaluation is not also copied into verdict_attestations.
Migration 017_policy_verdict_subject_membership.sql adds only a rebuildable membership index. The parent row has scalar artifact fields and cannot answer multi-subject image queries without this archive-derived index. Each child stores tenant, parent UUID, bounded component subject name and image digest; it contains no predicate, verdict or payload copy. Parent deletion cascades to its children. The existing tenant-offboarding handler may also discard this rebuildable index while retaining the authoritative parent; its active-hold override withholds the index as well. No new disposal path is added. Verified same-parent replay can restore missing memberships; conflicting values refuse rather than silently replace history. No historical row backfill is inferred.
The protocol permits at most 5,000 subjects, matching the existing owner-client ceiling (default 256), with payload and field-length limits. Its initial unmeasured capacity declarations are guardrails, not measured capacity or an entitlement to fill the table. Existing parent budgets and owner-wide capacity behavior remain.
Reading and operational boundary
An Evidence subject query uses the exact image digest and stored kind policy-verdict, not the predicate URI. The list response carries explicit evaluationScope and typed subject membership. Release Orchestrator preserves that scope and refuses a policy entry whose scalar document digest merely happens to match the image. Its chain view describes membership in one whole-release evaluation. Presence and recorded signing remain separate from actual signature verification; the read does not report a verified verdict.
Rows stored with kind policy-verdict before this contract carry no verified statement identity and no typed membership. The resolver therefore does not match them to an image, and the Evidence repository refuses to update them, so a proof refresh of such a row returns the stored entry unchanged. They stay retrievable as recorded; no backfill or reclassification is performed.
Later runtime acceptance needs compatible Policy, Evidence and Release Orchestrator source, the normal forward Evidence migrations, sanctioned producer identity/key pointers and public verdict trust, and an actual tenant evaluation. Source tests use isolated identities and ephemeral keys. They do not activate a policy, enroll keys, admit trust, sign live attestations or deploy services.
Re-verify source with the targeted xUnit wrapper against StellaOps.Policy.Engine.Tests, selecting PolicyEvidenceProductionPathTests; run the owner resolver and chain projection tests in StellaOps.ReleaseOrchestrator.Integration.Tests. The sprint receipt retains exact commands, source and compiled-input bindings, actual counts and refusal controls.
