@fluxpointstudios/orynq-sdk-tool-receipts 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Flux Point Studios
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs ADDED
@@ -0,0 +1,518 @@
1
+ 'use strict';
2
+
3
+ var orynqSdkProcessTrace = require('@fluxpointstudios/orynq-sdk-process-trace');
4
+ var utils = require('@fluxpointstudios/orynq-sdk-core/utils');
5
+ var crypto = require('crypto');
6
+
7
+ // src/record.ts
8
+ async function hashToolPayload(value) {
9
+ const serialized = typeof value === "string" ? value : utils.canonicalize(value);
10
+ return utils.sha256StringHex(serialized);
11
+ }
12
+ async function addToolReceipt(run, spanId, opts) {
13
+ if (!opts.toolId) throw new Error("addToolReceipt: toolId is required");
14
+ if (!opts.receipt) throw new Error("addToolReceipt: receipt is required");
15
+ const event = {
16
+ kind: "tool-receipt",
17
+ visibility: opts.visibility ?? "private",
18
+ toolId: opts.toolId,
19
+ request: opts.request,
20
+ response: opts.response,
21
+ receipt: opts.receipt
22
+ };
23
+ const recorded = await orynqSdkProcessTrace.addEvent(run, spanId, event);
24
+ return recorded;
25
+ }
26
+ var textEncoder = new TextEncoder();
27
+ function utf8(s) {
28
+ return Buffer.from(textEncoder.encode(s));
29
+ }
30
+ function constantTimeEqualHex(a, b) {
31
+ const ab = Buffer.from(a.toLowerCase(), "hex");
32
+ const bb = Buffer.from(b.toLowerCase(), "hex");
33
+ if (ab.length === 0 || ab.length !== bb.length) return false;
34
+ return crypto.timingSafeEqual(ab, bb);
35
+ }
36
+ async function resolveKey(event, ctx, { allowEmbedded }) {
37
+ if (ctx?.resolveKey) {
38
+ const k = await ctx.resolveKey(event);
39
+ if (k !== void 0) return k;
40
+ }
41
+ if (ctx?.keys && Object.prototype.hasOwnProperty.call(ctx.keys, event.receipt.signer)) {
42
+ return ctx.keys[event.receipt.signer];
43
+ }
44
+ if (allowEmbedded && ctx?.trustEmbeddedKeys === true) {
45
+ const p = event.receipt.params;
46
+ if (p && typeof p.publicKey === "string") return p.publicKey;
47
+ }
48
+ return void 0;
49
+ }
50
+ function keyToString(key) {
51
+ return typeof key === "string" ? key : Buffer.from(key).toString("utf8");
52
+ }
53
+ var SPKI_ALG_OIDS = [
54
+ // rsaEncryption 1.2.840.113549.1.1.1
55
+ [42, 134, 72, 134, 247, 13, 1, 1, 1],
56
+ // id-ecPublicKey 1.2.840.10045.2.1
57
+ [42, 134, 72, 206, 61, 2, 1],
58
+ // id-Ed25519 1.3.101.112
59
+ [43, 101, 112],
60
+ // id-Ed448 1.3.101.113
61
+ [43, 101, 113]
62
+ ];
63
+ function looksLikeDerPublicKey(bytes) {
64
+ if (bytes.length < 8 || bytes[0] !== 48) return false;
65
+ const lenByte = bytes[1];
66
+ let contentStart;
67
+ let contentLen;
68
+ if (lenByte < 128) {
69
+ contentStart = 2;
70
+ contentLen = lenByte;
71
+ } else if (lenByte === 129) {
72
+ contentStart = 3;
73
+ contentLen = bytes[2];
74
+ } else if (lenByte === 130) {
75
+ contentStart = 4;
76
+ contentLen = bytes[2] << 8 | bytes[3];
77
+ } else {
78
+ return false;
79
+ }
80
+ if (contentStart + contentLen !== bytes.length) return false;
81
+ return SPKI_ALG_OIDS.some((oid) => indexOfBytes(bytes, oid) !== -1);
82
+ }
83
+ function indexOfBytes(haystack, needle) {
84
+ outer: for (let i = 0; i + needle.length <= haystack.length; i++) {
85
+ for (let j = 0; j < needle.length; j++) {
86
+ if (haystack[i + j] !== needle[j]) continue outer;
87
+ }
88
+ return i;
89
+ }
90
+ return -1;
91
+ }
92
+ function looksLikeAsymmetricPublicKey(key) {
93
+ if (typeof key !== "string" && looksLikeDerPublicKey(key)) return true;
94
+ const s = keyToString(key).trim();
95
+ if (s.includes("-----BEGIN PUBLIC KEY-----") || s.includes("-----BEGIN RSA PUBLIC KEY-----")) {
96
+ return true;
97
+ }
98
+ if (s.startsWith("{")) {
99
+ try {
100
+ const jwk = JSON.parse(s);
101
+ const kty = typeof jwk.kty === "string" ? jwk.kty.toUpperCase() : "";
102
+ return kty === "RSA" || kty === "EC" || kty === "OKP";
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
107
+ return false;
108
+ }
109
+ function assertSymmetricSecret(key, algLabel) {
110
+ if (looksLikeAsymmetricPublicKey(key)) {
111
+ throw new Error(
112
+ `${algLabel}: refusing to HMAC with an asymmetric public key (algorithm confusion). Provide the signer's symmetric secret out-of-band, or pin the asymmetric alg via keyAlgs.`
113
+ );
114
+ }
115
+ }
116
+ function assertAlgAllowed(event, ctx, alg) {
117
+ const allow = ctx?.keyAlgs?.[event.receipt.signer];
118
+ if (allow && !allow.includes(alg)) {
119
+ throw new Error(
120
+ `alg "${alg}" is not in the expected-algorithm allow-list for signer "${event.receipt.signer}"`
121
+ );
122
+ }
123
+ }
124
+ function parseStripeSignature(raw, params) {
125
+ if (raw.includes("v1=") || raw.includes("t=")) {
126
+ const parts = raw.split(",").map((p) => p.trim());
127
+ let t2;
128
+ const v1 = [];
129
+ for (const part of parts) {
130
+ const eq = part.indexOf("=");
131
+ if (eq === -1) continue;
132
+ const k = part.slice(0, eq);
133
+ const v = part.slice(eq + 1);
134
+ if (k === "t") t2 = v;
135
+ else if (k === "v1") v1.push(v);
136
+ }
137
+ return t2 !== void 0 ? { t: t2, v1 } : { v1 };
138
+ }
139
+ const t = typeof params?.timestamp === "string" ? params.timestamp : void 0;
140
+ return t !== void 0 ? { t, v1: [raw] } : { v1: [raw] };
141
+ }
142
+ async function verifyStripeReceipt(event, ctx) {
143
+ const secret = await resolveKey(event, ctx, { allowEmbedded: false });
144
+ if (secret === void 0) {
145
+ throw new Error(
146
+ "stripe-webhook: signing secret not found \u2014 provide it via verify context (keys/resolveKey), not the trace"
147
+ );
148
+ }
149
+ const { t, v1 } = parseStripeSignature(event.receipt.signature, event.receipt.params);
150
+ if (t === void 0) throw new Error("stripe-webhook: missing timestamp (t)");
151
+ if (v1.length === 0) throw new Error("stripe-webhook: missing v1 signature");
152
+ const toleranceSec = ctx?.toleranceSec ?? 300;
153
+ const now = ctx?.nowSec ?? Math.floor(Date.now() / 1e3);
154
+ const ts = Number(t);
155
+ if (!Number.isFinite(ts)) throw new Error("stripe-webhook: invalid timestamp");
156
+ if (Math.abs(now - ts) > toleranceSec) {
157
+ throw new Error(`stripe-webhook: timestamp outside tolerance (${toleranceSec}s)`);
158
+ }
159
+ const signedBase = `${t}.${event.receipt.signedPayload}`;
160
+ const expected = crypto.createHmac("sha256", keyToString(secret)).update(signedBase).digest("hex");
161
+ return v1.some((candidate) => constantTimeEqualHex(expected, candidate));
162
+ }
163
+ async function verifyGitHubReceipt(event, ctx) {
164
+ const secret = await resolveKey(event, ctx, { allowEmbedded: false });
165
+ if (secret === void 0) {
166
+ throw new Error(
167
+ "github-webhook: signing secret not found \u2014 provide it via verify context (keys/resolveKey)"
168
+ );
169
+ }
170
+ const ts = event.receipt.params?.timestamp;
171
+ if (typeof ts === "string" || typeof ts === "number") {
172
+ const tsNum = Number(ts);
173
+ if (!Number.isFinite(tsNum)) throw new Error("github-webhook: invalid timestamp");
174
+ const toleranceSec = ctx?.toleranceSec ?? 300;
175
+ const now = ctx?.nowSec ?? Math.floor(Date.now() / 1e3);
176
+ if (Math.abs(now - tsNum) > toleranceSec) {
177
+ throw new Error(`github-webhook: timestamp outside tolerance (${toleranceSec}s)`);
178
+ }
179
+ }
180
+ const provided = event.receipt.signature.startsWith("sha256=") ? event.receipt.signature.slice("sha256=".length) : event.receipt.signature;
181
+ const expected = crypto.createHmac("sha256", keyToString(secret)).update(event.receipt.signedPayload).digest("hex");
182
+ return constantTimeEqualHex(expected, provided);
183
+ }
184
+ function parseJws(event) {
185
+ const sp = event.receipt.signedPayload;
186
+ const segments = sp.split(".");
187
+ let signingInput;
188
+ let sigB64;
189
+ if (segments.length === 3) {
190
+ signingInput = `${segments[0]}.${segments[1]}`;
191
+ sigB64 = segments[2];
192
+ } else if (segments.length === 2) {
193
+ signingInput = sp;
194
+ sigB64 = event.receipt.signature;
195
+ } else {
196
+ throw new Error("jws: signedPayload must be a compact JWS (h.p.s) or signing input (h.p)");
197
+ }
198
+ const headerJson = Buffer.from(segments[0], "base64url").toString("utf8");
199
+ const header = JSON.parse(headerJson);
200
+ return { signingInput, signature: Buffer.from(sigB64, "base64url"), header };
201
+ }
202
+ async function verifyJwsReceipt(event, ctx) {
203
+ const { signingInput, signature, header } = parseJws(event);
204
+ const alg = header.alg;
205
+ if (!alg || alg === "none") throw new Error(`jws: unsupported alg "${alg}"`);
206
+ assertAlgAllowed(event, ctx, alg);
207
+ const data = utf8(signingInput);
208
+ if (alg.startsWith("HS")) {
209
+ const secret = await resolveKey(event, ctx, { allowEmbedded: false });
210
+ if (secret === void 0) throw new Error(`jws(${alg}): HMAC secret not found in verify context`);
211
+ assertSymmetricSecret(secret, `jws(${alg})`);
212
+ const hashAlg = `sha${alg.slice(2)}`;
213
+ const expected = crypto.createHmac(hashAlg, keyToString(secret)).update(data).digest();
214
+ return expected.length === signature.length && crypto.timingSafeEqual(expected, signature);
215
+ }
216
+ const keyMaterial = await resolveKey(event, ctx, { allowEmbedded: true });
217
+ if (keyMaterial === void 0) throw new Error(`jws(${alg}): public key not found`);
218
+ const publicKey = toPublicKey(keyMaterial);
219
+ if (alg.startsWith("RS")) {
220
+ return crypto.verify(`sha${alg.slice(2)}`, data, publicKey, signature);
221
+ }
222
+ if (alg.startsWith("PS")) {
223
+ const bits = alg.slice(2);
224
+ return crypto.verify(
225
+ `sha${bits}`,
226
+ data,
227
+ { key: publicKey, padding: 6, saltLength: Number(bits) / 8 },
228
+ signature
229
+ );
230
+ }
231
+ if (alg.startsWith("ES")) {
232
+ return crypto.verify(
233
+ `sha${alg.slice(2)}`,
234
+ data,
235
+ { key: publicKey, dsaEncoding: "ieee-p1363" },
236
+ signature
237
+ );
238
+ }
239
+ if (alg === "EdDSA") {
240
+ return crypto.verify(null, data, publicKey, signature);
241
+ }
242
+ throw new Error(`jws: unsupported alg "${alg}"`);
243
+ }
244
+ var RFC9421_HASH = {
245
+ "rsa-pss-sha512": "sha512",
246
+ "rsa-v1_5-sha256": "sha256",
247
+ "ecdsa-p256-sha256": "sha256",
248
+ "ecdsa-p384-sha384": "sha384"
249
+ };
250
+ async function verifyHttpMessageReceipt(event, ctx) {
251
+ const params = event.receipt.params ?? {};
252
+ const alg = typeof params.alg === "string" ? params.alg : void 0;
253
+ if (!alg) {
254
+ throw new Error("http-message-signatures: receipt.params.alg is required (RFC 9421 alg id)");
255
+ }
256
+ assertAlgAllowed(event, ctx, alg);
257
+ const data = utf8(event.receipt.signedPayload);
258
+ const signature = decodeSignature(event.receipt.signature);
259
+ if (alg === "ed25519") {
260
+ const keyMaterial2 = await resolveKey(event, ctx, { allowEmbedded: true });
261
+ if (keyMaterial2 === void 0) throw new Error("http-message-signatures(ed25519): public key not found");
262
+ return crypto.verify(null, data, toPublicKey(keyMaterial2), signature);
263
+ }
264
+ if (alg === "hmac-sha256") {
265
+ const secret = await resolveKey(event, ctx, { allowEmbedded: false });
266
+ if (secret === void 0) throw new Error("http-message-signatures(hmac-sha256): secret not found");
267
+ assertSymmetricSecret(secret, "http-message-signatures(hmac-sha256)");
268
+ const expected = crypto.createHmac("sha256", keyToString(secret)).update(data).digest();
269
+ return expected.length === signature.length && crypto.timingSafeEqual(expected, signature);
270
+ }
271
+ const hash = RFC9421_HASH[alg];
272
+ if (!hash) throw new Error(`http-message-signatures: unsupported alg "${alg}"`);
273
+ const keyMaterial = await resolveKey(event, ctx, { allowEmbedded: true });
274
+ if (keyMaterial === void 0) throw new Error(`http-message-signatures(${alg}): public key not found`);
275
+ const publicKey = toPublicKey(keyMaterial);
276
+ if (alg === "rsa-pss-sha512") {
277
+ return crypto.verify(hash, data, { key: publicKey, padding: 6, saltLength: 64 }, signature);
278
+ }
279
+ if (alg === "rsa-v1_5-sha256") {
280
+ return crypto.verify(hash, data, publicKey, signature);
281
+ }
282
+ return crypto.verify(hash, data, { key: publicKey, dsaEncoding: "ieee-p1363" }, signature);
283
+ }
284
+ function toPublicKey(material) {
285
+ if (typeof material === "string") {
286
+ const trimmed = material.trim();
287
+ if (trimmed.startsWith("{")) {
288
+ return crypto.createPublicKey({ key: JSON.parse(trimmed), format: "jwk" });
289
+ }
290
+ return crypto.createPublicKey(material);
291
+ }
292
+ return crypto.createPublicKey(Buffer.from(material));
293
+ }
294
+ function decodeSignature(sig) {
295
+ if (sig.startsWith("0x")) return Buffer.from(sig.slice(2), "hex");
296
+ if (/[-_]/.test(sig)) return Buffer.from(sig, "base64url");
297
+ return Buffer.from(sig, "base64");
298
+ }
299
+ async function responseCommitmentHash(event) {
300
+ const { scheme, signedPayload } = event.receipt;
301
+ switch (scheme) {
302
+ case "jws": {
303
+ const segs = signedPayload.split(".");
304
+ if (segs.length < 2 || !segs[1]) return null;
305
+ const body = Buffer.from(segs[1], "base64url").toString("utf8");
306
+ return utils.sha256StringHex(body);
307
+ }
308
+ case "stripe-webhook":
309
+ case "github-webhook":
310
+ return utils.sha256StringHex(signedPayload);
311
+ case "http-message-signatures":
312
+ return contentDigestSha256Hex(signedPayload);
313
+ default:
314
+ return null;
315
+ }
316
+ }
317
+ function jwsBindingContext(event) {
318
+ if (event.receipt.scheme !== "jws") return null;
319
+ const seg0 = event.receipt.signedPayload.split(".")[0];
320
+ if (!seg0) return null;
321
+ try {
322
+ const header = JSON.parse(Buffer.from(seg0, "base64url").toString("utf8"));
323
+ const b = header.orynqBinding;
324
+ if (!b || typeof b.runId !== "string") return null;
325
+ return typeof b.requestHash === "string" ? { runId: b.runId, requestHash: b.requestHash } : { runId: b.runId };
326
+ } catch {
327
+ return null;
328
+ }
329
+ }
330
+ function contentDigestSha256Hex(signatureBase) {
331
+ for (const line of signatureBase.split("\n")) {
332
+ const m = /^"content-digest":\s*(.+)$/i.exec(line.trim());
333
+ if (!m) continue;
334
+ const d = /sha-256=:([A-Za-z0-9+/=]+):/.exec(m[1]);
335
+ if (!d || !d[1]) return null;
336
+ return Buffer.from(d[1], "base64").toString("hex");
337
+ }
338
+ return null;
339
+ }
340
+ var BUILTIN_TOOL_RECEIPT_VERIFIERS = {
341
+ "stripe-webhook": verifyStripeReceipt,
342
+ "github-webhook": verifyGitHubReceipt,
343
+ jws: verifyJwsReceipt,
344
+ "http-message-signatures": verifyHttpMessageReceipt
345
+ };
346
+ function hexEq(a, b) {
347
+ const na = a.startsWith("0x") ? a.slice(2) : a;
348
+ const nb = b.startsWith("0x") ? b.slice(2) : b;
349
+ return na.toLowerCase() === nb.toLowerCase();
350
+ }
351
+ function extractToolReceipts(bundle) {
352
+ return bundle.privateRun.events.filter(
353
+ (e) => e.kind === "tool-receipt"
354
+ );
355
+ }
356
+ async function verifyToolReceipt(event, ctx) {
357
+ const scheme = event.receipt.scheme;
358
+ const base = {
359
+ eventId: event.id,
360
+ toolId: event.toolId,
361
+ scheme,
362
+ signer: event.receipt.signer
363
+ };
364
+ const callBound = false;
365
+ const verifier = ctx?.verifiers?.[scheme] ?? BUILTIN_TOOL_RECEIPT_VERIFIERS[scheme];
366
+ if (!verifier) {
367
+ return {
368
+ ...base,
369
+ callBound,
370
+ verified: false,
371
+ error: `no verifier registered for scheme "${scheme}"`
372
+ };
373
+ }
374
+ try {
375
+ const sigValid = await verifier(event, ctx);
376
+ if (!sigValid) return { ...base, callBound, verified: false, reason: "signature-invalid" };
377
+ const commit = await responseCommitmentHash(event);
378
+ if (commit === null) {
379
+ return { ...base, callBound, verified: false, reason: "response-not-bound" };
380
+ }
381
+ if (!hexEq(commit, event.response.hash)) {
382
+ return { ...base, callBound, verified: false, reason: "response-binding-mismatch" };
383
+ }
384
+ if (event.response.payload !== void 0) {
385
+ const payloadHash = await hashToolPayload(event.response.payload);
386
+ if (!hexEq(payloadHash, event.response.hash)) {
387
+ return { ...base, callBound, verified: false, reason: "response-payload-mismatch" };
388
+ }
389
+ }
390
+ let provenCallBound = false;
391
+ if (scheme === "jws") {
392
+ const bound = jwsBindingContext(event);
393
+ if (bound !== null) {
394
+ if (ctx?.runId !== void 0 && bound.runId !== ctx.runId) {
395
+ return { ...base, callBound, verified: false, reason: "call-binding-mismatch" };
396
+ }
397
+ if (bound.requestHash !== void 0 && !hexEq(bound.requestHash, event.request.hash)) {
398
+ return { ...base, callBound, verified: false, reason: "call-binding-mismatch" };
399
+ }
400
+ provenCallBound = bound.requestHash !== void 0 && hexEq(bound.requestHash, event.request.hash) && ctx?.runId !== void 0 && bound.runId === ctx.runId;
401
+ }
402
+ }
403
+ if (ctx?.requireCallBinding && !provenCallBound) {
404
+ return { ...base, callBound: provenCallBound, verified: false, reason: "call-binding-required" };
405
+ }
406
+ return { ...base, callBound: provenCallBound, verified: true };
407
+ } catch (error) {
408
+ return {
409
+ ...base,
410
+ callBound,
411
+ verified: false,
412
+ error: error instanceof Error ? error.message : String(error)
413
+ };
414
+ }
415
+ }
416
+ async function verifyToolReceipts(bundle, ctx) {
417
+ const events = extractToolReceipts(bundle);
418
+ const boundCtx = {
419
+ ...ctx,
420
+ runId: ctx?.runId ?? bundle.privateRun.id
421
+ };
422
+ const results = [];
423
+ for (const event of events) {
424
+ results.push(await verifyToolReceipt(event, boundCtx));
425
+ }
426
+ const failed = results.filter((r) => !r.verified);
427
+ return {
428
+ valid: failed.length === 0,
429
+ errors: failed.map(
430
+ (f) => `tool-receipt "${f.toolId}" (${f.scheme}) failed${f.reason ? ` [${f.reason}]` : ""}${f.error ? `: ${f.error}` : ""}`
431
+ ),
432
+ results
433
+ };
434
+ }
435
+ async function verifyTrace(bundle, ctx) {
436
+ const outcome = await verifyToolReceipts(bundle, ctx);
437
+ const result = await orynqSdkProcessTrace.verifyBundle(bundle, {
438
+ toolReceipts: () => ({ valid: outcome.valid, errors: outcome.errors })
439
+ });
440
+ return { ...result, toolReceipts: outcome };
441
+ }
442
+ function b64url(input) {
443
+ return Buffer.from(input).toString("base64url");
444
+ }
445
+ function signJws(opts, signingInput) {
446
+ const data = Buffer.from(signingInput, "utf8");
447
+ if (opts.alg === "HS256") {
448
+ if (!opts.secret) throw new Error("createSigningProxy(HS256): `secret` is required");
449
+ return crypto.createHmac("sha256", opts.secret).update(data).digest();
450
+ }
451
+ if (!opts.privateKey) {
452
+ throw new Error(`createSigningProxy(${opts.alg}): \`privateKey\` is required`);
453
+ }
454
+ const key = typeof opts.privateKey === "string" ? crypto.createPrivateKey(opts.privateKey) : opts.privateKey;
455
+ if (opts.alg === "EdDSA") return crypto.sign(null, data, key);
456
+ if (opts.alg.startsWith("ES")) {
457
+ return crypto.sign(`sha${opts.alg.slice(2)}`, data, { key, dsaEncoding: "ieee-p1363" });
458
+ }
459
+ if (opts.alg.startsWith("PS")) {
460
+ return crypto.sign(`sha${opts.alg.slice(2)}`, data, {
461
+ key,
462
+ padding: 6,
463
+ saltLength: Number(opts.alg.slice(2)) / 8
464
+ });
465
+ }
466
+ if (opts.alg.startsWith("RS")) {
467
+ return crypto.sign(`sha${opts.alg.slice(2)}`, data, key);
468
+ }
469
+ throw new Error(`createSigningProxy: unsupported alg "${opts.alg}"`);
470
+ }
471
+ function createSigningProxy(opts) {
472
+ if (!opts.signer) throw new Error("createSigningProxy: `signer` is required");
473
+ return {
474
+ sign(payload) {
475
+ const header = { alg: opts.alg, typ: "JWT", kid: opts.signer };
476
+ if (opts.binding) {
477
+ header.orynqBinding = {
478
+ runId: opts.binding.runId,
479
+ ...opts.binding.requestHash !== void 0 ? { requestHash: opts.binding.requestHash } : {}
480
+ };
481
+ }
482
+ const h = b64url(JSON.stringify(header));
483
+ const body = typeof payload === "string" ? payload : utils.canonicalize(payload);
484
+ const p = b64url(body);
485
+ const signingInput = `${h}.${p}`;
486
+ const sigB64 = b64url(signJws(opts, signingInput));
487
+ return {
488
+ // signedPayload is the JWS *signing input* (the bytes actually signed);
489
+ // the signature is carried separately so it is independently checkable.
490
+ scheme: "jws",
491
+ signer: opts.signer,
492
+ signature: sigB64,
493
+ signedPayload: signingInput,
494
+ ...opts.publicKey ? { params: { publicKey: opts.publicKey } } : {}
495
+ };
496
+ }
497
+ };
498
+ }
499
+
500
+ // src/index.ts
501
+ var VERSION = "0.1.0";
502
+
503
+ exports.BUILTIN_TOOL_RECEIPT_VERIFIERS = BUILTIN_TOOL_RECEIPT_VERIFIERS;
504
+ exports.VERSION = VERSION;
505
+ exports.addToolReceipt = addToolReceipt;
506
+ exports.createSigningProxy = createSigningProxy;
507
+ exports.extractToolReceipts = extractToolReceipts;
508
+ exports.hashToolPayload = hashToolPayload;
509
+ exports.jwsBindingContext = jwsBindingContext;
510
+ exports.verifyGitHubReceipt = verifyGitHubReceipt;
511
+ exports.verifyHttpMessageReceipt = verifyHttpMessageReceipt;
512
+ exports.verifyJwsReceipt = verifyJwsReceipt;
513
+ exports.verifyStripeReceipt = verifyStripeReceipt;
514
+ exports.verifyToolReceipt = verifyToolReceipt;
515
+ exports.verifyToolReceipts = verifyToolReceipts;
516
+ exports.verifyTrace = verifyTrace;
517
+ //# sourceMappingURL=index.cjs.map
518
+ //# sourceMappingURL=index.cjs.map