One signed claim per event. One verifiable history at handoff.
Makoto is a source-first protocol for data provenance. It records an origin, appends independently signed transformation statements, lets data teams attach private JSON Schema rules by digest, and gives the recipient enough evidence to verify the history and the bytes it actually received.
{
"_type": "https://in-toto.io/Statement/v1",
"predicate": {
"event": {
"id": "urn:uuid:11111111-1111-4111-8111-111111111111",
"occurredAt": "2026-09-16T16:00:00Z"
},
"profiles": [],
"schemaVersion": "0.2",
"source": {
"kind": "urn:makoto:demo:source:synthetic-file",
"uri": "urn:makoto:demo:v0.2:source:customers-raw"
}
},
"predicateType": "https://usemakoto.dev/predicate/v0.2/origin",
"subject": [
{
"digest": {
"sha256": "4e90181e7f75ccdf0e4d81c83d281f53806bed241787cdc751e22b2d4cd19919"
},
"name": "customers.raw.json"
}
]
}Publication status. The normative text and implementation are available for public review. They are not yet an immutable tagged release.
Normative textHosted schemasRaw specificationSchema catalogRunnable proof
Read the normative text
Everything below this section is orientation. The obligations themselves — encodings, digest and signature rules, the statement model, profile resolution limits, the handoff anchor, the trust-policy grammar, the ordered verification algorithm, and every diagnostic code — are fixed in one document, reproduced in full at the normative text and served raw at /spec/v0.2/spec.md.
| Section | What it fixes |
|---|---|
| Terminology | The exact words: subject, artifact, statement, predecessor, root, head, closure, handoff. |
| Guarantee model | What a conforming verifier may conclude, and the list of things it may never conclude. |
| Encoding and cryptography | JSON and DSSE rules, canonical digests, accepted signature algorithms, lexical limits. |
| Statement model | The in-toto wrapper, the common predicate fields, and both predicate shapes. |
| Profiles | Digest-pinned closures, the bounded schema dialect, offline resolution, and content validation. |
| Handoff and bundle | Why completeness needs its own anchor, the manifest payload, and the reference layout. |
| Trust policy | The receiver-owned grammar: keys, roles, thresholds, freshness, and resource limits. |
| Verification algorithm | The ordered steps, the decision rules, and the per-check status contract. |
| CLI and library contract | Required commands, library operations, output shapes, and exit codes. |
| Hosted schemas and versioning | Which URLs are stable, what immutability means here, and how a correction is issued. |
| Security and privacy | Threats in scope, required mitigations, and the privacy model. |
| Diagnostic map | Every stable error and warning code, and the step that emits it. |
The record has four parts
- OriginThe first producer states where and how the data was collected and binds that claim to one or more exact artifact digests. An origin has no predecessor.
- TransformationsEvery later producer creates a new statement. It names the exact predecessor payload and exact input artifact, describes the operation, and binds any outputs. Earlier statements are never edited.
- AttestationsEach statement is carried in a DSSE envelope. Signatures authenticate exact payload bytes; receiver-owned policy separately decides whether those keys were authorized for those claims.
- HandoffThe sender signs the exact statement set, roots, heads, final artifacts, recipients, and required profiles being transferred. That commitment lets a receiver distinguish a complete handoff from a merely continuous fragment.
What one record actually looks like
This is the origin statement from the runnable proof, decoded from the signed envelope that ships in the bundle. It is not an illustration written for this page: the digest below is the one the verifier recomputes, and the same bytes are what the transformation that follows names as its predecessor.
{
"_type": "https://in-toto.io/Statement/v1",
"predicate": {
"event": {
"id": "urn:uuid:11111111-1111-4111-8111-111111111111",
"occurredAt": "2026-09-16T16:00:00Z"
},
"profiles": [],
"schemaVersion": "0.2",
"source": {
"kind": "urn:makoto:demo:source:synthetic-file",
"uri": "urn:makoto:demo:v0.2:source:customers-raw"
}
},
"predicateType": "https://usemakoto.dev/predicate/v0.2/origin",
"subject": [
{
"digest": {
"sha256": "4e90181e7f75ccdf0e4d81c83d281f53806bed241787cdc751e22b2d4cd19919"
},
"name": "customers.raw.json"
}
]
}
The envelope around it carries the signature. A signature authenticates these exact payload bytes and says nothing about whether that signer was allowed to make this claim — that is the receiver's policy decision, kept deliberately separate.
Core objects and their jobs
| Object | What it commits to | What remains outside it |
|---|---|---|
| Origin statement | Source claims, event identity and time, and exact observed subjects. | Whether the source claim is truthful. |
| Transformation statement | Operation metadata, predecessor payloads, input artifacts, and output subjects. | Proof that the claimed code actually executed. |
| DSSE envelope | The exact statement payload and one or more cryptographic signatures. | Authorization to make the claim. |
| Profile reference | A public or private JSON Schema root and its complete digest-pinned closure. | Ambient network retrieval or producer-controlled policy. |
| Handoff manifest | The exact evidence and artifacts intentionally transferred to named recipients. | Independent freshness expectations. |
| Trust policy | Which keys may make which claims, under which thresholds and resource limits. | It is supplied by the receiver, not trusted from the bundle. |
| Verification report | Separate outcomes for structure, cryptography, authorization, profiles, graph, handoff, freshness, and artifacts. | A single marketing-grade “trusted” badge. |
A small core; private rules without a fork
Core schemas close the portable protocol shapes. Organization-specific claims do not require a central registry or a public schema. A team can publish a profile, keep it on an authenticated internal catalog, or never publish it at all. The signed statement carries the profile URI plus the exact root and closure digests, so the receiver can resolve the bytes it has already authorized without trusting a live network lookup.
Profiles can constrain transformation metadata and, for supported structured artifacts, the artifact content itself: required fields, identifier formats, control characters, enumerated classifications, bounded string patterns, or the absence of disallowed fields. Passing a profile proves only that the inspected JSON matches those rules. It does not make the content truthful, safe, confidential, or legally compliant.
Verification is deliberately decomposed
| Question | Required evidence | Why it stays separate |
|---|---|---|
| Is the resource well-formed? | Core schema and any required profile pass. | Valid structure says nothing about identity. |
| Who controlled the signing key? | DSSE signature verifies over exact payload bytes. | A valid signature may belong to the wrong party. |
| Was that signer allowed? | Receiver policy authorizes the key for the selected claim and role. | Authorization is a consumer decision. |
| Does the history connect? | Every transformation resolves exact predecessor and artifact digests back to an origin. | Continuity does not prove that no branch was withheld. |
| Is this the complete handoff? | An authorized signed manifest commits to exact roots, heads, statements, profiles, and final artifacts. | An older complete handoff can still be replayed. |
| Are these the expected bytes? | Independent expected manifest, head, nonce, age, or artifact constraints; plus a digest of the bytes received. | Freshness and received-byte integrity are distinct. |
Machine-readable resources
Thirteen resources are served at stable URLs and pinned by digest in the catalog. The schema index reproduces every one of them in full, next to its identifier and its catalog digest, so a claim on a page can be checked against the bytes a resolver will actually fetch. The release pointer resolves the name “latest” to the current catalog, as a convenience for people. It is not an immutable protocol identifier: it moves when a new version is published, so never cite it as a schema $id, a $schema, or a pinned reference. Pin the versioned resource and its digest instead.
| Resource | Constrains |
|---|---|
| statement.schema.json | The common wrapper every predicate is carried in. |
| origin.schema.json | Source claims, event identity and time, first-observed subjects. |
| transform.schema.json | Operation metadata, exact predecessors, inputs, output subjects. |
| envelope.schema.json | The DSSE envelope and its signatures over exact payload bytes. |
| profile-reference.schema.json | A profile root plus its complete digest-pinned closure. |
| profile-dialect.schema.json | The bounded schema dialect a private profile may use. |
| handoff.schema.json | Roots, heads, statement set, required profiles, recipients. |
| bundle.schema.json | The on-disk layout a sender actually transfers. |
| dataset-manifest.schema.json | A partitioned dataset described as one subject. |
| trust-policy.schema.json | Receiver-owned keys, roles, thresholds, limits, and verification-summary trust inputs. |
| verification-report.schema.json | Per-check outcomes, never one collapsed verdict. |
| catalog.schema.json | The index itself, so a resolver can validate it before trusting it. |
| catalog.json | The published index and the digests everything else is pinned by. |
The runnable proof exercises an allowed handoff and seven deterministic denials against the pinned reference implementation.
Versions
Each protocol version is a separate family of identifiers. Evidence keeps the version it was written in, and a verifier never mixes two families in one verification.
| Version | Status | Text and schemas |
|---|---|---|
0.2 | Current. Everything else on this site describes it. | Normative text · Hosted schemas |
0.3 | Published for review; not yet tagged or the default. Adds a standard licence-claim profile and record-level commitments. | Normative text and hosted schemas |
Assurance levels
Like SLSA, Makoto separates producer tracks from receiver results. The Origin and Transform tracks each define three cumulative levels a producer adopts one step at a time; what a receiver established about one handoff is a separate verified property, carried in a standard SLSA Verification Summary Attestation. Neither is an allow/deny score or a correctness claim.
| Level | Origin track | Transform track |
|---|---|---|
| L1 | Traceable capture: the first observed bytes have origin evidence. | Traceable execution: every step binds exact inputs, predecessors, operation identity, and outputs. |
| L2 | Platform-authenticated capture: a hosted capture platform generates and signs that evidence. | Platform-authenticated execution: a hosted pipeline generates and signs the evidence. |
| L3 | Policy-controlled capture: the platform continuously enforces declared capture controls. | Hardened execution: isolated, ephemeral runs protect both execution and signing from user-controlled steps. |
The verified properties are MAKOTO_AUTHORIZED, MAKOTO_GRAPH_COMPLETE, MAKOTO_FRESHNESS_ANCHORED, MAKOTO_SCHEMA_CONFORMANT, and MAKOTO_REPRODUCED. Read the assurance model for each level's evidence and each property's exact issuance rule, or Section 8 for the normative text.
What Makoto does not claim
Makoto does not certify a source as truthful, prove that transformation code ran, establish data quality, prevent disclosure, grant legal compliance, or discover copies that nobody recorded. Hardware-backed keys, software-build provenance, transparency logs, access control, semantic catalogs, and pipeline observability can complement the protocol; none is silently implied by a passing Makoto report.
Challenge the protocol with an actual handoff.
Bring a source, two transformations, a private rule, a signer boundary, and a receiving decision. The useful contribution is the smallest case that exposes an ambiguity or produces a new conformance fixture.