ctxpkg.org

RFC 0002

Provenance attestations (v2.2)

Status: Draft — open for comments

Status#

StatusDraft — nothing here is normative yet
AuthorLeanCTX maintainers
Affectsspec (new manifest member), schema, conformance (new vectors), registry protocol (attestation display)
Commentstandard@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:

DemandTodayWith attestations
Source provenance — “this onboarding pack was distilled from our repository at commit X, not from somewhere unknown”Free-text provenance object, unsigned semanticsSigned source predicate: repository, commit digest, distillation timestamp
Pipeline identity — “a known tool version produced this, reproducibly”Tool name in provenance, unverifiableSigned 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 wordSigned 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 predicate member 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_type values 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:

TypeStatesKey fields
org.ctxpkg.source/v1where knowledge came fromrepository, commit, distilled_at
org.ctxpkg.builder/v1what produced the packagetool, version, config_digest
org.ctxpkg.redaction/v1which content checks ranscanner, version, verdict (pass/findings), findings_count
org.ctxpkg.review/v1who looked at itreviewer, 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. attestations lives in the manifest, outside content — 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#

AlternativeWhy not
Embed in-toto/DSSE envelopes verbatimBrings 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 onlyDies the moment a package leaves the registry — fails the format\u2019s core promise of offline verification.
Extend the package signature to cover pipeline factsConflates two trust roles: the author seals content; attesters vouch for process. One key doing both weakens each.
Do nothingGovernance 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_at is the signer\u2019s claim. Is RFC 3161 / transparency-log anchoring worth the complexity at this layer?