@sherlockhealth/verify
v0.1.0
Published
Standalone, offline verifier for OpenDoc protocol signed objects — price-locks, receipts, and signed Sure Price / availability attestations. Zero dependency on the protocol server.
Downloads
17
Readme
@sherlockhealth/verify
Standalone, offline verifier for OpenDoc protocol signed objects. Zero dependency on the OpenDoc protocol server — it only needs the signed object and a JWK Set.
OpenDoc signs its trust primitives as compact ES256 JWS over RFC 8785 (JCS) canonical JSON (Agent Protocol Doctrine §3):
| Object | type claim | Carries expiry? |
|---|---|---|
| Price-lock | opendoc.price_lock.v1 | yes (valid_until, 24h) |
| Receipt | opendoc.receipt.v1 | no |
| Sure Price quote | opendoc.sure_price.v1 | yes (valid_until) |
| Availability quote | opendoc.availability.v1 | yes (valid_until) |
The point of a signed object is that trust is a portable cryptographic fact,
not "trust our API". Anyone can verify one offline against the public JWK Set
published at https://api.opendoc.com/.well-known/opendoc-protocol/jwks.json.
Install
npm install @sherlockhealth/verify # or pnpm add / yarn addLibrary
import { verifyPriceLock, verify } from '@sherlockhealth/verify';
// Fetch the JWKS from the well-known URL and verify:
const verdict = await verifyPriceLock(jws);
// { valid: true, expired: false, claims: {...}, keyId: 'key-1', type: 'opendoc.price_lock.v1' }
if (!verdict.valid) throw new Error(`untrusted: ${verdict.reason}`);
if (verdict.expired) throw new Error('price-lock has expired — re-authorize');
// Air-gapped: supply the JWKS yourself, no network call.
import jwks from './opendoc-jwks.json' assert { type: 'json' };
const v2 = await verifyPriceLock(jws, { jwks });
// Any known type, no restriction:
const v3 = await verify(jws); // fetches JWKS
const v4 = await verify(jws, { jwksUrl: 'https://staging.example/.well-known/opendoc-protocol/jwks.json' });The verdict
interface Verdict {
valid: boolean; // signature verified AND claim shape valid
expired: boolean | null; // valid_until passed? null if no expiry / not verified
claims: Record<string, unknown> | null;
keyId: string | undefined; // kid from the JWS header (readable even on failure)
type: string | null; // the object's `type` claim
reason?: 'malformed_jws' | 'signature_invalid' | 'unknown_key'
| 'wrong_algorithm' | 'invalid_claim_shape' | 'type_mismatch'
| 'jwks_unavailable';
}Verification never throws on an untrusted object — it returns valid: false
with a reason. It only throws on genuine programming errors.
Per-type helpers (verifyPriceLock, verifyReceipt, verifySurePrice,
verifyAvailability) restrict acceptance to that type; a mismatch is
reason: 'type_mismatch'.
CLI
opendoc-verify price-lock <jws>
opendoc-verify receipt <jws> --json
opendoc-verify sure-price <jws> --jwks ./opendoc-jwks.json # air-gapped
opendoc-verify availability <jws> --jwks https://api.opendoc.com/.well-known/opendoc-protocol/jwks.json
opendoc-verify verify <jws> # any known type--jwks <file|url>— a local file path (air-gapped) or a URL. Omit to fetch the OpenDoc well-known JWKS.--json— machine-readable verdict on stdout.- Exit code
0only when the object is valid AND not expired;1otherwise.
✓ price-lock: VALID signature
type: opendoc.price_lock.v1
key id: key-1
expiry: within validity window
valid_until:2026-08-08T03:00:00.000Z
issuer: opendoc-protocolKey rotation
The JWKS may publish several keys (a current signing key plus retained previous
keys). Each signed object names its key via the kid header; this verifier
selects the matching published key automatically, so objects signed before a
rotation keep verifying for as long as their key stays published. See
docs/03-operations/SIGNING_KEY_ROTATION_RUNBOOK.md.
License
Apache-2.0 — see LICENSE and NOTICE.
The verifier is deliberately permissively licensed: anyone holding an OpenDoc signed object should be able to check it independently, without our permission and without touching our servers.
