# Wayreceipt

Got paid? Prove you delivered. Buyer disputes delivery? Check whether they countersigned the receipt. Wayreceipt is proof of delivery for agent work: a countersigned receipt shows a file with a given hash was handed over and that the buyer signed on receipt. It does not judge quality.

Wayreceipt: proof of delivery for agent work. The seller signs the deliverable hash (SHA-256) when work is delivered, and the buyer countersigns on arrival. If there is a delivery dispute later, the countersigned receipt shows what arrived and when. Anyone can check a receipt for free, with no sign-up.

**What a receipt proves:** the seller's key signed the SHA-256 hash of a file and a delivery time, and (when countersigned) the buyer's key signed the same receipt. It proves the file arrived, not that it was any good. Wayreceipt never holds or moves money between buyers and sellers.

Open source (MIT, see LICENSE). Built and run by AI agents.

Keywords: proof of delivery, delivery verification, delivery dispute, countersigned receipt, deliverable hash, verifiable credential, buyer countersignature, unpaid work, handoff, did:key.

All agent-facing wording (headline, hook, tool and skill descriptions, registry description) lives in `config/copy.json`; `node scripts/build-site.js` renders it everywhere and `node scripts/check-copy.js` checks it.

## Format

W3C Verifiable Credential 2.0, type `DeliveryReceipt`, with two Data Integrity proofs (`eddsa-jcs-2022`, Ed25519). Keys are `did:key` (offline) or `did:web`. The buyer's proof is a proof chain entry (`previousProof` = seller proof id). Spec sections and URLs: docs/SOURCES.md.

States: `countersigned` (both signed), `seller-only` (buyer has not countersigned), `invalid`.

## Library (ESM, Node 18+ and browsers)

```js
import { generateKey, didFromKey, issueReceipt, countersign, verifyReceipt, hashFile } from './src/index.js';
const seller = generateKey(), buyer = generateKey();
const fileSha256 = await hashFile(bytes);
const r1 = issueReceipt({ seller, buyer: buyer.did, fileSha256 });
const r2 = await countersign(r1, buyer, { fileSha256 });      // buyer checks its own copy's hash
const { valid, state, reasons } = await verifyReceipt(r2);     // true, 'countersigned', []
```

Browser build: `/lib/wayreceipt.js` (unminified bundle, built by `npm run build:site`).

## CLI

```
wayreceipt keygen --out seller.key                 # prints your did:key; file mode 0600
wayreceipt issue --key seller.key --buyer <did> --file report.pdf --out r.json
wayreceipt countersign --key buyer.key --receipt r.json --file report.pdf --out r.json
wayreceipt verify r.json [--file report.pdf]       # exit 0 countersigned, 1 seller-only, 2 invalid
wayreceipt verify --stdin                          # a receipt or one /log.jsonl line; one line of JSON out
wayreceipt check-link <did> <url>                  # exit 0 match, 1 mismatch, 2 unreachable
```

On this box `wayreceipt` is on PATH via `/usr/local/bin/wayreceipt` (wrapper for `node /workspace/venture/wayreceipt/bin/wayreceipt.js`).

## Production

Live at https://wayreceipt.pages.dev (Cloudflare Pages project `wayreceipt`, D1 `wayreceipt-log`). Deploy record and redeploy steps: docs/DEPLOY.md.

## Run locally

```
npm install
npm test                                  # node:test
node scripts/make-examples.js             # demo keys (.demo-keys/, private) + examples/*.json
npm run build:site                        # site/ (lib bundle, pages, llms.txt, openapi.yaml, agent card, /src/)
npx wrangler d1 migrations apply wayreceipt-log --local
npx wrangler dev --local --port 8787      # no Cloudflare login needed
node scripts/check-copy.js                # banned-word check on public copy
```

## Layout

| Path | What |
|---|---|
| src/index.js, src/jcs.js, src/base58.js | library |
| bin/wayreceipt.js | CLI |
| test/receipt.test.js | tests |
| worker/ | Cloudflare Worker: public log (log.js), MCP (mcp.js), routes (index.js) |
| migrations/0001_init.sql | D1 schema |
| config/copy.json | all agent-facing wording (MCP tool descriptions, llms.txt opener, agent card, OpenAPI summaries) |
| config/internal-dids.json | our own DIDs; their receipts are logged as `excluded: "ours"` |
| templates/ | page sources; `scripts/build-site.js` renders them into site/ |
| config/pages/wrangler.jsonc | Cloudflare Pages project config (D1 binding) |
| docs/ | DEPLOY.md, SOURCES.md, LOG_SCHEMA.md, MCP.md, REGISTRY.md, X402_SCOPE.md, DESIGN_NOTES.md, server.json and server.pages-dev.json (drafts, not published) |
| worker/x402.js | disabled 402 stub for a possible paid export route (off unless `X402_STUB=1`; never settles) |
| examples/ | countersigned, seller-only and tampered receipts plus the artifact |
| qa/ | screenshots, Lighthouse reports, test output |

## Discovery files (built into site/)

`/llms.txt`, `/openapi.yaml`, `/.well-known/agent-card.json` (A2A 1.0, copy at `/.well-known/agent.json`), `/server-card` and `/.well-known/mcp/server-card.json` (MCP Server Card, SEP-2127 shape), `/robots.txt`, `/sitemap.xml`, and `/.well-known/mcp-registry-auth` when `scripts/registry-auth-key.sh` has been run.

## Agent interfaces

- `/llms.txt`, `/openapi.yaml`, `/.well-known/agent-card.json` (A2A; `/.well-known/agent.json` is a copy).
- `/mcp`: MCP Streamable HTTP, tools `verify_delivery_receipt`, `lookup_delivery_receipt`, `log_stats`, `publish_delivery_receipt` (docs/MCP.md).
- `/src/`: source files, `wayreceipt-src.tar.gz`, `SHA256SUMS`, `SHA256SUMS.sig` (release key DID in `/src/RELEASE_KEY.txt`).
