Makoto
Menu
Receiver verification

A signature is one check, not the verdict.

A receiver should be able to verify the evidence without access to the producer’s platform: parse the resources, authenticate each signed claim, decide whether each signer was authorized, reconstruct the source-to-handoff graph, resolve exact schemas, and hash the bytes that actually arrived.

The code this page is about. Repeated from Run the reference verifier below.
uv run makoto verify bundle demos/end-to-end/generated/positive-bundle \
  --policy demos/end-to-end/generated/receiver/policy.json \
  --schema-catalog demos/end-to-end/generated/receiver/catalog.json \
  --expected-manifest sha256:e24aae77ba1374162edff49aa0ab1d8dd7e38c333e134a2128ad1598fec99fb7 \
  --expected-head sha256:99aee76db46a720405da6d0015d427c791b0d4e02367b9e2bc36ce82ae812bce \
  --expected-artifact demos/end-to-end/generated/receiver/expected-artifact.json \
  --evaluation-time 2026-09-16T16:00:00Z \
  --json

Verify from your language

All language interfaces →

Each page below is a complete working path: install, validate structure against the hosted schemas, produce an attestation, verify a bundle, branch on a failed verdict, and read the report the verifier returns.

What a real verdict checks

CheckQuestion answeredWhat failure means
StructureDo the statements, envelopes, profiles, policy, and handoff match their JSON Schemas?The evidence is malformed or violates a required profile.
CryptographyDo the DSSE signatures authenticate the exact payload bytes?The metadata changed, the signature is invalid, or the key does not match.
AuthorizationDoes receiver-owned policy trust each key for this source, operation, profile, and handoff?A mathematically valid signature was made by the wrong party.
GraphDoes every transformation point to the exact predecessor statement and input artifact?A step is missing, rewired, duplicated, or disconnected from an origin.
HandoffDoes the signed manifest commit to the exact roots, heads, profiles, statements, and final artifacts?The presented graph may be continuous but incomplete.
FreshnessDoes an independent expected manifest, head, nonce, artifact, or age bound match?An older valid handoff may have been replayed.
Data bytesDo the bytes received by the consumer match the signed artifact digest?The data changed after the claim was made.

Run the reference verifier

Start with the deterministic handoff fixture. It produces one allowed bundle and seven denial cases, so the first experience includes the failure boundary as well as the happy path.

git clone https://github.com/makoto-project/makoto.git
cd makoto
uv sync --locked --dev
./scripts/demo.sh --acceptance

To inspect the receiver command directly:

uv run makoto verify bundle demos/end-to-end/generated/positive-bundle \
  --policy demos/end-to-end/generated/receiver/policy.json \
  --schema-catalog demos/end-to-end/generated/receiver/catalog.json \
  --expected-manifest sha256:e24aae77ba1374162edff49aa0ab1d8dd7e38c333e134a2128ad1598fec99fb7 \
  --expected-head sha256:99aee76db46a720405da6d0015d427c791b0d4e02367b9e2bc36ce82ae812bce \
  --expected-artifact demos/end-to-end/generated/receiver/expected-artifact.json \
  --evaluation-time 2026-09-16T16:00:00Z \
  --json

Exit status 0 is an allow and 1 is a deny, each with a full JSON report on stdout; 2 and 3 mean the tool could not run and no verdict exists. The expected manifest, head, and artifact are the receiver’s independent anchors, and this policy requires all three: drop them and the same bundle is denied at step 11. The report keeps schema, signature, authorization, profile, graph, completeness, freshness, and artifact checks separate. A single boolean would hide which trust boundary failed.

Open the complete walkthrough and exact denial reports →

The fourteen ordered steps

Verification is a fixed sequence of fourteen steps carrying sixteen check identifiers, and the order is part of the contract. The codes below are the complete published diagnostic map for each step; W_ codes are warnings that never deny on their own. A later check reports skipped when an earlier one did not establish its prerequisite, so a report never implies more was proven than actually was. Every entry in checks[] carries an id, a status of pass, fail, indeterminate, not_checked, or skipped, and the prerequisiteChecks that caused a skip.

