RFC 0002
Provenance attestations (v2.2)
Status: Draft — open for comments
Status#
| Status | Draft — nothing here is normative yet |
| Author | LeanCTX maintainers |
| Affects | spec (new manifest member), schema, conformance (new vectors), registry protocol (attestation display) |
| Comment | standard@ctxpkg.org |
1Summary#
An optional attestations array in the manifest. Each entry is a
typed, independently signed statement about how the package came to
be: which source it was distilled from, which tool built it, which
redaction checks ran, who reviewed it. Attestations travel inside the
package and verify offline — the same property the format already has for
integrity and authorship. Inspired by what SLSA and in-toto did for the
software supply chain, expressed natively in CTXPKG JSON rather than as an
embedded foreign envelope.
2Motivation#
In plain terms: today a package proves who sealed it and that nothing changed since. It cannot prove where the knowledge came from or what checks it passed before sealing. For context that steers production agents, organizations need exactly that.
The signature (§9) authenticates the publisher. It says nothing about the pipeline before the signing key was applied. Three concrete demands keep arriving from governance-minded adopters:
| Demand | Today | With attestations |
|---|---|---|
| Source provenance — “this onboarding pack was distilled from our repository at commit X, not from somewhere unknown” | Free-text provenance object, unsigned semantics | Signed source predicate: repository, commit digest, distillation timestamp |
| Pipeline identity — “a known tool version produced this, reproducibly” | Tool name in provenance, unverifiable | Signed builder predicate: tool, version, configuration digest |
| Pre-publish checks — “a secret scan ran and passed; a human reviewed the diff” | Invisible; trust the publisher\u2019s word | Signed redaction and review predicates with scanner/reviewer identity and verdict |
Registries can then enforce upload policies (“only packages with a passing redaction attestation enter this namespace”), engines can gate installs (“agents in the finance workspace consume only source-attested context”), and auditors get evidence that travels with the artifact instead of living in a vendor dashboard.
3Design#
New optional manifest member:
"attestations": [
{
"predicate_type": "org.ctxpkg.source/v1",
"predicate": {
"repository": "https://github.com/acme/billing",
"commit": "9f2c1a…",
"distilled_at": "2026-06-11T14:02:11Z"
},
"signature": {
"algorithm": "ed25519",
"public_key": "…64 hex chars…",
"signature": "…128 hex chars…"
}
}
] - The signed message is the SHA-256 digest (lowercase hex, 64 ASCII characters) of the
predicatemember serialized by the document-text rule of §8 — the exact character range as written, insignificant whitespace removed. One hashing rule across the whole format; no new canonicalization. - Attestation signers are independent of the package signer. A CI system, a scanner and a human reviewer each sign with their own key. Verifying the package signature does not verify attestations, and vice versa.
- Attestations are claims by their signer, not facts endorsed by the format. Readers MUST verify the signature before surfacing a predicate, and MUST NOT treat an unverified predicate as anything but untrusted text.
- Unknown
predicate_typevalues follow the existing unknown-member rule: preserved on round-trip, ignored for decisions.
4Predicate types#
Like node and edge types, predicate types form an extensible registry with
a reserved org.ctxpkg.* prefix. Initial set:
| Type | States | Key fields |
|---|---|---|
org.ctxpkg.source/v1 | where knowledge came from | repository, commit, distilled_at |
org.ctxpkg.builder/v1 | what produced the package | tool, version, config_digest |
org.ctxpkg.redaction/v1 | which content checks ran | scanner, version, verdict (pass/findings), findings_count |
org.ctxpkg.review/v1 | who looked at it | reviewer, reviewed_at, verdict |
Vendor predicates use reverse-domain prefixes
(com.example.compliance/v1) and need no permission.
5Compatibility#
- Purely additive. Packages without attestations remain valid forever. v2 readers already preserve unknown manifest members on round-trip, so existing readers lose nothing.
- Integrity unchanged.
attestationslives in the manifest, outsidecontent— both integrity digests (§8) are unaffected. The package signature (§9) covers the content digest, not the attestations, so attestations can be added after content sealing without breaking the author signature — by design, mirroring how attestation frameworks attach to immutable artifacts. - Conformance levels untouched. Attestation verification is a capability flag, not a new level.
6Conformance#
Readers that claim attestation support MUST verify
attestation signatures by the §8 text rule and report per-attestation
verdicts (verified / invalid /
unknown-type). Two new golden vectors accompany the change:
one with a valid source+redaction pair, one with a tampered predicate whose
signature must fail. Both reference verifiers
(lean-ctx pack verify, @ctxpkg/verify) ship the
checks behind a flag while this RFC is in review.
7Drawbacks#
- Key sprawl. Every attesting system holds a key. Without a discovery story (open question 1) verification degrades to “a valid signature from a key nobody recognizes”.
- False authority. A verified signature on a worthless claim still looks green in a careless UI. The spec text must be blunt: an attestation is exactly as trustworthy as its signer.
- Size. Each attestation adds ~400 bytes. Negligible for typical packages, real for thousands of nodes with per-node ambitions — which is why attestations are manifest-level only.
8Alternatives#
| Alternative | Why not |
|---|---|
| Embed in-toto/DSSE envelopes verbatim | Brings base64 payloads, a second canonicalization scheme and a foreign trust model into a format whose virtue is one hashing rule. Interop is better served by a documented mapping than by embedding. |
| Registry-side metadata only | Dies the moment a package leaves the registry — fails the format\u2019s core promise of offline verification. |
| Extend the package signature to cover pipeline facts | Conflates two trust roles: the author seals content; attesters vouch for process. One key doing both weakens each. |
| Do nothing | Governance pressure lands anyway — as unverifiable free-text conventions in provenance, different per vendor, exactly what a standard exists to prevent. |
9Open questions#
- Key discovery. DNS TXT (like registry publisher verification), a well-known URL per signer domain, or a transparency log? Probably DNS first — the registry protocol already has the machinery.
- Predicate registry governance. Same lightweight process as node/edge types, or RFC-gated because predicates carry compliance weight?
- Threshold semantics. Should a reader be able to require “at least N independent review attestations”? Engines can build this; should the spec name it?
- Timestamping. Without a trusted time source,
distilled_atis the signer\u2019s claim. Is RFC 3161 / transparency-log anchoring worth the complexity at this layer?