Packages
Portable, non-authorizing charter-agreement format and verification protocol.
Retired package: Security issue - Ed25519 forgery under two weak key encodings; upgrade to 0.6.0 (GHSA-xr83-9hrm-7hpm)
Current section
Files
Jump to
Current section
Files
docs/guides/artifacts.md
# Artifacts
CAP has five artifact types. Four are attached compact JWS (RFC 7515) whose
signatures verify under the algorithm their registry row names — Ed25519,
and pure ML-DSA from `protocol_revision` 3; one is canonical unsigned JSON.
Every signed artifact's protected header is closed to exactly `alg` (a
registry name: `EdDSA` at any accepted revision, `Ed25519` from revision 2,
`ML-DSA-44/65/87` from revision 3), `kid` (a bounded-ASCII lookup hint with
no authority by itself), and `typ`. Every artifact carries
`protocol_revision` as a digest-covered member. All bytes — headers and
payloads — must already be canonical JSON.
The public API entry points for each artifact live on
`CharterAgreementProtocol`; the member tables below are the normative closed
sets (see [Protocol foundation](../protocol.md) for the full contract).
## Party Descriptor — `cap+party`
A party's declared key history. Genesis is self-signed by an active key
declared in the descriptor itself; the party's identifier is the descriptor's
domain-separated content digest.
| Member | Contract |
|---|---|
| `protocol_revision` | `1` through `3`; unknown revisions fail closed |
| `party_id` | conditional; tagged digest, present on successors |
| `prev_descriptor_digest` | conditional; exact predecessor digest, present on successors |
| `descriptor_number` | starts at 1, increases by exactly one |
| `verification_keys` | 1–32 unique keys, each with `key_id`, `algorithm` (`Ed25519`, or an ML-DSA name gated on `protocol_revision` 3; mixed sets are legal), base64url `public_key` at the algorithm's exact length, `status`; at least one `active` |
| `attestation_hints` | 0–16 non-normative hints; never dereferenced |
| `extensions` | closed envelope, registry-validated |
| `effective_from` | uppercase UTC RFC 3339 ending in `Z` |
A successor is signed by a key active in its predecessor; predecessor lineage
supplied as facts is reverified, never trusted. Before runtime signature
verification on the Ed25519 rows, CAP rejects noncanonical point encodings
and all eight low-order torsion encodings for both the public key and the
signature `R`; the ML-DSA rows enforce the registry's exact public-key and
signature byte lengths before cryptographic work.
## Charter Revision — unsigned canonical JSON
The agreed terms. Its digest is computed over the exact canonical bytes under
the `charter_revision_content` domain. Genesis is revision 1 and carries no
`charter_id`, `prev_revision_digest`, or supersession targets.
| Member | Contract |
|---|---|
| `protocol_revision` | `1` through `3` |
| `revision_number` | 1 at genesis, exactly one more per successor |
| `charter_id` | conditional; the genesis revision's digest on successors |
| `prev_revision_digest` | conditional; exact prior-numbered revision digest |
| `supersedes` | conditional; lower-numbered accepted revisions in the same charter |
| `parties` | exactly two uniquely named roles bound to tagged descriptor digests |
| `legal_text` | `content_digest` (raw legal bytes under the `legal_text` domain), `media_type`, optional `uri_hint` |
| `precedence_declaration` | exactly `legal_text_governs` or `machine_terms_govern`; never defaulted |
| `effective_from` / `effective_until` | pure UTC; `until` strictly later than `from` |
| `attribution_declaration` | closed attribution basis |
| `termination_rules` | non-empty unique bounded `reason_codes` |
| `abp_bindings` | exact published ABP identities per party role |
| `receipt_profile` | the receipt extension profile namespace |
| `extensions` | closed envelope, registry-validated |
## Acceptance — `cap+acceptance`
One party's signed assent to one exact revision. Eight closed claims:
`protocol_revision`, `charter_id`, `revision_number`, `revision_digest`,
conditional `prev_revision_digest`, `party_descriptor_digest`, `party_role`,
`accepted_at` (pure UTC).
Verification reconstructs the signed descriptor view from retained lineages and
requires exact equality with the revision's chain coordinates and exact
membership in its party bindings.
## Termination Notice — `cap+termination`
A pinned party's signed notice. Closed payload: `protocol_revision`,
`charter_id`, `governing_revision_digest`, `party_descriptor_digest`,
`party_role`, one `reason_code`, `effective_at`, `issued_at` (may equal, never
after, `effective_at`), and optional opaque `detail_digest`. The reason must be
present in the revision's termination declaration; the `kid` must resolve to an
active key in the pinned descriptor.
## Receipt — `cap+receipt`
Signed action evidence binding an invocation to the agreement that governed it.
See [Receipts](receipts.md) for the full contract including grant references
and decision/outcome pairs.
## Extension envelope (all artifacts)
Exactly `{"critical": {namespace: body}, "optional": {namespace: body}}`. A
namespace is at most 512 UTF-8 bytes in lowercase reverse-DNS-plus-path form
with exactly one slash; at most 32 namespaces per artifact; a namespace never
appears in both regions. Registered critical bodies validate against the exact
schema digest compiled into the registry; unknown critical namespaces fail
closed; unknown optional bodies are retained byte-exactly and quarantined. See
[Extensions and profiles](extensions.md).
## Facts records
Verification never returns raw artifacts as results — it returns *facts*:
redacted structural records (descriptor, acceptance, termination, revision,
chain, receipt facts). Facts implement redacted inspection so logs never expose
retained signed artifacts, and every facts record is built through one shared
constructor that forces the closed twelve-item `not_verified` floor. See
[Security model](security-model.md).