Skip to content

Verification receipts ​

A verification receipt is a small JSON file that sits beside a deal and lets someone who did not run the numbers confirm, offline, that those numbers follow from that record.

It answers exactly one question: do these outputs follow from these inputs?

It does not answer: are these inputs true?

That distinction is the whole point, so it is worth stating plainly before anything else.

What a receipt does and does not tell you ​

A receipt that verifies tells you two things:

  1. The deal's financial content is unchanged since the receipt was issued.
  2. The stated metrics — DSCR, LTV, cap rate, and the rest — follow deterministically from that content under a named calculation pack.

It tells you nothing about whether the inputs are true, complete, sourced from genuine documents, or reasonable. A deal asserting a fabricated NOI can carry a perfectly valid receipt. The arithmetic on a lie is still arithmetic.

So a receipt replaces this conversation:

"Where did this 1.28x DSCR come from?" "Our model." "Can I see the model?"

with this one:

"Where did this 1.28x DSCR come from?" "This record, under the multifamily pack v1.0.2. Here's the receipt — check it yourself."

What still needs human diligence is unchanged: whether the rent roll is real, whether the T-12 was doctored, whether the exit cap is defensible. A receipt moves arithmetic out of the trust budget so more of that budget is left for the questions that actually require judgment.

Why this is possible ​

UW Markdown has a rule that AI never does financial math. Extraction and narrative are model work; every NOI, DSCR, LTV, and IRR is computed deterministically by a calculation pack. The protocol fixes the rounding rule and precision, and the Excel export's formulas match the pack's quantized values exactly for the metrics the workbook covers.

That determinism is what makes a receipt meaningful. The same pack over the same canonical inputs produces the same outputs on any conforming implementation — so a third party can recompute and compare rather than take your word for it.

Issuing one ​

bash
uwmd receipt issue deal.uwx.md

That writes deal.receipt.json beside the deal. The receipt is detached — it is never embedded in the record. Embedding would change the record's own digest, and it would make it impossible for an auditor to attest to a file they must not modify.

In the reference editor, the Receipt tab does the same thing in the browser. Nothing is uploaded: issuance and verification run entirely client-side.

Issuance either produces a complete receipt or refuses with a reason. Complete means that every pack output is recorded, including its computed/uncomputed status; metric coverage can be partial. It refuses when the document has parse errors, when the asset class has no registered pack, or when a pack calculation fails to evaluate. Missing structured inputs that evaluate successfully to null are recorded as computed: false, not treated as an evaluation failure.

Validation, completeness and hashes ​

These checks answer different questions:

SurfaceQuestion
validateIs this valid UWMD under the applicable representation and validation rules?
Stage ReadinessAre this stage's required sections and fields present?
Receipt-pack metric completenessWhich declared metrics deterministically compute from the structured inputs?
CanonicalizationWhich exact semantic structure/UTF-8 bytes are committed to under the named canonicalization rules?
HashingWhat fingerprint identifies that canonical document or result/status set?
receipt issueCan a detached proof of that document and pack result/status set be created?
receipt verifyDoes the current document match the prior receipt's hashes, result statuses/values and applicable signature checks?

CLI reporting makes these distinctions explicit. For example, a multifamily document without asset-level equity inputs can issue with:

text
Receipt issued for deal.uwx.md → deal.receipt.json (org.uwmd.pack.multifamily@1.0.2)
Metric completeness: partial
Computed: 7/8
Uncomputed: cash_on_cash

The receipt records both computed and uncomputed result statuses. Matching uncomputed statuses can verify; they do not indicate a hash mismatch. Portfolio capitalization is not automatically allocated to an asset to fill this gap. validate reports receipt readiness and receipt/hash verification as not checked because it performs neither operation. Issuance prints the named canonicalization and document/results hashes but does not compare a prior receipt. Missing packs still refuse with RCP_PACK_UNRESOLVED; resolver failures retain available metric, section, variant and path diagnostics. A later hash mismatch is a separate verification result, reported with its existing code and expected/ actual digests. JSON output structure, codes and receipt payload semantics are unchanged; a failed recomputation's diagnostic message text is richer.

Verifying one ​

bash
uwmd receipt verify deal.uwx.md deal.receipt.json

The editor's Receipt tab and the VS Code extension's Verify Receipt for This Deal command do the same check. The extension only verifies — issuing while you are still editing produces a receipt that is stale on the next keystroke.

