Offline Ed25519 verifier for Aiyo execution receipts.
Aiyo issues cryptographically-signed execution receipts at the moment it authorizes a consequential action. Every receipt is verifiable offline against Aiyo's published JWKS — no round-trip to Aiyo required at verification time, no Aiyo-side infrastructure needed. This package is that verifier.
Aiyoplane, Inc. · Apache 2.0 licensed · aiyoplane.com
The fastest way to verify a receipt is with npx — no install required:
npx @aiyoplane/verify path/to/receipt.txtTo install globally as aiyo-verify:
npm install -g @aiyoplane/verifyTo use programmatically in a Node project:
npm install @aiyoplane/verifyRequires Node.js 18 or newer.
# Verify a receipt from a file
npx @aiyoplane/verify receipt.txt
# Verify a receipt passed inline
npx @aiyoplane/verify --receipt "v2.eyJpaWQiOiJpbnRlbnRfMTIz...Q.MEQCIH...IDA"
# Read from stdin
cat receipt.txt | npx @aiyoplane/verify -
# Emit JSON for machine consumption
npx @aiyoplane/verify --json receipt.txt
# Override the JWKS URL (e.g., for self-hosted Aiyo)
npx @aiyoplane/verify --jwks-url https://api.example.com/.well-known/aiyo-jwks.json receipt.txtExit codes:
0— receipt verified successfully1— receipt invalid (bad signature, expired, malformed, unknown key)2— usage error (missing input, unrecognized flag, etc.)
Example output (human-readable):
✓ Valid receipt (v2)
intent_id: intent_abc123
context_id: ctx_xyz789
merchant_id: mrch_aiyoplane
issued_at: 2026-09-28T14:22:31.000Z
expires_at: 2026-09-28T14:27:31.000Z
Example output (--json):
{
"valid": true,
"version": "v2",
"claims": {
"iid": "intent_abc123",
"cid": "ctx_xyz789",
"mid": "mrch_aiyoplane",
"iat": 1727533351,
"exp": 1727533651
}
}import { verify } from '@aiyoplane/verify';
const receipt = 'v2.eyJpaWQiOiJpbnRlbnRfMTIz...Q.MEQCIH...IDA';
try {
const { claims } = await verify(receipt);
console.log(`Authorized action ${claims.iid} for merchant ${claims.mid}`);
console.log(`Receipt valid until ${new Date(claims.exp * 1000).toISOString()}`);
} catch (err) {
console.error(`Receipt failed verification: ${err.message} (${err.code})`);
}await verify(receipt, {
// Override the JWKS URL (default: https://api.aiyoplane.com/.well-known/aiyo-jwks.json)
jwksUrl: 'https://api.example.com/.well-known/aiyo-jwks.json',
// Required for v1 HMAC receipts (local development only)
hmacSecret: process.env.AIYO_HMAC_SECRET,
// Override "now" for expiration checks (useful in tests)
now: new Date('2026-09-28T14:24:00Z'),
// Clock skew tolerance in seconds (default: 30)
clockSkewSeconds: 60,
// Provide a custom fetch (useful for behind-a-proxy or in tests)
fetchImpl: myFetch,
});Every verification failure throws a typed error. Handle specific failure modes distinctly:
import {
verify,
ReceiptFormatError,
ReceiptSignatureError,
ReceiptExpiredError,
JwksFetchError,
UnknownKeyError,
UnsupportedVersionError,
} from '@aiyoplane/verify';
try {
await verify(receipt);
} catch (err) {
if (err instanceof ReceiptExpiredError) {
// Receipt was validly signed but has expired
} else if (err instanceof ReceiptSignatureError) {
// Signature did not verify — possible tampering or key mismatch
} else if (err instanceof JwksFetchError) {
// Could not reach the JWKS endpoint — treat as a transient failure and retry
} else {
// Everything else — malformed receipt, unknown kid, unsupported version
}
}Aiyo receipts are strings of the form:
v2.<base64url(payload_json)>.<base64url(ed25519_signature)>
The payload is a JSON object with these required claims:
| Claim | Type | Meaning |
|---|---|---|
iid |
string | Intent ID — the specific action this receipt authorized |
mid |
string | Merchant ID — whose policy this decision was under |
exp |
number | Expiration timestamp (unix seconds) |
iat |
number | Issued-at timestamp (unix seconds) |
cid |
string | Context ID — correlation across a broader flow (optional) |
kid |
string | Key ID — which JWKS key was used to sign (optional; JWKS default used if absent) |
The signature is Ed25519 over the base64url-encoded payload segment (JWS-style compact signing).
The v1 format (v1.…) uses HMAC-SHA256 with a shared secret and is intended for local development / in-process use only. Production receipts should always be v2.
The default JWKS URL is:
https://api.aiyoplane.com/.well-known/aiyo-jwks.json
This URL serves the current Aiyo signing key(s) as a standard RFC 7517 JWK Set. Keys are cached in-memory (respecting Cache-Control: max-age where present, defaulting to 10 minutes). If a receipt references a kid not in the cache, the JWKS is automatically refreshed once — handling key rotation gracefully.
To verify receipts issued by a self-hosted Aiyo instance, pass --jwks-url (CLI) or jwksUrl (programmatic).
To force a fresh JWKS fetch in a long-running process:
import { clearJwksCache, fetchJwks } from '@aiyoplane/verify';
clearJwksCache();
await fetchJwks(); // Refetches from the default URLThird parties — auditors, downstream systems, merchants' own compliance tooling — can verify Aiyo receipts without depending on Aiyo being available at verification time. The receipt is a portable, independently-verifiable artifact. That's the property that makes Aiyo's authorization decision durable across systems, and it's the property this package makes trivial to use.
For the broader architectural context, see the DMARC precedent post and the MCP-authz reference implementation.
Aiyo is the settlement-verified Economic Execution Authorization plane for autonomous systems. Its Runtime Decision Point verifies that settlement actually occurred on a real rail, evaluates merchant policy, and issues a portable authorization artifact that unlocks the action a payment — or any qualifying condition — was supposed to enable. Verify First. Execute Second.
Apache License 2.0 © 2026 Aiyoplane, Inc. — see LICENSE and NOTICE.