@nextera.one/axis-server-sdk 2.2.0 → 2.2.2

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.
Files changed (53) hide show
  1. package/dist/axis-sensor-GBEI3Fab.d.mts +209 -0
  2. package/dist/axis-sensor-GBEI3Fab.d.ts +209 -0
  3. package/dist/cce/index.d.mts +162 -0
  4. package/dist/cce/index.d.ts +162 -0
  5. package/dist/cce/index.js +1502 -0
  6. package/dist/cce/index.js.map +1 -0
  7. package/dist/cce/index.mjs +1442 -0
  8. package/dist/cce/index.mjs.map +1 -0
  9. package/dist/cce-pipeline-B-zUBHo3.d.mts +294 -0
  10. package/dist/cce-pipeline-DbGBSsCG.d.ts +294 -0
  11. package/dist/core/index.d.mts +23 -2
  12. package/dist/core/index.d.ts +23 -2
  13. package/dist/idel/index.d.mts +24 -0
  14. package/dist/idel/index.d.ts +24 -0
  15. package/dist/idel/index.js +306 -0
  16. package/dist/idel/index.js.map +1 -0
  17. package/dist/idel/index.mjs +279 -0
  18. package/dist/idel/index.mjs.map +1 -0
  19. package/dist/idel.types-DuUAcOnQ.d.mts +83 -0
  20. package/dist/idel.types-DuUAcOnQ.d.ts +83 -0
  21. package/dist/index-B2G6cbRL.d.mts +824 -0
  22. package/dist/index-DbSxdR0f.d.ts +824 -0
  23. package/dist/index-_S4fmVUJ.d.mts +501 -0
  24. package/dist/index-l3Hhirqb.d.ts +501 -0
  25. package/dist/index.d.mts +91 -3501
  26. package/dist/index.d.ts +91 -3501
  27. package/dist/index.js +5052 -4618
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +5018 -4597
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/needle/index.d.mts +4 -0
  32. package/dist/needle/index.d.ts +4 -0
  33. package/dist/needle/index.js +3499 -0
  34. package/dist/needle/index.js.map +1 -0
  35. package/dist/needle/index.mjs +3528 -0
  36. package/dist/needle/index.mjs.map +1 -0
  37. package/dist/sensors/index.d.mts +5 -0
  38. package/dist/sensors/index.d.ts +5 -0
  39. package/dist/sensors/index.js +12860 -0
  40. package/dist/sensors/index.js.map +1 -0
  41. package/dist/sensors/index.mjs +12928 -0
  42. package/dist/sensors/index.mjs.map +1 -0
  43. package/dist/timeline/index.d.mts +54 -0
  44. package/dist/timeline/index.d.ts +54 -0
  45. package/dist/timeline/index.js +389 -0
  46. package/dist/timeline/index.js.map +1 -0
  47. package/dist/timeline/index.mjs +362 -0
  48. package/dist/timeline/index.mjs.map +1 -0
  49. package/dist/timeline.types-Cn0aqbUj.d.mts +125 -0
  50. package/dist/timeline.types-Cn0aqbUj.d.ts +125 -0
  51. package/package.json +28 -10
  52. package/dist/index-VxXqZPuH.d.mts +0 -51
  53. package/dist/index-VxXqZPuH.d.ts +0 -51