Scroll horizontally to see the diagnostic codes for each step.
StepCheck idWhat it establishesDiagnostic codes
0policy loadBefore any bundle is read, the policy itself is checked for rules that shadow one another.W_POLICY_RULE_OVERLAP
1load-safelyThe bundle unpacks inside its own root, within resource limits.E_BUNDLE_UNSAFE_PATH, E_HANDOFF_REQUIRED, E_RESOURCE_LIMIT
2parse-strictly, core-schemasEvery resource is well-formed JSON with no duplicate keys, and each envelope carries the expected payload type.E_BUNDLE_UNSAFE_PATH, E_CORE_SCHEMA, E_ENVELOPE_MALFORMED, E_HANDOFF_REQUIRED, E_JSON_DUPLICATE_KEY, E_JSON_INVALID, E_PAYLOAD_TYPE, E_RESOURCE_LIMIT
3index-payloadsEach payload is addressed by its own digest, so later steps reference exact bytes.E_JSON_DUPLICATE_KEY, E_JSON_INVALID, E_RESOURCE_LIMIT, E_STATEMENT_DIGEST
4core-schemasStatements, predicates, handoff, policy, and catalog match the pinned core schemas.E_BUNDLE_UNSAFE_PATH, E_CATALOG_INVALID, E_CORE_SCHEMA, E_JSON_DUPLICATE_KEY, E_JSON_INVALID, E_PREDICATE_SEMANTICS_UNSUPPORTED, E_PROFILE_TARGET_MISSING, E_RESOURCE_LIMIT
5signaturesEach DSSE signature authenticates the exact payload bytes that were signed.E_RESOURCE_LIMIT, E_SIGNATURE_INVALID, W_SIGNATURE_UNKNOWN
6authorization-thresholdsSigner counts and thresholds satisfy receiver policy.E_RESOURCE_LIMIT, E_SIGNER_UNAUTHORIZED
7metadata-profiles, authorizationDeclared profiles resolve to pinned digests, and each key is authorized for this source, operation, and profile.E_PROFILE_INVALID, E_PROFILE_UNRESOLVED, E_RESOURCE_LIMIT, W_PROFILE_INDETERMINATE, W_PROFILE_RESOURCE_LIMIT
8graph-dependency-artifactsThe artifacts a transformation claims as inputs match their declared digests and manifests.E_ARTIFACT_DIGEST, E_BUNDLE_UNSAFE_PATH, E_DATASET_MANIFEST_INVALID, E_DATASET_MANIFEST_REQUIRED, E_PROFILE_INVALID, E_PROFILE_UNRESOLVED, E_RESOURCE_LIMIT, W_ARTIFACT_VALIDATION_LIMIT, W_PROFILE_INDETERMINATE
9graphEvery step binds a real predecessor and its exact subject, with no cycles or duplicated event identifiers.E_EVENT_ID_DUPLICATE, E_GRAPH_CYCLE, E_INPUT_DIGEST, E_PREDECESSOR_MISSING, E_PREDECESSOR_SUBJECT, E_RESOURCE_LIMIT
10roots-and-headsEach chain terminates at a valid origin.E_RESOURCE_LIMIT, E_ROOT_INVALID
11completeness-anchor, freshness-anchorsThe signed manifest set is complete, and the receiver’s independent expectations for manifest, heads, artifacts, recipient, nonce, and age hold.E_EXPECTED_ARTIFACT, E_EXPECTED_HEAD, E_EXPECTED_MANIFEST, E_FRESHNESS_REQUIRED, E_HANDOFF_NONCE, E_HANDOFF_RECIPIENT, E_HANDOFF_STALE, E_MANIFEST_SET, E_REQUIRED_PROFILE_MISSING, E_RESOURCE_LIMIT, W_FRESHNESS_NOT_CHECKED
12artifact-bytesThe bytes on disk hash to the signed artifact digest at the declared size.E_ARTIFACT_DIGEST, E_ARTIFACT_MISSING, E_ARTIFACT_SIZE, E_BUNDLE_UNSAFE_PATH, E_DATASET_MANIFEST_INVALID, E_RESOURCE_LIMIT, W_HISTORICAL_ARTIFACT_NOT_CHECKED
13artifact-profilesThe structured artifact satisfies the content rules its profile declares.E_ARTIFACT_FORMAT, E_PROFILE_INVALID, E_PROFILE_UNRESOLVED, E_REQUIRED_PROFILE_MISSING, E_RESOURCE_LIMIT, W_ARTIFACT_UNPROFILED, W_ARTIFACT_VALIDATION_LIMIT, W_PROFILE_INDETERMINATE
14decisionThe single allow, deny, or indeterminate outcome derived from everything above.E_RESOURCE_LIMIT

