Skip to main content
Version: v0.10.13

Verification

PEAC verification is offline and deterministic. Given a receipt, the issuer's public keys, and optionally the policy file, verification produces the same result every time with no external dependencies.

Zero network calls to the issuer required

Verification runs entirely locally. The verifier needs only the receipt (a JWS string) and the issuer's JWKS (public keys). No callback to the issuer, no token introspection endpoint, no online dependency.


The 12 verification checks

The verifier runs a fixed sequence of checks against each receipt. All checks must pass for the receipt to be considered verified.

#CheckDescription
1Signature validEd25519 signature verification against the issuer's public key
2Algorithm allowedAlgorithm must be EdDSA (no other algorithms accepted)
3Type header presenttyp must be peac-receipt/0.1
4Key ID resolveskid must exist in the issuer's JWKS
5Claims well-formedAll required claims present and correctly typed
6Not expiredexp not in the past, if present
7Issued-at validiat is a valid Unix timestamp, not in the future
8PEAC object validpeac object passes Zod schema validation
9Attestation type knownattestation_type is a registered type
10Status validstatus is a valid lifecycle stage
11Extension schemas validAll extensions pass their registered schema validation
12Policy bindingPolicy matched (Wire 0.2 only -- currently returns unavailable)

Basic verification

Pass a receipt JWS string and the issuer URL. The verifier fetches the issuer's JWKS automatically.

verify.ts
import { verifyReceipt } from '@peac/protocol';

const result = await verifyReceipt(receiptJWS, {
issuerUrl: 'https://api.example.com',
});

console.log(result.verified); // true or false
console.log(result.checks); // array of 12 check results
console.log(result.claims); // decoded claims (if verified)
console.log(result.policy_binding); // 'verified' | 'failed' | 'unavailable'

Offline verification

For fully offline verification -- no network calls at all -- provide the JWKS directly.

offline-verify.ts
import { verifyReceipt } from '@peac/protocol';

const result = await verifyReceipt(receiptJWS, {
jwks: {
keys: [
{
kty: 'OKP',
crv: 'Ed25519',
use: 'sig',
kid: 'peac-2026-02',
x: '<base64url-public-key>',
alg: 'EdDSA',
},
],
},
});

if (result.verified) {
console.log('Receipt is valid:', result.claims.jti);
} else {
console.log('Verification failed:', result.errors);
}

Result types

VerificationResult

The top-level result returned by verifyReceipt.

VerificationResult
interface VerificationResult {
/** Whether all checks passed */
verified: boolean;
/** Results for each of the 12 checks */
checks: CheckResult[];
/** Decoded receipt claims (populated only when verified) */
claims?: ReceiptClaims;
/** Policy binding status: verified, failed, or unavailable */
policy_binding: 'verified' | 'failed' | 'unavailable';
/** Errors encountered during verification (populated only on failure) */
errors?: VerificationError[];
}

CheckResult

Each entry in the checks array describes the outcome of a single verification check.

CheckResult
interface CheckResult {
/** Check number (1-12) */
id: number;
/** Human-readable check name */
name: string;
/** Whether this check passed */
passed: boolean;
/** Additional detail on failure */
message?: string;
}
Dual-representation check

The verifier detects mismatches between different representations of the same receipt. If a receipt appears in both the auth/evidence header format and the _jws field format, both representations must contain identical content. A mismatch causes verification to fail.


CLI verification

Verify a receipt directly from the command line.

Terminal -- verify with issuer URL
npx peac verify <receipt-jws> --issuer https://api.example.com

Verify from a file with a local JWKS.

Terminal -- verify with local keys
npx peac verify --file receipt.jws --jwks keys.json

The CLI outputs a structured result with pass/fail status for each of the 12 checks.


MCP server verification

AI agents using the PEAC MCP server can verify receipts via the peac_verify tool.

MCP Tool Call -- peac_verify
{
"tool": "peac_verify",
"arguments": {
"receipt": "<jws-string>",
"issuer_url": "https://api.example.com"
}
}

The response includes the full verification result, check details, decoded claims, and server metadata in the _meta field.


Next steps