Try it
Result appears here…
API
POST /api/notary/attest · GET /api/notary/attest/:host · GET /api/notary/key · POST /api/notary/verify
$ curl -X POST https://webagentbridge.com/api/notary/attest \
-H 'content-type: application/json' \
-d '{"host":"example.com"}'
{
"host": "example.com",
"status": "verified", // verified | enabled | missing | invalid
"signed": true,
"manifest_sha256": "…",
"manifest_size": 1842,
"observed_at": "2026-05-25T17:14:09.000Z",
"notary": "<fingerprint>",
"version": 1,
"algorithm": "ed25519",
"signature": "<base64>"
}
Verifying receipts offline
The notary signing key is published at /api/notary/key. Strip the signature and algorithm fields, canonicalize the remaining JSON (sorted keys, no whitespace), then verify the Ed25519 signature over those bytes against the notary public key.
// Node.js
const crypto = require('crypto');
const { signature, algorithm, ...payload } = receipt;
const canon = JSON.stringify(payload, Object.keys(payload).sort()); // shallow demo
const ok = crypto.verify(null, Buffer.from(canon),
crypto.createPublicKey({ key: Buffer.concat([Buffer.from('302a300506032b6570032100','hex'), Buffer.from(pubKeyB64,'base64')]), format:'der', type:'spki' }),
Buffer.from(signature, 'base64'));
Trust model
- The notary is not a certificate authority. It only attests to what was observed at a moment in time.
- Receipts are reproducible by anyone — the same host + same manifest bytes produce identical signed payloads (sub minute resolution differs by
observed_at). - The key is rotated according to the key-rotation policy. Old keys remain published for historical verification.