Your first receipt
Record a run, finalize a receipt, verify it independently.
Three moves: build the run through typed event builders, finalize with a signer, verify with a public key.
import {
createReceipt,
createEd25519Signer,
} from "@0xsarwagya/clinical-receipt";
const run = await createReceipt({
workflow: { id: "discharge-review", version: "2.1.0" },
});
await run.input.observed({ value: { resourceType: "Observation", id: "obs-1" } });
await run.model.responded({ value: { text: "Consider urgent cardiology review." } });
await run.output.committed({ value: { text: "Urgent cardiology review today." } });
const signer = await createEd25519Signer({ generate: true });
const receipt = await run.finalize({ signer });Every builder call returns { id, sequence, commitment } — the id is
derived from the committed form so a receiver never has to trust the
recorder's claim about it.
Verify
import {
verifyReceipt,
importVerificationKey,
} from "@0xsarwagya/clinical-receipt/verify";
const key = await importVerificationKey(publicJwk);
const report = await verifyReceipt(receipt, { keys: [key] });
console.log(report.ok);report.ok is true only when the root recomputes, every event
recomputes to its declared id, and every signature verifies. Warnings
(no external timestamp, self-attested key, timeline inconsistencies)
show up separately — they are visible without being fatal.