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.
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.
| # | Check | Description |
|---|---|---|
| 1 | Signature valid | Ed25519 signature verification against the issuer's public key |
| 2 | Algorithm allowed | Algorithm must be EdDSA (no other algorithms accepted) |
| 3 | Type header present | typ must be peac-receipt/0.1 |
| 4 | Key ID resolves | kid must exist in the issuer's JWKS |
| 5 | Claims well-formed | All required claims present and correctly typed |
| 6 | Not expired | exp not in the past, if present |
| 7 | Issued-at valid | iat is a valid Unix timestamp, not in the future |
| 8 | PEAC object valid | peac object passes Zod schema validation |
| 9 | Attestation type known | attestation_type is a registered type |
| 10 | Status valid | status is a valid lifecycle stage |
| 11 | Extension schemas valid | All extensions pass their registered schema validation |
| 12 | Policy binding | Policy 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.
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.
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.
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.
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;
}
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.
npx peac verify <receipt-jws> --issuer https://api.example.com
Verify from a file with a local JWKS.
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.
{
"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.