@@ -0,0 +1,1442 @@
1
+ // src/cce/cce.types.ts
2
+ var CCE_PROTOCOL_VERSION = "cce-v1";
3
+ var CCE_DERIVATION = {
4
+ /** Request execution context */
5
+ REQUEST: "axis:cce:req:v1",
6
+ /** Response execution context */
7
+ RESPONSE: "axis:cce:resp:v1",
8
+ /** Witness binding context */
9
+ WITNESS: "axis:cce:witness:v1"
10
+ };
11
+ var CCE_AES_KEY_BYTES = 32;
12
+ var CCE_IV_BYTES = 12;
13
+ var CCE_TAG_BYTES = 16;
14
+ var CCE_NONCE_BYTES = 32;
15
+ var CCE_ERROR = {
16
+ // Envelope errors
17
+ INVALID_ENVELOPE: "CCE_INVALID_ENVELOPE",
18
+ UNSUPPORTED_VERSION: "CCE_UNSUPPORTED_VERSION",
19
+ MISSING_CAPSULE: "CCE_MISSING_CAPSULE",
20
+ MISSING_ENCRYPTED_KEY: "CCE_MISSING_ENCRYPTED_KEY",
21
+ // Signature errors
22
+ CLIENT_SIG_INVALID: "CCE_CLIENT_SIG_INVALID",
23
+ CLIENT_KEY_NOT_FOUND: "CCE_CLIENT_KEY_NOT_FOUND",
24
+ // Capsule errors
25
+ CAPSULE_SIG_INVALID: "CCE_CAPSULE_SIG_INVALID",
26
+ CAPSULE_EXPIRED: "CCE_CAPSULE_EXPIRED",
27
+ CAPSULE_NOT_YET_VALID: "CCE_CAPSULE_NOT_YET_VALID",
28
+ CAPSULE_REVOKED: "CCE_CAPSULE_REVOKED",
29
+ CAPSULE_CONSUMED: "CCE_CAPSULE_CONSUMED",
30
+ // Binding errors
31
+ AUDIENCE_MISMATCH: "CCE_AUDIENCE_MISMATCH",
32
+ INTENT_MISMATCH: "CCE_INTENT_MISMATCH",
33
+ TPS_WINDOW_EXPIRED: "CCE_TPS_WINDOW_EXPIRED",
34
+ TPS_WINDOW_FUTURE: "CCE_TPS_WINDOW_FUTURE",
35
+ // Replay / nonce errors
36
+ REPLAY_DETECTED: "CCE_REPLAY_DETECTED",
37
+ NONCE_REUSED: "CCE_NONCE_REUSED",
38
+ // Decryption errors
39
+ DECRYPTION_FAILED: "CCE_DECRYPTION_FAILED",
40
+ KEY_UNWRAP_FAILED: "CCE_KEY_UNWRAP_FAILED",
41
+ AEAD_TAG_MISMATCH: "CCE_AEAD_TAG_MISMATCH",
42
+ PAYLOAD_TOO_LARGE: "CCE_PAYLOAD_TOO_LARGE",
43
+ // Schema / validation errors
44
+ PAYLOAD_SCHEMA_INVALID: "CCE_PAYLOAD_SCHEMA_INVALID",
45
+ INTENT_SCHEMA_MISMATCH: "CCE_INTENT_SCHEMA_MISMATCH",
46
+ // Policy errors
47
+ POLICY_DENIED: "CCE_POLICY_DENIED",
48
+ CONSTRAINT_VIOLATED: "CCE_CONSTRAINT_VIOLATED",
49
+ // Handler errors
50
+ HANDLER_NOT_FOUND: "CCE_HANDLER_NOT_FOUND",
51
+ HANDLER_EXECUTION_FAILED: "CCE_HANDLER_EXECUTION_FAILED",
52
+ HANDLER_TIMEOUT: "CCE_HANDLER_TIMEOUT",
53
+ // Response errors
54
+ RESPONSE_ENCRYPTION_FAILED: "CCE_RESPONSE_ENCRYPTION_FAILED"
55
+ };
56
+ var CceError = class extends Error {
57
+ constructor(code, message, metadata) {
58
+ super(`[${code}] ${message}`);
59
+ this.code = code;
60
+ this.metadata = metadata;
61
+ this.name = "CceError";
62
+ }
63
+ /** Whether this error is safe to expose to the client */
64
+ get clientSafe() {
65
+ const internal = [
66
+ CCE_ERROR.DECRYPTION_FAILED,
67
+ CCE_ERROR.KEY_UNWRAP_FAILED,
68
+ CCE_ERROR.AEAD_TAG_MISMATCH,
69
+ CCE_ERROR.HANDLER_EXECUTION_FAILED,
70
+ CCE_ERROR.RESPONSE_ENCRYPTION_FAILED
71
+ ];
72
+ return !internal.includes(this.code);
73
+ }
74
+ /** Get client-safe representation */
75
+ toClientError() {
76
+ if (this.clientSafe) {
77
+ return { code: this.code, message: this.message };
78
+ }
79
+ return {
80
+ code: CCE_ERROR.DECRYPTION_FAILED,
81
+ message: "Request processing failed"
82
+ };
83
+ }
84
+ };
85
+
86
+ // src/cce/cce-derivation.service.ts
87
+ import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
88
+ import { hkdf } from "@noble/hashes/hkdf.js";
89
+ import { sha256 } from "@noble/hashes/sha2.js";
90
+ function buildSalt(capsuleId, capsuleNonce, requestNonce) {
91
+ const encoder = new TextEncoder();
92
+ const data = encoder.encode(
93
+ capsuleId + "|" + capsuleNonce + "|" + requestNonce
94
+ );
95
+ return sha256(data);
96
+ }
97
+ function buildInfo(contextPrefix, capsule, extraNonce) {
98
+ const encoder = new TextEncoder();
99
+ const parts = [
100
+ contextPrefix,
101
+ capsule.sub,
102
+ capsule.kid,
103
+ capsule.intent,
104
+ capsule.aud,
105
+ String(capsule.tps_from),
106
+ String(capsule.tps_to),
107
+ capsule.policy_hash ?? "",
108
+ capsule.ver
109
+ ];
110
+ if (extraNonce) {
111
+ parts.push(extraNonce);
112
+ }
113
+ return encoder.encode(parts.join("|"));
114
+ }
115
+ function deriveRequestExecutionKey(input) {
116
+ const ikm = hexToBytes(input.axisLocalSecret);
117
+ const salt = buildSalt(
118
+ input.capsule.capsule_id,
119
+ input.capsule.capsule_nonce,
120
+ input.requestNonce
121
+ );
122
+ const info = buildInfo(CCE_DERIVATION.REQUEST, input.capsule);
123
+ return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
124
+ }
125
+ function deriveResponseExecutionKey(input) {
126
+ const ikm = hexToBytes(input.axisLocalSecret);
127
+ const encoder = new TextEncoder();
128
+ const saltData = encoder.encode(
129
+ input.capsule.capsule_id + "|" + input.capsule.capsule_nonce + "|" + input.requestNonce + "|" + input.responseNonce
130
+ );
131
+ const salt = sha256(saltData);
132
+ const info = buildInfo(
133
+ CCE_DERIVATION.RESPONSE,
134
+ input.capsule,
135
+ input.responseNonce
136
+ );
137
+ return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
138
+ }
139
+ function deriveWitnessKey(input) {
140
+ const ikm = hexToBytes(input.axisLocalSecret);
141
+ const salt = buildSalt(
142
+ input.capsule.capsule_id,
143
+ input.capsule.capsule_nonce,
144
+ input.requestNonce
145
+ );
146
+ const info = buildInfo(CCE_DERIVATION.WITNESS, input.capsule);
147
+ return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
148
+ }
149
+ function buildExecutionContext(input, requestId) {
150
+ const executionKey = deriveRequestExecutionKey(input);
151
+ const keyHash = bytesToHex(sha256(executionKey));
152
+ executionKey.fill(0);
153
+ return {
154
+ execution_key_hash: keyHash,
155
+ request_id: requestId,
156
+ capsule_id: input.capsule.capsule_id,
157
+ sub: input.capsule.sub,
158
+ kid: input.capsule.kid,
159
+ intent: input.capsule.intent,
160
+ aud: input.capsule.aud,
161
+ tps_from: input.capsule.tps_from,
162
+ tps_to: input.capsule.tps_to,
163
+ policy_hash: input.capsule.policy_hash,
164
+ derived_at: Math.floor(Date.now() / 1e3),
165
+ valid: true
166
+ };
167
+ }
168
+ function generateCceNonce() {
169
+ const bytes = new Uint8Array(CCE_NONCE_BYTES);
170
+ crypto.getRandomValues(bytes);
171
+ return bytesToHex(bytes);
172
+ }
173
+
174
+ // src/cce/cce-crypto.ts
175
+ import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
176
+ import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
177
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
178
+ function aesGcmEncrypt(key, plaintext, aad) {
179
+ if (key.length !== CCE_AES_KEY_BYTES) {
180
+ throw new Error(`AES key must be ${CCE_AES_KEY_BYTES} bytes`);
181
+ }
182
+ const iv = randomBytes(CCE_IV_BYTES);
183
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
184
+ if (aad) {
185
+ cipher.setAAD(aad);
186
+ }
187
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
188
+ const tag = cipher.getAuthTag();
189
+ return {
190
+ iv: new Uint8Array(iv),
191
+ ciphertext: new Uint8Array(encrypted),
192
+ tag: new Uint8Array(tag)
193
+ };
194
+ }
195
+ function aesGcmDecrypt(key, iv, ciphertext, tag, aad) {
196
+ if (key.length !== CCE_AES_KEY_BYTES) {
197
+ throw new Error(`AES key must be ${CCE_AES_KEY_BYTES} bytes`);
198
+ }
199
+ if (iv.length !== CCE_IV_BYTES) {
200
+ throw new Error(`IV must be ${CCE_IV_BYTES} bytes`);
201
+ }
202
+ if (tag.length !== CCE_TAG_BYTES) {
203
+ throw new Error(`Tag must be ${CCE_TAG_BYTES} bytes`);
204
+ }
205
+ try {
206
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
207
+ decipher.setAuthTag(tag);
208
+ if (aad) {
209
+ decipher.setAAD(aad);
210
+ }
211
+ const decrypted = Buffer.concat([
212
+ decipher.update(ciphertext),
213
+ decipher.final()
214
+ ]);
215
+ return new Uint8Array(decrypted);
216
+ } catch {
217
+ return null;
218
+ }
219
+ }
220
+ function generateAesKey() {
221
+ return new Uint8Array(randomBytes(CCE_AES_KEY_BYTES));
222
+ }
223
+ function generateIv() {
224
+ return new Uint8Array(randomBytes(CCE_IV_BYTES));
225
+ }
226
+ function base64UrlEncode(bytes) {
227
+ return Buffer.from(bytes).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
228
+ }
229
+ function base64UrlDecode(input) {
230
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
231
+ const padding = "=".repeat((4 - base64.length % 4) % 4);
232
+ return new Uint8Array(Buffer.from(base64 + padding, "base64"));
233
+ }
234
+ function hashPayload(payload) {
235
+ return bytesToHex2(sha2562(payload));
236
+ }
237
+ var nodeAesGcmProvider = {
238
+ async decrypt(key, iv, ciphertext, tag, aad) {
239
+ return aesGcmDecrypt(key, iv, ciphertext, tag, aad);
240
+ }
241
+ };
242
+
243
+ // src/cce/cce-response.service.ts
244
+ import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils.js";
245
+ import { randomBytes as randomBytes2 } from "crypto";
246
+ async function buildCceResponse(options, clientKeyEncryptor, axisSigner) {
247
+ const { request, capsule, status, body, clientPublicKeyHex, witnessRef } = options;
248
+ const responseNonce = bytesToHex3(
249
+ new Uint8Array(randomBytes2(CCE_NONCE_BYTES))
250
+ );
251
+ const responseId = generateResponseId();
252
+ const aesKey = generateAesKey();
253
+ const aad = buildResponseAad(
254
+ request.request_id,
255
+ responseId,
256
+ request.correlation_id,
257
+ capsule.capsule_id,
258
+ responseNonce
259
+ );
260
+ const { iv, ciphertext, tag } = aesGcmEncrypt(aesKey, body, aad);
261
+ const encryptedKey = await clientKeyEncryptor.wrapKey(
262
+ aesKey,
263
+ request.client_kid,
264
+ clientPublicKeyHex
265
+ );
266
+ aesKey.fill(0);
267
+ const encryptedPayload = {
268
+ alg: "AES-256-GCM",
269
+ iv: base64UrlEncode(iv),
270
+ ciphertext: base64UrlEncode(ciphertext),
271
+ tag: base64UrlEncode(tag)
272
+ };
273
+ const algorithms = {
274
+ kem: encryptedKey.alg,
275
+ enc: "AES-256-GCM",
276
+ kdf: "HKDF-SHA256",
277
+ sig: "EdDSA"
278
+ };
279
+ const unsignedResponse = {
280
+ ver: CCE_PROTOCOL_VERSION,
281
+ response_id: responseId,
282
+ request_id: request.request_id,
283
+ correlation_id: request.correlation_id,
284
+ capsule_id: capsule.capsule_id,
285
+ encrypted_key: encryptedKey,
286
+ encrypted_payload: encryptedPayload,
287
+ response_nonce: responseNonce,
288
+ algorithms,
289
+ status,
290
+ ...witnessRef ? { witness_ref: witnessRef } : {}
291
+ };
292
+ const signPayload = new TextEncoder().encode(canonicalize(unsignedResponse));
293
+ const axisSig = await axisSigner.sign(signPayload);
294
+ const envelope = {
295
+ ...unsignedResponse,
296
+ axis_sig: axisSig
297
+ };
298
+ return {
299
+ envelope,
300
+ responsePayloadHash: hashPayload(body)
301
+ };
302
+ }
303
+ function buildCceErrorResponse(requestId, correlationId, status, errorCode, message) {
304
+ return {
305
+ ver: CCE_PROTOCOL_VERSION,
306
+ request_id: requestId,
307
+ correlation_id: correlationId,
308
+ status,
309
+ error: { code: errorCode, message }
310
+ };
311
+ }
312
+ function generateResponseId() {
313
+ const bytes = randomBytes2(16);
314
+ return "resp_" + bytesToHex3(new Uint8Array(bytes)).slice(0, 24);
315
+ }
316
+ function buildResponseAad(requestId, responseId, correlationId, capsuleId, responseNonce) {
317
+ const parts = [
318
+ requestId,
319
+ responseId,
320
+ correlationId,
321
+ capsuleId,
322
+ responseNonce
323
+ ];
324
+ return new TextEncoder().encode(parts.join("|"));
325
+ }
326
+ function canonicalize(obj) {
327
+ if (Array.isArray(obj)) {
328
+ return "[" + obj.map(canonicalize).join(",") + "]";
329
+ }
330
+ if (obj !== null && typeof obj === "object") {
331
+ const sorted = Object.keys(obj).sort().map(
332
+ (k) => JSON.stringify(k) + ":" + canonicalize(obj[k])
333
+ );
334
+ return "{" + sorted.join(",") + "}";
335
+ }
336
+ return JSON.stringify(obj);
337
+ }
338
+
339
+ // src/cce/cce-witness.observer.ts
340
+ import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils.js";
341
+ import { hkdf as hkdf2 } from "@noble/hashes/hkdf.js";
342
+ import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
343
+ var InMemoryCceWitnessStore = class {
344
+ constructor() {
345
+ this.records = [];
346
+ }
347
+ async record(witness) {
348
+ this.records.push(witness);
349
+ }
350
+ getByRequestId(requestId) {
351
+ return this.records.find((w) => w.request_id === requestId);
352
+ }
353
+ getByCapsuleId(capsuleId) {
354
+ return this.records.filter((w) => w.capsule_id === capsuleId);
355
+ }
356
+ };
357
+ function buildWitnessRecord(envelope, capsule, verification, execution, options) {
358
+ const witnessId = generateWitnessId(envelope.request_id, capsule.capsule_id);
359
+ const executionContextHash = computeExecutionContextHash(
360
+ options.axisLocalSecret,
361
+ capsule,
362
+ envelope.request_nonce
363
+ );
364
+ return {
365
+ witness_id: witnessId,
366
+ request_id: envelope.request_id,
367
+ capsule_id: capsule.capsule_id,
368
+ sub: capsule.sub,
369
+ intent: capsule.intent,
370
+ aud: capsule.aud,
371
+ tps_from: capsule.tps_from,
372
+ tps_to: capsule.tps_to,
373
+ timestamp: Math.floor(Date.now() / 1e3),
374
+ verification: {
375
+ client_sig: verification.clientSigVerified,
376
+ capsule_sig: verification.capsuleSigVerified,
377
+ tps_valid: verification.tpsValid,
378
+ audience_match: verification.audienceMatch,
379
+ intent_match: verification.intentMatch,
380
+ replay_clean: verification.replayClean,
381
+ nonce_unique: verification.nonceUnique,
382
+ decryption_ok: verification.decryptionOk
383
+ },
384
+ execution: {
385
+ status: execution.status,
386
+ handler_duration_ms: execution.handlerDurationMs,
387
+ ...execution.effect ? { effect: execution.effect } : {}
388
+ },
389
+ response_encrypted: options.responseEncrypted,
390
+ execution_context_hash: executionContextHash,
391
+ ...options.requestPayload ? { request_payload_hash: hashPayload(options.requestPayload) } : {},
392
+ ...options.responsePayload ? { response_payload_hash: hashPayload(options.responsePayload) } : {}
393
+ };
394
+ }
395
+ function extractVerificationState(metadata) {
396
+ return {
397
+ clientSigVerified: metadata.cceClientSigVerified === true,
398
+ capsuleSigVerified: metadata.cceCapsuleVerified === true,
399
+ tpsValid: metadata.cceTpsValid === true,
400
+ audienceMatch: metadata.cceBindingVerified === true,
401
+ intentMatch: metadata.cceBindingVerified === true,
402
+ replayClean: metadata.cceReplayClean === true,
403
+ nonceUnique: metadata.cceReplayClean === true,
404
+ decryptionOk: metadata.cceDecryptionOk === true
405
+ };
406
+ }
407
+ function generateWitnessId(requestId, capsuleId) {
408
+ const input = `witness:${requestId}:${capsuleId}:${Date.now()}`;
409
+ const hash = sha2563(new TextEncoder().encode(input));
410
+ return "wit_" + bytesToHex4(hash).slice(0, 24);
411
+ }
412
+ function computeExecutionContextHash(axisLocalSecret, capsule, requestNonce) {
413
+ const encoder = new TextEncoder();
414
+ const ikm = hexToBytes2(axisLocalSecret);
415
+ const salt = sha2563(
416
+ encoder.encode(
417
+ capsule.capsule_id + "|" + capsule.capsule_nonce + "|" + requestNonce
418
+ )
419
+ );
420
+ const info = encoder.encode(
421
+ [
422
+ CCE_DERIVATION.WITNESS,
423
+ capsule.sub,
424
+ capsule.kid,
425
+ capsule.intent,
426
+ capsule.aud,
427
+ String(capsule.tps_from),
428
+ String(capsule.tps_to),
429
+ capsule.policy_hash ?? "",
430
+ capsule.ver
431
+ ].join("|")
432
+ );
433
+ const witnessKey = hkdf2(sha2563, ikm, salt, info, 32);
434
+ const hash = bytesToHex4(sha2563(witnessKey));
435
+ witnessKey.fill(0);
436
+ return hash;
437
+ }
438
+ function hexToBytes2(hex) {
439
+ const bytes = new Uint8Array(hex.length / 2);
440
+ for (let i = 0; i < bytes.length; i++) {
441
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
442
+ }
443
+ return bytes;
444
+ }
445
+
446
+ // src/sensor/axis-sensor.ts
447
+ function normalizeSensorDecision(sensorDecision) {
448
+ if ("action" in sensorDecision) {
449
+ switch (sensorDecision.action) {
450
+ case "ALLOW":
451
+ return {
452
+ allow: true,
453
+ riskScore: 0,
454
+ reasons: [],
455
+ meta: sensorDecision.meta
456
+ };
457
+ case "DENY":
458
+ return {
459
+ allow: false,
460
+ riskScore: 100,
461
+ reasons: [sensorDecision.code, sensorDecision.reason].filter(
462
+ Boolean
463
+ ),
464
+ meta: sensorDecision.meta,
465
+ retryAfterMs: sensorDecision.retryAfterMs
466
+ };
467
+ case "THROTTLE":
468
+ return {
469
+ allow: false,
470
+ riskScore: 50,
471
+ reasons: ["RATE_LIMIT"],
472
+ retryAfterMs: sensorDecision.retryAfterMs,
473
+ meta: sensorDecision.meta
474
+ };
475
+ case "FLAG":
476
+ return {
477
+ allow: true,
478
+ riskScore: sensorDecision.scoreDelta,
479
+ reasons: sensorDecision.reasons,
480
+ meta: sensorDecision.meta
481
+ };
482
+ }
483
+ }
484
+ return {
485
+ allow: sensorDecision.allow,
486
+ riskScore: sensorDecision.riskScore,
487
+ reasons: sensorDecision.reasons,
488
+ tags: sensorDecision.tags,
489
+ meta: sensorDecision.meta,
490
+ tighten: sensorDecision.tighten,
491
+ retryAfterMs: sensorDecision.retryAfterMs
492
+ };
493
+ }
494
+
495
+ // src/cce/cce-pipeline.ts
496
+ async function executeCcePipeline(envelope, config) {
497
+ const startTime = Date.now();
498
+ if (envelope.ver !== CCE_PROTOCOL_VERSION) {
499
+ return {
500
+ ok: false,
501
+ error: {
502
+ code: CCE_ERROR.UNSUPPORTED_VERSION,
503
+ message: `Unsupported version: ${envelope.ver}`
504
+ },
505
+ status: "ERROR"
506
+ };
507
+ }
508
+ const sensorInput = {
509
+ intent: envelope.capsule.intent,
510
+ metadata: {
511
+ cce: true,
512
+ cceEnvelope: envelope,
513
+ contentType: "application/axis-cce"
514
+ }
515
+ };
516
+ const sortedSensors = [...config.sensors].sort(
517
+ (a, b) => (a.order ?? 999) - (b.order ?? 999)
518
+ );
519
+ for (const sensor of sortedSensors) {
520
+ if (sensor.supports && !sensor.supports(sensorInput)) {
521
+ continue;
522
+ }
523
+ let decision;
524
+ try {
525
+ decision = await sensor.run(sensorInput);
526
+ } catch (err) {
527
+ return {
528
+ ok: false,
529
+ error: {
530
+ code: CCE_ERROR.DECRYPTION_FAILED,
531
+ message: `Sensor ${sensor.name} failed`
532
+ },
533
+ status: "ERROR"
534
+ };
535
+ }
536
+ const normalized = normalizeSensorDecision(decision);
537
+ if (!normalized.allow) {
538
+ const code = normalized.reasons[0]?.split(":")[0] ?? CCE_ERROR.DECRYPTION_FAILED;
539
+ return {
540
+ ok: false,
541
+ error: { code, message: normalized.reasons.join("; ") },
542
+ status: "DENIED"
543
+ };
544
+ }
545
+ }
546
+ const capsule = sensorInput.metadata?.cceCapsule;
547
+ const decryptedPayload = sensorInput.metadata?.cceDecryptedPayload;
548
+ const clientKey = sensorInput.metadata?.cceClientKey;
549
+ if (!capsule || !decryptedPayload || !clientKey) {
550
+ return {
551
+ ok: false,
552
+ error: {
553
+ code: CCE_ERROR.DECRYPTION_FAILED,
554
+ message: "Sensor chain did not produce required outputs"
555
+ },
556
+ status: "ERROR"
557
+ };
558
+ }
559
+ const derivationInput = {
560
+ axisLocalSecret: config.axisLocalSecret,
561
+ capsule,
562
+ requestNonce: envelope.request_nonce
563
+ };
564
+ const executionContext = buildExecutionContext(
565
+ derivationInput,
566
+ envelope.request_id
567
+ );
568
+ if (config.policyEvaluator) {
569
+ try {
570
+ const policyDecision = await config.policyEvaluator.evaluate({
571
+ envelope,
572
+ capsule,
573
+ executionContext,
574
+ decryptedPayload,
575
+ clientPublicKeyHex: clientKey.publicKeyHex
576
+ });
577
+ if (!policyDecision.allow) {
578
+ const verification2 = extractVerificationState(sensorInput.metadata ?? {});
579
+ const witness2 = buildWitnessRecord(
580
+ envelope,
581
+ capsule,
582
+ verification2,
583
+ {
584
+ status: "DENIED",
585
+ handlerDurationMs: 0,
586
+ effect: "policy_denied"
587
+ },
588
+ {
589
+ axisLocalSecret: config.axisLocalSecret,
590
+ requestPayload: decryptedPayload,
591
+ responseEncrypted: false
592
+ }
593
+ );
594
+ await config.witnessStore.record(witness2);
595
+ return {
596
+ ok: false,
597
+ error: {
598
+ code: policyDecision.code ?? CCE_ERROR.POLICY_DENIED,
599
+ message: policyDecision.message ?? "Request denied by policy evaluator"
600
+ },
601
+ status: "DENIED"
602
+ };
603
+ }
604
+ } catch (err) {
605
+ return {
606
+ ok: false,
607
+ error: {
608
+ code: CCE_ERROR.POLICY_DENIED,
609
+ message: "Policy evaluator failed"
610
+ },
611
+ status: "ERROR"
612
+ };
613
+ }
614
+ }
615
+ const handler = config.handlers.get(capsule.intent);
616
+ if (!handler) {
617
+ return {
618
+ ok: false,
619
+ error: {
620
+ code: CCE_ERROR.HANDLER_NOT_FOUND,
621
+ message: `No handler for intent: ${capsule.intent}`
622
+ },
623
+ status: "ERROR"
624
+ };
625
+ }
626
+ const handlerContext = {
627
+ capsule,
628
+ executionContext,
629
+ envelope,
630
+ clientPublicKeyHex: clientKey.publicKeyHex,
631
+ intent: capsule.intent,
632
+ sub: capsule.sub
633
+ };
634
+ let result;
635
+ const handlerStart = Date.now();
636
+ try {
637
+ result = await handler(decryptedPayload, handlerContext);
638
+ } catch (err) {
639
+ const handlerDuration2 = Date.now() - handlerStart;
640
+ const verification2 = extractVerificationState(sensorInput.metadata ?? {});
641
+ const witness2 = buildWitnessRecord(
642
+ envelope,
643
+ capsule,
644
+ verification2,
645
+ { status: "FAILED", handlerDurationMs: handlerDuration2 },
646
+ {
647
+ axisLocalSecret: config.axisLocalSecret,
648
+ requestPayload: decryptedPayload,
649
+ responseEncrypted: false
650
+ }
651
+ );
652
+ await config.witnessStore.record(witness2);
653
+ return {
654
+ ok: false,
655
+ error: {
656
+ code: CCE_ERROR.HANDLER_EXECUTION_FAILED,
657
+ message: "Handler execution failed"
658
+ },
659
+ status: "FAILED"
660
+ };
661
+ }
662
+ const handlerDuration = Date.now() - handlerStart;
663
+ let responseEnvelope;
664
+ let responsePayloadHash;
665
+ try {
666
+ const responseResult = await buildCceResponse(
667
+ {
668
+ request: envelope,
669
+ capsule,
670
+ status: result.status,
671
+ body: result.body,
672
+ clientPublicKeyHex: clientKey.publicKeyHex
673
+ },
674
+ config.clientKeyEncryptor,
675
+ config.axisSigner
676
+ );
677
+ responseEnvelope = responseResult.envelope;
678
+ responsePayloadHash = responseResult.responsePayloadHash;
679
+ } catch (err) {
680
+ return {
681
+ ok: false,
682
+ error: {
683
+ code: CCE_ERROR.RESPONSE_ENCRYPTION_FAILED,
684
+ message: "Response encryption failed"
685
+ },
686
+ status: "ERROR"
687
+ };
688
+ }
689
+ const verification = extractVerificationState(sensorInput.metadata ?? {});
690
+ const witness = buildWitnessRecord(
691
+ envelope,
692
+ capsule,
693
+ verification,
694
+ {
695
+ status: result.status,
696
+ handlerDurationMs: handlerDuration,
697
+ effect: result.effect
698
+ },
699
+ {
700
+ axisLocalSecret: config.axisLocalSecret,
701
+ requestPayload: decryptedPayload,
702
+ responsePayload: result.body,
703
+ responseEncrypted: true
704
+ }
705
+ );
706
+ await config.witnessStore.record(witness);
707
+ return {
708
+ ok: true,
709
+ response: responseEnvelope,
710
+ witnessId: witness.witness_id
711
+ };
712
+ }
713
+
714
+ // src/cce/sensors/cce-envelope-validation.sensor.ts
715
+ var REQUIRED_FIELDS = [
716
+ "ver",
717
+ "request_id",
718
+ "correlation_id",
719
+ "client_kid",
720
+ "capsule",
721
+ "encrypted_key",
722
+ "encrypted_payload",
723
+ "request_nonce",
724
+ "client_sig",
725
+ "content_type",
726
+ "algorithms"
727
+ ];
728
+ var CceEnvelopeValidationSensor = class {
729
+ constructor() {
730
+ this.name = "cce.envelope.validation";
731
+ this.order = 5;
732
+ this.phase = "PRE_DECODE";
733
+ }
734
+ supports(input) {
735
+ return input.metadata?.cce === true || input.metadata?.contentType === "application/axis-cce";
736
+ }
737
+ async run(input) {
738
+ const envelope = input.metadata?.cceEnvelope;
739
+ if (!envelope) {
740
+ return {
741
+ allow: false,
742
+ riskScore: 100,
743
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
744
+ code: CCE_ERROR.INVALID_ENVELOPE
745
+ };
746
+ }
747
+ for (const field of REQUIRED_FIELDS) {
748
+ if (envelope[field] === void 0 || envelope[field] === null) {
749
+ return {
750
+ allow: false,
751
+ riskScore: 100,
752
+ reasons: [`${CCE_ERROR.INVALID_ENVELOPE}: missing ${field}`],
753
+ code: CCE_ERROR.INVALID_ENVELOPE
754
+ };
755
+ }
756
+ }
757
+ if (envelope.ver !== CCE_PROTOCOL_VERSION) {
758
+ return {
759
+ allow: false,
760
+ riskScore: 100,
761
+ reasons: [`${CCE_ERROR.UNSUPPORTED_VERSION}: ${envelope.ver}`],
762
+ code: CCE_ERROR.UNSUPPORTED_VERSION
763
+ };
764
+ }
765
+ if (!/^[0-9a-f]+$/i.test(envelope.request_nonce)) {
766
+ return {
767
+ allow: false,
768
+ riskScore: 100,
769
+ reasons: [
770
+ `${CCE_ERROR.INVALID_ENVELOPE}: invalid request_nonce format`
771
+ ],
772
+ code: CCE_ERROR.INVALID_ENVELOPE
773
+ };
774
+ }
775
+ if (envelope.request_nonce.length !== CCE_NONCE_BYTES * 2) {
776
+ return {
777
+ allow: false,
778
+ riskScore: 100,
779
+ reasons: [`${CCE_ERROR.INVALID_ENVELOPE}: request_nonce wrong length`],
780
+ code: CCE_ERROR.INVALID_ENVELOPE
781
+ };
782
+ }
783
+ const capsule = envelope.capsule;
784
+ if (!capsule.capsule_id || !capsule.ver || !capsule.sub || !capsule.kid || !capsule.intent || !capsule.aud || !capsule.issuer_sig) {
785
+ return {
786
+ allow: false,
787
+ riskScore: 100,
788
+ reasons: [`${CCE_ERROR.MISSING_CAPSULE}: incomplete capsule claims`],
789
+ code: CCE_ERROR.MISSING_CAPSULE
790
+ };
791
+ }
792
+ if (!envelope.encrypted_key.ciphertext || !envelope.encrypted_key.alg) {
793
+ return {
794
+ allow: false,
795
+ riskScore: 100,
796
+ reasons: [
797
+ `${CCE_ERROR.MISSING_ENCRYPTED_KEY}: incomplete encrypted_key`
798
+ ],
799
+ code: CCE_ERROR.MISSING_ENCRYPTED_KEY
800
+ };
801
+ }
802
+ input.metadata = input.metadata ?? {};
803
+ input.metadata.cceEnvelopeValid = true;
804
+ return {
805
+ decision: "ALLOW" /* ALLOW */,
806
+ allow: true,
807
+ riskScore: 0,
808
+ reasons: []
809
+ };
810
+ }
811
+ };
812
+
813
+ // src/cce/sensors/cce-client-signature.sensor.ts
814
+ var CceClientSignatureSensor = class {
815
+ constructor(keyResolver, signatureVerifier) {
816
+ this.keyResolver = keyResolver;
817
+ this.signatureVerifier = signatureVerifier;
818
+ this.name = "cce.client.signature";
819
+ this.order = 45;
820
+ this.phase = "POST_DECODE";
821
+ }
822
+ supports(input) {
823
+ return input.metadata?.cceEnvelopeValid === true;
824
+ }
825
+ async run(input) {
826
+ const envelope = input.metadata?.cceEnvelope;
827
+ if (!envelope) {
828
+ return {
829
+ allow: false,
830
+ riskScore: 100,
831
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
832
+ code: CCE_ERROR.INVALID_ENVELOPE
833
+ };
834
+ }
835
+ const keyRecord = await this.keyResolver.resolve(envelope.client_kid);
836
+ if (!keyRecord) {
837
+ return {
838
+ allow: false,
839
+ riskScore: 100,
840
+ reasons: [
841
+ `${CCE_ERROR.CLIENT_KEY_NOT_FOUND}: kid=${envelope.client_kid}`
842
+ ],
843
+ code: CCE_ERROR.CLIENT_KEY_NOT_FOUND
844
+ };
845
+ }
846
+ const { client_sig, ...signable } = envelope;
847
+ const canonical = canonicalize2(signable);
848
+ const message = new TextEncoder().encode(canonical);
849
+ const valid = await this.signatureVerifier.verify(
850
+ message,
851
+ client_sig.value,
852
+ keyRecord.publicKeyHex,
853
+ keyRecord.alg
854
+ );
855
+ if (!valid) {
856
+ return {
857
+ allow: false,
858
+ riskScore: 100,
859
+ reasons: [CCE_ERROR.CLIENT_SIG_INVALID],
860
+ code: CCE_ERROR.CLIENT_SIG_INVALID
861
+ };
862
+ }
863
+ input.metadata = input.metadata ?? {};
864
+ input.metadata.cceClientKey = keyRecord;
865
+ input.metadata.cceClientSigVerified = true;
866
+ return {
867
+ decision: "ALLOW" /* ALLOW */,
868
+ allow: true,
869
+ riskScore: 0,
870
+ reasons: [],
871
+ meta: { kid: envelope.client_kid }
872
+ };
873
+ }
874
+ };
875
+ function canonicalize2(obj) {
876
+ if (Array.isArray(obj)) {
877
+ return "[" + obj.map(canonicalize2).join(",") + "]";
878
+ }
879
+ if (obj !== null && typeof obj === "object") {
880
+ const sorted = Object.keys(obj).sort().map(
881
+ (k) => JSON.stringify(k) + ":" + canonicalize2(obj[k])
882
+ );
883
+ return "{" + sorted.join(",") + "}";
884
+ }
885
+ return JSON.stringify(obj);
886
+ }
887
+
888
+ // src/cce/sensors/cce-capsule-verification.sensor.ts
889
+ import { blake3 } from "@noble/hashes/blake3.js";
890
+ import { bytesToHex as bytesToHex5 } from "@noble/hashes/utils.js";
891
+ var CceCapsuleVerificationSensor = class {
892
+ constructor(issuerKeyResolver, capsuleVerifier) {
893
+ this.issuerKeyResolver = issuerKeyResolver;
894
+ this.capsuleVerifier = capsuleVerifier;
895
+ this.name = "cce.capsule.verification";
896
+ this.order = 50;
897
+ this.phase = "POST_DECODE";
898
+ }
899
+ supports(input) {
900
+ return input.metadata?.cceEnvelopeValid === true;
901
+ }
902
+ async run(input) {
903
+ const capsule = input.metadata?.cceEnvelope?.capsule;
904
+ if (!capsule) {
905
+ return {
906
+ allow: false,
907
+ riskScore: 100,
908
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
909
+ code: CCE_ERROR.MISSING_CAPSULE
910
+ };
911
+ }
912
+ if (capsule.ver !== CCE_PROTOCOL_VERSION) {
913
+ return {
914
+ allow: false,
915
+ riskScore: 100,
916
+ reasons: [
917
+ `${CCE_ERROR.CAPSULE_SIG_INVALID}: wrong version ${capsule.ver}`
918
+ ],
919
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
920
+ };
921
+ }
922
+ const { capsule_id, issuer_sig, ...claimsBody } = capsule;
923
+ const expectedId = computeCceCapsuleId(claimsBody);
924
+ if (capsule_id !== expectedId) {
925
+ return {
926
+ allow: false,
927
+ riskScore: 100,
928
+ reasons: [`${CCE_ERROR.CAPSULE_SIG_INVALID}: content hash mismatch`],
929
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
930
+ };
931
+ }
932
+ const issuerKey = await this.issuerKeyResolver.resolve(
933
+ capsule.issuer_sig.kid
934
+ );
935
+ if (!issuerKey) {
936
+ return {
937
+ allow: false,
938
+ riskScore: 100,
939
+ reasons: [`${CCE_ERROR.CAPSULE_SIG_INVALID}: issuer key not found`],
940
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
941
+ };
942
+ }
943
+ const { issuer_sig: sig, ...rest } = capsule;
944
+ const sigValid = await this.capsuleVerifier.verify(
945
+ rest,
946
+ sig,
947
+ issuerKey.publicKeyHex
948
+ );
949
+ if (!sigValid) {
950
+ return {
951
+ allow: false,
952
+ riskScore: 100,
953
+ reasons: [CCE_ERROR.CAPSULE_SIG_INVALID],
954
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
955
+ };
956
+ }
957
+ const nowSeconds = Math.floor(Date.now() / 1e3);
958
+ if (capsule.exp < nowSeconds) {
959
+ return {
960
+ allow: false,
961
+ riskScore: 100,
962
+ reasons: [`${CCE_ERROR.CAPSULE_EXPIRED}: exp=${capsule.exp}`],
963
+ code: CCE_ERROR.CAPSULE_EXPIRED
964
+ };
965
+ }
966
+ if (capsule.iat > nowSeconds + 5) {
967
+ return {
968
+ allow: false,
969
+ riskScore: 100,
970
+ reasons: [`${CCE_ERROR.CAPSULE_NOT_YET_VALID}: iat=${capsule.iat}`],
971
+ code: CCE_ERROR.CAPSULE_NOT_YET_VALID
972
+ };
973
+ }
974
+ input.metadata = input.metadata ?? {};
975
+ input.metadata.cceCapsuleVerified = true;
976
+ input.metadata.cceCapsule = capsule;
977
+ return {
978
+ decision: "ALLOW" /* ALLOW */,
979
+ allow: true,
980
+ riskScore: 0,
981
+ reasons: [],
982
+ meta: { capsule_id: capsule.capsule_id }
983
+ };
984
+ }
985
+ };
986
+ function canonicalize3(obj) {
987
+ if (Array.isArray(obj)) {
988
+ return "[" + obj.map(canonicalize3).join(",") + "]";
989
+ }
990
+ if (obj !== null && typeof obj === "object") {
991
+ const sorted = Object.keys(obj).sort().map(
992
+ (k) => JSON.stringify(k) + ":" + canonicalize3(obj[k])
993
+ );
994
+ return "{" + sorted.join(",") + "}";
995
+ }
996
+ return JSON.stringify(obj);
997
+ }
998
+ function computeCceCapsuleId(claims) {
999
+ const canonical = canonicalize3(claims);
1000
+ const hash = blake3(new TextEncoder().encode(canonical));
1001
+ return "cce_b3_" + bytesToHex5(hash).slice(0, 32);
1002
+ }
1003
+
1004
+ // src/cce/sensors/cce-tps-window.sensor.ts
1005
+ var DEFAULT_SKEW_MS = 5e3;
1006
+ var CceTpsWindowSensor = class {
1007
+ constructor(skewMs = DEFAULT_SKEW_MS) {
1008
+ this.skewMs = skewMs;
1009
+ this.name = "cce.tps.window";
1010
+ this.order = 92;
1011
+ this.phase = "POST_DECODE";
1012
+ }
1013
+ supports(input) {
1014
+ return input.metadata?.cceCapsuleVerified === true;
1015
+ }
1016
+ async run(input) {
1017
+ const capsule = input.metadata?.cceCapsule;
1018
+ if (!capsule) {
1019
+ return {
1020
+ allow: false,
1021
+ riskScore: 100,
1022
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
1023
+ code: CCE_ERROR.MISSING_CAPSULE
1024
+ };
1025
+ }
1026
+ const nowMs = Date.now();
1027
+ if (nowMs > capsule.tps_to + this.skewMs) {
1028
+ return {
1029
+ allow: false,
1030
+ riskScore: 100,
1031
+ reasons: [
1032
+ `${CCE_ERROR.TPS_WINDOW_EXPIRED}: window ended at ${capsule.tps_to}, now=${nowMs}`
1033
+ ],
1034
+ code: CCE_ERROR.TPS_WINDOW_EXPIRED
1035
+ };
1036
+ }
1037
+ if (nowMs < capsule.tps_from - this.skewMs) {
1038
+ return {
1039
+ allow: false,
1040
+ riskScore: 100,
1041
+ reasons: [
1042
+ `${CCE_ERROR.TPS_WINDOW_FUTURE}: window starts at ${capsule.tps_from}, now=${nowMs}`
1043
+ ],
1044
+ code: CCE_ERROR.TPS_WINDOW_FUTURE
1045
+ };
1046
+ }
1047
+ input.metadata = input.metadata ?? {};
1048
+ input.metadata.cceTpsValid = true;
1049
+ return {
1050
+ decision: "ALLOW" /* ALLOW */,
1051
+ allow: true,
1052
+ riskScore: 0,
1053
+ reasons: []
1054
+ };
1055
+ }
1056
+ };
1057
+
1058
+ // src/cce/sensors/cce-audience-intent-binding.sensor.ts
1059
+ var CceAudienceIntentBindingSensor = class {
1060
+ constructor(axisAudience) {
1061
+ this.axisAudience = axisAudience;
1062
+ this.name = "cce.audience.intent.binding";
1063
+ this.order = 95;
1064
+ this.phase = "POST_DECODE";
1065
+ }
1066
+ supports(input) {
1067
+ return input.metadata?.cceCapsuleVerified === true;
1068
+ }
1069
+ async run(input) {
1070
+ const capsule = input.metadata?.cceCapsule;
1071
+ const envelope = input.metadata?.cceEnvelope;
1072
+ if (!capsule || !envelope) {
1073
+ return {
1074
+ allow: false,
1075
+ riskScore: 100,
1076
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
1077
+ code: CCE_ERROR.MISSING_CAPSULE
1078
+ };
1079
+ }
1080
+ if (capsule.aud !== this.axisAudience) {
1081
+ return {
1082
+ allow: false,
1083
+ riskScore: 100,
1084
+ reasons: [
1085
+ `${CCE_ERROR.AUDIENCE_MISMATCH}: capsule.aud=${capsule.aud}, expected=${this.axisAudience}`
1086
+ ],
1087
+ code: CCE_ERROR.AUDIENCE_MISMATCH
1088
+ };
1089
+ }
1090
+ const requestIntent = input.intent ?? input.metadata?.cceRequestIntent;
1091
+ if (requestIntent && capsule.intent !== requestIntent) {
1092
+ return {
1093
+ allow: false,
1094
+ riskScore: 100,
1095
+ reasons: [
1096
+ `${CCE_ERROR.INTENT_MISMATCH}: capsule.intent=${capsule.intent}, request=${requestIntent}`
1097
+ ],
1098
+ code: CCE_ERROR.INTENT_MISMATCH
1099
+ };
1100
+ }
1101
+ if (envelope.client_kid !== capsule.kid) {
1102
+ return {
1103
+ allow: false,
1104
+ riskScore: 100,
1105
+ reasons: [
1106
+ `${CCE_ERROR.INTENT_MISMATCH}: envelope.kid=${envelope.client_kid}, capsule.kid=${capsule.kid}`
1107
+ ],
1108
+ code: CCE_ERROR.INTENT_MISMATCH
1109
+ };
1110
+ }
1111
+ input.metadata = input.metadata ?? {};
1112
+ input.metadata.cceBindingVerified = true;
1113
+ return {
1114
+ decision: "ALLOW" /* ALLOW */,
1115
+ allow: true,
1116
+ riskScore: 0,
1117
+ reasons: []
1118
+ };
1119
+ }
1120
+ };
1121
+
1122
+ // src/cce/sensors/cce-replay-protection.sensor.ts
1123
+ var InMemoryCceReplayStore = class {
1124
+ constructor() {
1125
+ this.nonces = /* @__PURE__ */ new Map();
1126
+ this.consumed = /* @__PURE__ */ new Set();
1127
+ this.revoked = /* @__PURE__ */ new Set();
1128
+ }
1129
+ async checkAndMark(key, ttlMs) {
1130
+ this.cleanup();
1131
+ if (this.nonces.has(key)) return false;
1132
+ this.nonces.set(key, Date.now() + ttlMs);
1133
+ return true;
1134
+ }
1135
+ async isCapsuleConsumed(capsuleId) {
1136
+ return this.consumed.has(capsuleId);
1137
+ }
1138
+ async markCapsuleConsumed(capsuleId, _ttlMs) {
1139
+ this.consumed.add(capsuleId);
1140
+ }
1141
+ async isCapsuleRevoked(capsuleId) {
1142
+ return this.revoked.has(capsuleId);
1143
+ }
1144
+ /** Revoke a capsule (for testing/admin) */
1145
+ revoke(capsuleId) {
1146
+ this.revoked.add(capsuleId);
1147
+ }
1148
+ cleanup() {
1149
+ const now = Date.now();
1150
+ for (const [key, expiresAt] of this.nonces) {
1151
+ if (expiresAt < now) this.nonces.delete(key);
1152
+ }
1153
+ }
1154
+ };
1155
+ var CceReplayProtectionSensor = class {
1156
+ constructor(replayStore, options) {
1157
+ this.replayStore = replayStore;
1158
+ this.name = "cce.replay.protection";
1159
+ this.order = 98;
1160
+ this.phase = "POST_DECODE";
1161
+ this.nonceTtlMs = options?.nonceTtlMs ?? 5 * 60 * 1e3;
1162
+ }
1163
+ supports(input) {
1164
+ return input.metadata?.cceCapsuleVerified === true;
1165
+ }
1166
+ async run(input) {
1167
+ const capsule = input.metadata?.cceCapsule;
1168
+ const envelope = input.metadata?.cceEnvelope;
1169
+ if (!capsule || !envelope) {
1170
+ return {
1171
+ allow: false,
1172
+ riskScore: 100,
1173
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
1174
+ code: CCE_ERROR.MISSING_CAPSULE
1175
+ };
1176
+ }
1177
+ const revoked = await this.replayStore.isCapsuleRevoked(capsule.capsule_id);
1178
+ if (revoked) {
1179
+ return {
1180
+ allow: false,
1181
+ riskScore: 100,
1182
+ reasons: [`${CCE_ERROR.CAPSULE_REVOKED}: ${capsule.capsule_id}`],
1183
+ code: CCE_ERROR.CAPSULE_REVOKED
1184
+ };
1185
+ }
1186
+ if (capsule.mode === "SINGLE_USE") {
1187
+ const consumed = await this.replayStore.isCapsuleConsumed(
1188
+ capsule.capsule_id
1189
+ );
1190
+ if (consumed) {
1191
+ return {
1192
+ allow: false,
1193
+ riskScore: 100,
1194
+ reasons: [`${CCE_ERROR.CAPSULE_CONSUMED}: ${capsule.capsule_id}`],
1195
+ code: CCE_ERROR.CAPSULE_CONSUMED
1196
+ };
1197
+ }
1198
+ }
1199
+ const nonceKey = `cce:nonce:${capsule.sub}:${capsule.aud}:${capsule.intent}:${envelope.request_nonce}`;
1200
+ const nonceValid = await this.replayStore.checkAndMark(
1201
+ nonceKey,
1202
+ this.nonceTtlMs
1203
+ );
1204
+ if (!nonceValid) {
1205
+ return {
1206
+ allow: false,
1207
+ riskScore: 100,
1208
+ reasons: [
1209
+ `${CCE_ERROR.NONCE_REUSED}: ${envelope.request_nonce.slice(0, 16)}...`
1210
+ ],
1211
+ code: CCE_ERROR.NONCE_REUSED
1212
+ };
1213
+ }
1214
+ if (capsule.mode === "SINGLE_USE") {
1215
+ const capsuleTtl = (capsule.exp - capsule.iat) * 1e3 + 6e4;
1216
+ await this.replayStore.markCapsuleConsumed(
1217
+ capsule.capsule_id,
1218
+ capsuleTtl
1219
+ );
1220
+ }
1221
+ input.metadata = input.metadata ?? {};
1222
+ input.metadata.cceReplayClean = true;
1223
+ return {
1224
+ decision: "ALLOW" /* ALLOW */,
1225
+ allow: true,
1226
+ riskScore: 0,
1227
+ reasons: []
1228
+ };
1229
+ }
1230
+ };
1231
+
1232
+ // src/cce/sensors/cce-payload-decryption.sensor.ts
1233
+ var CcePayloadDecryptionSensor = class {
1234
+ constructor(keyProvider, aesProvider, maxPayloadBytes = 64 * 1024, payloadValidator) {
1235
+ this.keyProvider = keyProvider;
1236
+ this.aesProvider = aesProvider;
1237
+ this.maxPayloadBytes = maxPayloadBytes;
1238
+ this.payloadValidator = payloadValidator;
1239
+ this.name = "cce.payload.decryption";
1240
+ this.order = 145;
1241
+ this.phase = "POST_DECODE";
1242
+ }
1243
+ supports(input) {
1244
+ return input.metadata?.cceEnvelopeValid === true && input.metadata?.cceClientSigVerified === true && input.metadata?.cceCapsuleVerified === true && input.metadata?.cceReplayClean === true;
1245
+ }
1246
+ async run(input) {
1247
+ const envelope = input.metadata?.cceEnvelope;
1248
+ if (!envelope) {
1249
+ return {
1250
+ allow: false,
1251
+ riskScore: 100,
1252
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
1253
+ code: CCE_ERROR.INVALID_ENVELOPE
1254
+ };
1255
+ }
1256
+ let aesKey;
1257
+ try {
1258
+ aesKey = await this.keyProvider.unwrapKey(
1259
+ envelope.encrypted_key.ciphertext,
1260
+ envelope.encrypted_key.alg,
1261
+ envelope.encrypted_key.axis_kid,
1262
+ envelope.encrypted_key.ephemeral_pk
1263
+ );
1264
+ } catch {
1265
+ return {
1266
+ allow: false,
1267
+ riskScore: 100,
1268
+ reasons: [CCE_ERROR.KEY_UNWRAP_FAILED],
1269
+ code: CCE_ERROR.KEY_UNWRAP_FAILED
1270
+ };
1271
+ }
1272
+ if (!aesKey) {
1273
+ return {
1274
+ allow: false,
1275
+ riskScore: 100,
1276
+ reasons: [CCE_ERROR.KEY_UNWRAP_FAILED],
1277
+ code: CCE_ERROR.KEY_UNWRAP_FAILED
1278
+ };
1279
+ }
1280
+ let iv;
1281
+ let ciphertext;
1282
+ let tag;
1283
+ try {
1284
+ iv = base64UrlDecode2(envelope.encrypted_payload.iv);
1285
+ ciphertext = base64UrlDecode2(envelope.encrypted_payload.ciphertext);
1286
+ tag = base64UrlDecode2(envelope.encrypted_payload.tag);
1287
+ } catch {
1288
+ return {
1289
+ allow: false,
1290
+ riskScore: 100,
1291
+ reasons: [`${CCE_ERROR.DECRYPTION_FAILED}: invalid base64url encoding`],
1292
+ code: CCE_ERROR.DECRYPTION_FAILED
1293
+ };
1294
+ }
1295
+ if (ciphertext.length > this.maxPayloadBytes) {
1296
+ return {
1297
+ allow: false,
1298
+ riskScore: 100,
1299
+ reasons: [
1300
+ `${CCE_ERROR.PAYLOAD_TOO_LARGE}: ${ciphertext.length} > ${this.maxPayloadBytes}`
1301
+ ],
1302
+ code: CCE_ERROR.PAYLOAD_TOO_LARGE
1303
+ };
1304
+ }
1305
+ const aad = buildAad(envelope);
1306
+ let plaintext;
1307
+ try {
1308
+ plaintext = await this.aesProvider.decrypt(
1309
+ aesKey,
1310
+ iv,
1311
+ ciphertext,
1312
+ tag,
1313
+ aad
1314
+ );
1315
+ } catch {
1316
+ plaintext = null;
1317
+ } finally {
1318
+ aesKey.fill(0);
1319
+ }
1320
+ if (!plaintext) {
1321
+ return {
1322
+ allow: false,
1323
+ riskScore: 100,
1324
+ reasons: [CCE_ERROR.AEAD_TAG_MISMATCH],
1325
+ code: CCE_ERROR.AEAD_TAG_MISMATCH
1326
+ };
1327
+ }
1328
+ const capsule = input.metadata?.cceCapsule;
1329
+ if (capsule && isJsonContentType(envelope.content_type)) {
1330
+ const parsed = tryParseJsonObject(plaintext);
1331
+ if (parsed && typeof parsed.intent === "string") {
1332
+ if (parsed.intent !== capsule.intent) {
1333
+ return {
1334
+ allow: false,
1335
+ riskScore: 100,
1336
+ reasons: [
1337
+ `${CCE_ERROR.INTENT_SCHEMA_MISMATCH}: payload.intent=${parsed.intent}, capsule.intent=${capsule.intent}`
1338
+ ],
1339
+ code: CCE_ERROR.INTENT_SCHEMA_MISMATCH
1340
+ };
1341
+ }
1342
+ input.metadata = input.metadata ?? {};
1343
+ input.metadata.cceRequestIntent = parsed.intent;
1344
+ }
1345
+ }
1346
+ if (this.payloadValidator) {
1347
+ const verdict = await this.payloadValidator.validate(plaintext, envelope);
1348
+ if (!verdict.ok) {
1349
+ const code = verdict.code ?? CCE_ERROR.PAYLOAD_SCHEMA_INVALID;
1350
+ return {
1351
+ allow: false,
1352
+ riskScore: 100,
1353
+ reasons: [verdict.reason ?? code],
1354
+ code
1355
+ };
1356
+ }
1357
+ if (verdict.intent) {
1358
+ input.metadata = input.metadata ?? {};
1359
+ input.metadata.cceRequestIntent = verdict.intent;
1360
+ }
1361
+ }
1362
+ input.metadata = input.metadata ?? {};
1363
+ input.metadata.cceDecryptedPayload = plaintext;
1364
+ input.metadata.cceDecryptionOk = true;
1365
+ return {
1366
+ decision: "ALLOW" /* ALLOW */,
1367
+ allow: true,
1368
+ riskScore: 0,
1369
+ reasons: []
1370
+ };
1371
+ }
1372
+ };
1373
+ function buildAad(envelope) {
1374
+ const parts = [
1375
+ envelope.ver,
1376
+ envelope.request_id,
1377
+ envelope.correlation_id,
1378
+ envelope.client_kid,
1379
+ envelope.capsule.capsule_id,
1380
+ envelope.capsule.intent,
1381
+ envelope.capsule.aud,
1382
+ envelope.request_nonce
1383
+ ];
1384
+ return new TextEncoder().encode(parts.join("|"));
1385
+ }
1386
+ function isJsonContentType(contentType) {
1387
+ return typeof contentType === "string" && contentType.toLowerCase().includes("application/json");
1388
+ }
1389
+ function tryParseJsonObject(payload) {
1390
+ try {
1391
+ const parsed = JSON.parse(new TextDecoder().decode(payload));
1392
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1393
+ return parsed;
1394
+ }
1395
+ return null;
1396
+ } catch {
1397
+ return null;
1398
+ }
1399
+ }
1400
+ function base64UrlDecode2(input) {
1401
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
1402
+ const padding = "=".repeat((4 - base64.length % 4) % 4);
1403
+ return new Uint8Array(Buffer.from(base64 + padding, "base64"));
1404
+ }
1405
+ export {
1406
+ CCE_AES_KEY_BYTES,
1407
+ CCE_DERIVATION,
1408
+ CCE_ERROR,
1409
+ CCE_IV_BYTES,
1410
+ CCE_NONCE_BYTES,
1411
+ CCE_PROTOCOL_VERSION,
1412
+ CCE_TAG_BYTES,
1413
+ CceAudienceIntentBindingSensor,
1414
+ CceCapsuleVerificationSensor,
1415
+ CceClientSignatureSensor,
1416
+ CceEnvelopeValidationSensor,
1417
+ CceError,
1418
+ CcePayloadDecryptionSensor,
1419
+ CceReplayProtectionSensor,
1420
+ CceTpsWindowSensor,
1421
+ InMemoryCceReplayStore,
1422
+ InMemoryCceWitnessStore,
1423
+ aesGcmDecrypt,
1424
+ aesGcmEncrypt,
1425
+ base64UrlDecode,
1426
+ base64UrlEncode,
1427
+ buildCceErrorResponse,
1428
+ buildCceResponse,
1429
+ buildExecutionContext,
1430
+ buildWitnessRecord,
1431
+ deriveRequestExecutionKey,
1432
+ deriveResponseExecutionKey,
1433
+ deriveWitnessKey,
1434
+ executeCcePipeline,
1435
+ extractVerificationState,
1436
+ generateAesKey,
1437
+ generateCceNonce,
1438
+ generateIv,
1439
+ hashPayload,
1440
+ nodeAesGcmProvider
1441
+ };
1442
+ //# sourceMappingURL=index.mjs.map