@scopebond/verify
v0.4.0
Published
scopebond-verify — the deterministic verdict library: violates(policy, receipts, claimed). The moat-bearing reference implementation.
Downloads
865
Maintainers
Readme
@scopebond/verify
scopebond-verify — the deterministic, reproducible verdict library. The same
code runs in the gateway (real-time, single receipt) and at claim time (over the
full receipt set). This is the moat-bearing artifact (D27): whoever owns the
reference violates() and the vectors owns the definition of a scope violation.
import { violates, validateIntent, validatePolicy } from "@scopebond/verify";
validatePolicy(policy); // full, closed vocabulary-v1 document
validateIntent(intent); // closed action shape with finite numeric values
const v = violates(policy, receipts, claimed, { at, gatewaysComplete });
// → { violated, clause_id, explanation, inputs_hash, undetermined? }inputs_hash uses the shared strict canonical serializer also used by gateway and
SDK signatures; cross-package vectors pin the resulting bytes.
Guarantees
- Pure & deterministic — no network, no wall-clock. The evaluation timestamp
is an input (
opts.at, defaultclaimed.timestamp), so a verdict reproduces bit-for-bit;inputs_hashcommits to every input. - Only executed actions can violate. A denied, non-executed action (enforce mode, prevented) is never a violation; a monitored action that executed over a limit is. The coverage buckets of the vocabulary (§4/§5) fall out of this rule.
- Ambiguity resolves for the operator — limits use strict
>; exactly at the limit is allowed. - All applicable clauses are considered — an enforcing violation outranks approval and monitor violations regardless of clause order.
- Action allowlists are closed — when present, an unlisted action violates the allowlist; bounded numeric and patterned values must have the required type.
globalscope returnsundetermined(notviolated) when the caller signals the cross-gateway receipt set is incomplete.
Implemented
spend_limit (per-action + windowed, principal/global), rate_limit,
require_approval, sequence, time_window, endpoint_allowlist /
endpoint_denylist, address_allowlist / address_denylist,
contract_allowlist, action_allowlist (param bounds), key_policy.
Intent shape conventions
Finalized alongside the gateway/SDK; used by the clause logic and the vectors:
- amount actions —
intent.asset,intent.amount - HTTP actions —
intent.params.host,.path,.method - on-chain actions —
intent.params.to,.chain_id,.contract,.selector - signing key —
intent.signer
Receipt signatures (@scopebond/verify/signature)
import { verifyReceiptSignature } from "@scopebond/verify/signature";
const result = await verifyReceiptSignature(receipt, attesterPublicKeyPemOrJwk);
// { valid, signature_valid, key_binding_valid, alg_supported, attester_kind_supported }Verifies the attester's Ed25519 signature over the RFC 8785 canonical payload and checks
that payload.attester.kid is the key's derived id. WebCrypto only, no node: imports:
the same code runs in Node, browsers and Cloudflare Workers. Receipts v1 use Ed25519
from a gateway attester; other algorithms and attester kinds the schema reserves are
reported as unsupported, never valid. It never throws for a malformed receipt or key.
Conformance suite
vectors/conformance.json is the reference vector suite (D27): every implemented
clause type across prevented / covered / ambiguity / refused cases. pnpm test
runs every vector through violates() and checks the verdict. A gateway build is
"Scopebond-compatible" only if it produces identical verdicts on this suite.
Anchor verification (@scopebond/verify/anchor)
Verifies the gateway's receipt-log anchors with WebCrypto only (Node, browsers,
Workers). v2 anchors (algo: "rfc9162-sha256") use the RFC 9162 Merkle tree and are
Ed25519-signed by the attester; legacy v1 anchors (sha256-merkle) still verify.
import { verifyAnchorSignature, verifyInclusionProof, receiptLeafHash } from "@scopebond/verify/anchor";
await verifyAnchorSignature(anchor, attesterPublicJwk);
await verifyInclusionProof({
leaf_hash: await receiptLeafHash(receipt.payload),
leaf_index, tree_size: anchor.tree_size, audit_path, root: anchor.root,
});Also: verifyConsistencyProof, verifyAnchorChain, verifyAnchorRoot,
merkleTreeHash, inclusionProof, consistencyProof, merkleRootV1. Exact
definitions: SPEC.md "Anchors"; vectors: vectors/merkle-rfc9162.json.
Examples
Runnable end to end against a real gateway (asserted in CI by pnpm run test:examples):
examples/verify-receipt-offline.mjs—verifyReceiptSignatureon a gateway receipt; a tampered copy fails.examples/verify-anchor-inclusion.mjs— a signed v2 anchor and an inclusion proof verified offline; a wrong-index proof fails.
[PLANNED]
oracle_condition(best-effort external data) and active-key/list history inputs.- Exact RFC 8785 (JCS) canonicalization for
inputs_hash(currently a deterministic sorted-key serialization). - Expanded vectors as the vocabulary grows.
Types
Written in TypeScript; ships .d.ts. Public types include Policy, Clause,
Receipt, Intent, Approval, Verdict, Options, and ValidationResult.
Test
pnpm test # tsc build, then node --test