Why the order is published. A receiver that reruns these checks in a different order can reach a different answer from the same bundle. The sequence, the skip semantics, and the diagnostic codes are the interoperability surface, not an implementation detail.

Reading the report

Every run emits one document against the verification-report schema. Read decision first, then primaryError to learn which boundary failed, then checks[] to see what was actually established.

{
  "reportVersion": "0.2",
  "decision": "deny",
  "reportTruncated": false,
  "primaryError": "E_ARTIFACT_DIGEST",
  "bundleId": "urn:uuid:55555555-5555-4555-8555-555555555555",
  "evaluationTime": "2026-09-16T16:00:00Z",
  "policyDigest": {
    "sha256": "614eb137303bcea78f25ded2e332a10649358484f9582984c1730ddc106ebfcd"
  },
  "policyDigestEncoding": "exact-input-bytes",
  "coreCatalogDigest": {
    "sha256": "b68cb2cae64cfcd2a5021a725505ccbaa80dfaa053322ce276af9c6650860304"
  },
  "manifestDigest": {
    "sha256": "e24aae77ba1374162edff49aa0ab1d8dd7e38c333e134a2128ad1598fec99fb7"
  },
  "expectedManifestDigest": {
    "sha256": "e24aae77ba1374162edff49aa0ab1d8dd7e38c333e134a2128ad1598fec99fb7"
  },
  "summary": {
    "artifactsChecked": 0,
    "artifactsDeclared": 1,
    "heads": 1,
    "historicalMaterialsChecked": 0,
    "historicalMaterialsDeclared": 0,
    "manifestSignaturesAuthorized": 1,
    "manifestSignaturesRequired": 1,
    "manifestSignaturesValid": 1,
    "profilesDeclared": 1,
    "profilesValidated": 1,
    "quarantinedStatementsTotal": 0,
    "roots": 1,
    "signaturesChecked": 4,
    "signaturesTotal": 4,
    "signaturesValid": 4,
    "statementsAuthorized": 3,
    "statementsReachable": 3,
    "statementsTotal": 3,
    "statementsValid": 3
  },
  "checks": [
    { "id": "load-safely",                "status": "pass",    "prerequisiteChecks": [] },
    { "id": "parse-strictly",             "status": "pass",    "prerequisiteChecks": [] },
    { "id": "index-payloads",             "status": "pass",    "prerequisiteChecks": [] },
    { "id": "core-schemas",               "status": "pass",    "prerequisiteChecks": [] },
    { "id": "signatures",                 "status": "pass",    "prerequisiteChecks": [] },
    { "id": "authorization-thresholds",   "status": "pass",    "prerequisiteChecks": [] },
    { "id": "metadata-profiles",          "status": "pass",    "prerequisiteChecks": [] },
    { "id": "authorization",              "status": "pass",    "prerequisiteChecks": [] },
    { "id": "graph-dependency-artifacts", "status": "pass",    "prerequisiteChecks": [] },
    { "id": "graph",                      "status": "pass",    "prerequisiteChecks": [] },
    { "id": "roots-and-heads",            "status": "pass",    "prerequisiteChecks": [] },
    { "id": "completeness-anchor",        "status": "pass",    "prerequisiteChecks": [] },
    { "id": "freshness-anchors",          "status": "pass",    "prerequisiteChecks": [] },
    { "id": "artifact-bytes",             "status": "fail",    "prerequisiteChecks": [] },
    { "id": "artifact-profiles",          "status": "pass",    "prerequisiteChecks": [] },
    { "id": "decision",                   "status": "pass",    "prerequisiteChecks": [] }
  ],
  "errors": [
    {
      "causedByCheck": "artifact-bytes",
      "code": "E_ARTIFACT_DIGEST",
      "context": {
        "artifactDigest": {
          "sha256": "ecd821dbe6f4204c4aad64878643d7954cb8c94abbf8d0b28f9c2bfc272f781e"
        },
        "head": {
          "sha256": "99aee76db46a720405da6d0015d427c791b0d4e02367b9e2bc36ce82ae812bce"
        },
        "subjectName": "customers.public.json"
      },
      "message": "final artifact bytes differ from the signed digest",
      "step": 12
    }
  ],
  "warnings": [],
  "tool": {
    "name": "makoto",
    "version": "0.3.0a0"
  }
}

