@peac/protocol 0.9.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # @peac/protocol
2
+
3
+ PEAC protocol implementation - receipt issuance and verification
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @peac/protocol
9
+ ```
10
+
11
+ ## Documentation
12
+
13
+ See [peacprotocol.org](https://peacprotocol.org) for full documentation.
14
+
15
+ ## License
16
+
17
+ Apache-2.0
18
+
19
+ ---
20
+
21
+ PEAC Protocol is an open source project stewarded by Originary and community contributors.
22
+
23
+ [Originary](https://www.originary.xyz) | [Docs](https://peacprotocol.org) | [GitHub](https://github.com/peacprotocol/peac)
@@ -0,0 +1,19 @@
1
+ /**
2
+ * PEAC discovery manifest parsing (/.well-known/peac.txt)
3
+ */
4
+ import { PEACDiscovery } from '@peac/schema';
5
+ /**
6
+ * Parse a PEAC discovery manifest from YAML-like text
7
+ *
8
+ * @param text - PEAC discovery text (≤20 lines, ≤2000 bytes)
9
+ * @returns Parsed discovery manifest
10
+ */
11
+ export declare function parseDiscovery(text: string): PEACDiscovery;
12
+ /**
13
+ * Fetch and parse PEAC discovery from an issuer URL
14
+ *
15
+ * @param issuerUrl - Issuer URL (https://)
16
+ * @returns Parsed discovery manifest
17
+ */
18
+ export declare function fetchDiscovery(issuerUrl: string): Promise<PEACDiscovery>;
19
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAA4B,MAAM,cAAc,CAAC;AAEvE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAkF1D;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA0B9E"}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ /**
3
+ * PEAC discovery manifest parsing (/.well-known/peac.txt)
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseDiscovery = parseDiscovery;
7
+ exports.fetchDiscovery = fetchDiscovery;
8
+ const schema_1 = require("@peac/schema");
9
+ /**
10
+ * Parse a PEAC discovery manifest from YAML-like text
11
+ *
12
+ * @param text - PEAC discovery text (≤20 lines, ≤2000 bytes)
13
+ * @returns Parsed discovery manifest
14
+ */
15
+ function parseDiscovery(text) {
16
+ // Validate size
17
+ const bytes = new TextEncoder().encode(text).length;
18
+ if (bytes > schema_1.PEAC_DISCOVERY_MAX_BYTES) {
19
+ throw new Error(`Discovery manifest exceeds ${schema_1.PEAC_DISCOVERY_MAX_BYTES} bytes (got ${bytes})`);
20
+ }
21
+ const lines = text.trim().split('\n');
22
+ if (lines.length > 20) {
23
+ throw new Error(`Discovery manifest exceeds 20 lines (got ${lines.length})`);
24
+ }
25
+ const discovery = {
26
+ payments: [],
27
+ };
28
+ let inPayments = false;
29
+ for (const line of lines) {
30
+ const trimmed = line.trim();
31
+ if (!trimmed || trimmed.startsWith('#')) {
32
+ continue; // Skip empty lines and comments
33
+ }
34
+ if (trimmed === 'payments:') {
35
+ inPayments = true;
36
+ continue;
37
+ }
38
+ if (inPayments) {
39
+ // Support both "rail:" (new) and "scheme:" (deprecated) for backward compatibility
40
+ if (trimmed.startsWith('- rail:') || trimmed.startsWith('- scheme:')) {
41
+ const rail = trimmed.replace(/^- (rail|scheme):/, '').trim();
42
+ discovery.payments.push({ rail });
43
+ }
44
+ else if (trimmed.startsWith('info:')) {
45
+ const info = trimmed.replace('info:', '').trim();
46
+ if (discovery.payments.length > 0) {
47
+ discovery.payments[discovery.payments.length - 1].info = info;
48
+ }
49
+ }
50
+ else if (!trimmed.startsWith(' ') && trimmed.includes(':')) {
51
+ // New top-level key, exit payments section
52
+ inPayments = false;
53
+ }
54
+ }
55
+ if (!inPayments && trimmed.includes(':')) {
56
+ const [key, ...valueParts] = trimmed.split(':');
57
+ const value = valueParts.join(':').trim();
58
+ switch (key.trim()) {
59
+ case 'version':
60
+ discovery.version = value;
61
+ break;
62
+ case 'issuer':
63
+ discovery.issuer = value;
64
+ break;
65
+ case 'verify':
66
+ discovery.verify = value;
67
+ break;
68
+ case 'jwks':
69
+ discovery.jwks = value;
70
+ break;
71
+ case 'aipref':
72
+ discovery.aipref = value;
73
+ break;
74
+ case 'slos':
75
+ discovery.slos = value;
76
+ break;
77
+ case 'security':
78
+ discovery.security = value;
79
+ break;
80
+ }
81
+ }
82
+ }
83
+ // Validate required fields
84
+ if (!discovery.version)
85
+ throw new Error('Missing required field: version');
86
+ if (!discovery.issuer)
87
+ throw new Error('Missing required field: issuer');
88
+ if (!discovery.verify)
89
+ throw new Error('Missing required field: verify');
90
+ if (!discovery.jwks)
91
+ throw new Error('Missing required field: jwks');
92
+ return discovery;
93
+ }
94
+ /**
95
+ * Fetch and parse PEAC discovery from an issuer URL
96
+ *
97
+ * @param issuerUrl - Issuer URL (https://)
98
+ * @returns Parsed discovery manifest
99
+ */
100
+ async function fetchDiscovery(issuerUrl) {
101
+ if (!issuerUrl.startsWith('https://')) {
102
+ throw new Error('Issuer URL must be https://');
103
+ }
104
+ const discoveryUrl = `${issuerUrl}/.well-known/peac.txt`;
105
+ try {
106
+ const resp = await fetch(discoveryUrl, {
107
+ headers: { Accept: 'text/plain' },
108
+ signal: AbortSignal.timeout(5000),
109
+ });
110
+ if (!resp.ok) {
111
+ throw new Error(`Discovery fetch failed: ${resp.status}`);
112
+ }
113
+ const text = await resp.text();
114
+ return parseDiscovery(text);
115
+ }
116
+ catch (err) {
117
+ throw new Error(`Failed to fetch discovery from ${issuerUrl}: ${err instanceof Error ? err.message : String(err)}`);
118
+ }
119
+ }
120
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * HTTP header utilities for PEAC receipts
3
+ */
4
+ /**
5
+ * Set PEAC-Receipt header on a response
6
+ *
7
+ * @param headers - Headers object (e.g., Response.headers)
8
+ * @param receiptJws - JWS compact serialization
9
+ */
10
+ export declare function setReceiptHeader(headers: Headers, receiptJws: string): void;
11
+ /**
12
+ * Get PEAC-Receipt header from a request/response
13
+ *
14
+ * @param headers - Headers object
15
+ * @returns JWS compact serialization, or null if not present
16
+ */
17
+ export declare function getReceiptHeader(headers: Headers): string | null;
18
+ /**
19
+ * Set Vary: PEAC-Receipt header for caching
20
+ *
21
+ * @param headers - Response headers
22
+ */
23
+ export declare function setVaryHeader(headers: Headers): void;
24
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAE3E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAWpD"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * HTTP header utilities for PEAC receipts
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.setReceiptHeader = setReceiptHeader;
7
+ exports.getReceiptHeader = getReceiptHeader;
8
+ exports.setVaryHeader = setVaryHeader;
9
+ const schema_1 = require("@peac/schema");
10
+ /**
11
+ * Set PEAC-Receipt header on a response
12
+ *
13
+ * @param headers - Headers object (e.g., Response.headers)
14
+ * @param receiptJws - JWS compact serialization
15
+ */
16
+ function setReceiptHeader(headers, receiptJws) {
17
+ headers.set(schema_1.PEAC_RECEIPT_HEADER, receiptJws);
18
+ }
19
+ /**
20
+ * Get PEAC-Receipt header from a request/response
21
+ *
22
+ * @param headers - Headers object
23
+ * @returns JWS compact serialization, or null if not present
24
+ */
25
+ function getReceiptHeader(headers) {
26
+ return headers.get(schema_1.PEAC_RECEIPT_HEADER);
27
+ }
28
+ /**
29
+ * Set Vary: PEAC-Receipt header for caching
30
+ *
31
+ * @param headers - Response headers
32
+ */
33
+ function setVaryHeader(headers) {
34
+ const existing = headers.get('Vary');
35
+ if (existing) {
36
+ // Append to existing Vary header
37
+ const varies = existing.split(',').map((v) => v.trim());
38
+ if (!varies.includes(schema_1.PEAC_RECEIPT_HEADER)) {
39
+ headers.set('Vary', `${existing}, ${schema_1.PEAC_RECEIPT_HEADER}`);
40
+ }
41
+ }
42
+ else {
43
+ headers.set('Vary', schema_1.PEAC_RECEIPT_HEADER);
44
+ }
45
+ }
46
+ //# sourceMappingURL=headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.js","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAUH,4CAEC;AAQD,4CAEC;AAOD,sCAWC;AAtCD,yCAAmD;AAEnD;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,OAAgB,EAAE,UAAkB;IACnE,OAAO,CAAC,GAAG,CAAC,4BAAmB,EAAE,UAAU,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,OAAgB;IAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,4BAAmB,CAAC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa,CAAC,OAAgB;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,QAAQ,EAAE,CAAC;QACb,iCAAiC;QACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAAmB,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,QAAQ,KAAK,4BAAmB,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,4BAAmB,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * PEAC Protocol Implementation
3
+ * Receipt issuance and verification with JWKS caching
4
+ */
5
+ export * from './issue';
6
+ export * from './verify';
7
+ export * from './headers';
8
+ export * from './discovery';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * PEAC Protocol Implementation
4
+ * Receipt issuance and verification with JWKS caching
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ __exportStar(require("./issue"), exports);
22
+ __exportStar(require("./verify"), exports);
23
+ __exportStar(require("./headers"), exports);
24
+ __exportStar(require("./discovery"), exports);
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;AAEH,0CAAwB;AACxB,2CAAyB;AACzB,4CAA0B;AAC1B,8CAA4B"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Receipt issuance
3
+ * Validates input, generates UUIDv7 rid, and signs with Ed25519
4
+ */
5
+ import { PEACReceiptClaims, SubjectProfileSnapshot } from '@peac/schema';
6
+ /**
7
+ * Options for issuing a receipt
8
+ */
9
+ export interface IssueOptions {
10
+ /** Issuer URL (https://) */
11
+ iss: string;
12
+ /** Audience / resource URL (https://) */
13
+ aud: string;
14
+ /** Amount in smallest currency unit */
15
+ amt: number;
16
+ /** ISO 4217 currency code (uppercase) */
17
+ cur: string;
18
+ /** Payment rail identifier */
19
+ rail: string;
20
+ /** Rail-specific payment reference */
21
+ reference: string;
22
+ /** Asset transferred (e.g., "USD", "USDC", "BTC") - defaults to currency if not provided */
23
+ asset?: string;
24
+ /** Environment ("live" or "test") - defaults to "test" */
25
+ env?: 'live' | 'test';
26
+ /** Network/rail identifier (optional, SHOULD for crypto) */
27
+ network?: string;
28
+ /** Facilitator reference (optional) */
29
+ facilitator_ref?: string;
30
+ /** Rail-specific evidence (opaque) - defaults to empty object if not provided */
31
+ evidence?: unknown;
32
+ /** Idempotency key (optional) */
33
+ idempotency_key?: string;
34
+ /** Rail-specific metadata (optional) */
35
+ metadata?: Record<string, unknown>;
36
+ /** Subject URI (optional) */
37
+ subject?: string;
38
+ /** Extensions (optional) */
39
+ ext?: PEACReceiptClaims['ext'];
40
+ /** Expiry timestamp (Unix seconds, optional) */
41
+ exp?: number;
42
+ /** Subject profile snapshot for envelope (v0.9.17+, optional) */
43
+ subject_snapshot?: SubjectProfileSnapshot;
44
+ /** Ed25519 private key (32 bytes) */
45
+ privateKey: Uint8Array;
46
+ /** Key ID (ISO 8601 timestamp) */
47
+ kid: string;
48
+ }
49
+ /**
50
+ * Result of issuing a receipt
51
+ */
52
+ export interface IssueResult {
53
+ /** JWS compact serialization */
54
+ jws: string;
55
+ /** Validated subject snapshot (if provided) */
56
+ subject_snapshot?: SubjectProfileSnapshot;
57
+ }
58
+ /**
59
+ * Issue a PEAC receipt
60
+ *
61
+ * @param options - Receipt options
62
+ * @returns Issue result with JWS and optional subject_snapshot
63
+ */
64
+ export declare function issue(options: IssueOptions): Promise<IssueResult>;
65
+ /**
66
+ * Issue a PEAC receipt and return just the JWS string
67
+ *
68
+ * Convenience wrapper for common header-centric flows where only the JWS is needed.
69
+ * For access to validated subject_snapshot, use issue() instead.
70
+ *
71
+ * @param options - Receipt options
72
+ * @returns JWS compact serialization
73
+ */
74
+ export declare function issueJws(options: IssueOptions): Promise<string>;
75
+ //# sourceMappingURL=issue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue.d.ts","sourceRoot":"","sources":["../src/issue.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EACL,iBAAiB,EAEjB,sBAAsB,EAEvB,MAAM,cAAc,CAAC;AAEtB;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,4BAA4B;IAC5B,GAAG,EAAE,MAAM,CAAC;IAEZ,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IAEZ,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IAEZ,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IAEZ,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IAEb,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAElB,4FAA4F;IAC5F,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEtB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAE/B,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAE1C,qCAAqC;IACrC,UAAU,EAAE,UAAU,CAAC;IAEvB,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IAEZ,+CAA+C;IAC/C,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;CAC3C;AAED;;;;;GAKG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA2EvE;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrE"}
package/dist/issue.js ADDED
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ /**
3
+ * Receipt issuance
4
+ * Validates input, generates UUIDv7 rid, and signs with Ed25519
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.issue = issue;
8
+ exports.issueJws = issueJws;
9
+ const uuidv7_1 = require("uuidv7");
10
+ const crypto_1 = require("@peac/crypto");
11
+ const schema_1 = require("@peac/schema");
12
+ /**
13
+ * Issue a PEAC receipt
14
+ *
15
+ * @param options - Receipt options
16
+ * @returns Issue result with JWS and optional subject_snapshot
17
+ */
18
+ async function issue(options) {
19
+ // Validate URLs
20
+ if (!options.iss.startsWith('https://')) {
21
+ throw new Error('Issuer URL must start with https://');
22
+ }
23
+ if (!options.aud.startsWith('https://')) {
24
+ throw new Error('Audience URL must start with https://');
25
+ }
26
+ if (options.subject && !options.subject.startsWith('https://')) {
27
+ throw new Error('Subject URI must start with https://');
28
+ }
29
+ // Validate currency code
30
+ if (!/^[A-Z]{3}$/.test(options.cur)) {
31
+ throw new Error('Currency must be ISO 4217 uppercase (e.g., USD)');
32
+ }
33
+ // Validate amount
34
+ if (!Number.isInteger(options.amt) || options.amt < 0) {
35
+ throw new Error('Amount must be a non-negative integer');
36
+ }
37
+ // Validate expiry (if provided)
38
+ if (options.exp !== undefined) {
39
+ if (!Number.isInteger(options.exp) || options.exp < 0) {
40
+ throw new Error('Expiry must be a non-negative integer');
41
+ }
42
+ }
43
+ // Generate UUIDv7 for receipt ID
44
+ const rid = (0, uuidv7_1.uuidv7)();
45
+ // Get current timestamp
46
+ const iat = Math.floor(Date.now() / 1000);
47
+ // Build receipt claims
48
+ const claims = {
49
+ iss: options.iss,
50
+ aud: options.aud,
51
+ iat,
52
+ rid,
53
+ amt: options.amt,
54
+ cur: options.cur,
55
+ payment: {
56
+ rail: options.rail,
57
+ reference: options.reference,
58
+ amount: options.amt,
59
+ currency: options.cur,
60
+ asset: options.asset ?? options.cur, // Default asset to currency for backward compatibility
61
+ env: options.env ?? 'test', // Default to test environment for backward compatibility
62
+ evidence: options.evidence ?? {}, // Default to empty object for backward compatibility
63
+ ...(options.network && { network: options.network }),
64
+ ...(options.facilitator_ref && { facilitator_ref: options.facilitator_ref }),
65
+ ...(options.idempotency_key && { idempotency_key: options.idempotency_key }),
66
+ ...(options.metadata && { metadata: options.metadata }),
67
+ },
68
+ ...(options.exp && { exp: options.exp }),
69
+ ...(options.subject && { subject: { uri: options.subject } }),
70
+ ...(options.ext && { ext: options.ext }),
71
+ };
72
+ // Validate claims with Zod
73
+ schema_1.ReceiptClaims.parse(claims);
74
+ // Validate subject_snapshot if provided (v0.9.17+)
75
+ // This validates schema and logs advisory PII warning if applicable
76
+ const validatedSnapshot = (0, schema_1.validateSubjectSnapshot)(options.subject_snapshot);
77
+ // Sign with Ed25519
78
+ const jws = await (0, crypto_1.sign)(claims, options.privateKey, options.kid);
79
+ return {
80
+ jws,
81
+ ...(validatedSnapshot && { subject_snapshot: validatedSnapshot }),
82
+ };
83
+ }
84
+ /**
85
+ * Issue a PEAC receipt and return just the JWS string
86
+ *
87
+ * Convenience wrapper for common header-centric flows where only the JWS is needed.
88
+ * For access to validated subject_snapshot, use issue() instead.
89
+ *
90
+ * @param options - Receipt options
91
+ * @returns JWS compact serialization
92
+ */
93
+ async function issueJws(options) {
94
+ const result = await issue(options);
95
+ return result.jws;
96
+ }
97
+ //# sourceMappingURL=issue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue.js","sourceRoot":"","sources":["../src/issue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Receipt verification with JWKS fetching and caching
3
+ */
4
+ import { PEACReceiptClaims, SubjectProfileSnapshot } from '@peac/schema';
5
+ /**
6
+ * Verification result
7
+ */
8
+ export interface VerifyResult {
9
+ /** Verification succeeded */
10
+ ok: true;
11
+ /** Receipt claims */
12
+ claims: PEACReceiptClaims;
13
+ /** Subject profile snapshot (v0.9.17+, if provided) */
14
+ subject_snapshot?: SubjectProfileSnapshot;
15
+ /** Performance metrics */
16
+ perf?: {
17
+ verify_ms: number;
18
+ jwks_fetch_ms?: number;
19
+ };
20
+ }
21
+ /**
22
+ * Verification failure
23
+ */
24
+ export interface VerifyFailure {
25
+ /** Verification failed */
26
+ ok: false;
27
+ /** Error reason */
28
+ reason: string;
29
+ /** Error details */
30
+ details?: string;
31
+ }
32
+ /**
33
+ * Options for verifying a receipt
34
+ */
35
+ export interface VerifyOptions {
36
+ /** JWS compact serialization */
37
+ receiptJws: string;
38
+ /** Subject profile snapshot (v0.9.17+, optional envelope metadata) */
39
+ subject_snapshot?: SubjectProfileSnapshot;
40
+ }
41
+ /**
42
+ * Verify a PEAC receipt JWS
43
+ *
44
+ * @param optionsOrJws - Verify options or JWS compact serialization (for backwards compatibility)
45
+ * @returns Verification result or failure
46
+ */
47
+ export declare function verifyReceipt(optionsOrJws: string | VerifyOptions): Promise<VerifyResult | VerifyFailure>;
48
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EACL,iBAAiB,EAEjB,sBAAsB,EAEvB,MAAM,cAAc,CAAC;AA8BtB;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6BAA6B;IAC7B,EAAE,EAAE,IAAI,CAAC;IAET,qBAAqB;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAE1B,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAE1C,0BAA0B;IAC1B,IAAI,CAAC,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,EAAE,EAAE,KAAK,CAAC;IAEV,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IAEf,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmGD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IAEnB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;CAC3C;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GAAG,aAAa,GACnC,OAAO,CAAC,YAAY,GAAG,aAAa,CAAC,CA6EvC"}
package/dist/verify.js ADDED
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ /**
3
+ * Receipt verification with JWKS fetching and caching
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.verifyReceipt = verifyReceipt;
7
+ const crypto_1 = require("@peac/crypto");
8
+ const schema_1 = require("@peac/schema");
9
+ /**
10
+ * In-memory JWKS cache
11
+ * Maps issuer URL to { keys, expiresAt }
12
+ */
13
+ const jwksCache = new Map();
14
+ /**
15
+ * Cache TTL (5 minutes)
16
+ */
17
+ const CACHE_TTL_MS = 5 * 60 * 1000;
18
+ /**
19
+ * Fetch JWKS from issuer (SSRF-safe)
20
+ */
21
+ async function fetchJWKS(issuerUrl) {
22
+ // SSRF protection: only allow https://
23
+ if (!issuerUrl.startsWith('https://')) {
24
+ throw new Error('Issuer URL must be https://');
25
+ }
26
+ // Construct JWKS URL from discovery
27
+ const discoveryUrl = `${issuerUrl}/.well-known/peac.txt`;
28
+ try {
29
+ const discoveryResp = await fetch(discoveryUrl, {
30
+ headers: { Accept: 'text/plain' },
31
+ // Timeout after 5 seconds
32
+ signal: AbortSignal.timeout(5000),
33
+ });
34
+ if (!discoveryResp.ok) {
35
+ throw new Error(`Discovery fetch failed: ${discoveryResp.status}`);
36
+ }
37
+ const discoveryText = await discoveryResp.text();
38
+ // Parse YAML-like discovery (simple key: value parsing)
39
+ const jwksLine = discoveryText.split('\n').find((line) => line.startsWith('jwks:'));
40
+ if (!jwksLine) {
41
+ throw new Error('No jwks field in discovery');
42
+ }
43
+ const jwksUrl = jwksLine.replace('jwks:', '').trim();
44
+ // SSRF protection: verify JWKS URL is also https://
45
+ if (!jwksUrl.startsWith('https://')) {
46
+ throw new Error('JWKS URL must be https://');
47
+ }
48
+ // Fetch JWKS
49
+ const jwksResp = await fetch(jwksUrl, {
50
+ headers: { Accept: 'application/json' },
51
+ signal: AbortSignal.timeout(5000),
52
+ });
53
+ if (!jwksResp.ok) {
54
+ throw new Error(`JWKS fetch failed: ${jwksResp.status}`);
55
+ }
56
+ const jwks = (await jwksResp.json());
57
+ return jwks;
58
+ }
59
+ catch (err) {
60
+ throw new Error(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
61
+ }
62
+ }
63
+ /**
64
+ * Get JWKS (from cache or fetch)
65
+ */
66
+ async function getJWKS(issuerUrl) {
67
+ const now = Date.now();
68
+ // Check cache
69
+ const cached = jwksCache.get(issuerUrl);
70
+ if (cached && cached.expiresAt > now) {
71
+ return { jwks: cached.keys, fromCache: true };
72
+ }
73
+ // Fetch fresh JWKS
74
+ const jwks = await fetchJWKS(issuerUrl);
75
+ // Cache it
76
+ jwksCache.set(issuerUrl, {
77
+ keys: jwks,
78
+ expiresAt: now + CACHE_TTL_MS,
79
+ });
80
+ return { jwks, fromCache: false };
81
+ }
82
+ /**
83
+ * Convert JWK x coordinate to Ed25519 public key
84
+ */
85
+ function jwkToPublicKey(jwk) {
86
+ if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {
87
+ throw new Error('Only Ed25519 keys (OKP/Ed25519) are supported');
88
+ }
89
+ // Decode base64url x coordinate
90
+ const xBytes = Buffer.from(jwk.x, 'base64url');
91
+ if (xBytes.length !== 32) {
92
+ throw new Error('Ed25519 public key must be 32 bytes');
93
+ }
94
+ return new Uint8Array(xBytes);
95
+ }
96
+ /**
97
+ * Verify a PEAC receipt JWS
98
+ *
99
+ * @param optionsOrJws - Verify options or JWS compact serialization (for backwards compatibility)
100
+ * @returns Verification result or failure
101
+ */
102
+ async function verifyReceipt(optionsOrJws) {
103
+ // Support both old (string) and new (options) signatures for backwards compatibility
104
+ const receiptJws = typeof optionsOrJws === 'string' ? optionsOrJws : optionsOrJws.receiptJws;
105
+ const inputSnapshot = typeof optionsOrJws === 'string' ? undefined : optionsOrJws.subject_snapshot;
106
+ const startTime = performance.now();
107
+ let jwksFetchTime;
108
+ try {
109
+ // Decode JWS to get issuer
110
+ const { header, payload } = (0, crypto_1.decode)(receiptJws);
111
+ // Validate claims structure
112
+ schema_1.ReceiptClaims.parse(payload);
113
+ // Check expiry
114
+ if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
115
+ return {
116
+ ok: false,
117
+ reason: 'expired',
118
+ details: `Receipt expired at ${new Date(payload.exp * 1000).toISOString()}`,
119
+ };
120
+ }
121
+ // Fetch JWKS
122
+ const jwksFetchStart = performance.now();
123
+ const { jwks, fromCache } = await getJWKS(payload.iss);
124
+ if (!fromCache) {
125
+ jwksFetchTime = performance.now() - jwksFetchStart;
126
+ }
127
+ // Find key by kid
128
+ const jwk = jwks.keys.find((k) => k.kid === header.kid);
129
+ if (!jwk) {
130
+ return {
131
+ ok: false,
132
+ reason: 'unknown_key',
133
+ details: `No key found with kid=${header.kid}`,
134
+ };
135
+ }
136
+ // Convert JWK to public key
137
+ const publicKey = jwkToPublicKey(jwk);
138
+ // Verify signature
139
+ const result = await (0, crypto_1.verify)(receiptJws, publicKey);
140
+ if (!result.valid) {
141
+ return {
142
+ ok: false,
143
+ reason: 'invalid_signature',
144
+ details: 'Ed25519 signature verification failed',
145
+ };
146
+ }
147
+ // Validate subject_snapshot if provided (v0.9.17+)
148
+ // This validates schema and logs advisory PII warning if applicable
149
+ const validatedSnapshot = (0, schema_1.validateSubjectSnapshot)(inputSnapshot);
150
+ const verifyTime = performance.now() - startTime;
151
+ return {
152
+ ok: true,
153
+ claims: payload,
154
+ ...(validatedSnapshot && { subject_snapshot: validatedSnapshot }),
155
+ perf: {
156
+ verify_ms: verifyTime,
157
+ ...(jwksFetchTime && { jwks_fetch_ms: jwksFetchTime }),
158
+ },
159
+ };
160
+ }
161
+ catch (err) {
162
+ return {
163
+ ok: false,
164
+ reason: 'verification_error',
165
+ details: err instanceof Error ? err.message : String(err),
166
+ };
167
+ }
168
+ }
169
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@peac/protocol",
3
+ "version": "0.9.18",
4
+ "description": "PEAC protocol implementation - receipt issuance and verification",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/peacprotocol/peac.git",
10
+ "directory": "packages/protocol"
11
+ },
12
+ "author": "jithinraj <7850727+jithinraj@users.noreply.github.com>",
13
+ "license": "Apache-2.0",
14
+ "bugs": {
15
+ "url": "https://github.com/peacprotocol/peac/issues"
16
+ },
17
+ "homepage": "https://github.com/peacprotocol/peac#readme",
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "clean": "rm -rf dist"
30
+ },
31
+ "dependencies": {
32
+ "@peac/schema": "workspace:*",
33
+ "@peac/crypto": "workspace:*",
34
+ "uuidv7": "^0.6.3"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^20.10.0",
38
+ "typescript": "^5.3.3",
39
+ "vitest": "^1.1.0"
40
+ }
41
+ }