@coolwithakay/uatp 0.1.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.
@@ -0,0 +1,268 @@
1
+ export { bytesToHex, hexToBytes } from '@noble/hashes/utils';
2
+
3
+ /**
4
+ * UATP TypeScript SDK - Type Definitions
5
+ */
6
+ interface ReasoningStep {
7
+ step: number;
8
+ thought: string;
9
+ confidence: number;
10
+ action?: string;
11
+ data_sources?: DataSource[];
12
+ reasoning?: string;
13
+ plain_language?: string;
14
+ }
15
+ interface DataSource {
16
+ source: string;
17
+ value: unknown;
18
+ timestamp?: string;
19
+ api_endpoint?: string;
20
+ }
21
+ interface CertifyOptions {
22
+ /** Description of the task */
23
+ task: string;
24
+ /** The AI's final decision */
25
+ decision: string;
26
+ /** List of reasoning steps */
27
+ reasoning: ReasoningStep[];
28
+ /** Optional passphrase for key encryption (recommended for production) */
29
+ passphrase?: string;
30
+ /** If true, derive passphrase from browser/device info (default: true) */
31
+ deviceBound?: boolean;
32
+ /** Overall confidence score (0-1). Auto-calculated if not provided */
33
+ confidence?: number;
34
+ /** Additional metadata to attach */
35
+ metadata?: Record<string, unknown>;
36
+ /** Request RFC 3161 timestamp from server (default: true) */
37
+ requestTimestamp?: boolean;
38
+ /** Store capsule on UATP server (default: false) */
39
+ storeOnServer?: boolean;
40
+ }
41
+ interface SignedCapsule {
42
+ /** Unique capsule identifier */
43
+ capsuleId: string;
44
+ /** Ed25519 signature (hex) */
45
+ signature: string;
46
+ /** Public key for verification (hex) */
47
+ publicKey: string;
48
+ /** SHA-256 hash of content (hex) */
49
+ contentHash: string;
50
+ /** ISO timestamp of signing */
51
+ timestamp: string;
52
+ /** Original content that was signed */
53
+ content: CapsuleContent;
54
+ /** RFC 3161 timestamp token (if requested) */
55
+ timestampToken?: string;
56
+ /** Timestamp authority used */
57
+ timestampTsa?: string;
58
+ /** Whether stored on server */
59
+ serverStored?: boolean;
60
+ /** URL to verify on server */
61
+ proofUrl?: string;
62
+ }
63
+ interface CapsuleContent {
64
+ task: string;
65
+ decision: string;
66
+ reasoning_chain: ReasoningStep[];
67
+ confidence: number;
68
+ metadata: Record<string, unknown>;
69
+ }
70
+ interface CapsuleProof {
71
+ capsuleId: string;
72
+ capsuleType: string;
73
+ status: string;
74
+ timestamp: Date;
75
+ payload: Record<string, unknown>;
76
+ rawData: Record<string, unknown>;
77
+ }
78
+ interface VerificationResult {
79
+ valid: boolean;
80
+ signatureValid: boolean;
81
+ hashValid: boolean;
82
+ timestampValid?: boolean;
83
+ errors?: string[];
84
+ }
85
+ interface UATPConfig {
86
+ /** API key for authenticated requests */
87
+ apiKey?: string;
88
+ /** Base URL of UATP server (default: http://localhost:8000) */
89
+ baseUrl?: string;
90
+ /** Request timeout in milliseconds (default: 30000) */
91
+ timeout?: number;
92
+ }
93
+ interface KeyPair {
94
+ privateKey: Uint8Array;
95
+ publicKey: Uint8Array;
96
+ }
97
+
98
+ /**
99
+ * UATP TypeScript SDK - Client
100
+ *
101
+ * Zero-Trust Architecture:
102
+ * - Private keys NEVER leave your device
103
+ * - All signing happens locally using Ed25519
104
+ * - Only content hash sent to server for RFC 3161 timestamping
105
+ * - Capsules can be verified independently without UATP
106
+ */
107
+
108
+ declare class UATP {
109
+ private apiKey?;
110
+ private baseUrl;
111
+ private timeout;
112
+ /**
113
+ * Initialize UATP client.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * // Local development
118
+ * const client = new UATP();
119
+ *
120
+ * // Production
121
+ * const client = new UATP({
122
+ * apiKey: 'your-api-key',
123
+ * baseUrl: 'https://api.uatp.io'
124
+ * });
125
+ * ```
126
+ */
127
+ constructor(config?: UATPConfig);
128
+ /**
129
+ * Create a cryptographically certified capsule for an AI decision.
130
+ *
131
+ * ZERO-TRUST: Private key NEVER leaves your device.
132
+ * - All signing happens locally using Ed25519
133
+ * - Only the content hash is sent to UATP for timestamping
134
+ * - Capsules can be verified independently without UATP
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const result = await client.certify({
139
+ * task: 'Loan decision',
140
+ * decision: 'Approved for $50,000 at 5.2% APR',
141
+ * reasoning: [
142
+ * { step: 1, thought: 'Credit score 720 (excellent)', confidence: 0.95 },
143
+ * { step: 2, thought: 'Debt-to-income 0.28 (acceptable)', confidence: 0.90 }
144
+ * ]
145
+ * });
146
+ *
147
+ * console.log(result.capsuleId);
148
+ * console.log(result.signature);
149
+ * ```
150
+ */
151
+ certify(options: CertifyOptions): Promise<SignedCapsule>;
152
+ /**
153
+ * Retrieve full cryptographic proof for a capsule.
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * const proof = await client.getProof('cap_abc123');
158
+ * console.log(proof.payload.task);
159
+ * ```
160
+ */
161
+ getProof(capsuleId: string): Promise<CapsuleProof>;
162
+ /**
163
+ * List capsules from the server.
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * const capsules = await client.listCapsules(10);
168
+ * for (const cap of capsules) {
169
+ * console.log(cap.capsuleId, cap.status);
170
+ * }
171
+ * ```
172
+ */
173
+ listCapsules(limit?: number): Promise<CapsuleProof[]>;
174
+ /**
175
+ * Verify a capsule locally without server.
176
+ *
177
+ * This can be done by anyone - no UATP infrastructure required.
178
+ * Only uses cryptographic data embedded in the capsule.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * const result = client.verifyLocal(capsule);
183
+ * if (result.valid) {
184
+ * console.log('Capsule is authentic!');
185
+ * }
186
+ * ```
187
+ */
188
+ verifyLocal(capsule: SignedCapsule): VerificationResult;
189
+ /**
190
+ * Record the actual outcome of an AI decision (ground truth).
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * await client.recordOutcome('cap_abc123', {
195
+ * result: 'successful',
196
+ * aiWasCorrect: true,
197
+ * financialImpact: 2500,
198
+ * notes: 'Loan fully paid on time'
199
+ * });
200
+ * ```
201
+ */
202
+ recordOutcome(capsuleId: string, outcome: {
203
+ result?: string;
204
+ aiWasCorrect?: boolean;
205
+ financialImpact?: number;
206
+ customerSatisfaction?: number;
207
+ notes?: string;
208
+ }): Promise<boolean>;
209
+ /**
210
+ * Internal fetch wrapper with timeout and headers.
211
+ */
212
+ private fetch;
213
+ }
214
+
215
+ /**
216
+ * UATP TypeScript SDK - Cryptographic Operations
217
+ *
218
+ * Zero-Trust Architecture:
219
+ * - Private keys NEVER leave your device
220
+ * - All signing happens locally using Ed25519
221
+ * - Keys are derived from passphrase using PBKDF2 (480,000 iterations)
222
+ */
223
+
224
+ /**
225
+ * Derive an Ed25519 key pair from a passphrase.
226
+ * Uses PBKDF2-HMAC-SHA256 with 480,000 iterations.
227
+ */
228
+ declare function deriveKeyPair(passphrase: string): KeyPair;
229
+ /**
230
+ * Generate a device-bound passphrase.
231
+ *
232
+ * SECURITY BOUNDARY: This is a CONVENIENCE feature, not a security feature.
233
+ * For production, use an explicit passphrase.
234
+ */
235
+ declare function deriveDevicePassphrase(): string;
236
+ /**
237
+ * Canonicalize content for signing.
238
+ * Ensures deterministic JSON serialization.
239
+ */
240
+ declare function canonicalize(obj: unknown): string;
241
+ /**
242
+ * Hash content using SHA-256.
243
+ */
244
+ declare function hashContent(content: CapsuleContent): string;
245
+ /**
246
+ * Sign content with Ed25519.
247
+ */
248
+ declare function sign(content: CapsuleContent, privateKey: Uint8Array): string;
249
+ /**
250
+ * Verify an Ed25519 signature.
251
+ */
252
+ declare function verify(content: CapsuleContent, signature: string, publicKey: string): boolean;
253
+ /**
254
+ * Create a signed capsule.
255
+ */
256
+ declare function createSignedCapsule(content: CapsuleContent, keyPair: KeyPair): SignedCapsule;
257
+ /**
258
+ * Verify a signed capsule locally.
259
+ * No server required - pure cryptographic verification.
260
+ */
261
+ declare function verifyCapsule(capsule: SignedCapsule): {
262
+ valid: boolean;
263
+ signatureValid: boolean;
264
+ hashValid: boolean;
265
+ errors: string[];
266
+ };
267
+
268
+ export { type CapsuleContent, type CapsuleProof, type CertifyOptions, type DataSource, type KeyPair, type ReasoningStep, type SignedCapsule, UATP, type UATPConfig, type VerificationResult, canonicalize, createSignedCapsule, deriveDevicePassphrase, deriveKeyPair, hashContent, sign, verify, verifyCapsule };
package/dist/index.js ADDED
@@ -0,0 +1,446 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ UATP: () => UATP,
34
+ bytesToHex: () => import_utils.bytesToHex,
35
+ canonicalize: () => canonicalize,
36
+ createSignedCapsule: () => createSignedCapsule,
37
+ deriveDevicePassphrase: () => deriveDevicePassphrase,
38
+ deriveKeyPair: () => deriveKeyPair,
39
+ hashContent: () => hashContent,
40
+ hexToBytes: () => import_utils.hexToBytes,
41
+ sign: () => sign2,
42
+ verify: () => verify2,
43
+ verifyCapsule: () => verifyCapsule
44
+ });
45
+ module.exports = __toCommonJS(index_exports);
46
+
47
+ // src/crypto.ts
48
+ var ed = __toESM(require("@noble/ed25519"));
49
+ var import_sha256 = require("@noble/hashes/sha256");
50
+ var import_pbkdf2 = require("@noble/hashes/pbkdf2");
51
+ var import_utils = require("@noble/hashes/utils");
52
+ ed.etc.sha512Sync = (...m) => {
53
+ const { sha512 } = require("@noble/hashes/sha512");
54
+ return sha512(ed.etc.concatBytes(...m));
55
+ };
56
+ var PBKDF2_ITERATIONS = 48e4;
57
+ var SALT_PREFIX = "uatp-v1-key-derivation";
58
+ function deriveKeyPair(passphrase) {
59
+ const salt = new TextEncoder().encode(SALT_PREFIX);
60
+ const passphraseBytes = new TextEncoder().encode(passphrase);
61
+ const seed = (0, import_pbkdf2.pbkdf2)(import_sha256.sha256, passphraseBytes, salt, {
62
+ c: PBKDF2_ITERATIONS,
63
+ dkLen: 32
64
+ });
65
+ const privateKey = seed;
66
+ const publicKey = ed.getPublicKey(privateKey);
67
+ return { privateKey, publicKey };
68
+ }
69
+ function deriveDevicePassphrase() {
70
+ const factors = [];
71
+ if (typeof window !== "undefined") {
72
+ factors.push(window.navigator.userAgent);
73
+ factors.push(window.navigator.language);
74
+ factors.push(String(window.screen.width));
75
+ factors.push(String(window.screen.height));
76
+ factors.push(window.location.hostname);
77
+ } else if (typeof process !== "undefined") {
78
+ factors.push(process.platform);
79
+ factors.push(process.arch);
80
+ factors.push(process.env.USER || process.env.USERNAME || "");
81
+ factors.push(require("os").hostname());
82
+ }
83
+ const combined = factors.join(":");
84
+ const hash = (0, import_sha256.sha256)(new TextEncoder().encode(combined));
85
+ return `device_${(0, import_utils.bytesToHex)(hash).slice(0, 32)}`;
86
+ }
87
+ function canonicalize(obj) {
88
+ return JSON.stringify(obj, Object.keys(obj).sort());
89
+ }
90
+ function hashContent(content) {
91
+ const canonical = canonicalize(content);
92
+ const hash = (0, import_sha256.sha256)(new TextEncoder().encode(canonical));
93
+ return (0, import_utils.bytesToHex)(hash);
94
+ }
95
+ function sign2(content, privateKey) {
96
+ const canonical = canonicalize(content);
97
+ const messageBytes = new TextEncoder().encode(canonical);
98
+ const signature = ed.sign(messageBytes, privateKey);
99
+ return (0, import_utils.bytesToHex)(signature);
100
+ }
101
+ function verify2(content, signature, publicKey) {
102
+ try {
103
+ const canonical = canonicalize(content);
104
+ const messageBytes = new TextEncoder().encode(canonical);
105
+ const signatureBytes = (0, import_utils.hexToBytes)(signature);
106
+ const publicKeyBytes = (0, import_utils.hexToBytes)(publicKey);
107
+ return ed.verify(signatureBytes, messageBytes, publicKeyBytes);
108
+ } catch {
109
+ return false;
110
+ }
111
+ }
112
+ function generateCapsuleId() {
113
+ const timestamp = Date.now().toString(36);
114
+ const randomBytes = new Uint8Array(8);
115
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
116
+ crypto.getRandomValues(randomBytes);
117
+ } else {
118
+ const { randomBytes: nodeRandom } = require("crypto");
119
+ const buf = nodeRandom(8);
120
+ randomBytes.set(buf);
121
+ }
122
+ const random = (0, import_utils.bytesToHex)(randomBytes).slice(0, 8);
123
+ return `cap_${timestamp}_${random}`;
124
+ }
125
+ function createSignedCapsule(content, keyPair) {
126
+ const capsuleId = generateCapsuleId();
127
+ const contentHash = hashContent(content);
128
+ const signature = sign2(content, keyPair.privateKey);
129
+ const publicKey = (0, import_utils.bytesToHex)(keyPair.publicKey);
130
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
131
+ return {
132
+ capsuleId,
133
+ signature,
134
+ publicKey,
135
+ contentHash,
136
+ timestamp,
137
+ content
138
+ };
139
+ }
140
+ function verifyCapsule(capsule) {
141
+ const errors = [];
142
+ const computedHash = hashContent(capsule.content);
143
+ const hashValid = computedHash === capsule.contentHash;
144
+ if (!hashValid) {
145
+ errors.push("Content hash mismatch - capsule may have been tampered with");
146
+ }
147
+ const signatureValid = verify2(
148
+ capsule.content,
149
+ capsule.signature,
150
+ capsule.publicKey
151
+ );
152
+ if (!signatureValid) {
153
+ errors.push("Signature verification failed");
154
+ }
155
+ return {
156
+ valid: hashValid && signatureValid,
157
+ signatureValid,
158
+ hashValid,
159
+ errors
160
+ };
161
+ }
162
+
163
+ // src/client.ts
164
+ var DEFAULT_BASE_URL = "http://localhost:8000";
165
+ var DEFAULT_TIMEOUT = 3e4;
166
+ var SDK_VERSION = "0.1.0";
167
+ var UATP = class {
168
+ /**
169
+ * Initialize UATP client.
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * // Local development
174
+ * const client = new UATP();
175
+ *
176
+ * // Production
177
+ * const client = new UATP({
178
+ * apiKey: 'your-api-key',
179
+ * baseUrl: 'https://api.uatp.io'
180
+ * });
181
+ * ```
182
+ */
183
+ constructor(config = {}) {
184
+ this.apiKey = config.apiKey;
185
+ this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
186
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
187
+ if (this.baseUrl === DEFAULT_BASE_URL) {
188
+ console.warn("UATP client using localhost - set baseUrl for production");
189
+ }
190
+ }
191
+ /**
192
+ * Create a cryptographically certified capsule for an AI decision.
193
+ *
194
+ * ZERO-TRUST: Private key NEVER leaves your device.
195
+ * - All signing happens locally using Ed25519
196
+ * - Only the content hash is sent to UATP for timestamping
197
+ * - Capsules can be verified independently without UATP
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const result = await client.certify({
202
+ * task: 'Loan decision',
203
+ * decision: 'Approved for $50,000 at 5.2% APR',
204
+ * reasoning: [
205
+ * { step: 1, thought: 'Credit score 720 (excellent)', confidence: 0.95 },
206
+ * { step: 2, thought: 'Debt-to-income 0.28 (acceptable)', confidence: 0.90 }
207
+ * ]
208
+ * });
209
+ *
210
+ * console.log(result.capsuleId);
211
+ * console.log(result.signature);
212
+ * ```
213
+ */
214
+ async certify(options) {
215
+ const {
216
+ task,
217
+ decision,
218
+ reasoning,
219
+ passphrase,
220
+ deviceBound = true,
221
+ confidence,
222
+ metadata = {},
223
+ requestTimestamp = true,
224
+ storeOnServer = false
225
+ } = options;
226
+ if (!task || typeof task !== "string") {
227
+ throw new Error("task must be a non-empty string");
228
+ }
229
+ if (!decision || typeof decision !== "string") {
230
+ throw new Error("decision must be a non-empty string");
231
+ }
232
+ if (!reasoning || !Array.isArray(reasoning) || reasoning.length === 0) {
233
+ throw new Error("reasoning must be a non-empty array");
234
+ }
235
+ let signingPassphrase;
236
+ if (passphrase) {
237
+ if (passphrase.length < 8) {
238
+ throw new Error("passphrase must be at least 8 characters");
239
+ }
240
+ signingPassphrase = passphrase;
241
+ } else if (deviceBound) {
242
+ console.warn(
243
+ "Using device-bound passphrase (DEVELOPMENT ONLY). For production, provide an explicit passphrase."
244
+ );
245
+ signingPassphrase = deriveDevicePassphrase();
246
+ } else {
247
+ throw new Error("Either provide a passphrase or set deviceBound=true");
248
+ }
249
+ const calculatedConfidence = confidence ?? reasoning.reduce((sum, step) => sum + (step.confidence || 0.5), 0) / reasoning.length;
250
+ const content = {
251
+ task,
252
+ decision,
253
+ reasoning_chain: reasoning,
254
+ confidence: calculatedConfidence,
255
+ metadata: {
256
+ ...metadata,
257
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
258
+ sdk_version: SDK_VERSION,
259
+ signing_mode: "local"
260
+ }
261
+ };
262
+ const keyPair = deriveKeyPair(signingPassphrase);
263
+ const signed = createSignedCapsule(content, keyPair);
264
+ if (requestTimestamp) {
265
+ try {
266
+ const response = await this.fetch("/timestamp", {
267
+ method: "POST",
268
+ body: JSON.stringify({ hash: signed.contentHash })
269
+ });
270
+ if (response.ok) {
271
+ const tsData = await response.json();
272
+ signed.timestampToken = tsData.rfc3161;
273
+ signed.timestampTsa = tsData.tsa;
274
+ }
275
+ } catch (error) {
276
+ console.warn(
277
+ "Could not obtain timestamp. Capsule is still valid, just without external timestamp.",
278
+ error
279
+ );
280
+ }
281
+ }
282
+ if (storeOnServer) {
283
+ try {
284
+ const capsuleData = {
285
+ ...signed,
286
+ type: "reasoning_trace",
287
+ payload: content
288
+ };
289
+ const response = await this.fetch("/capsules/store", {
290
+ method: "POST",
291
+ body: JSON.stringify(capsuleData)
292
+ });
293
+ if (response.ok) {
294
+ signed.serverStored = true;
295
+ signed.proofUrl = `${this.baseUrl}/capsules/${signed.capsuleId}/verify`;
296
+ }
297
+ } catch (error) {
298
+ console.warn("Could not store on server. Capsule is still valid locally.", error);
299
+ signed.serverStored = false;
300
+ }
301
+ }
302
+ return signed;
303
+ }
304
+ /**
305
+ * Retrieve full cryptographic proof for a capsule.
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * const proof = await client.getProof('cap_abc123');
310
+ * console.log(proof.payload.task);
311
+ * ```
312
+ */
313
+ async getProof(capsuleId) {
314
+ const response = await this.fetch(`/capsules/${capsuleId}`);
315
+ if (!response.ok) {
316
+ if (response.status === 404) {
317
+ throw new Error(`Capsule ${capsuleId} not found`);
318
+ }
319
+ throw new Error(`Failed to retrieve proof: ${response.status}`);
320
+ }
321
+ const data = await response.json();
322
+ const capsule = data.capsule || data;
323
+ return {
324
+ capsuleId: capsule.capsule_id || capsule.id || capsuleId,
325
+ capsuleType: capsule.type || capsule.capsule_type || "unknown",
326
+ status: capsule.status || "unknown",
327
+ timestamp: new Date(capsule.timestamp),
328
+ payload: capsule.payload || {},
329
+ rawData: data
330
+ };
331
+ }
332
+ /**
333
+ * List capsules from the server.
334
+ *
335
+ * @example
336
+ * ```typescript
337
+ * const capsules = await client.listCapsules(10);
338
+ * for (const cap of capsules) {
339
+ * console.log(cap.capsuleId, cap.status);
340
+ * }
341
+ * ```
342
+ */
343
+ async listCapsules(limit = 10) {
344
+ const response = await this.fetch(`/capsules?demo_mode=false&per_page=${limit}`);
345
+ if (!response.ok) {
346
+ throw new Error(`Failed to list capsules: ${response.status}`);
347
+ }
348
+ const data = await response.json();
349
+ return (data.capsules || []).slice(0, limit).map((item) => ({
350
+ capsuleId: item.capsule_id,
351
+ capsuleType: item.type || "unknown",
352
+ status: item.status || "unknown",
353
+ timestamp: new Date(item.timestamp),
354
+ payload: item.payload || {},
355
+ rawData: item
356
+ }));
357
+ }
358
+ /**
359
+ * Verify a capsule locally without server.
360
+ *
361
+ * This can be done by anyone - no UATP infrastructure required.
362
+ * Only uses cryptographic data embedded in the capsule.
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const result = client.verifyLocal(capsule);
367
+ * if (result.valid) {
368
+ * console.log('Capsule is authentic!');
369
+ * }
370
+ * ```
371
+ */
372
+ verifyLocal(capsule) {
373
+ return verifyCapsule(capsule);
374
+ }
375
+ /**
376
+ * Record the actual outcome of an AI decision (ground truth).
377
+ *
378
+ * @example
379
+ * ```typescript
380
+ * await client.recordOutcome('cap_abc123', {
381
+ * result: 'successful',
382
+ * aiWasCorrect: true,
383
+ * financialImpact: 2500,
384
+ * notes: 'Loan fully paid on time'
385
+ * });
386
+ * ```
387
+ */
388
+ async recordOutcome(capsuleId, outcome) {
389
+ const params = new URLSearchParams();
390
+ if (outcome.result) params.set("outcome_status", outcome.result);
391
+ if (outcome.notes) params.set("notes", outcome.notes);
392
+ if (outcome.customerSatisfaction) {
393
+ params.set("rating", String(outcome.customerSatisfaction));
394
+ }
395
+ const response = await this.fetch(
396
+ `/capsules/${capsuleId}/outcome?${params.toString()}`,
397
+ { method: "POST" }
398
+ );
399
+ if (response.status === 401) {
400
+ throw new Error("Authentication required. Outcome tracking requires a valid API key.");
401
+ }
402
+ if (response.status === 404) {
403
+ throw new Error(`Capsule ${capsuleId} not found`);
404
+ }
405
+ if (!response.ok) {
406
+ throw new Error(`Failed to record outcome: ${response.status}`);
407
+ }
408
+ return true;
409
+ }
410
+ /**
411
+ * Internal fetch wrapper with timeout and headers.
412
+ */
413
+ async fetch(path, options = {}) {
414
+ const controller = new AbortController();
415
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
416
+ try {
417
+ const response = await fetch(`${this.baseUrl}${path}`, {
418
+ ...options,
419
+ signal: controller.signal,
420
+ headers: {
421
+ "Content-Type": "application/json",
422
+ "User-Agent": `uatp-typescript-sdk/${SDK_VERSION}`,
423
+ ...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {},
424
+ ...options.headers
425
+ }
426
+ });
427
+ return response;
428
+ } finally {
429
+ clearTimeout(timeoutId);
430
+ }
431
+ }
432
+ };
433
+ // Annotate the CommonJS export names for ESM import in node:
434
+ 0 && (module.exports = {
435
+ UATP,
436
+ bytesToHex,
437
+ canonicalize,
438
+ createSignedCapsule,
439
+ deriveDevicePassphrase,
440
+ deriveKeyPair,
441
+ hashContent,
442
+ hexToBytes,
443
+ sign,
444
+ verify,
445
+ verifyCapsule
446
+ });