@oneshot-agent/sdk 0.33.0 → 0.35.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/README.md +56 -7
- package/dist/bin/verify-receipt.d.ts +3 -0
- package/dist/bin/verify-receipt.d.ts.map +1 -0
- package/dist/bin/verify-receipt.js +20 -0
- package/dist/bin/verify-receipt.js.map +1 -0
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +60 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +217 -14
- package/dist/index.js.map +1 -1
- package/dist/receipt-verify-cli.d.ts +15 -0
- package/dist/receipt-verify-cli.d.ts.map +1 -0
- package/dist/receipt-verify-cli.js +85 -0
- package/dist/receipt-verify-cli.js.map +1 -0
- package/dist/receipt.d.ts +104 -0
- package/dist/receipt.d.ts.map +1 -0
- package/dist/receipt.js +243 -0
- package/dist/receipt.js.map +1 -0
- package/dist/types.d.ts +356 -9
- package/dist/types.d.ts.map +1 -1
- package/package.json +22 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export declare const RECEIPT_SIGNATURE_ALG: "Ed25519";
|
|
2
|
+
/**
|
|
3
|
+
* Fields covered by a receipt signature. Every one of them is public: the
|
|
4
|
+
* canonical payload contains nothing the public receipt projection omits, so
|
|
5
|
+
* whoever holds the receipt can recompute the exact signed bytes.
|
|
6
|
+
*/
|
|
7
|
+
export interface SignableReceipt {
|
|
8
|
+
receiptId: string;
|
|
9
|
+
agentId: string;
|
|
10
|
+
soulAgentId?: string | null;
|
|
11
|
+
jobId?: string | null;
|
|
12
|
+
goalId?: string | null;
|
|
13
|
+
category: string;
|
|
14
|
+
subcategory: string;
|
|
15
|
+
amountUsdc: string;
|
|
16
|
+
serviceFee?: string | null;
|
|
17
|
+
status: string;
|
|
18
|
+
executionStatus: string;
|
|
19
|
+
settlementTx?: string | null;
|
|
20
|
+
createdAt: Date | string;
|
|
21
|
+
settledAt?: Date | string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Signing-time revision counter. Bound into the signed bytes, so two signed
|
|
24
|
+
* copies of the same `receipt_id` order from the bytes alone — no database
|
|
25
|
+
* lookup required. Defaults to 1; every re-sign persists one more.
|
|
26
|
+
*/
|
|
27
|
+
revision?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Free-form receipt metadata (`memo`, `decisionContext`, etc. — see
|
|
30
|
+
* `apps/api-service/src/services/receipts.ts`'s `createReceipt`). The
|
|
31
|
+
* metadata VALUE is never placed in the signed bytes — it is free-form and
|
|
32
|
+
* unbounded — only its digest is (see `computeMetadataDigest`), so a memo
|
|
33
|
+
* or decision-context edit invalidates the signature until the receipt is
|
|
34
|
+
* re-signed. `null` and absent digest identically.
|
|
35
|
+
*/
|
|
36
|
+
metadata?: unknown;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* SHA-256 hex digest of a receipt's `metadata`, key-order-independent.
|
|
40
|
+
*
|
|
41
|
+
* `metadata: null` (and `metadata: undefined`, treated identically —
|
|
42
|
+
* `receipts.metadata` is a nullable jsonb column, and the two never carry
|
|
43
|
+
* different meaning here) canonicalize to the literal 4-byte string `null`,
|
|
44
|
+
* exactly like every other nested `null`/absent value, rather than a special
|
|
45
|
+
* case — this is the stable, documented digest for a metadata-less receipt.
|
|
46
|
+
*/
|
|
47
|
+
export declare function computeMetadataDigest(metadata: unknown): string;
|
|
48
|
+
/** The signature fields signReceipt() attaches to a SignableReceipt. */
|
|
49
|
+
export interface ReceiptSignatureFields {
|
|
50
|
+
signature: string;
|
|
51
|
+
digest: string;
|
|
52
|
+
keyId: string;
|
|
53
|
+
alg?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Deterministic JSON payload signed for every terminal receipt. Object
|
|
57
|
+
* insertion order is intentional and is part of the wire format. Amounts are
|
|
58
|
+
* strings, timestamps are UTC ISO-8601, absent optionals are `null`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function canonicalizeReceipt(receipt: SignableReceipt): string;
|
|
61
|
+
/** One entry from the published JWKS (`/.well-known/oneshot-receipts.json`). */
|
|
62
|
+
export interface ReceiptJwk {
|
|
63
|
+
kty: string;
|
|
64
|
+
crv: string;
|
|
65
|
+
x: string;
|
|
66
|
+
kid: string;
|
|
67
|
+
alg?: string;
|
|
68
|
+
use?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface ReceiptJwks {
|
|
71
|
+
keys: ReceiptJwk[];
|
|
72
|
+
}
|
|
73
|
+
/** Reasons a receipt can fail verification — distinguishable, not a generic false. */
|
|
74
|
+
export type ReceiptVerificationFailureReason = 'malformed_receipt' | 'key_not_found' | 'digest_mismatch' | 'signature_invalid';
|
|
75
|
+
export type ReceiptVerificationResult = {
|
|
76
|
+
valid: true;
|
|
77
|
+
keyId: string;
|
|
78
|
+
} | {
|
|
79
|
+
valid: false;
|
|
80
|
+
reason: ReceiptVerificationFailureReason;
|
|
81
|
+
keyId?: string;
|
|
82
|
+
message: string;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Verify a signed receipt offline against a JWKS.
|
|
86
|
+
*
|
|
87
|
+
* Recomputes the canonical payload via `canonicalizeReceipt` (including the
|
|
88
|
+
* metadata digest, so the receipt's `metadata` must be the value that was
|
|
89
|
+
* signed), matches the receipt's `keyId` against the JWKS `kid`s (so a
|
|
90
|
+
* rotated-out key still verifies as long as the JWKS retains it), checks the
|
|
91
|
+
* SHA-256 digest, and checks the Ed25519 signature. A `key_id` that matches
|
|
92
|
+
* no key in the JWKS fails with `reason: 'key_not_found'` rather than a
|
|
93
|
+
* generic false.
|
|
94
|
+
*/
|
|
95
|
+
export declare function verifyReceipt(receipt: SignableReceipt & Partial<ReceiptSignatureFields>, jwks: ReceiptJwks): ReceiptVerificationResult;
|
|
96
|
+
/**
|
|
97
|
+
* Adapt the wire-format (snake_case) JSON a caller gets from the API — e.g.
|
|
98
|
+
* `GET /v1/analytics/receipts` or a receipt JSON file — into the shape
|
|
99
|
+
* `verifyReceipt` expects. The audit export (`GET /v1/audit/receipts`) names
|
|
100
|
+
* the attestation fields `receipt_*` because its plain `signature` is the
|
|
101
|
+
* on-chain settlement transaction; both shapes are accepted.
|
|
102
|
+
*/
|
|
103
|
+
export declare function receiptFromWire(input: object): SignableReceipt & Partial<ReceiptSignatureFields>;
|
|
104
|
+
//# sourceMappingURL=receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAiCA,eAAO,MAAM,qBAAqB,EAAG,SAAkB,CAAC;AAExD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAoCD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAG/D;AAED,wEAAwE;AACxE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAOD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAmBpE;AAED,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,EAAE,CAAC;CACpB;AAED,sFAAsF;AACtF,MAAM,MAAM,gCAAgC,GACxC,mBAAmB,GACnB,eAAe,GACf,iBAAiB,GACjB,mBAAmB,CAAC;AAExB,MAAM,MAAM,yBAAyB,GACjC;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gCAAgC,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhG;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,sBAAsB,CAAC,EAC1D,IAAI,EAAE,WAAW,GAChB,yBAAyB,CAuE3B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA+BhG"}
|
package/dist/receipt.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RECEIPT_SIGNATURE_ALG = void 0;
|
|
4
|
+
exports.computeMetadataDigest = computeMetadataDigest;
|
|
5
|
+
exports.canonicalizeReceipt = canonicalizeReceipt;
|
|
6
|
+
exports.verifyReceipt = verifyReceipt;
|
|
7
|
+
exports.receiptFromWire = receiptFromWire;
|
|
8
|
+
/**
|
|
9
|
+
* Receipt canonicalization + offline verification.
|
|
10
|
+
*
|
|
11
|
+
* The api-service signs every terminal receipt with Ed25519
|
|
12
|
+
* (`apps/api-service/src/services/receipt-signing.ts`) and publishes the
|
|
13
|
+
* verification keys at `/.well-known/oneshot-receipts.json`. Anyone holding a
|
|
14
|
+
* receipt from `GET /v1/analytics/receipts` (or the audit export) can verify
|
|
15
|
+
* it offline with `verifyReceipt` below, or with the `oneshot-verify-receipt`
|
|
16
|
+
* CLI.
|
|
17
|
+
*
|
|
18
|
+
* `canonicalizeReceipt` here is the ONE canonical implementation. The
|
|
19
|
+
* api-service signer imports it from this file (see
|
|
20
|
+
* `apps/api-service/src/services/receipt-signing.ts`) rather than keeping
|
|
21
|
+
* its own copy, so signer and verifier can never drift apart — see
|
|
22
|
+
* `tests/unit/receipt-verify.test.ts`'s "shared canonicalizer" guard.
|
|
23
|
+
*
|
|
24
|
+
* There is exactly ONE payload format, and it carries no version field. The
|
|
25
|
+
* signing key is ours and every receipt is an attestation by us, so when the
|
|
26
|
+
* payload has to change we change this function and re-sign every stored
|
|
27
|
+
* receipt (`scripts/resign-receipts.ts`, bumping `revision`) instead of
|
|
28
|
+
* forking a numbered format and carrying verification code for old ones.
|
|
29
|
+
*
|
|
30
|
+
* This lives in `libs/agent-sdk` (not `libs/shared-types`) because
|
|
31
|
+
* `libs/agent-sdk` is the package that ships to npm as a standalone SDK
|
|
32
|
+
* with no other workspace dependencies (see package.json — only `ethers`).
|
|
33
|
+
* `libs/shared-types` is never published, so a published SDK cannot import
|
|
34
|
+
* from it without breaking for every external consumer. api-service is not
|
|
35
|
+
* published, so it can and does import straight from this file's source
|
|
36
|
+
* (the same pattern `apps/api-service/src/mcp/agent.ts` already uses for
|
|
37
|
+
* `OneShot` itself).
|
|
38
|
+
*/
|
|
39
|
+
const node_crypto_1 = require("node:crypto");
|
|
40
|
+
exports.RECEIPT_SIGNATURE_ALG = 'Ed25519';
|
|
41
|
+
/**
|
|
42
|
+
* Deterministic JSON serialization used ONLY to derive `metadata_digest` — it
|
|
43
|
+
* is never itself part of the signed bytes. Object keys are sorted
|
|
44
|
+
* recursively (arrays keep their order) so two semantically equal metadata
|
|
45
|
+
* objects with differently-ordered keys hash identically, at any nesting
|
|
46
|
+
* depth. `undefined` values inside objects are dropped (mirrors
|
|
47
|
+
* `JSON.stringify`'s own behavior for object properties) so an
|
|
48
|
+
* explicitly-`undefined` key and an absent key digest the same way.
|
|
49
|
+
*/
|
|
50
|
+
function canonicalizeMetadataValue(value) {
|
|
51
|
+
if (Array.isArray(value))
|
|
52
|
+
return value.map(canonicalizeMetadataValue);
|
|
53
|
+
if (value && typeof value === 'object' && !(value instanceof Date)) {
|
|
54
|
+
// A plain `{}` accumulator is unsafe here: `Object.prototype` defines
|
|
55
|
+
// `__proto__` as an ACCESSOR, so `sorted['__proto__'] = x` on an
|
|
56
|
+
// ordinary object sets the object's prototype instead of creating an
|
|
57
|
+
// own data property — and JSON.stringify only serializes own
|
|
58
|
+
// properties, so the assigned value would silently vanish from the
|
|
59
|
+
// digest input. `JSON.parse` itself creates a genuine own `__proto__`
|
|
60
|
+
// property (via CreateDataProperty, not the setter), so a caller
|
|
61
|
+
// sending `decisionContext: { "__proto__": {...} }` in a JSON request
|
|
62
|
+
// body reaches this function with a real own key — a null-prototype
|
|
63
|
+
// accumulator has no inherited `__proto__` accessor to intercept the
|
|
64
|
+
// assignment, so it is preserved as an ordinary own property instead.
|
|
65
|
+
const sorted = Object.create(null);
|
|
66
|
+
for (const key of Object.keys(value).sort()) {
|
|
67
|
+
const entry = value[key];
|
|
68
|
+
if (entry === undefined)
|
|
69
|
+
continue;
|
|
70
|
+
sorted[key] = canonicalizeMetadataValue(entry);
|
|
71
|
+
}
|
|
72
|
+
return sorted;
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* SHA-256 hex digest of a receipt's `metadata`, key-order-independent.
|
|
78
|
+
*
|
|
79
|
+
* `metadata: null` (and `metadata: undefined`, treated identically —
|
|
80
|
+
* `receipts.metadata` is a nullable jsonb column, and the two never carry
|
|
81
|
+
* different meaning here) canonicalize to the literal 4-byte string `null`,
|
|
82
|
+
* exactly like every other nested `null`/absent value, rather than a special
|
|
83
|
+
* case — this is the stable, documented digest for a metadata-less receipt.
|
|
84
|
+
*/
|
|
85
|
+
function computeMetadataDigest(metadata) {
|
|
86
|
+
const canonical = JSON.stringify(canonicalizeMetadataValue(metadata ?? null));
|
|
87
|
+
return (0, node_crypto_1.createHash)('sha256').update(canonical, 'utf8').digest('hex');
|
|
88
|
+
}
|
|
89
|
+
function receiptTimestamp(value) {
|
|
90
|
+
if (value == null)
|
|
91
|
+
return null;
|
|
92
|
+
return (value instanceof Date ? value : new Date(value)).toISOString();
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Deterministic JSON payload signed for every terminal receipt. Object
|
|
96
|
+
* insertion order is intentional and is part of the wire format. Amounts are
|
|
97
|
+
* strings, timestamps are UTC ISO-8601, absent optionals are `null`.
|
|
98
|
+
*/
|
|
99
|
+
function canonicalizeReceipt(receipt) {
|
|
100
|
+
return JSON.stringify({
|
|
101
|
+
receipt_id: receipt.receiptId,
|
|
102
|
+
agent_id: receipt.agentId,
|
|
103
|
+
soul_agent_id: receipt.soulAgentId ?? null,
|
|
104
|
+
job_id: receipt.jobId ?? null,
|
|
105
|
+
goal_id: receipt.goalId ?? null,
|
|
106
|
+
category: receipt.category,
|
|
107
|
+
subcategory: receipt.subcategory,
|
|
108
|
+
amount_usdc: String(receipt.amountUsdc),
|
|
109
|
+
service_fee: String(receipt.serviceFee ?? '0'),
|
|
110
|
+
status: receipt.status,
|
|
111
|
+
execution_status: receipt.executionStatus,
|
|
112
|
+
settlement_tx: receipt.settlementTx ?? null,
|
|
113
|
+
created_at: receiptTimestamp(receipt.createdAt),
|
|
114
|
+
settled_at: receiptTimestamp(receipt.settledAt),
|
|
115
|
+
revision: receipt.revision === undefined ? 1 : receipt.revision,
|
|
116
|
+
metadata_digest: computeMetadataDigest(receipt.metadata),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Verify a signed receipt offline against a JWKS.
|
|
121
|
+
*
|
|
122
|
+
* Recomputes the canonical payload via `canonicalizeReceipt` (including the
|
|
123
|
+
* metadata digest, so the receipt's `metadata` must be the value that was
|
|
124
|
+
* signed), matches the receipt's `keyId` against the JWKS `kid`s (so a
|
|
125
|
+
* rotated-out key still verifies as long as the JWKS retains it), checks the
|
|
126
|
+
* SHA-256 digest, and checks the Ed25519 signature. A `key_id` that matches
|
|
127
|
+
* no key in the JWKS fails with `reason: 'key_not_found'` rather than a
|
|
128
|
+
* generic false.
|
|
129
|
+
*/
|
|
130
|
+
function verifyReceipt(receipt, jwks) {
|
|
131
|
+
if (!receipt || typeof receipt !== 'object' || Array.isArray(receipt) ||
|
|
132
|
+
!jwks || typeof jwks !== 'object' || !Array.isArray(jwks.keys) ||
|
|
133
|
+
!jwks.keys.every(k => k && typeof k === 'object' && typeof k.kid === 'string')) {
|
|
134
|
+
return { valid: false, reason: 'malformed_receipt', message: 'Expected a receipt object and a JWKS keys array' };
|
|
135
|
+
}
|
|
136
|
+
const { signature, digest, keyId } = receipt;
|
|
137
|
+
if (typeof signature !== 'string' || !signature || typeof digest !== 'string' || !digest || typeof keyId !== 'string' || !keyId ||
|
|
138
|
+
(receipt.alg !== undefined && receipt.alg !== exports.RECEIPT_SIGNATURE_ALG)) {
|
|
139
|
+
return {
|
|
140
|
+
valid: false,
|
|
141
|
+
reason: 'malformed_receipt',
|
|
142
|
+
message: 'Receipt is missing signature, digest, or keyId',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const key = jwks.keys.find((candidate) => candidate.kid === keyId);
|
|
146
|
+
if (!key) {
|
|
147
|
+
return {
|
|
148
|
+
valid: false,
|
|
149
|
+
reason: 'key_not_found',
|
|
150
|
+
keyId,
|
|
151
|
+
message: `No key with kid "${keyId}" was found in the supplied JWKS`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
let payload;
|
|
155
|
+
try {
|
|
156
|
+
payload = Buffer.from(canonicalizeReceipt(receipt), 'utf8');
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return { valid: false, reason: 'malformed_receipt', keyId, message: 'Receipt contains invalid signed fields or timestamps' };
|
|
160
|
+
}
|
|
161
|
+
const expectedDigest = (0, node_crypto_1.createHash)('sha256').update(payload).digest('hex');
|
|
162
|
+
if (expectedDigest !== digest) {
|
|
163
|
+
return {
|
|
164
|
+
valid: false,
|
|
165
|
+
reason: 'digest_mismatch',
|
|
166
|
+
keyId,
|
|
167
|
+
message: 'SHA-256 digest does not match the recomputed canonical payload',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
let publicKey;
|
|
171
|
+
try {
|
|
172
|
+
if (key.kty !== 'OKP' || key.crv !== 'Ed25519' || (key.use !== undefined && key.use !== 'sig') ||
|
|
173
|
+
(key.alg !== undefined && !['Ed25519', 'EdDSA'].includes(key.alg)))
|
|
174
|
+
throw new Error('Expected an Ed25519 signing key');
|
|
175
|
+
publicKey = (0, node_crypto_1.createPublicKey)({ key: { kty: key.kty, crv: key.crv, x: key.x }, format: 'jwk' });
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
return {
|
|
179
|
+
valid: false,
|
|
180
|
+
reason: 'malformed_receipt',
|
|
181
|
+
keyId,
|
|
182
|
+
message: `Could not construct a public key from JWKS entry "${keyId}": ${err.message}`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
let signatureValid = false;
|
|
186
|
+
try {
|
|
187
|
+
signatureValid = (0, node_crypto_1.verify)(null, payload, publicKey, Buffer.from(signature, 'base64url'));
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
signatureValid = false;
|
|
191
|
+
}
|
|
192
|
+
if (!signatureValid) {
|
|
193
|
+
return {
|
|
194
|
+
valid: false,
|
|
195
|
+
reason: 'signature_invalid',
|
|
196
|
+
keyId,
|
|
197
|
+
message: 'Ed25519 signature does not match the recomputed canonical payload',
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return { valid: true, keyId };
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Adapt the wire-format (snake_case) JSON a caller gets from the API — e.g.
|
|
204
|
+
* `GET /v1/analytics/receipts` or a receipt JSON file — into the shape
|
|
205
|
+
* `verifyReceipt` expects. The audit export (`GET /v1/audit/receipts`) names
|
|
206
|
+
* the attestation fields `receipt_*` because its plain `signature` is the
|
|
207
|
+
* on-chain settlement transaction; both shapes are accepted.
|
|
208
|
+
*/
|
|
209
|
+
function receiptFromWire(input) {
|
|
210
|
+
if (!input || Array.isArray(input) || typeof input !== 'object')
|
|
211
|
+
throw new Error('Receipt must be a JSON object');
|
|
212
|
+
const wire = { ...input };
|
|
213
|
+
const own = (key) => Object.prototype.hasOwnProperty.call(wire, key);
|
|
214
|
+
const audit = own('receipt_signature');
|
|
215
|
+
const algKey = audit ? 'receipt_alg' : 'alg';
|
|
216
|
+
if (own(algKey) && wire[algKey] !== 'Ed25519')
|
|
217
|
+
throw new Error('Unsupported receipt signature algorithm');
|
|
218
|
+
const str = (value) => (typeof value === 'string' ? value : undefined);
|
|
219
|
+
const num = (value) => (typeof value === 'number' ? value : undefined);
|
|
220
|
+
return {
|
|
221
|
+
receiptId: str(wire.receipt_id) ?? str(wire.receiptId) ?? '',
|
|
222
|
+
agentId: str(wire.agent_id) ?? str(wire.agentId) ?? '',
|
|
223
|
+
soulAgentId: str(wire.soul_agent_id) ?? str(wire.soulAgentId) ?? null,
|
|
224
|
+
jobId: str(wire.job_id) ?? str(wire.jobId) ?? null,
|
|
225
|
+
goalId: str(wire.goal_id) ?? str(wire.goalId) ?? null,
|
|
226
|
+
category: str(wire.category) ?? '',
|
|
227
|
+
subcategory: str(wire.subcategory) ?? '',
|
|
228
|
+
amountUsdc: str(wire.amount_usdc) ?? str(wire.amountUsdc) ?? '0',
|
|
229
|
+
serviceFee: str(wire.service_fee) ?? str(wire.serviceFee) ?? null,
|
|
230
|
+
status: str(wire.status) ?? '',
|
|
231
|
+
executionStatus: str(wire.execution_status) ?? str(wire.executionStatus) ?? '',
|
|
232
|
+
settlementTx: str(wire.settlement_tx) ?? str(wire.settlementTx) ?? (audit ? str(wire.signature) : null) ?? null,
|
|
233
|
+
createdAt: str(wire.created_at) ?? str(wire.createdAt) ?? new Date(0).toISOString(),
|
|
234
|
+
settledAt: str(wire.settled_at) ?? str(wire.settledAt) ?? null,
|
|
235
|
+
revision: audit ? num(wire.receipt_revision) : num(wire.revision),
|
|
236
|
+
metadata: wire.metadata,
|
|
237
|
+
signature: str(audit ? wire.receipt_signature : wire.signature),
|
|
238
|
+
digest: str(audit ? wire.receipt_digest : wire.digest),
|
|
239
|
+
keyId: audit ? str(wire.receipt_key_id) : str(wire.key_id) ?? str(wire.keyId),
|
|
240
|
+
alg: str(audit ? wire.receipt_alg : wire.alg),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=receipt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"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"}
|