Charter Agreement Protocol
Portable, non-authorizing charter-agreement format and verification protocol for bilateral commercial agreements. CAP verifies signed, byte-exact evidence of what two parties agreed, which revision governed a given instant, and which agreement state governed a signed action — without a central authority, and without ever making the decision for you.
CAP verifies. It never authorizes. Every facts record carries a closed
twelve-item not_verified floor — authority, execution, billing, term
satisfaction, legal validity, and more — that no API can shrink. Hosts read
the evidence and decide.
What it does
| Artifact | Form | Proves |
|---|---|---|
| Party Descriptor | signed JWS (cap+party) |
A party's declared key history (Ed25519, and ML-DSA from protocol_revision 3) with predecessor-bound transitions and fork evidence |
| Charter Revision | canonical JSON | Agreed terms: parties, roles, legal-text digest, precedence, effective window, termination reasons, exact deployment bindings |
| Acceptance | signed JWS (cap+acceptance) |
Bilateral signed assent to exact revision bytes |
| Termination Notice | signed JWS (cap+termination) |
Signed closure of the charter at a pure UTC instant |
| Receipt | signed JWS (cap+receipt) |
A signed action bound to exact revision coordinates, deployment digest, and grant evidence |
Set-level verification composes the artifacts into structural facts:
verify_chain/5 re-verifies everything from raw bytes;
governing_revision/2 answers "which revision governed at this instant" with
a digest, :contested, or :none — never a silent tie-break. Same-signer
equivocation is retained as signed evidence with no winner. The only repair
for a contested view is a countersigned supersession revision.
The foundation is byte-exact by construction: strict unpadded base64url, deterministic tagged JSON decoding, RFC 8785 canonicalization, and domain-separated SHA-256 digests. A certified 100-case corpus runs through a pure Elixir runner and a builtins-only Node TypeScript verifier that must produce byte-identical canonical reports — two independent implementations, zero shared code.
When to use it — and when not
Use CAP when two independent parties need portable, re-verifiable agreement evidence exchanged as bytes: agent commerce charters, bilateral supplier terms, key-history continuity proofs, action receipts for audit.
Do not use CAP for authorization decisions, live revocation checks, term
evaluation (CAP leaves term_satisfaction in its omission floor), legal
adjudication, or single-party self-attestation — every one of those is
explicitly outside what verification proves. See the
security model for the full proves/never-proves
table.
Quick start
Elixir ~> 1.19 (tested lines 1.19.x and 1.20.x) on Erlang/OTP 28 or 29 —
the range and the supported-OTP set are enforced in-repo (Mix refuses an
Elixir outside the range and config/config.exs refuses an OTP major outside
the set, both before anything compiles), and they move in lockstep with
.tool-versions and the CI lanes (see
the supported-toolchain ADR). Zero runtime
dependencies (OTP :crypto only). From
protocol_revision 3 the runtime floor has a second axis: ML-DSA verification
needs an OTP runtime whose linked crypto library is OpenSSL ≥ 3.5 (FIPS 204
landed there; OTP ≥ 28.1 with OpenSSL ≥ 3.5 is the declared floor — a runtime
linked against OpenSSL 3.0.x cannot generate or verify ML-DSA keys, and OTP 27
exposes no ML-DSA algorithms to :crypto even with OpenSSL ≥ 3.5 linked). The
shipped corpus contains ML-DSA cases, so the quick-start verification below
needs that floor too:
{:charter_agreement_protocol, "~> 0.3.0"}
Then verify the shipped, certified corpus from your dependent project:
$ mix run -e 'System.halt(CharterAgreementProtocol.Conformance.Cli.run(["--corpus", "deps/charter_agreement_protocol/priv/conformance"]))'
The command prints the canonical JSON report and exits 0 when
all 100 certified cases recomputed and agreed. Full walkthrough:
Getting started.
Try it
- TypeScript verifier on npm:
npm install @charter-agreement-protocol/verifier— the independent second verifier as a standalone package under thecharter-agreement-protocolorganization, with the certified corpus vendored inside. - Runnable notebooks: charter tour and fork repair — a complete bilateral charter with real Ed25519 signatures, and a manufactured equivocation with its countersigned repair.
- Repository demo:
mix run examples/supplier_fork_demo.exs— equivocation evidence, contested governing view, an action receipt inside the fork, and the repair, in nine lines of output.
Guarantees at the call boundary
- Failures are typed and value-free — closed error codes, protocol-owned subjects, never rejected input — so verification failures are safe to log.
- Facts implement redacted inspection — retained signed artifacts never appear in logs.
- Verification is pure: no clock, filesystem, network, or environment. Callers supply time, limits, trust anchors, and keys.
- Key custody stays outside the protocol: CAP builds the exact RFC 7515
signing input, you sign it,
assemble_compact/2accepts only an external raw signature at the registry row's exact length (64 bytes forEd25519; 2420/3309/4627 for the ML-DSA parameterizations), and hosts post-verify before serving the compact. A reviewed companion signer implements that host glue for you —charter_agreement_signer(atomic kid/key snapshot, wrong-key guard, post-sign verify, refusal surfacing); verifiers never depend on it, and hosts may always hand-roll per the spec instead.
Guides
- Overview — the problem, the artifact family, the design principles
- Getting started — install, first verification, first signature
- Artifacts — every artifact's wire shape and closed claim set
- Verification semantics — forks, contested views, supersession, governing computation
- Receipts — binding actions to agreements; ABP and BAP identity composition
- Extensions and profiles — the registry, criticality, quarantine
- Security model — proves / never proves, the omission floor, architecture enforcement
- Recipes — end-to-end integration patterns
- Conformance — the certified corpus, the gate battery, the certified identities
- FAQ
- Protocol foundation — the normative specification
- Indexed-price profile, errata policy, ADRs: no version tokens in identifiers, conformance and release-candidate boundary
Status
The approved protocol core, normative specification set, certified corpus
with four recorded identities, independent second verifier, mutation
battery, and release-candidate gates are implemented and green in CI. The
0.3.x line carries protocol_revision 3 — the ML-DSA registry act
(RFC 9964): the ML-DSA-44/65/87 names verify from revision 3, the
descriptor key grammar admits ML-DSA keys gated on revision, producers
mint ML-DSA-65 at revision 3 alongside Ed25519 at revision 2, and the
resource boundary is byte-weighted for PQ-sized artifacts. 0.3.1 corrects
the declared runtime floor (OTP ≥ 28.1 with a linked OpenSSL ≥ 3.5),
runs CI on an ML-DSA-capable substrate, and re-trues the shipped
documentation to the revision-3 surface. protocol_revision 2
(RFC 9864 alg names) and revision-1 artifacts remain verifiable. Building
an archive remains verification evidence only — never authority to
publish.
Development
mix deps.get
mix quality
mix quality is the complete gate — audits, formatting, warnings-as-errors
compile, strict credo, the full test suite with its coverage threshold,
certified-conformance verification and regeneration identity, all 25 named
source mutations, Elixir/TypeScript verifier agreement over repository and
unpacked-package corpora, dialyzer, docs, and the reproducible
release-candidate archive. Contribution bar and invariants:
CONTRIBUTING.md.
License
Apache-2.0 — see LICENSE.