This is a real report. It is the reference verifier’s output for the fixture bundle whose delivered data was changed after signing, trimmed only by omitting the per-statement, per-profile, and per-artifact arrays. Signatures, authorization, and the graph all pass; artifact-bytes alone fails, at step 12. The complete report is published with the walkthrough.

  • decisionallow, deny, or indeterminateTreat anything other than allow as a refusal. indeterminate means the verifier could not establish the answer, which is not the same as proving the evidence good.
  • primaryErrorThe one code that explains the outcomeRoute on this before reading the array. It is null on an allow.
  • checksWhat was actually establishedA skipped status with a non-empty prerequisiteChecks is the verifier saying it did not test this, rather than that it passed.
  • errorsOrdered diagnosticsEach carries code, step, causedByCheck, a human message, and a typed context.
  • warningsNon-fatal gapsCodes such as W_FRESHNESS_NOT_CHECKED or W_ARTIFACT_UNPROFILED mark evidence the run did not cover. An allow with warnings is a narrower claim than an allow without them.
  • reportTruncatedCompleteness of the document itselfWhen true, the arrays were cut at a resource limit. Do not infer absence of a finding from a truncated report.

Use hosted schemas for structural validation

The hosted schemas are ordinary Draft 2020-12 JSON Schemas. Any conforming validator can check an individual resource. This is useful at editors, CI gates, and ingestion boundaries, but it does not validate signatures, signer authorization, graph completeness, freshness, or data bytes.

uvx --from check-jsonschema check-jsonschema \
  --schemafile https://usemakoto.dev/schema/v0.2/trust-policy.schema.json \
  policy.json

The hosted catalog publishes every core schema and its SHA-256 digest. Private organizational profiles can use their own URI and remain unpublished; the signed profile reference pins the exact root and closure digests so a receiver can resolve them from an authenticated local catalog.

See every external validator, command by command →

Content rules remain separate from integrity

A digest can prove that invisible Unicode or another dangerous string has not changed. It cannot make that string safe. Put normalization, control-character, field, or bounded-pattern rules in an organizational profile and require the receiver to validate the actual structured artifact against that profile.

What verification still cannot prove

Passing every check proves that exact bytes match authorized signed claims and the receiver’s independent expectations. It does not prove that a source told the truth, that a claimed transformation actually ran, that the data is safe or high quality, or that signed metadata is confidential. Those are separate controls and evidence.

Implement another verifier

Match the report contract, not just the JSON shape.

A useful implementation must preserve the distinctions between authenticity and authorization, continuity and completeness, and completeness and freshness. Start from the public schemas, conformance fixtures, denial reports, and reference implementation.