Status: NORMATIVE
This document describes normative behavior using concrete JSON test vectors.
Each vector has:
- A filename and location
- A short description
- Expected validation result (
VALIDor specificPEACError) - Key assertions that implementations MUST verify
tests/vectors/
├── golden/ # Valid receipts (positive cases)
├── negative/ # Envelope-level validation errors
└── http/ # HTTP-context specific cases (DPoP, etc.)
Note: tests/vectors/http/ contains vectors that require HTTP context (method, URI, headers) for validation. Pure envelope validators MAY skip these vectors.
File: tests/vectors/golden/receipt-minimal-no-payment.json
Description:
- No payment evidence
- Single control step with
access-policy-serviceengine - Free-tier resource access (non-monetary)
- Shows that control can exist without payment
Expected: VALID
Key assertions:
- Envelope structure is valid (auth/evidence/meta)
- Control chain has exactly 1 step
- Control decision is
allow(consistent with chain) - Payment evidence is absent (
evidence.paymentis undefined) - Control is optional when no payment present
- Meta block contains non-normative debug info
File: tests/vectors/golden/receipt-payment-single-control.json
Description:
- Payment present (x402 rail, Lightning Network)
- Single control step with
spend-control-serviceengine - DPoP transport binding present
- Shows canonical "typical agentic paid call"
Expected: VALID
Key assertions:
- Payment evidence is present with all required fields:
rail: "x402"reference: Lightning invoice identifieramount: 300 (cents)currency: "USD"asset: "BTC"env: "live"network: "lightning"
- Control block is present (REQUIRED when payment exists)
- Control chain has 1 step with
allowresult - DPoP binding has
jktandnonce - Limits snapshot shows per-transaction and per-day limits
File: tests/vectors/golden/receipt-payment-multi-control-veto.json
Description:
- Payment present (card-network rail)
- Three control steps: mandate-service (allow), risk-engine (allow), spend-control-service (deny)
- Combinator is
any_can_veto - Final decision is
deny(one veto blocks entire transaction) - Shows multi-party governance flow
Expected: VALID (but decision is deny)
Key assertions:
- Control chain has 3 steps
- Steps 1 and 2 have
result: "allow" - Step 3 has
result: "deny"with budget exceeded reason - Control decision is
deny(consistent withany_can_vetosemantics) - Payment rail is
card-network(vendor-neutral) - Limits snapshot in step 3 shows monthly budget state
Purpose:
- Proves
any_can_vetocombinator semantics - Proves that
decisionmust match chain results - Reference for enterprise policy flows (TAP/AP2-like patterns)
File: tests/vectors/golden/receipt-http402-x402-single-control.json
Description:
- HTTP 402 enforcement pattern
- Payment via x402 rail (Base USDC)
- Single control step with spend limits
- DPoP transport binding
- Demonstrates
enforcement.method = "http-402"usage
Expected: VALID
Key assertions:
auth.enforcement.methodis"http-402"evidence.payment.railis"x402"evidence.payment.networkis"base-mainnet"evidence.payment.assetis"USDC"- Control block is present (REQUIRED for http-402 enforcement)
- DPoP binding demonstrates transport security
- Rail-specific evidence includes tx_hash, block_number, chain_id
Purpose:
- Shows HTTP 402 as enforcement method (not baked into core)
- Demonstrates x402 rail with Base/USDC (HTTP 402 payment flow)
- Shows how control requirement applies to enforcement.method=="http-402"
File: tests/vectors/negative/receipt-payment-missing-control.json
Description:
- Payment evidence is present
- Control block is intentionally omitted from
auth - Violates protocol invariant: payment requires control
Expected: INVALID with E_CONTROL_REQUIRED
Expected error:
{
"code": "E_CONTROL_REQUIRED",
"category": "validation",
"severity": "error",
"retryable": false,
"http_status": 400,
"pointer": "/auth/control",
"remediation": "Add a control block when payment evidence is present"
}File: tests/vectors/negative/receipt-control-inconsistent-decision.json
Description:
- Control chain has 1 step with
result: "deny" - Control decision is
allow(inconsistent) - Violates protocol invariant: with
any_can_veto, any deny must result in deny decision
Expected: INVALID with E_INVALID_CONTROL_CHAIN
Expected error:
{
"code": "E_INVALID_CONTROL_CHAIN",
"category": "validation",
"severity": "error",
"retryable": false,
"http_status": 400,
"pointer": "/auth/control/decision",
"remediation": "Decision 'allow' inconsistent with chain; expected 'deny' for any_can_veto"
}File: tests/vectors/negative/receipt-expired.json
Description:
- Receipt has
expclaim set to 2021-01-01 (past) - All other fields are valid
- Violates temporal validity
Expected: INVALID with E_EXPIRED_RECEIPT
Expected error:
{
"code": "E_EXPIRED_RECEIPT",
"category": "validation",
"severity": "error",
"retryable": false,
"http_status": 401,
"pointer": "/auth/exp",
"remediation": "Receipt has expired; use a current receipt"
}File: tests/vectors/negative/receipt-policy-ssrf-blocked.json
Description:
- Receipt has
policy_uripointing to AWS metadata endpoint (169.254.169.254) - Tests SSRF protection requirements
- Verifier must block request to private/metadata IP
Expected: INVALID with E_SSRF_BLOCKED
Expected error:
{
"code": "E_SSRF_BLOCKED",
"category": "verification",
"severity": "error",
"retryable": false,
"remediation": "SSRF protection blocked request to private/metadata IP: 169.254.169.254"
}Test requirement:
- Verifier MUST NOT fetch from metadata IPs
- Verifier MUST apply SSRF protections per PROTOCOL-BEHAVIOR.md Section 6
File: tests/vectors/negative/receipt-invalid-signature.json
Description:
- Structurally valid envelope
- JWS signature is invalid (will fail cryptographic verification)
- Tests signature validation
Expected: INVALID with E_INVALID_SIGNATURE
Expected error:
{
"code": "E_INVALID_SIGNATURE",
"category": "verification",
"severity": "error",
"retryable": false,
"remediation": "JWS signature verification failed"
}Note: This vector includes a JWS compact serialization with an intentionally invalid signature.
These vectors require HTTP request context (method, URI, headers) for validation.
File: tests/vectors/http/http-dpop-replay.json
Description:
- Valid DPoP proof structure
- Nonce has been used before (replay attack)
- Tests L3/L4 nonce replay protection
Expected: INVALID with E_DPOP_REPLAY
Expected error:
{
"code": "E_DPOP_REPLAY",
"category": "verification",
"severity": "error",
"retryable": false,
"remediation": "DPoP nonce has already been used"
}Test requirement:
- Verifier MUST track used nonces (in-memory or distributed cache)
- Verifier MUST reject reused nonces within TTL window (60 seconds)
File: tests/vectors/http/http-dpop-invalid-jkt.json
Description:
- DPoP proof has
jktclaim jktdoes not match SHA-256 thumbprint ofjwkin header- Tests key thumbprint validation
Expected: INVALID with E_DPOP_INVALID
Expected error:
{
"code": "E_DPOP_INVALID",
"category": "verification",
"severity": "error",
"retryable": false,
"remediation": "DPoP jkt does not match public key thumbprint"
}Test requirement:
- Verifier MUST compute
jkt = base64url(SHA256(JCS(jwk))) - Verifier MUST compare computed jkt with claim value
Validators MUST:
- Load each golden vector and verify it returns
VALID(no errors) - Load each negative vector and verify it returns the expected
PEACError - Check that error
code,category,retryable, andpointermatch expectations
Envelope-only validators:
- MUST validate
tests/vectors/golden/*.json - MUST validate
tests/vectors/negative/*.json - MAY skip
tests/vectors/http/*.json(requires HTTP context)
Full verifiers:
- MUST validate all vectors including
tests/vectors/http/*.json - MUST implement DPoP verification per PROTOCOL-BEHAVIOR.md Section 7
When adding new protocol features:
- Create at least one golden vector demonstrating valid usage
- Create at least one negative vector for each new validation rule
- Update this document with descriptions and expected results
- Wire vectors into CI test suite
These vectors are normative for all PEAC implementations (TypeScript, Go, Rust, Python, etc.).
Each implementation MUST:
- Parse all golden vectors successfully
- Reject all negative vectors with correct error codes
- Produce identical canonicalization (JCS) for signing
- Verify JWS signatures consistently
- v0.9.15 (2025-01-18): Initial test vectors with envelope structure, control chain, structured errors, HTTP 402 enforcement, and security vectors (SSRF, DPoP, signature)