After source, referenced-input and authentication preflights, verification recomputes the results it can establish. It never treats a matching result digest as proof of correct computation.

Three answers, not two ​

Verification reports one of three states, and deliberately never collapses the third into either of the others.

VerdictExit codeMeaning
verified0The record is unchanged and every stated result recomputes.
failed1The digest, a result, or the signature disagrees.
unverifiable3This verifier cannot decide.

unverifiable is the one people are most likely to get wrong, so it has its own exit code. You get it when the verifier lacks the named calculation pack, holds a different version of it, cannot parse the record, or encounters a signature it cannot authenticate with an available backend. Invalid supplied signatures fail; absent verification capability is distinct.

Unverifiable is not a failure. It is the absence of evidence, not evidence of absence. Reporting it as a failure would cry wolf every time a verifier is missing a pack, and users who see false alarms learn to ignore real ones. Reporting it as success would be straightforwardly dangerous.

If you are scripting a gate, treat exit 1 as a stop and exit 3 as "get a verifier that has the pack" — not as a pass and not as a rejection.

uwmd verify-receipt <receipt.json> <file> --json is the conformance protocol's receipt subcommand (RFC 0115, Protocol §II.6a.1). It verifies as a Verifier without a calculation pack does: it recomputes stated builtin dictionary results under the record's declared rule set. A mismatch fails with RCP-03 or RCP-07; matching results leave overall unverifiable with RCP-05/06 when full claimed pack/output coverage is unproven. Digest, referenced-input, rule-set and signature checks still decide first. It exits 1 only on failed or an unreadable receipt. uwmd receipt verify is the pack-based check above.

Receipts go stale, and that is normal ​

A receipt describes one exact version of a deal. Edit the deal and the receipt no longer applies — its digest was computed over the old content.

That is expected, not alarming. The reference editor distinguishes the two cases so an ordinary edit never looks like tampering: a receipt you issued yourself, against a deal you have since edited, shows as stale with a prompt to re-issue. A mismatch on a receipt that arrived from somewhere else shows as failed, because there you genuinely do not know why the content differs.

Issue the receipt when you are finished editing, and send it with the file.

What is inside ​

Current receipt excerpt from the reference CLI, with the result list trimmed to two of the eight outputs and other envelope members omitted:

json
{
  "receipt_version": "1.1",
  "subject": {
    "representation": "uwx-markdown",
    "representation_version": "1.0",
    "canonicalization": "uw-envelope-semantic",
    "canonicalization_version": "1.0",
    "digest": "sha256:d6d385866e5ed70f78776974810adc2ae0c3348b728a0c92df75610ef6f14be0"
  },
  "computation": {
    "pack": "org.uwmd.pack.multifamily",
    "pack_version": "1.0.2",
    "engine": "@uwmd/verify",
    "engine_version": "0.2.1",
    "protocol_version": "4.1.0",
    "rule_set": "3.0",
    "results": [
      {
        "calc_id": "cap_rate",
        "value": 0.055088,
        "unit": "%",
        "computed": true
      },
      {
        "calc_id": "cash_on_cash",
        "value": 0.04189,
        "unit": "%",
        "computed": true
      }
    ]
  }
}

Three details worth understanding:

The digest covers meaning, not bytes. It is computed over a canonical form of the deal's financial content, so reflowing a label, changing comma grouping, or switching line endings does not break a receipt. Changing a number does.

computed: false distinguishes missing inputs from a computed zero. A pack declares a fixed set of outputs, and the receipt lists all of them. When the deal lacks the inputs for one, it is recorded as {"value": null, "computed": false} rather than as a number nobody calculated — the deal above supports all eight of its pack's outputs, but a leaner record often will not. A receipt never carries a hand-entered figure.

Validation counts are recorded, not enforced. A receipt notes how many errors and warnings the policy set found at issuance. Financial threshold bands are separate deal flags unless institution policy escalates one into a validation issue. A low-DSCR deal can carry a valid receipt; the receipt verifies the stated arithmetic, not a credit recommendation.

Signing ​

Receipts may be unsigned (signature: null) or signed. The @uwmd/signing package implements receipt signing with signReceipt and stampReceiptSignature; its signatureVerifier plugs into verifyReceipt. See the signing package guide.

An available backend authenticates a supplied signature before recomputation. An invalid signature fails with RCP-01; a verifier without the required backend reports RCP-08 unverifiable. Neither case silently ignores the signature. Key distribution and the truth of source facts remain separate from arithmetic.

Reading further ​

Released under the MIT License.