@nextera.one/axis-server-sdk 1.4.0 → 1.6.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/dist/index.js CHANGED
@@ -47,16 +47,26 @@ __export(index_exports, {
47
47
  AXIS_UPLOAD_SESSION_STORE: () => AXIS_UPLOAD_SESSION_STORE,
48
48
  AXIS_VERSION: () => import_axis_protocol2.AXIS_VERSION,
49
49
  Ats1Codec: () => ats1_exports,
50
+ AxisContext: () => AxisContext,
51
+ AxisDemoPubkey: () => AxisDemoPubkey,
52
+ AxisError: () => AxisError,
50
53
  AxisFilesDownloadHandler: () => AxisFilesDownloadHandler,
51
54
  AxisFilesFinalizeHandler: () => AxisFilesFinalizeHandler,
52
55
  AxisFrameZ: () => AxisFrameZ,
53
56
  AxisIdDto: () => AxisIdDto,
57
+ AxisIp: () => AxisIp,
54
58
  AxisPacketTags: () => T,
55
59
  AxisPartialType: () => AxisPartialType,
60
+ AxisRaw: () => AxisRaw,
56
61
  AxisResponseDto: () => AxisResponseDto,
62
+ AxisSensorChainService: () => AxisSensorChainService,
57
63
  AxisTlvDto: () => AxisTlvDto,
64
+ BAND: () => BAND,
58
65
  BodyProfile: () => import_axis_protocol2.BodyProfile,
59
66
  CAPABILITIES: () => CAPABILITIES,
67
+ CCE_ERROR: () => CCE_ERROR,
68
+ CCE_PROTOCOL_VERSION: () => CCE_PROTOCOL_VERSION,
69
+ CceError: () => CceError,
60
70
  ContractViolationError: () => ContractViolationError,
61
71
  DEFAULT_CONTRACTS: () => DEFAULT_CONTRACTS,
62
72
  DEFAULT_TIMEOUT: () => DEFAULT_TIMEOUT,
@@ -72,7 +82,10 @@ __export(index_exports, {
72
82
  FLAG_CHAIN_REQ: () => import_axis_protocol2.FLAG_CHAIN_REQ,
73
83
  FLAG_HAS_WITNESS: () => import_axis_protocol2.FLAG_HAS_WITNESS,
74
84
  HANDLER_METADATA_KEY: () => HANDLER_METADATA_KEY,
85
+ HANDLER_SENSORS_KEY: () => HANDLER_SENSORS_KEY,
75
86
  Handler: () => Handler,
87
+ HandlerDiscoveryService: () => HandlerDiscoveryService,
88
+ HandlerSensors: () => HandlerSensors,
76
89
  INTENT_BODY_KEY: () => INTENT_BODY_KEY,
77
90
  INTENT_METADATA_KEY: () => INTENT_METADATA_KEY,
78
91
  INTENT_REQUIREMENTS: () => INTENT_REQUIREMENTS,
@@ -99,6 +112,7 @@ __export(index_exports, {
99
112
  NCERT_PUB: () => import_axis_protocol2.NCERT_PUB,
100
113
  NCERT_SCOPE: () => import_axis_protocol2.NCERT_SCOPE,
101
114
  NCERT_SIG: () => import_axis_protocol2.NCERT_SIG,
115
+ PRE_DECODE_BOUNDARY: () => PRE_DECODE_BOUNDARY,
102
116
  PROOF_CAPABILITIES: () => PROOF_CAPABILITIES,
103
117
  PROOF_CAPSULE: () => import_axis_protocol2.PROOF_CAPSULE,
104
118
  PROOF_JWT: () => import_axis_protocol2.PROOF_JWT,
@@ -113,11 +127,15 @@ __export(index_exports, {
113
127
  RESPONSE_TAG_UPDATED_AT: () => RESPONSE_TAG_UPDATED_AT,
114
128
  RESPONSE_TAG_UPDATED_BY: () => RESPONSE_TAG_UPDATED_BY,
115
129
  RiskDecision: () => RiskDecision,
130
+ SENSOR_METADATA_KEY: () => SENSOR_METADATA_KEY,
116
131
  Schema2002_PasskeyLoginOptionsRes: () => Schema2002_PasskeyLoginOptionsRes,
117
132
  Schema2011_PasskeyLoginVerifyReq: () => Schema2011_PasskeyLoginVerifyReq,
118
133
  Schema2012_PasskeyLoginVerifyRes: () => Schema2012_PasskeyLoginVerifyRes,
119
134
  Schema2021_PasskeyRegisterOptionsReq: () => Schema2021_PasskeyRegisterOptionsReq,
135
+ Sensor: () => Sensor,
120
136
  SensorDecisions: () => SensorDecisions,
137
+ SensorDiscoveryService: () => SensorDiscoveryService,
138
+ SensorRegistry: () => SensorRegistry,
121
139
  TLV: () => import_axis_protocol.TLV,
122
140
  TLV_ACTOR_ID: () => import_axis_protocol2.TLV_ACTOR_ID,
123
141
  TLV_AUD: () => import_axis_protocol2.TLV_AUD,
@@ -175,10 +193,12 @@ __export(index_exports, {
175
193
  canonicalJson: () => canonicalJson,
176
194
  canonicalJsonExcluding: () => canonicalJsonExcluding,
177
195
  canonicalizeObservation: () => canonicalizeObservation,
196
+ cce: () => cce_exports,
178
197
  classifyIntent: () => classifyIntent,
179
198
  computeReceiptHash: () => computeReceiptHash,
180
199
  computeSignaturePayload: () => computeSignaturePayload,
181
200
  core: () => core_exports,
201
+ createObservation: () => createObservation,
182
202
  crypto: () => crypto_exports,
183
203
  decodeArray: () => import_axis_protocol.decodeArray,
184
204
  decodeAxis1Frame: () => decodeAxis1Frame,
@@ -195,8 +215,11 @@ __export(index_exports, {
195
215
  encodeQueueMessage: () => encodeQueueMessage,
196
216
  encodeTLVs: () => import_axis_protocol.encodeTLVs,
197
217
  encodeVarint: () => import_axis_protocol3.encodeVarint,
218
+ endStage: () => endStage,
198
219
  engine: () => engine_exports,
220
+ executeCcePipeline: () => executeCcePipeline,
199
221
  extractDtoSchema: () => extractDtoSchema,
222
+ finalizeObservation: () => finalizeObservation,
200
223
  generateEd25519KeyPair: () => generateEd25519KeyPair,
201
224
  getSignTarget: () => getSignTarget,
202
225
  hasScope: () => hasScope,
@@ -215,14 +238,16 @@ __export(index_exports, {
215
238
  parseAutoClaimEntries: () => parseAutoClaimEntries,
216
239
  parseScope: () => parseScope,
217
240
  parseStreamEntries: () => parseStreamEntries,
241
+ recordSensor: () => recordSensor,
218
242
  resolveTimeout: () => resolveTimeout,
219
243
  schemas: () => schemas_exports,
220
244
  security: () => security_exports,
221
245
  sensitivityName: () => sensitivityName,
222
246
  sensors: () => sensors_exports,
223
- sha256: () => sha256,
247
+ sha256: () => sha2564,
224
248
  signFrame: () => signFrame,
225
249
  stableJsonStringify: () => stableJsonStringify,
250
+ startStage: () => startStage,
226
251
  tlv: () => tlv,
227
252
  u64be: () => u64be,
228
253
  unpackPasskeyLoginOptionsReq: () => unpackPasskeyLoginOptionsReq,
@@ -293,8 +318,24 @@ function IntentSensors(sensors) {
293
318
  };
294
319
  }
295
320
 
296
- // src/decorators/tlv-field.decorator.ts
321
+ // src/decorators/handler-sensors.decorator.ts
297
322
  var import_reflect_metadata4 = require("reflect-metadata");
323
+ var HANDLER_SENSORS_KEY = "axis:handler:sensors";
324
+ function HandlerSensors(sensors) {
325
+ return (target) => {
326
+ Reflect.defineMetadata(HANDLER_SENSORS_KEY, sensors, target);
327
+ };
328
+ }
329
+
330
+ // src/decorators/sensor.decorator.ts
331
+ var import_common2 = require("@nestjs/common");
332
+ var SENSOR_METADATA_KEY = "axis:sensor";
333
+ function Sensor(options) {
334
+ return (0, import_common2.SetMetadata)(SENSOR_METADATA_KEY, options ?? true);
335
+ }
336
+
337
+ // src/decorators/tlv-field.decorator.ts
338
+ var import_reflect_metadata5 = require("reflect-metadata");
298
339
  var TLV_FIELDS_KEY = "axis:tlv:fields";
299
340
  var TLV_VALIDATORS_KEY = "axis:tlv:validators";
300
341
  function TlvField(tag, options) {
@@ -352,7 +393,7 @@ function TlvRange(min, max, message) {
352
393
  }
353
394
 
354
395
  // src/decorators/dto-schema.util.ts
355
- var import_reflect_metadata5 = require("reflect-metadata");
396
+ var import_reflect_metadata6 = require("reflect-metadata");
356
397
 
357
398
  // src/core/tlv.ts
358
399
  var import_axis_protocol = require("@nextera.one/axis-protocol");
@@ -453,7 +494,7 @@ __decorateClass([
453
494
  ], AxisIdDto.prototype, "id", 2);
454
495
 
455
496
  // src/base/axis-partial-type.ts
456
- var import_reflect_metadata6 = require("reflect-metadata");
497
+ var import_reflect_metadata7 = require("reflect-metadata");
457
498
  function AxisPartialType(BaseDto) {
458
499
  class PartialDto extends BaseDto {
459
500
  }
@@ -499,7 +540,7 @@ __decorateClass([
499
540
  ], AxisResponseDto.prototype, "updated_by", 2);
500
541
 
501
542
  // src/engine/intent.router.ts
502
- var import_common2 = require("@nestjs/common");
543
+ var import_common3 = require("@nestjs/common");
503
544
 
504
545
  // src/sensor/axis-sensor.ts
505
546
  var Decision = /* @__PURE__ */ ((Decision2) => {
@@ -600,11 +641,631 @@ var SensorDecisions = {
600
641
  }
601
642
  };
602
643
 
644
+ // src/cce/cce-derivation.service.ts
645
+ var import_utils = require("@noble/hashes/utils.js");
646
+ var import_hkdf = require("@noble/hashes/hkdf.js");
647
+ var import_sha2 = require("@noble/hashes/sha2.js");
648
+
649
+ // src/cce/cce.types.ts
650
+ var CCE_PROTOCOL_VERSION = "cce-v1";
651
+ var CCE_DERIVATION = {
652
+ /** Request execution context */
653
+ REQUEST: "axis:cce:req:v1",
654
+ /** Response execution context */
655
+ RESPONSE: "axis:cce:resp:v1",
656
+ /** Witness binding context */
657
+ WITNESS: "axis:cce:witness:v1"
658
+ };
659
+ var CCE_AES_KEY_BYTES = 32;
660
+ var CCE_IV_BYTES = 12;
661
+ var CCE_TAG_BYTES = 16;
662
+ var CCE_NONCE_BYTES = 32;
663
+ var CCE_ERROR = {
664
+ // Envelope errors
665
+ INVALID_ENVELOPE: "CCE_INVALID_ENVELOPE",
666
+ UNSUPPORTED_VERSION: "CCE_UNSUPPORTED_VERSION",
667
+ MISSING_CAPSULE: "CCE_MISSING_CAPSULE",
668
+ MISSING_ENCRYPTED_KEY: "CCE_MISSING_ENCRYPTED_KEY",
669
+ // Signature errors
670
+ CLIENT_SIG_INVALID: "CCE_CLIENT_SIG_INVALID",
671
+ CLIENT_KEY_NOT_FOUND: "CCE_CLIENT_KEY_NOT_FOUND",
672
+ // Capsule errors
673
+ CAPSULE_SIG_INVALID: "CCE_CAPSULE_SIG_INVALID",
674
+ CAPSULE_EXPIRED: "CCE_CAPSULE_EXPIRED",
675
+ CAPSULE_NOT_YET_VALID: "CCE_CAPSULE_NOT_YET_VALID",
676
+ CAPSULE_REVOKED: "CCE_CAPSULE_REVOKED",
677
+ CAPSULE_CONSUMED: "CCE_CAPSULE_CONSUMED",
678
+ // Binding errors
679
+ AUDIENCE_MISMATCH: "CCE_AUDIENCE_MISMATCH",
680
+ INTENT_MISMATCH: "CCE_INTENT_MISMATCH",
681
+ TPS_WINDOW_EXPIRED: "CCE_TPS_WINDOW_EXPIRED",
682
+ TPS_WINDOW_FUTURE: "CCE_TPS_WINDOW_FUTURE",
683
+ // Replay / nonce errors
684
+ REPLAY_DETECTED: "CCE_REPLAY_DETECTED",
685
+ NONCE_REUSED: "CCE_NONCE_REUSED",
686
+ // Decryption errors
687
+ DECRYPTION_FAILED: "CCE_DECRYPTION_FAILED",
688
+ KEY_UNWRAP_FAILED: "CCE_KEY_UNWRAP_FAILED",
689
+ AEAD_TAG_MISMATCH: "CCE_AEAD_TAG_MISMATCH",
690
+ PAYLOAD_TOO_LARGE: "CCE_PAYLOAD_TOO_LARGE",
691
+ // Schema / validation errors
692
+ PAYLOAD_SCHEMA_INVALID: "CCE_PAYLOAD_SCHEMA_INVALID",
693
+ INTENT_SCHEMA_MISMATCH: "CCE_INTENT_SCHEMA_MISMATCH",
694
+ // Policy errors
695
+ POLICY_DENIED: "CCE_POLICY_DENIED",
696
+ CONSTRAINT_VIOLATED: "CCE_CONSTRAINT_VIOLATED",
697
+ // Handler errors
698
+ HANDLER_NOT_FOUND: "CCE_HANDLER_NOT_FOUND",
699
+ HANDLER_EXECUTION_FAILED: "CCE_HANDLER_EXECUTION_FAILED",
700
+ HANDLER_TIMEOUT: "CCE_HANDLER_TIMEOUT",
701
+ // Response errors
702
+ RESPONSE_ENCRYPTION_FAILED: "CCE_RESPONSE_ENCRYPTION_FAILED"
703
+ };
704
+ var CceError = class extends Error {
705
+ constructor(code, message, metadata) {
706
+ super(`[${code}] ${message}`);
707
+ this.code = code;
708
+ this.metadata = metadata;
709
+ this.name = "CceError";
710
+ }
711
+ /** Whether this error is safe to expose to the client */
712
+ get clientSafe() {
713
+ const internal = [
714
+ CCE_ERROR.DECRYPTION_FAILED,
715
+ CCE_ERROR.KEY_UNWRAP_FAILED,
716
+ CCE_ERROR.AEAD_TAG_MISMATCH,
717
+ CCE_ERROR.HANDLER_EXECUTION_FAILED,
718
+ CCE_ERROR.RESPONSE_ENCRYPTION_FAILED
719
+ ];
720
+ return !internal.includes(this.code);
721
+ }
722
+ /** Get client-safe representation */
723
+ toClientError() {
724
+ if (this.clientSafe) {
725
+ return { code: this.code, message: this.message };
726
+ }
727
+ return {
728
+ code: CCE_ERROR.DECRYPTION_FAILED,
729
+ message: "Request processing failed"
730
+ };
731
+ }
732
+ };
733
+
734
+ // src/cce/cce-derivation.service.ts
735
+ function buildSalt(capsuleId, capsuleNonce, requestNonce) {
736
+ const encoder = new TextEncoder();
737
+ const data = encoder.encode(
738
+ capsuleId + "|" + capsuleNonce + "|" + requestNonce
739
+ );
740
+ return (0, import_sha2.sha256)(data);
741
+ }
742
+ function buildInfo(contextPrefix, capsule, extraNonce) {
743
+ const encoder = new TextEncoder();
744
+ const parts = [
745
+ contextPrefix,
746
+ capsule.sub,
747
+ capsule.kid,
748
+ capsule.intent,
749
+ capsule.aud,
750
+ String(capsule.tps_from),
751
+ String(capsule.tps_to),
752
+ capsule.policy_hash ?? "",
753
+ capsule.ver
754
+ ];
755
+ if (extraNonce) {
756
+ parts.push(extraNonce);
757
+ }
758
+ return encoder.encode(parts.join("|"));
759
+ }
760
+ function deriveRequestExecutionKey(input) {
761
+ const ikm = (0, import_utils.hexToBytes)(input.axisLocalSecret);
762
+ const salt = buildSalt(
763
+ input.capsule.capsule_id,
764
+ input.capsule.capsule_nonce,
765
+ input.requestNonce
766
+ );
767
+ const info = buildInfo(CCE_DERIVATION.REQUEST, input.capsule);
768
+ return (0, import_hkdf.hkdf)(import_sha2.sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
769
+ }
770
+ function deriveResponseExecutionKey(input) {
771
+ const ikm = (0, import_utils.hexToBytes)(input.axisLocalSecret);
772
+ const encoder = new TextEncoder();
773
+ const saltData = encoder.encode(
774
+ input.capsule.capsule_id + "|" + input.capsule.capsule_nonce + "|" + input.requestNonce + "|" + input.responseNonce
775
+ );
776
+ const salt = (0, import_sha2.sha256)(saltData);
777
+ const info = buildInfo(
778
+ CCE_DERIVATION.RESPONSE,
779
+ input.capsule,
780
+ input.responseNonce
781
+ );
782
+ return (0, import_hkdf.hkdf)(import_sha2.sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
783
+ }
784
+ function deriveWitnessKey(input) {
785
+ const ikm = (0, import_utils.hexToBytes)(input.axisLocalSecret);
786
+ const salt = buildSalt(
787
+ input.capsule.capsule_id,
788
+ input.capsule.capsule_nonce,
789
+ input.requestNonce
790
+ );
791
+ const info = buildInfo(CCE_DERIVATION.WITNESS, input.capsule);
792
+ return (0, import_hkdf.hkdf)(import_sha2.sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
793
+ }
794
+ function buildExecutionContext(input, requestId) {
795
+ const executionKey = deriveRequestExecutionKey(input);
796
+ const keyHash = (0, import_utils.bytesToHex)((0, import_sha2.sha256)(executionKey));
797
+ executionKey.fill(0);
798
+ return {
799
+ execution_key_hash: keyHash,
800
+ request_id: requestId,
801
+ capsule_id: input.capsule.capsule_id,
802
+ sub: input.capsule.sub,
803
+ kid: input.capsule.kid,
804
+ intent: input.capsule.intent,
805
+ aud: input.capsule.aud,
806
+ tps_from: input.capsule.tps_from,
807
+ tps_to: input.capsule.tps_to,
808
+ policy_hash: input.capsule.policy_hash,
809
+ derived_at: Math.floor(Date.now() / 1e3),
810
+ valid: true
811
+ };
812
+ }
813
+ function generateCceNonce() {
814
+ const bytes2 = new Uint8Array(CCE_NONCE_BYTES);
815
+ crypto.getRandomValues(bytes2);
816
+ return (0, import_utils.bytesToHex)(bytes2);
817
+ }
818
+
819
+ // src/cce/cce-response.service.ts
820
+ var import_utils3 = require("@noble/hashes/utils.js");
821
+ var import_crypto2 = require("crypto");
822
+
823
+ // src/cce/cce-crypto.ts
824
+ var import_utils2 = require("@noble/hashes/utils.js");
825
+ var import_sha22 = require("@noble/hashes/sha2.js");
826
+ var import_crypto = require("crypto");
827
+ function aesGcmEncrypt(key, plaintext, aad) {
828
+ if (key.length !== CCE_AES_KEY_BYTES) {
829
+ throw new Error(`AES key must be ${CCE_AES_KEY_BYTES} bytes`);
830
+ }
831
+ const iv = (0, import_crypto.randomBytes)(CCE_IV_BYTES);
832
+ const cipher = (0, import_crypto.createCipheriv)("aes-256-gcm", key, iv);
833
+ if (aad) {
834
+ cipher.setAAD(aad);
835
+ }
836
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
837
+ const tag = cipher.getAuthTag();
838
+ return {
839
+ iv: new Uint8Array(iv),
840
+ ciphertext: new Uint8Array(encrypted),
841
+ tag: new Uint8Array(tag)
842
+ };
843
+ }
844
+ function aesGcmDecrypt(key, iv, ciphertext, tag, aad) {
845
+ if (key.length !== CCE_AES_KEY_BYTES) {
846
+ throw new Error(`AES key must be ${CCE_AES_KEY_BYTES} bytes`);
847
+ }
848
+ if (iv.length !== CCE_IV_BYTES) {
849
+ throw new Error(`IV must be ${CCE_IV_BYTES} bytes`);
850
+ }
851
+ if (tag.length !== CCE_TAG_BYTES) {
852
+ throw new Error(`Tag must be ${CCE_TAG_BYTES} bytes`);
853
+ }
854
+ try {
855
+ const decipher = (0, import_crypto.createDecipheriv)("aes-256-gcm", key, iv);
856
+ decipher.setAuthTag(tag);
857
+ if (aad) {
858
+ decipher.setAAD(aad);
859
+ }
860
+ const decrypted = Buffer.concat([
861
+ decipher.update(ciphertext),
862
+ decipher.final()
863
+ ]);
864
+ return new Uint8Array(decrypted);
865
+ } catch {
866
+ return null;
867
+ }
868
+ }
869
+ function generateAesKey() {
870
+ return new Uint8Array((0, import_crypto.randomBytes)(CCE_AES_KEY_BYTES));
871
+ }
872
+ function generateIv() {
873
+ return new Uint8Array((0, import_crypto.randomBytes)(CCE_IV_BYTES));
874
+ }
875
+ function base64UrlEncode(bytes2) {
876
+ return Buffer.from(bytes2).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
877
+ }
878
+ function base64UrlDecode(input) {
879
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
880
+ const padding = "=".repeat((4 - base64.length % 4) % 4);
881
+ return new Uint8Array(Buffer.from(base64 + padding, "base64"));
882
+ }
883
+ function hashPayload(payload) {
884
+ return (0, import_utils2.bytesToHex)((0, import_sha22.sha256)(payload));
885
+ }
886
+ var nodeAesGcmProvider = {
887
+ async decrypt(key, iv, ciphertext, tag, aad) {
888
+ return aesGcmDecrypt(key, iv, ciphertext, tag, aad);
889
+ }
890
+ };
891
+
892
+ // src/cce/cce-response.service.ts
893
+ async function buildCceResponse(options, clientKeyEncryptor, axisSigner) {
894
+ const { request, capsule, status, body, clientPublicKeyHex, witnessRef } = options;
895
+ const responseNonce = (0, import_utils3.bytesToHex)(
896
+ new Uint8Array((0, import_crypto2.randomBytes)(CCE_NONCE_BYTES))
897
+ );
898
+ const responseId = generateResponseId();
899
+ const aesKey = generateAesKey();
900
+ const aad = buildResponseAad(
901
+ request.request_id,
902
+ responseId,
903
+ request.correlation_id,
904
+ capsule.capsule_id,
905
+ responseNonce
906
+ );
907
+ const { iv, ciphertext, tag } = aesGcmEncrypt(aesKey, body, aad);
908
+ const encryptedKey = await clientKeyEncryptor.wrapKey(
909
+ aesKey,
910
+ request.client_kid,
911
+ clientPublicKeyHex
912
+ );
913
+ aesKey.fill(0);
914
+ const encryptedPayload = {
915
+ alg: "AES-256-GCM",
916
+ iv: base64UrlEncode(iv),
917
+ ciphertext: base64UrlEncode(ciphertext),
918
+ tag: base64UrlEncode(tag)
919
+ };
920
+ const algorithms = {
921
+ kem: encryptedKey.alg,
922
+ enc: "AES-256-GCM",
923
+ kdf: "HKDF-SHA256",
924
+ sig: "EdDSA"
925
+ };
926
+ const unsignedResponse = {
927
+ ver: CCE_PROTOCOL_VERSION,
928
+ response_id: responseId,
929
+ request_id: request.request_id,
930
+ correlation_id: request.correlation_id,
931
+ encrypted_key: encryptedKey,
932
+ encrypted_payload: encryptedPayload,
933
+ response_nonce: responseNonce,
934
+ algorithms,
935
+ status,
936
+ ...witnessRef ? { witness_ref: witnessRef } : {}
937
+ };
938
+ const signPayload = new TextEncoder().encode(canonicalize(unsignedResponse));
939
+ const axisSig = await axisSigner.sign(signPayload);
940
+ const envelope = {
941
+ ...unsignedResponse,
942
+ axis_sig: axisSig
943
+ };
944
+ return {
945
+ envelope,
946
+ responsePayloadHash: hashPayload(body)
947
+ };
948
+ }
949
+ function buildCceErrorResponse(requestId, correlationId, status, errorCode, message) {
950
+ return {
951
+ ver: CCE_PROTOCOL_VERSION,
952
+ request_id: requestId,
953
+ correlation_id: correlationId,
954
+ status,
955
+ error: { code: errorCode, message }
956
+ };
957
+ }
958
+ function generateResponseId() {
959
+ const bytes2 = (0, import_crypto2.randomBytes)(16);
960
+ return "resp_" + (0, import_utils3.bytesToHex)(new Uint8Array(bytes2)).slice(0, 24);
961
+ }
962
+ function buildResponseAad(requestId, responseId, correlationId, capsuleId, responseNonce) {
963
+ const parts = [
964
+ requestId,
965
+ responseId,
966
+ correlationId,
967
+ capsuleId,
968
+ responseNonce
969
+ ];
970
+ return new TextEncoder().encode(parts.join("|"));
971
+ }
972
+ function canonicalize(obj) {
973
+ if (Array.isArray(obj)) {
974
+ return "[" + obj.map(canonicalize).join(",") + "]";
975
+ }
976
+ if (obj !== null && typeof obj === "object") {
977
+ const sorted = Object.keys(obj).sort().map(
978
+ (k) => JSON.stringify(k) + ":" + canonicalize(obj[k])
979
+ );
980
+ return "{" + sorted.join(",") + "}";
981
+ }
982
+ return JSON.stringify(obj);
983
+ }
984
+
985
+ // src/cce/cce-witness.observer.ts
986
+ var import_utils4 = require("@noble/hashes/utils.js");
987
+ var import_hkdf2 = require("@noble/hashes/hkdf.js");
988
+ var import_sha23 = require("@noble/hashes/sha2.js");
989
+ var InMemoryCceWitnessStore = class {
990
+ constructor() {
991
+ this.records = [];
992
+ }
993
+ async record(witness) {
994
+ this.records.push(witness);
995
+ }
996
+ getByRequestId(requestId) {
997
+ return this.records.find((w) => w.request_id === requestId);
998
+ }
999
+ getByCapsuleId(capsuleId) {
1000
+ return this.records.filter((w) => w.capsule_id === capsuleId);
1001
+ }
1002
+ };
1003
+ function buildWitnessRecord(envelope, capsule, verification, execution, options) {
1004
+ const witnessId = generateWitnessId(envelope.request_id, capsule.capsule_id);
1005
+ const executionContextHash = computeExecutionContextHash(
1006
+ options.axisLocalSecret,
1007
+ capsule,
1008
+ envelope.request_nonce
1009
+ );
1010
+ return {
1011
+ witness_id: witnessId,
1012
+ request_id: envelope.request_id,
1013
+ capsule_id: capsule.capsule_id,
1014
+ sub: capsule.sub,
1015
+ intent: capsule.intent,
1016
+ aud: capsule.aud,
1017
+ tps_from: capsule.tps_from,
1018
+ tps_to: capsule.tps_to,
1019
+ timestamp: Math.floor(Date.now() / 1e3),
1020
+ verification: {
1021
+ client_sig: verification.clientSigVerified,
1022
+ capsule_sig: verification.capsuleSigVerified,
1023
+ tps_valid: verification.tpsValid,
1024
+ audience_match: verification.audienceMatch,
1025
+ intent_match: verification.intentMatch,
1026
+ replay_clean: verification.replayClean,
1027
+ nonce_unique: verification.nonceUnique,
1028
+ decryption_ok: verification.decryptionOk
1029
+ },
1030
+ execution: {
1031
+ status: execution.status,
1032
+ handler_duration_ms: execution.handlerDurationMs,
1033
+ ...execution.effect ? { effect: execution.effect } : {}
1034
+ },
1035
+ response_encrypted: options.responseEncrypted,
1036
+ execution_context_hash: executionContextHash,
1037
+ ...options.requestPayload ? { request_payload_hash: hashPayload(options.requestPayload) } : {},
1038
+ ...options.responsePayload ? { response_payload_hash: hashPayload(options.responsePayload) } : {}
1039
+ };
1040
+ }
1041
+ function extractVerificationState(metadata) {
1042
+ return {
1043
+ clientSigVerified: metadata.cceClientSigVerified === true,
1044
+ capsuleSigVerified: metadata.cceCapsuleVerified === true,
1045
+ tpsValid: metadata.cceTpsValid === true,
1046
+ audienceMatch: metadata.cceBindingVerified === true,
1047
+ intentMatch: metadata.cceBindingVerified === true,
1048
+ replayClean: metadata.cceReplayClean === true,
1049
+ nonceUnique: metadata.cceReplayClean === true,
1050
+ decryptionOk: metadata.cceDecryptionOk === true
1051
+ };
1052
+ }
1053
+ function generateWitnessId(requestId, capsuleId) {
1054
+ const input = `witness:${requestId}:${capsuleId}:${Date.now()}`;
1055
+ const hash = (0, import_sha23.sha256)(new TextEncoder().encode(input));
1056
+ return "wit_" + (0, import_utils4.bytesToHex)(hash).slice(0, 24);
1057
+ }
1058
+ function computeExecutionContextHash(axisLocalSecret, capsule, requestNonce) {
1059
+ const encoder = new TextEncoder();
1060
+ const ikm = hexToBytes2(axisLocalSecret);
1061
+ const salt = (0, import_sha23.sha256)(
1062
+ encoder.encode(
1063
+ capsule.capsule_id + "|" + capsule.capsule_nonce + "|" + requestNonce
1064
+ )
1065
+ );
1066
+ const info = encoder.encode(
1067
+ [
1068
+ CCE_DERIVATION.WITNESS,
1069
+ capsule.sub,
1070
+ capsule.kid,
1071
+ capsule.intent,
1072
+ capsule.aud,
1073
+ String(capsule.tps_from),
1074
+ String(capsule.tps_to),
1075
+ capsule.policy_hash ?? "",
1076
+ capsule.ver
1077
+ ].join("|")
1078
+ );
1079
+ const witnessKey = (0, import_hkdf2.hkdf)(import_sha23.sha256, ikm, salt, info, 32);
1080
+ const hash = (0, import_utils4.bytesToHex)((0, import_sha23.sha256)(witnessKey));
1081
+ witnessKey.fill(0);
1082
+ return hash;
1083
+ }
1084
+ function hexToBytes2(hex) {
1085
+ const bytes2 = new Uint8Array(hex.length / 2);
1086
+ for (let i = 0; i < bytes2.length; i++) {
1087
+ bytes2[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
1088
+ }
1089
+ return bytes2;
1090
+ }
1091
+
1092
+ // src/cce/cce-pipeline.ts
1093
+ async function executeCcePipeline(envelope, config) {
1094
+ const startTime = Date.now();
1095
+ if (envelope.ver !== CCE_PROTOCOL_VERSION) {
1096
+ return {
1097
+ ok: false,
1098
+ error: {
1099
+ code: CCE_ERROR.UNSUPPORTED_VERSION,
1100
+ message: `Unsupported version: ${envelope.ver}`
1101
+ },
1102
+ status: "ERROR"
1103
+ };
1104
+ }
1105
+ const sensorInput = {
1106
+ intent: envelope.capsule.intent,
1107
+ metadata: {
1108
+ cce: true,
1109
+ cceEnvelope: envelope,
1110
+ contentType: "application/axis-cce"
1111
+ }
1112
+ };
1113
+ const sortedSensors = [...config.sensors].sort(
1114
+ (a, b) => (a.order ?? 999) - (b.order ?? 999)
1115
+ );
1116
+ for (const sensor of sortedSensors) {
1117
+ if (sensor.supports && !sensor.supports(sensorInput)) {
1118
+ continue;
1119
+ }
1120
+ let decision;
1121
+ try {
1122
+ decision = await sensor.run(sensorInput);
1123
+ } catch (err) {
1124
+ return {
1125
+ ok: false,
1126
+ error: {
1127
+ code: CCE_ERROR.DECRYPTION_FAILED,
1128
+ message: `Sensor ${sensor.name} failed`
1129
+ },
1130
+ status: "ERROR"
1131
+ };
1132
+ }
1133
+ const normalized = normalizeSensorDecision(decision);
1134
+ if (!normalized.allow) {
1135
+ const code = normalized.reasons[0]?.split(":")[0] ?? CCE_ERROR.DECRYPTION_FAILED;
1136
+ return {
1137
+ ok: false,
1138
+ error: { code, message: normalized.reasons.join("; ") },
1139
+ status: "DENIED"
1140
+ };
1141
+ }
1142
+ }
1143
+ const capsule = sensorInput.metadata?.cceCapsule;
1144
+ const decryptedPayload = sensorInput.metadata?.cceDecryptedPayload;
1145
+ const clientKey = sensorInput.metadata?.cceClientKey;
1146
+ if (!capsule || !decryptedPayload || !clientKey) {
1147
+ return {
1148
+ ok: false,
1149
+ error: {
1150
+ code: CCE_ERROR.DECRYPTION_FAILED,
1151
+ message: "Sensor chain did not produce required outputs"
1152
+ },
1153
+ status: "ERROR"
1154
+ };
1155
+ }
1156
+ const derivationInput = {
1157
+ axisLocalSecret: config.axisLocalSecret,
1158
+ capsule,
1159
+ requestNonce: envelope.request_nonce
1160
+ };
1161
+ const executionContext = buildExecutionContext(
1162
+ derivationInput,
1163
+ envelope.request_id
1164
+ );
1165
+ const handler = config.handlers.get(capsule.intent);
1166
+ if (!handler) {
1167
+ return {
1168
+ ok: false,
1169
+ error: {
1170
+ code: CCE_ERROR.HANDLER_NOT_FOUND,
1171
+ message: `No handler for intent: ${capsule.intent}`
1172
+ },
1173
+ status: "ERROR"
1174
+ };
1175
+ }
1176
+ const handlerContext = {
1177
+ capsule,
1178
+ executionContext,
1179
+ envelope,
1180
+ clientPublicKeyHex: clientKey.publicKeyHex,
1181
+ intent: capsule.intent,
1182
+ sub: capsule.sub
1183
+ };
1184
+ let result;
1185
+ const handlerStart = Date.now();
1186
+ try {
1187
+ result = await handler(decryptedPayload, handlerContext);
1188
+ } catch (err) {
1189
+ const handlerDuration2 = Date.now() - handlerStart;
1190
+ const verification2 = extractVerificationState(sensorInput.metadata ?? {});
1191
+ const witness2 = buildWitnessRecord(
1192
+ envelope,
1193
+ capsule,
1194
+ verification2,
1195
+ { status: "FAILED", handlerDurationMs: handlerDuration2 },
1196
+ {
1197
+ axisLocalSecret: config.axisLocalSecret,
1198
+ requestPayload: decryptedPayload,
1199
+ responseEncrypted: false
1200
+ }
1201
+ );
1202
+ await config.witnessStore.record(witness2);
1203
+ return {
1204
+ ok: false,
1205
+ error: {
1206
+ code: CCE_ERROR.HANDLER_EXECUTION_FAILED,
1207
+ message: "Handler execution failed"
1208
+ },
1209
+ status: "FAILED"
1210
+ };
1211
+ }
1212
+ const handlerDuration = Date.now() - handlerStart;
1213
+ let responseEnvelope;
1214
+ let responsePayloadHash;
1215
+ try {
1216
+ const responseResult = await buildCceResponse(
1217
+ {
1218
+ request: envelope,
1219
+ capsule,
1220
+ status: result.status,
1221
+ body: result.body,
1222
+ clientPublicKeyHex: clientKey.publicKeyHex
1223
+ },
1224
+ config.clientKeyEncryptor,
1225
+ config.axisSigner
1226
+ );
1227
+ responseEnvelope = responseResult.envelope;
1228
+ responsePayloadHash = responseResult.responsePayloadHash;
1229
+ } catch (err) {
1230
+ return {
1231
+ ok: false,
1232
+ error: {
1233
+ code: CCE_ERROR.RESPONSE_ENCRYPTION_FAILED,
1234
+ message: "Response encryption failed"
1235
+ },
1236
+ status: "ERROR"
1237
+ };
1238
+ }
1239
+ const verification = extractVerificationState(sensorInput.metadata ?? {});
1240
+ const witness = buildWitnessRecord(
1241
+ envelope,
1242
+ capsule,
1243
+ verification,
1244
+ {
1245
+ status: result.status,
1246
+ handlerDurationMs: handlerDuration,
1247
+ effect: result.effect
1248
+ },
1249
+ {
1250
+ axisLocalSecret: config.axisLocalSecret,
1251
+ requestPayload: decryptedPayload,
1252
+ responsePayload: result.body,
1253
+ responseEncrypted: true
1254
+ }
1255
+ );
1256
+ await config.witnessStore.record(witness);
1257
+ return {
1258
+ ok: true,
1259
+ response: responseEnvelope,
1260
+ witnessId: witness.witness_id
1261
+ };
1262
+ }
1263
+
603
1264
  // src/engine/intent.router.ts
604
1265
  var IntentRouter = class {
605
1266
  constructor(moduleRef) {
606
1267
  this.moduleRef = moduleRef;
607
- this.logger = new import_common2.Logger(IntentRouter.name);
1268
+ this.logger = new import_common3.Logger(IntentRouter.name);
608
1269
  /** Internal registry of dynamic intent handlers */
609
1270
  this.handlers = /* @__PURE__ */ new Map();
610
1271
  /** Per-intent sensor classes (resolved at call time) */
@@ -617,6 +1278,10 @@ var IntentRouter = class {
617
1278
  this.intentValidators = /* @__PURE__ */ new Map();
618
1279
  /** Per-intent operation kind */
619
1280
  this.intentKinds = /* @__PURE__ */ new Map();
1281
+ /** CCE handler registry */
1282
+ this.cceHandlers = /* @__PURE__ */ new Map();
1283
+ /** CCE pipeline configuration (set via configureCce) */
1284
+ this.ccePipelineConfig = null;
620
1285
  }
621
1286
  getSchema(intent) {
622
1287
  return this.intentSchemas.get(intent);
@@ -666,6 +1331,7 @@ var IntentRouter = class {
666
1331
  );
667
1332
  const prefix = handlerMeta?.intent || instance.name;
668
1333
  const routes = Reflect.getMetadata(INTENT_ROUTES_KEY, instance.constructor) || [];
1334
+ const handlerSensors = Reflect.getMetadata(HANDLER_SENSORS_KEY, instance.constructor) || [];
669
1335
  for (const route of routes) {
670
1336
  const intentName = route.absolute ? route.action : `${prefix}.${route.action}`;
671
1337
  const fn = instance[route.methodName].bind(instance);
@@ -674,7 +1340,12 @@ var IntentRouter = class {
674
1340
  } else {
675
1341
  this.register(intentName, fn);
676
1342
  }
677
- this.registerIntentMeta(intentName, Object.getPrototypeOf(instance), String(route.methodName));
1343
+ this.registerIntentMeta(
1344
+ intentName,
1345
+ Object.getPrototypeOf(instance),
1346
+ String(route.methodName),
1347
+ handlerSensors
1348
+ );
678
1349
  }
679
1350
  const proto = Object.getPrototypeOf(instance);
680
1351
  for (const key of Object.getOwnPropertyNames(proto)) {
@@ -683,7 +1354,7 @@ var IntentRouter = class {
683
1354
  if (!this.handlers.has(meta.intent)) {
684
1355
  this.register(meta.intent, instance[key].bind(instance));
685
1356
  }
686
- this.registerIntentMeta(meta.intent, proto, key);
1357
+ this.registerIntentMeta(meta.intent, proto, key, handlerSensors);
687
1358
  }
688
1359
  }
689
1360
  /**
@@ -819,14 +1490,22 @@ var IntentRouter = class {
819
1490
  this.logger.warn(`${intent} failed in ${ms}ms - ${error}`);
820
1491
  }
821
1492
  }
822
- registerIntentMeta(intent, proto, methodName) {
1493
+ registerIntentMeta(intent, proto, methodName, handlerSensors) {
823
1494
  const decoder = Reflect.getMetadata(INTENT_BODY_KEY, proto, methodName);
824
1495
  if (decoder) {
825
1496
  this.intentDecoders.set(intent, decoder);
826
1497
  }
827
- const sensors = Reflect.getMetadata(INTENT_SENSORS_KEY, proto, methodName);
828
- if (sensors && Array.isArray(sensors) && sensors.length > 0) {
829
- this.intentSensors.set(intent, sensors);
1498
+ const intentSensors = Reflect.getMetadata(
1499
+ INTENT_SENSORS_KEY,
1500
+ proto,
1501
+ methodName
1502
+ );
1503
+ const combined = [
1504
+ ...handlerSensors || [],
1505
+ ...Array.isArray(intentSensors) ? intentSensors : []
1506
+ ];
1507
+ if (combined.length > 0) {
1508
+ this.intentSensors.set(intent, combined);
830
1509
  }
831
1510
  const meta = Reflect.getMetadata(INTENT_METADATA_KEY, proto, methodName);
832
1511
  if (meta) {
@@ -866,6 +1545,58 @@ var IntentRouter = class {
866
1545
  }
867
1546
  }
868
1547
  }
1548
+ // ===========================================================================
1549
+ // CCE — Capsule-Carried Encryption Support
1550
+ // ===========================================================================
1551
+ /**
1552
+ * Configure the CCE pipeline.
1553
+ * Must be called before routeCce() can process encrypted requests.
1554
+ */
1555
+ configureCce(config) {
1556
+ this.ccePipelineConfig = config;
1557
+ this.logger.log("CCE pipeline configured");
1558
+ }
1559
+ /**
1560
+ * Register a CCE-encrypted intent handler.
1561
+ * CCE handlers receive decrypted payloads and execution context.
1562
+ */
1563
+ registerCceHandler(intent, handler) {
1564
+ this.cceHandlers.set(intent, handler);
1565
+ this.logger.debug(`CCE handler registered: ${intent}`);
1566
+ }
1567
+ /**
1568
+ * Check if a CCE handler exists for the given intent.
1569
+ */
1570
+ hasCceHandler(intent) {
1571
+ return this.cceHandlers.has(intent);
1572
+ }
1573
+ /**
1574
+ * Route a CCE-encrypted request through the full pipeline.
1575
+ *
1576
+ * Steps:
1577
+ * 1. Sensor chain (envelope validation → capsule verification → replay → decrypt)
1578
+ * 2. Execution context derivation
1579
+ * 3. Handler execution
1580
+ * 4. Response encryption
1581
+ * 5. Witness recording
1582
+ */
1583
+ async routeCce(envelope) {
1584
+ if (!this.ccePipelineConfig) {
1585
+ return {
1586
+ ok: false,
1587
+ error: {
1588
+ code: "CCE_NOT_CONFIGURED",
1589
+ message: "CCE pipeline not configured. Call configureCce() first."
1590
+ },
1591
+ status: "ERROR"
1592
+ };
1593
+ }
1594
+ const config = {
1595
+ ...this.ccePipelineConfig,
1596
+ handlers: this.cceHandlers
1597
+ };
1598
+ return executeCcePipeline(envelope, config);
1599
+ }
869
1600
  storeSchema(meta) {
870
1601
  if (meta.dto) {
871
1602
  if (meta.tlv && meta.tlv.length > 0) {
@@ -925,10 +1656,27 @@ IntentRouter.BUILTIN_INTENTS = /* @__PURE__ */ new Set([
925
1656
  "axis.intent.exec"
926
1657
  ]);
927
1658
  IntentRouter = __decorateClass([
928
- (0, import_common2.Injectable)(),
929
- __decorateParam(0, (0, import_common2.Optional)())
1659
+ (0, import_common3.Injectable)(),
1660
+ __decorateParam(0, (0, import_common3.Optional)())
930
1661
  ], IntentRouter);
931
1662
 
1663
+ // src/engine/sensor-bands.ts
1664
+ var BAND = {
1665
+ /** Pre-decode: raw byte validation, geo, budget, magic */
1666
+ WIRE: 0,
1667
+ /** Post-decode: identity resolution, capsule, proof */
1668
+ IDENTITY: 40,
1669
+ /** Post-decode: authorization, signature, rate limiting */
1670
+ POLICY: 90,
1671
+ /** Post-decode: content validation, TLV, schema, files */
1672
+ CONTENT: 140,
1673
+ /** Post-decode: business logic sensors, streams, WS */
1674
+ BUSINESS: 200,
1675
+ /** Post-decode: audit, logging (always last) */
1676
+ AUDIT: 900
1677
+ };
1678
+ var PRE_DECODE_BOUNDARY = 40;
1679
+
932
1680
  // src/engine/observation/stable-json.ts
933
1681
  function normalize(value) {
934
1682
  if (Array.isArray(value)) {
@@ -1028,7 +1776,7 @@ function fieldsToMap(fields) {
1028
1776
  }
1029
1777
 
1030
1778
  // src/engine/observation/observation-hash.ts
1031
- var import_crypto = require("crypto");
1779
+ var import_crypto3 = require("crypto");
1032
1780
  function canonicalizeObservation(obs) {
1033
1781
  const obj = {
1034
1782
  id: obs.id,
@@ -1065,7 +1813,7 @@ function canonicalizeObservation(obs) {
1065
1813
  }
1066
1814
  function hashObservation(obs) {
1067
1815
  const canonical = canonicalizeObservation(obs);
1068
- return (0, import_crypto.createHash)("sha256").update(canonical).digest("hex");
1816
+ return (0, import_crypto3.createHash)("sha256").update(canonical).digest("hex");
1069
1817
  }
1070
1818
  function buildUnsignedWitness(obs) {
1071
1819
  if (!obs.decision || !obs.endMs) {
@@ -1140,7 +1888,7 @@ function verifyResponse(ctx, response) {
1140
1888
  var import_axis_protocol3 = require("@nextera.one/axis-protocol");
1141
1889
 
1142
1890
  // src/core/signature.ts
1143
- var crypto = __toESM(require("crypto"));
1891
+ var crypto2 = __toESM(require("crypto"));
1144
1892
 
1145
1893
  // src/core/axis-bin.ts
1146
1894
  var z = __toESM(require("zod"));
@@ -1270,19 +2018,19 @@ function signFrame(frame, privateKey) {
1270
2018
  32
1271
2019
  ]);
1272
2020
  const pkcs8Key = Buffer.concat([pkcs8Prefix, privateKey]);
1273
- keyObject = crypto.createPrivateKey({
2021
+ keyObject = crypto2.createPrivateKey({
1274
2022
  key: pkcs8Key,
1275
2023
  format: "der",
1276
2024
  type: "pkcs8"
1277
2025
  });
1278
2026
  } else {
1279
- keyObject = crypto.createPrivateKey({
2027
+ keyObject = crypto2.createPrivateKey({
1280
2028
  key: privateKey,
1281
2029
  format: "der",
1282
2030
  type: "pkcs8"
1283
2031
  });
1284
2032
  }
1285
- const signature = crypto.sign(null, payload, keyObject);
2033
+ const signature = crypto2.sign(null, payload, keyObject);
1286
2034
  if (signature.length !== 64) {
1287
2035
  throw new Error("Ed25519 signature must be 64 bytes");
1288
2036
  }
@@ -1314,19 +2062,19 @@ function verifyFrameSignature(frame, publicKey) {
1314
2062
  0
1315
2063
  ]);
1316
2064
  const spkiKey = Buffer.concat([spkiPrefix, publicKey]);
1317
- keyObject = crypto.createPublicKey({
2065
+ keyObject = crypto2.createPublicKey({
1318
2066
  key: spkiKey,
1319
2067
  format: "der",
1320
2068
  type: "spki"
1321
2069
  });
1322
2070
  } else {
1323
- keyObject = crypto.createPublicKey({
2071
+ keyObject = crypto2.createPublicKey({
1324
2072
  key: publicKey,
1325
2073
  format: "der",
1326
2074
  type: "spki"
1327
2075
  });
1328
2076
  }
1329
- const valid = crypto.verify(
2077
+ const valid = crypto2.verify(
1330
2078
  null,
1331
2079
  payload,
1332
2080
  keyObject,
@@ -1338,17 +2086,17 @@ function verifyFrameSignature(frame, publicKey) {
1338
2086
  }
1339
2087
  }
1340
2088
  function generateEd25519KeyPair() {
1341
- const { privateKey, publicKey } = crypto.generateKeyPairSync("ed25519");
2089
+ const { privateKey, publicKey } = crypto2.generateKeyPairSync("ed25519");
1342
2090
  return {
1343
2091
  privateKey: privateKey.export({ type: "pkcs8", format: "der" }),
1344
2092
  publicKey: publicKey.export({ type: "spki", format: "der" })
1345
2093
  };
1346
2094
  }
1347
- function sha256(data) {
1348
- return crypto.createHash("sha256").update(data).digest();
2095
+ function sha2564(data) {
2096
+ return crypto2.createHash("sha256").update(data).digest();
1349
2097
  }
1350
2098
  function computeReceiptHash(receiptBytes, prevHash) {
1351
- const hasher = crypto.createHash("sha256");
2099
+ const hasher = crypto2.createHash("sha256");
1352
2100
  hasher.update(receiptBytes);
1353
2101
  if (prevHash && prevHash.length > 0) {
1354
2102
  hasher.update(prevHash);
@@ -1406,12 +2154,12 @@ __export(ats1_exports, {
1406
2154
  encodeU64BE: () => encodeU64BE,
1407
2155
  encodeUVarint: () => encodeUVarint,
1408
2156
  logicalBodyToTLVs: () => logicalBodyToTLVs,
1409
- sha256: () => sha2562,
2157
+ sha256: () => sha2565,
1410
2158
  tlvsToLogicalBody: () => tlvsToLogicalBody,
1411
2159
  tlvsToMap: () => tlvsToMap,
1412
2160
  validateTLVsAgainstSchema: () => validateTLVsAgainstSchema
1413
2161
  });
1414
- var import_crypto2 = require("crypto");
2162
+ var import_crypto4 = require("crypto");
1415
2163
  var DEFAULT_LIMITS = {
1416
2164
  maxVarintBytes: 10,
1417
2165
  maxTlvCount: 512,
@@ -1460,8 +2208,8 @@ function decodeU64BE(buf) {
1460
2208
  if (buf.length !== 8) throw new Error("decodeU64BE: length must be 8");
1461
2209
  return buf.readBigUInt64BE(0);
1462
2210
  }
1463
- function sha2562(data) {
1464
- return (0, import_crypto2.createHash)("sha256").update(data).digest();
2211
+ function sha2565(data) {
2212
+ return (0, import_crypto4.createHash)("sha256").update(data).digest();
1465
2213
  }
1466
2214
  function encodeTLV(tag, value) {
1467
2215
  if (!Number.isInteger(tag) || tag <= 0)
@@ -1777,7 +2525,7 @@ function decodeAxisHeaderFromTLVs(hdrTlvs, limits = DEFAULT_LIMITS) {
1777
2525
  function encodeAxisRequestBinary(schema, req, limits = DEFAULT_LIMITS) {
1778
2526
  const bodyTlvs = logicalBodyToTLVs(schema, req.body, limits);
1779
2527
  const bodyBytes = encodeTLVStreamCanonical(bodyTlvs);
1780
- const bodyHash = sha2562(bodyBytes);
2528
+ const bodyHash = sha2565(bodyBytes);
1781
2529
  const hdr = {
1782
2530
  ...req.hdr,
1783
2531
  schemaId: schema.schemaId,
@@ -1793,7 +2541,7 @@ function decodeAxisRequestBinary(schema, hdrBytes, bodyBytes, limits = DEFAULT_L
1793
2541
  const hdr = decodeAxisHeaderFromTLVs(hdrTlvs, limits);
1794
2542
  if (hdr.schemaId !== schema.schemaId)
1795
2543
  throw new Error("decodeAxisRequestBinary: schemaId mismatch");
1796
- const bh = sha2562(bodyBytes);
2544
+ const bh = sha2565(bodyBytes);
1797
2545
  if (!Buffer.from(hdr.bodyHash).equals(bh))
1798
2546
  throw new Error("decodeAxisRequestBinary: body_hash mismatch");
1799
2547
  const body = tlvsToLogicalBody(schema, bodyTlvs, limits);
@@ -1866,7 +2614,7 @@ function packPasskeyLoginOptionsReq(params) {
1866
2614
  }
1867
2615
  );
1868
2616
  const body = encodeTLVStreamCanonical(bodyTlvs);
1869
- const bodyHash = sha2562(body);
2617
+ const bodyHash = sha2565(body);
1870
2618
  const hdr = buildAts1Hdr({
1871
2619
  intentId: params.intentId,
1872
2620
  schemaId: ATS1_SCHEMA.PASSKEY_LOGIN_OPTIONS_REQ,
@@ -1935,7 +2683,7 @@ function packPasskeyRegisterOptionsReq(params) {
1935
2683
  }
1936
2684
  );
1937
2685
  const body = encodeTLVStreamCanonical(bodyTlvs);
1938
- const bodyHash = sha2562(body);
2686
+ const bodyHash = sha2565(body);
1939
2687
  const hdr = buildAts1Hdr({
1940
2688
  intentId: params.intentId,
1941
2689
  schemaId: ATS1_SCHEMA.PASSKEY_REGISTER_OPTIONS_REQ,
@@ -1966,7 +2714,7 @@ function packPasskeyLoginVerifyReq(params) {
1966
2714
  }
1967
2715
  });
1968
2716
  const body = encodeTLVStreamCanonical(bodyTlvs);
1969
- const bodyHash = sha2562(body);
2717
+ const bodyHash = sha2565(body);
1970
2718
  const hdr = buildAts1Hdr({
1971
2719
  intentId: params.intentId,
1972
2720
  schemaId: ATS1_SCHEMA.PASSKEY_LOGIN_VERIFY_REQ,
@@ -2050,7 +2798,7 @@ function packPasskeyLoginVerifyRes(params) {
2050
2798
  }
2051
2799
 
2052
2800
  // src/codec/tlv.encode.ts
2053
- var import_crypto3 = require("crypto");
2801
+ var import_crypto5 = require("crypto");
2054
2802
  function encVarint(x) {
2055
2803
  if (x < 0n) throw new Error("VARINT_NEG");
2056
2804
  const out = [];
@@ -2078,7 +2826,7 @@ function bytes(b) {
2078
2826
  return Buffer.isBuffer(b) ? b : Buffer.from(b);
2079
2827
  }
2080
2828
  function nonce16() {
2081
- return (0, import_crypto3.randomBytes)(16);
2829
+ return (0, import_crypto5.randomBytes)(16);
2082
2830
  }
2083
2831
  function tlv(type, value) {
2084
2832
  if (!Number.isSafeInteger(type) || type < 0) throw new Error("TLV_BAD_TYPE");
@@ -2624,9 +3372,9 @@ function isAdminOpcode(op) {
2624
3372
  }
2625
3373
 
2626
3374
  // src/core/receipt.ts
2627
- var import_crypto4 = require("crypto");
3375
+ var import_crypto6 = require("crypto");
2628
3376
  function buildReceiptHash(prevHash, pid, actorId, intent, effect, ts) {
2629
- const h = (0, import_crypto4.createHash)("sha256");
3377
+ const h = (0, import_crypto6.createHash)("sha256");
2630
3378
  if (prevHash) h.update(prevHash);
2631
3379
  h.update(pid);
2632
3380
  h.update(Buffer.from(actorId, "utf8"));
@@ -2805,8 +3553,8 @@ function isTimestampValid(ts, skewSeconds = 120) {
2805
3553
  }
2806
3554
 
2807
3555
  // src/upload/axis-files.handlers.ts
2808
- var import_common3 = require("@nestjs/common");
2809
- var crypto2 = __toESM(require("crypto"));
3556
+ var import_common4 = require("@nestjs/common");
3557
+ var crypto3 = __toESM(require("crypto"));
2810
3558
 
2811
3559
  // src/upload/upload.tokens.ts
2812
3560
  var AXIS_UPLOAD_SESSION_STORE = "AXIS_UPLOAD_SESSION_STORE";
@@ -2818,7 +3566,7 @@ var AxisFilesDownloadHandler = class {
2818
3566
  constructor(sessions, files) {
2819
3567
  this.sessions = sessions;
2820
3568
  this.files = files;
2821
- this.logger = new import_common3.Logger(AxisFilesDownloadHandler.name);
3569
+ this.logger = new import_common4.Logger(AxisFilesDownloadHandler.name);
2822
3570
  this.name = "axis.files.download";
2823
3571
  this.open = true;
2824
3572
  this.description = "File download handler";
@@ -2883,16 +3631,16 @@ __decorateClass([
2883
3631
  ], AxisFilesDownloadHandler.prototype, "execute", 1);
2884
3632
  AxisFilesDownloadHandler = __decorateClass([
2885
3633
  Handler("axis.files.download"),
2886
- (0, import_common3.Injectable)(),
2887
- __decorateParam(0, (0, import_common3.Inject)(AXIS_UPLOAD_SESSION_STORE)),
2888
- __decorateParam(1, (0, import_common3.Inject)(AXIS_UPLOAD_FILE_STORE))
3634
+ (0, import_common4.Injectable)(),
3635
+ __decorateParam(0, (0, import_common4.Inject)(AXIS_UPLOAD_SESSION_STORE)),
3636
+ __decorateParam(1, (0, import_common4.Inject)(AXIS_UPLOAD_FILE_STORE))
2889
3637
  ], AxisFilesDownloadHandler);
2890
3638
  var AxisFilesFinalizeHandler = class {
2891
3639
  constructor(sessions, files, keyring) {
2892
3640
  this.sessions = sessions;
2893
3641
  this.files = files;
2894
3642
  this.keyring = keyring;
2895
- this.logger = new import_common3.Logger(AxisFilesFinalizeHandler.name);
3643
+ this.logger = new import_common4.Logger(AxisFilesFinalizeHandler.name);
2896
3644
  this.name = "axis.files.finalize";
2897
3645
  this.open = false;
2898
3646
  this.description = "File upload finalization handler";
@@ -2907,7 +3655,7 @@ var AxisFilesFinalizeHandler = class {
2907
3655
  if (!await this.files.hasTemp(fileId)) {
2908
3656
  throw new Error("CHUNKS_NOT_FOUND");
2909
3657
  }
2910
- const hash = crypto2.createHash("sha256");
3658
+ const hash = crypto3.createHash("sha256");
2911
3659
  const rs = this.files.createTempReadStream(fileId);
2912
3660
  for await (const chunk of rs) {
2913
3661
  hash.update(chunk);
@@ -2968,11 +3716,11 @@ __decorateClass([
2968
3716
  ], AxisFilesFinalizeHandler.prototype, "execute", 1);
2969
3717
  AxisFilesFinalizeHandler = __decorateClass([
2970
3718
  Handler("axis.files.finalize"),
2971
- (0, import_common3.Injectable)(),
2972
- __decorateParam(0, (0, import_common3.Inject)(AXIS_UPLOAD_SESSION_STORE)),
2973
- __decorateParam(1, (0, import_common3.Inject)(AXIS_UPLOAD_FILE_STORE)),
2974
- __decorateParam(2, (0, import_common3.Optional)()),
2975
- __decorateParam(2, (0, import_common3.Inject)(AXIS_UPLOAD_RECEIPT_SIGNER))
3719
+ (0, import_common4.Injectable)(),
3720
+ __decorateParam(0, (0, import_common4.Inject)(AXIS_UPLOAD_SESSION_STORE)),
3721
+ __decorateParam(1, (0, import_common4.Inject)(AXIS_UPLOAD_FILE_STORE)),
3722
+ __decorateParam(2, (0, import_common4.Optional)()),
3723
+ __decorateParam(2, (0, import_common4.Inject)(AXIS_UPLOAD_RECEIPT_SIGNER))
2976
3724
  ], AxisFilesFinalizeHandler);
2977
3725
 
2978
3726
  // src/upload/disk-upload-file.store.ts
@@ -3031,92 +3779,54 @@ var DiskUploadFileStore = class {
3031
3779
  }
3032
3780
  };
3033
3781
 
3034
- // src/core/index.ts
3035
- var core_exports = {};
3036
- __export(core_exports, {
3037
- AXIS_MAGIC: () => import_axis_protocol2.AXIS_MAGIC,
3038
- AXIS_VERSION: () => import_axis_protocol2.AXIS_VERSION,
3039
- AxisError: () => AxisError,
3040
- AxisFrameZ: () => AxisFrameZ,
3041
- BodyProfile: () => import_axis_protocol2.BodyProfile,
3042
- ERR_BAD_SIGNATURE: () => import_axis_protocol2.ERR_BAD_SIGNATURE,
3043
- ERR_CONTRACT_VIOLATION: () => import_axis_protocol2.ERR_CONTRACT_VIOLATION,
3044
- ERR_INVALID_PACKET: () => import_axis_protocol2.ERR_INVALID_PACKET,
3045
- ERR_REPLAY_DETECTED: () => import_axis_protocol2.ERR_REPLAY_DETECTED,
3046
- FLAG_BODY_TLV: () => import_axis_protocol2.FLAG_BODY_TLV,
3047
- FLAG_CHAIN_REQ: () => import_axis_protocol2.FLAG_CHAIN_REQ,
3048
- FLAG_HAS_WITNESS: () => import_axis_protocol2.FLAG_HAS_WITNESS,
3049
- MAX_BODY_LEN: () => import_axis_protocol2.MAX_BODY_LEN,
3050
- MAX_FRAME_LEN: () => import_axis_protocol2.MAX_FRAME_LEN,
3051
- MAX_HDR_LEN: () => import_axis_protocol2.MAX_HDR_LEN,
3052
- MAX_SIG_LEN: () => import_axis_protocol2.MAX_SIG_LEN,
3053
- NCERT_ALG: () => import_axis_protocol2.NCERT_ALG,
3054
- NCERT_EXP: () => import_axis_protocol2.NCERT_EXP,
3055
- NCERT_ISSUER_KID: () => import_axis_protocol2.NCERT_ISSUER_KID,
3056
- NCERT_KID: () => import_axis_protocol2.NCERT_KID,
3057
- NCERT_NBF: () => import_axis_protocol2.NCERT_NBF,
3058
- NCERT_NODE_ID: () => import_axis_protocol2.NCERT_NODE_ID,
3059
- NCERT_PAYLOAD: () => import_axis_protocol2.NCERT_PAYLOAD,
3060
- NCERT_PUB: () => import_axis_protocol2.NCERT_PUB,
3061
- NCERT_SCOPE: () => import_axis_protocol2.NCERT_SCOPE,
3062
- NCERT_SIG: () => import_axis_protocol2.NCERT_SIG,
3063
- PROOF_CAPSULE: () => import_axis_protocol2.PROOF_CAPSULE,
3064
- PROOF_JWT: () => import_axis_protocol2.PROOF_JWT,
3065
- PROOF_LOOM: () => import_axis_protocol2.PROOF_LOOM,
3066
- PROOF_MTLS: () => import_axis_protocol2.PROOF_MTLS,
3067
- PROOF_NONE: () => import_axis_protocol2.PROOF_NONE,
3068
- PROOF_WITNESS: () => import_axis_protocol2.PROOF_WITNESS,
3069
- ProofType: () => import_axis_protocol2.ProofType,
3070
- TLV: () => import_axis_protocol.TLV,
3071
- TLV_ACTOR_ID: () => import_axis_protocol2.TLV_ACTOR_ID,
3072
- TLV_AUD: () => import_axis_protocol2.TLV_AUD,
3073
- TLV_BODY_ARR: () => import_axis_protocol2.TLV_BODY_ARR,
3074
- TLV_BODY_OBJ: () => import_axis_protocol2.TLV_BODY_OBJ,
3075
- TLV_CAPSULE: () => import_axis_protocol2.TLV_CAPSULE,
3076
- TLV_EFFECT: () => import_axis_protocol2.TLV_EFFECT,
3077
- TLV_ERROR_CODE: () => import_axis_protocol2.TLV_ERROR_CODE,
3078
- TLV_ERROR_MSG: () => import_axis_protocol2.TLV_ERROR_MSG,
3079
- TLV_INDEX: () => import_axis_protocol2.TLV_INDEX,
3080
- TLV_INTENT: () => import_axis_protocol2.TLV_INTENT,
3081
- TLV_KID: () => import_axis_protocol2.TLV_KID,
3082
- TLV_LOOM_PRESENCE_ID: () => import_axis_protocol2.TLV_LOOM_PRESENCE_ID,
3083
- TLV_LOOM_THREAD_HASH: () => import_axis_protocol2.TLV_LOOM_THREAD_HASH,
3084
- TLV_LOOM_WRIT: () => import_axis_protocol2.TLV_LOOM_WRIT,
3085
- TLV_NODE: () => import_axis_protocol2.TLV_NODE,
3086
- TLV_NODE_CERT_HASH: () => import_axis_protocol2.TLV_NODE_CERT_HASH,
3087
- TLV_NODE_KID: () => import_axis_protocol2.TLV_NODE_KID,
3088
- TLV_NONCE: () => import_axis_protocol2.TLV_NONCE,
3089
- TLV_OFFSET: () => import_axis_protocol2.TLV_OFFSET,
3090
- TLV_OK: () => import_axis_protocol2.TLV_OK,
3091
- TLV_PID: () => import_axis_protocol2.TLV_PID,
3092
- TLV_PREV_HASH: () => import_axis_protocol2.TLV_PREV_HASH,
3093
- TLV_PROOF_REF: () => import_axis_protocol2.TLV_PROOF_REF,
3094
- TLV_PROOF_TYPE: () => import_axis_protocol2.TLV_PROOF_TYPE,
3095
- TLV_REALM: () => import_axis_protocol2.TLV_REALM,
3096
- TLV_RECEIPT_HASH: () => import_axis_protocol2.TLV_RECEIPT_HASH,
3097
- TLV_RID: () => import_axis_protocol2.TLV_RID,
3098
- TLV_SHA256_CHUNK: () => import_axis_protocol2.TLV_SHA256_CHUNK,
3099
- TLV_TRACE_ID: () => import_axis_protocol2.TLV_TRACE_ID,
3100
- TLV_TS: () => import_axis_protocol2.TLV_TS,
3101
- TLV_UPLOAD_ID: () => import_axis_protocol2.TLV_UPLOAD_ID,
3102
- computeReceiptHash: () => computeReceiptHash,
3103
- computeSignaturePayload: () => computeSignaturePayload,
3104
- decodeArray: () => import_axis_protocol.decodeArray,
3105
- decodeFrame: () => decodeFrame,
3106
- decodeObject: () => import_axis_protocol.decodeObject,
3107
- decodeTLVs: () => import_axis_protocol.decodeTLVs,
3108
- decodeTLVsList: () => import_axis_protocol.decodeTLVsList,
3109
- decodeVarint: () => import_axis_protocol3.decodeVarint,
3110
- encodeFrame: () => encodeFrame,
3111
- encodeTLVs: () => import_axis_protocol.encodeTLVs,
3112
- encodeVarint: () => import_axis_protocol3.encodeVarint,
3113
- generateEd25519KeyPair: () => generateEd25519KeyPair,
3114
- getSignTarget: () => getSignTarget,
3115
- sha256: () => sha256,
3116
- signFrame: () => signFrame,
3117
- varintLength: () => import_axis_protocol3.varintLength,
3118
- verifyFrameSignature: () => verifyFrameSignature
3119
- });
3782
+ // src/decorators/axis-request.decorator.ts
3783
+ var import_common5 = require("@nestjs/common");
3784
+ function resolveIp(req) {
3785
+ return req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.headers["x-real-ip"] || req.socket.remoteAddress || void 0;
3786
+ }
3787
+ var AxisRaw = (0, import_common5.createParamDecorator)(
3788
+ (_data, ctx) => {
3789
+ const req = ctx.switchToHttp().getRequest();
3790
+ return req.body;
3791
+ }
3792
+ );
3793
+ var AxisIp = (0, import_common5.createParamDecorator)(
3794
+ (_data, ctx) => {
3795
+ const req = ctx.switchToHttp().getRequest();
3796
+ return resolveIp(req);
3797
+ }
3798
+ );
3799
+ var AxisContext = (0, import_common5.createParamDecorator)(
3800
+ (_data, ctx) => {
3801
+ const req = ctx.switchToHttp().getRequest();
3802
+ const axisData = req.axis || {};
3803
+ return {
3804
+ raw: req.body,
3805
+ ip: resolveIp(req),
3806
+ preDecodeInput: axisData.preDecodeInput,
3807
+ frameBytesCount: axisData.frameBytesCount || 0
3808
+ };
3809
+ }
3810
+ );
3811
+ var AxisDemoPubkey = (0, import_common5.createParamDecorator)(
3812
+ (_data, ctx) => {
3813
+ if (process.env.NODE_ENV !== "development") return void 0;
3814
+ const req = ctx.switchToHttp().getRequest();
3815
+ return req.headers["x-demo-pubkey"];
3816
+ }
3817
+ );
3818
+ var AxisFrame3 = (0, import_common5.createParamDecorator)(
3819
+ (_data, ctx) => {
3820
+ const req = ctx.switchToHttp().getRequest();
3821
+ const decoded = req.axisDecoded;
3822
+ if (!decoded) {
3823
+ throw new Error(
3824
+ "@AxisFrame() requires AxisDecodeInterceptor on the route. Add @UseInterceptors(AxisDecodeInterceptor) to use this decorator."
3825
+ );
3826
+ }
3827
+ return decoded;
3828
+ }
3829
+ );
3120
3830
 
3121
3831
  // src/core/axis-error.ts
3122
3832
  var AxisError = class extends Error {
@@ -3129,348 +3839,246 @@ var AxisError = class extends Error {
3129
3839
  }
3130
3840
  };
3131
3841
 
3132
- // src/crypto/index.ts
3133
- var crypto_exports = {};
3134
- __export(crypto_exports, {
3135
- ProofVerificationService: () => ProofVerificationService,
3136
- b64urlDecode: () => b64urlDecode,
3137
- b64urlDecodeString: () => b64urlDecodeString,
3138
- b64urlEncode: () => b64urlEncode,
3139
- b64urlEncodeString: () => b64urlEncodeString,
3140
- canonicalJson: () => canonicalJson,
3141
- canonicalJsonExcluding: () => canonicalJsonExcluding
3142
- });
3143
-
3144
- // src/crypto/proof-verification.service.ts
3145
- var import_common4 = require("@nestjs/common");
3146
- var crypto3 = __toESM(require("crypto"));
3147
- var nacl = __toESM(require("tweetnacl"));
3148
- var ProofVerificationService = class {
3149
- constructor() {
3150
- this.logger = new import_common4.Logger(ProofVerificationService.name);
3151
- // Cache of registered device public keys (deviceId -> pubKey)
3152
- this.deviceKeys = /* @__PURE__ */ new Map();
3153
- // Cache of trusted mTLS certificate fingerprints
3154
- this.trustedCerts = /* @__PURE__ */ new Map();
3842
+ // src/engine/handler-discovery.service.ts
3843
+ var import_common6 = require("@nestjs/common");
3844
+ var HandlerDiscoveryService = class {
3845
+ constructor(discovery, scanner, router) {
3846
+ this.discovery = discovery;
3847
+ this.scanner = scanner;
3848
+ this.router = router;
3849
+ this.logger = new import_common6.Logger(HandlerDiscoveryService.name);
3155
3850
  }
3156
- /**
3157
- * Verifies an authentication proof based on its type.
3158
- *
3159
- * **Supported Types:**
3160
- * - 1 (CAPSULE): Delegated to `verifyCapsuleProof`
3161
- * - 2 (JWT): Verified by `verifyJWTProof`
3162
- * - 3 (MTLS_ID): Verified by `verifyMTLSProof`
3163
- * - 4 (DEVICE_SE): Verified by `verifyDeviceSEProof`
3164
- *
3165
- * @param {ProofType} proofType - The numeric AXIS proof type
3166
- * @param {Uint8Array} proofRef - The binary reference or token for the proof
3167
- * @param {Object} context - Additional metadata required for specific proof types
3168
- * @param {Uint8Array} [context.signTarget] - The canonical bytes that were signed (for Ed25519)
3169
- * @param {Uint8Array} [context.signature] - The signature to verify (for Ed25519)
3170
- * @param {MTLSContext} [context.mtls] - mTLS certificate data
3171
- * @param {DeviceSEContext} [context.deviceSE] - Device Secure Element information
3172
- * @returns {Promise<ProofVerificationResult>} The outcome of the verification
3173
- */
3174
- async verifyProof(proofType, proofRef, context) {
3175
- switch (proofType) {
3176
- case 1:
3177
- return this.verifyCapsuleProof(proofRef);
3178
- case 2:
3179
- return this.verifyJWTProof(proofRef);
3180
- case 3:
3181
- return this.verifyMTLSProof(context.mtls);
3182
- case 4:
3183
- return this.verifyDeviceSEProof(
3184
- context.signTarget,
3185
- context.signature,
3186
- context.deviceSE
3851
+ onModuleInit() {
3852
+ const providers = this.discovery.getProviders();
3853
+ let totalIntents = 0;
3854
+ for (const wrapper of providers) {
3855
+ const { instance, metatype } = wrapper;
3856
+ if (!instance || !metatype) continue;
3857
+ const handlerMeta = Reflect.getMetadata(HANDLER_METADATA_KEY, metatype);
3858
+ if (!handlerMeta) continue;
3859
+ const handlerName = handlerMeta.intent || metatype.name;
3860
+ const proto = Object.getPrototypeOf(instance);
3861
+ const methods = this.scanner.getAllMethodNames(proto);
3862
+ let registered = 0;
3863
+ const handlerSensors = Reflect.getMetadata(HANDLER_SENSORS_KEY, metatype) || [];
3864
+ for (const methodName of methods) {
3865
+ const meta = Reflect.getMetadata(
3866
+ INTENT_METADATA_KEY,
3867
+ proto,
3868
+ methodName
3869
+ );
3870
+ if (!meta?.intent) continue;
3871
+ if (!this.router.has(meta.intent)) {
3872
+ this.router.register(
3873
+ meta.intent,
3874
+ instance[methodName].bind(instance)
3875
+ );
3876
+ registered++;
3877
+ totalIntents++;
3878
+ }
3879
+ this.router.registerIntentMeta(
3880
+ meta.intent,
3881
+ proto,
3882
+ methodName,
3883
+ handlerSensors
3187
3884
  );
3188
- default:
3189
- return { valid: false, error: `Unknown proof type: ${proofType}` };
3190
- }
3191
- }
3192
- /**
3193
- * Verify CAPSULE proof (delegated to CapsuleService)
3194
- */
3195
- async verifyCapsuleProof(proofRef) {
3196
- const capsuleId = new TextDecoder().decode(proofRef);
3197
- return {
3198
- valid: true,
3199
- metadata: { capsuleId, requiresCapsuleValidation: true }
3200
- };
3201
- }
3202
- /**
3203
- * Verifies a JSON Web Token (JWT) proof.
3204
- *
3205
- * **Validation Logic:**
3206
- * 1. Decodes the token string.
3207
- * 2. Checks for valid 3-part JWT structure.
3208
- * 3. Validates `exp` (expiration) and `nbf` (not before) claims.
3209
- * 4. Extracts `actor_id` or `sub` as the identity.
3210
- *
3211
- * @param {Uint8Array} proofRef - Binary representation of the JWT string
3212
- * @returns {Promise<ProofVerificationResult>} Result including the actor identifier
3213
- */
3214
- async verifyJWTProof(proofRef) {
3215
- try {
3216
- const token = new TextDecoder().decode(proofRef);
3217
- const parts = token.split(".");
3218
- if (parts.length !== 3) {
3219
- return { valid: false, error: "Invalid JWT format" };
3220
- }
3221
- const header = JSON.parse(Buffer.from(parts[0], "base64url").toString());
3222
- const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
3223
- if (payload.exp && Date.now() / 1e3 > payload.exp) {
3224
- return { valid: false, error: "JWT expired" };
3225
3885
  }
3226
- if (payload.nbf && Date.now() / 1e3 < payload.nbf) {
3227
- return { valid: false, error: "JWT not yet valid" };
3886
+ if (registered > 0) {
3887
+ this.logger.log(
3888
+ `Auto-registered ${registered} intents from ${handlerName}`
3889
+ );
3228
3890
  }
3229
- return {
3230
- valid: true,
3231
- actorId: payload.sub || payload.actor_id,
3232
- metadata: { iss: payload.iss, scope: payload.scope }
3233
- };
3234
- } catch (e) {
3235
- const message = e instanceof Error ? e.message : "Unknown error";
3236
- return { valid: false, error: `JWT parse error: ${message}` };
3237
3891
  }
3892
+ this.logger.log(
3893
+ `Handler discovery complete: ${totalIntents} intents auto-registered`
3894
+ );
3238
3895
  }
3239
- /**
3240
- * Verify mTLS client certificate proof
3241
- */
3242
- async verifyMTLSProof(mtls) {
3243
- if (!mtls) {
3244
- return { valid: false, error: "No mTLS context provided" };
3245
- }
3246
- if (!mtls.verified) {
3247
- return { valid: false, error: "mTLS not verified by TLS terminator" };
3248
- }
3249
- if (mtls.clientCertFingerprint) {
3250
- const trusted = this.trustedCerts.get(mtls.clientCertFingerprint);
3251
- if (trusted) {
3252
- return {
3253
- valid: true,
3254
- actorId: trusted.actorId,
3255
- metadata: {
3256
- fingerprint: mtls.clientCertFingerprint,
3257
- subject: mtls.clientCertSubject
3258
- }
3259
- };
3896
+ };
3897
+ HandlerDiscoveryService = __decorateClass([
3898
+ (0, import_common6.Injectable)()
3899
+ ], HandlerDiscoveryService);
3900
+
3901
+ // src/engine/sensor-discovery.service.ts
3902
+ var import_common7 = require("@nestjs/common");
3903
+ var SensorDiscoveryService = class {
3904
+ constructor(discovery, reflector, registry) {
3905
+ this.discovery = discovery;
3906
+ this.reflector = reflector;
3907
+ this.registry = registry;
3908
+ this.logger = new import_common7.Logger(SensorDiscoveryService.name);
3909
+ }
3910
+ onApplicationBootstrap() {
3911
+ const providers = this.discovery.getProviders();
3912
+ let count = 0;
3913
+ for (const wrapper of providers) {
3914
+ const { instance } = wrapper;
3915
+ if (!instance || !instance.constructor) continue;
3916
+ const meta = this.reflector.get(
3917
+ SENSOR_METADATA_KEY,
3918
+ instance.constructor
3919
+ );
3920
+ if (!meta) continue;
3921
+ const sensor = instance;
3922
+ if (!sensor.name || sensor.order === void 0) {
3923
+ this.logger.warn(
3924
+ `@Sensor() on ${instance.constructor.name} missing name or order \u2014 skipped`
3925
+ );
3926
+ continue;
3260
3927
  }
3261
- }
3262
- if (mtls.clientCertSubject) {
3263
- const cnMatch = mtls.clientCertSubject.match(/CN=([^,]+)/);
3264
- if (cnMatch) {
3265
- return {
3266
- valid: true,
3267
- actorId: cnMatch[1],
3268
- metadata: {
3269
- subject: mtls.clientCertSubject,
3270
- issuer: mtls.clientCertIssuer
3271
- }
3272
- };
3928
+ if (!sensor.phase) {
3929
+ const decoratorPhase = meta !== true ? meta.phase : void 0;
3930
+ sensor.phase = decoratorPhase ?? (sensor.order < PRE_DECODE_BOUNDARY ? "PRE_DECODE" : "POST_DECODE");
3273
3931
  }
3932
+ this.registry.register(sensor);
3933
+ count++;
3274
3934
  }
3275
- return { valid: false, error: "Could not extract actor from certificate" };
3935
+ this.logger.log(`Auto-registered ${count} sensors via @Sensor()`);
3936
+ }
3937
+ };
3938
+ SensorDiscoveryService = __decorateClass([
3939
+ (0, import_common7.Injectable)()
3940
+ ], SensorDiscoveryService);
3941
+
3942
+ // src/engine/registry/sensor.registry.ts
3943
+ var import_common8 = require("@nestjs/common");
3944
+ var SensorRegistry = class {
3945
+ constructor(configService) {
3946
+ this.configService = configService;
3947
+ this.sensors = [];
3948
+ this.logger = new import_common8.Logger(SensorRegistry.name);
3276
3949
  }
3277
3950
  /**
3278
- * Verify Device Secure Element signature
3951
+ * Registers a new sensor in the registry.
3952
+ *
3953
+ * Validates that:
3954
+ * - AxisSensor has a unique name
3955
+ * - AxisSensor has an order field
3956
+ * - Pre-decode sensors have order < 40
3957
+ * - Post-decode sensors have order >= 40
3958
+ *
3959
+ * @param {AxisSensor} sensor - The sensor instance to register
3960
+ * @throws Error if validation fails
3279
3961
  */
3280
- async verifyDeviceSEProof(signTarget, signature, deviceSE) {
3281
- if (!deviceSE || !signTarget || !signature) {
3282
- return { valid: false, error: "Missing Device SE context" };
3962
+ register(sensor) {
3963
+ if (!sensor.name) {
3964
+ throw new Error("AxisSensor must have a name");
3283
3965
  }
3284
- let publicKey = deviceSE.publicKey;
3285
- const registeredKey = this.deviceKeys.get(deviceSE.deviceId);
3286
- if (registeredKey) {
3287
- publicKey = registeredKey;
3966
+ const enabledSensorsStr = this.configService.get("ENABLED_SENSORS");
3967
+ const disabledSensorsStr = this.configService.get("DISABLED_SENSORS");
3968
+ const enabledSensors = enabledSensorsStr ? enabledSensorsStr.split(",").map((s) => s.trim()) : null;
3969
+ const disabledSensors = disabledSensorsStr ? disabledSensorsStr.split(",").map((s) => s.trim()) : [];
3970
+ if (enabledSensors && !enabledSensors.includes(sensor.name)) {
3971
+ this.logger.log(`Skipping disabled sensor (not in ENABLED_SENSORS): ${sensor.name}`);
3972
+ return;
3288
3973
  }
3289
- if (!publicKey || publicKey.length !== 32) {
3290
- return {
3291
- valid: false,
3292
- error: "Invalid or unregistered device public key"
3293
- };
3974
+ if (disabledSensors.includes(sensor.name)) {
3975
+ this.logger.log(`Skipping disabled sensor (in DISABLED_SENSORS): ${sensor.name}`);
3976
+ return;
3294
3977
  }
3295
- try {
3296
- const valid = nacl.sign.detached.verify(signTarget, signature, publicKey);
3297
- if (!valid) {
3298
- return { valid: false, error: "Device signature verification failed" };
3299
- }
3300
- return {
3301
- valid: true,
3302
- actorId: deviceSE.deviceId,
3303
- metadata: { deviceId: deviceSE.deviceId, proofType: "DEVICE_SE" }
3304
- };
3305
- } catch (e) {
3306
- const message = e instanceof Error ? e.message : "Unknown error";
3307
- return {
3308
- valid: false,
3309
- error: `Signature verification error: ${message}`
3310
- };
3978
+ if (sensor.order === void 0) {
3979
+ throw new Error(`AxisSensor "${sensor.name}" must have an order field`);
3980
+ }
3981
+ const isPreDecodeSensor = this.isPreDecodeSensor(sensor);
3982
+ const isPostDecodeSensor = this.isPostDecodeSensor(sensor);
3983
+ if (isPreDecodeSensor && sensor.order >= 40) {
3984
+ this.logger.warn(
3985
+ `AxisSensor "${sensor.name}" is marked as PRE_DECODE but has order ${sensor.order} (should be < 40)`
3986
+ );
3987
+ }
3988
+ if (isPostDecodeSensor && sensor.order < 40) {
3989
+ this.logger.warn(
3990
+ `AxisSensor "${sensor.name}" is marked as POST_DECODE but has order ${sensor.order} (should be >= 40)`
3991
+ );
3311
3992
  }
3993
+ this.sensors.push(sensor);
3994
+ const phaseLabel = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase || "UNKNOWN";
3995
+ this.logger.debug(
3996
+ `Registered sensor: ${sensor.name} (order: ${sensor.order}, phase: ${phaseLabel})`
3997
+ );
3312
3998
  }
3313
3999
  /**
3314
- * Registers a public key for a trusted device.
3315
- * This key will be used for future `DEVICE_SE` proof verifications.
4000
+ * Returns all registered sensors, sorted by their execution order.
3316
4001
  *
3317
- * @param {string} deviceId - Unique identifier for the device
3318
- * @param {Uint8Array} publicKey - 32-byte Ed25519 public key
3319
- * @throws {Error} If the public key is not 32 bytes
4002
+ * @returns {AxisSensor[]} A sorted array of sensors
3320
4003
  */
3321
- registerDeviceKey(deviceId, publicKey) {
3322
- if (publicKey.length !== 32) {
3323
- throw new Error("Device public key must be 32 bytes (Ed25519)");
3324
- }
3325
- this.deviceKeys.set(deviceId, publicKey);
3326
- this.logger.log(`Registered device key for ${deviceId}`);
4004
+ list() {
4005
+ return [...this.sensors].sort(
4006
+ (a, b) => (a.order ?? 999) - (b.order ?? 999)
4007
+ );
3327
4008
  }
3328
4009
  /**
3329
- * Unregister a device
4010
+ * Returns only pre-decode sensors (order < 40).
4011
+ * These sensors run in middleware on raw bytes before frame decoding.
4012
+ *
4013
+ * @returns {AxisPreSensor[]} Pre-decode sensors sorted by order
3330
4014
  */
3331
- unregisterDevice(deviceId) {
3332
- return this.deviceKeys.delete(deviceId);
4015
+ getPreDecodeSensors() {
4016
+ return this.list().filter((s) => (s.order ?? 999) < 40);
3333
4017
  }
3334
4018
  /**
3335
- * Registers a trusted mTLS certificate fingerprint and associates it with an actor.
4019
+ * Returns only post-decode sensors (order >= 40).
4020
+ * These sensors run in the controller on fully decoded frames.
3336
4021
  *
3337
- * @param {string} fingerprint - SHA-256 fingerprint of the client certificate
3338
- * @param {string} actorId - The actor to associate with this certificate
4022
+ * @returns {AxisPostSensor[]} Post-decode sensors sorted by order
3339
4023
  */
3340
- registerMTLSCert(fingerprint, actorId) {
3341
- this.trustedCerts.set(fingerprint, { actorId, issuedAt: Date.now() });
3342
- this.logger.log(`Registered mTLS cert ${fingerprint} for actor ${actorId}`);
4024
+ getPostDecodeSensors() {
4025
+ return this.list().filter(
4026
+ (s) => (s.order ?? 999) >= 40
4027
+ );
3343
4028
  }
3344
4029
  /**
3345
- * Revoke an mTLS certificate
4030
+ * Helper: Check if a sensor is a pre-decode sensor.
4031
+ *
4032
+ * @private
4033
+ * @param {AxisSensor} sensor - The sensor to check
4034
+ * @returns {boolean} True if sensor is pre-decode
3346
4035
  */
3347
- revokeMTLSCert(fingerprint) {
3348
- return this.trustedCerts.delete(fingerprint);
4036
+ isPreDecodeSensor(sensor) {
4037
+ const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
4038
+ return phase === "PRE_DECODE" || (sensor.order ?? 999) < 40;
3349
4039
  }
3350
4040
  /**
3351
- * Calculate certificate fingerprint (SHA-256)
4041
+ * Helper: Check if a sensor is a post-decode sensor.
4042
+ *
4043
+ * @private
4044
+ * @param {AxisSensor} sensor - The sensor to check
4045
+ * @returns {boolean} True if sensor is post-decode
3352
4046
  */
3353
- static calculateFingerprint(certPem) {
3354
- const der = Buffer.from(
3355
- certPem.replace(/-----BEGIN CERTIFICATE-----/, "").replace(/-----END CERTIFICATE-----/, "").replace(/\s/g, ""),
3356
- "base64"
3357
- );
3358
- return crypto3.createHash("sha256").update(der).digest("hex");
4047
+ isPostDecodeSensor(sensor) {
4048
+ const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
4049
+ return phase === "POST_DECODE" || (sensor.order ?? 999) >= 40;
4050
+ }
4051
+ /**
4052
+ * Returns sensor count by phase.
4053
+ * Useful for diagnostics and monitoring.
4054
+ *
4055
+ * @returns {{preDecodeCount: number, postDecodeCount: number}}
4056
+ */
4057
+ getSensorCountByPhase() {
4058
+ return {
4059
+ preDecodeCount: this.getPreDecodeSensors().length,
4060
+ postDecodeCount: this.getPostDecodeSensors().length
4061
+ };
4062
+ }
4063
+ /**
4064
+ * Clears all registered sensors.
4065
+ * Useful for testing.
4066
+ *
4067
+ * @internal
4068
+ */
4069
+ clear() {
4070
+ this.sensors = [];
3359
4071
  }
3360
4072
  };
3361
- ProofVerificationService = __decorateClass([
3362
- (0, import_common4.Injectable)()
3363
- ], ProofVerificationService);
3364
-
3365
- // src/decorators/index.ts
3366
- var decorators_exports = {};
3367
- __export(decorators_exports, {
3368
- AxisContext: () => AxisContext,
3369
- AxisDemoPubkey: () => AxisDemoPubkey,
3370
- AxisFrame: () => AxisFrame3,
3371
- AxisIp: () => AxisIp,
3372
- AxisRaw: () => AxisRaw,
3373
- HANDLER_METADATA_KEY: () => HANDLER_METADATA_KEY,
3374
- Handler: () => Handler,
3375
- INTENT_BODY_KEY: () => INTENT_BODY_KEY,
3376
- INTENT_METADATA_KEY: () => INTENT_METADATA_KEY,
3377
- INTENT_ROUTES_KEY: () => INTENT_ROUTES_KEY,
3378
- INTENT_SENSORS_KEY: () => INTENT_SENSORS_KEY,
3379
- Intent: () => Intent,
3380
- IntentBody: () => IntentBody,
3381
- IntentSensors: () => IntentSensors,
3382
- SENSOR_METADATA_KEY: () => SENSOR_METADATA_KEY,
3383
- Sensor: () => Sensor,
3384
- TLV_FIELDS_KEY: () => TLV_FIELDS_KEY,
3385
- TLV_VALIDATORS_KEY: () => TLV_VALIDATORS_KEY,
3386
- TlvEnum: () => TlvEnum,
3387
- TlvField: () => TlvField,
3388
- TlvMinLen: () => TlvMinLen,
3389
- TlvRange: () => TlvRange,
3390
- TlvUtf8Pattern: () => TlvUtf8Pattern,
3391
- TlvValidate: () => TlvValidate,
3392
- buildDtoDecoder: () => buildDtoDecoder,
3393
- extractDtoSchema: () => extractDtoSchema
3394
- });
3395
-
3396
- // src/decorators/axis-request.decorator.ts
3397
- var import_common5 = require("@nestjs/common");
3398
- function resolveIp(req) {
3399
- return req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.headers["x-real-ip"] || req.socket.remoteAddress || void 0;
3400
- }
3401
- var AxisRaw = (0, import_common5.createParamDecorator)(
3402
- (_data, ctx) => {
3403
- const req = ctx.switchToHttp().getRequest();
3404
- return req.body;
3405
- }
3406
- );
3407
- var AxisIp = (0, import_common5.createParamDecorator)(
3408
- (_data, ctx) => {
3409
- const req = ctx.switchToHttp().getRequest();
3410
- return resolveIp(req);
3411
- }
3412
- );
3413
- var AxisContext = (0, import_common5.createParamDecorator)(
3414
- (_data, ctx) => {
3415
- const req = ctx.switchToHttp().getRequest();
3416
- const axisData = req.axis || {};
3417
- return {
3418
- raw: req.body,
3419
- ip: resolveIp(req),
3420
- preDecodeInput: axisData.preDecodeInput,
3421
- frameBytesCount: axisData.frameBytesCount || 0
3422
- };
3423
- }
3424
- );
3425
- var AxisDemoPubkey = (0, import_common5.createParamDecorator)(
3426
- (_data, ctx) => {
3427
- if (process.env.NODE_ENV !== "development") return void 0;
3428
- const req = ctx.switchToHttp().getRequest();
3429
- return req.headers["x-demo-pubkey"];
3430
- }
3431
- );
3432
- var AxisFrame3 = (0, import_common5.createParamDecorator)(
3433
- (_data, ctx) => {
3434
- const req = ctx.switchToHttp().getRequest();
3435
- const decoded = req.axisDecoded;
3436
- if (!decoded) {
3437
- throw new Error(
3438
- "@AxisFrame() requires AxisDecodeInterceptor on the route. Add @UseInterceptors(AxisDecodeInterceptor) to use this decorator."
3439
- );
3440
- }
3441
- return decoded;
3442
- }
3443
- );
3444
-
3445
- // src/decorators/sensor.decorator.ts
3446
- var import_common6 = require("@nestjs/common");
3447
- var SENSOR_METADATA_KEY = "axis:sensor";
3448
- function Sensor(options) {
3449
- return (0, import_common6.SetMetadata)(SENSOR_METADATA_KEY, options ?? true);
3450
- }
3451
-
3452
- // src/engine/index.ts
3453
- var engine_exports = {};
3454
- __export(engine_exports, {
3455
- BAND: () => BAND,
3456
- HandlerDiscoveryService: () => HandlerDiscoveryService,
3457
- IntentRouter: () => IntentRouter,
3458
- PRE_DECODE_BOUNDARY: () => PRE_DECODE_BOUNDARY,
3459
- SensorDiscoveryService: () => SensorDiscoveryService,
3460
- SensorRegistry: () => SensorRegistry,
3461
- createObservation: () => createObservation,
3462
- endStage: () => endStage,
3463
- finalizeObservation: () => finalizeObservation,
3464
- observation: () => observation_exports,
3465
- recordSensor: () => recordSensor,
3466
- startStage: () => startStage
3467
- });
4073
+ SensorRegistry = __decorateClass([
4074
+ (0, import_common8.Injectable)()
4075
+ ], SensorRegistry);
3468
4076
 
3469
4077
  // src/engine/axis-observation.ts
3470
- var import_crypto5 = require("crypto");
4078
+ var import_crypto7 = require("crypto");
3471
4079
  function createObservation(transport, ip) {
3472
4080
  return {
3473
- id: (0, import_crypto5.randomBytes)(16).toString("hex"),
4081
+ id: (0, import_crypto7.randomBytes)(16).toString("hex"),
3474
4082
  startMs: Date.now(),
3475
4083
  transport,
3476
4084
  ip,
@@ -3502,251 +4110,1186 @@ function finalizeObservation(obs, decision, statusCode, resultCode) {
3502
4110
  if (resultCode) obs.resultCode = resultCode;
3503
4111
  }
3504
4112
 
3505
- // src/engine/handler-discovery.service.ts
3506
- var import_common7 = require("@nestjs/common");
3507
- var HandlerDiscoveryService = class {
3508
- constructor(discovery, scanner, router) {
3509
- this.discovery = discovery;
3510
- this.scanner = scanner;
3511
- this.router = router;
3512
- this.logger = new import_common7.Logger(HandlerDiscoveryService.name);
4113
+ // src/security/axis-sensor-chain.service.ts
4114
+ var import_common9 = require("@nestjs/common");
4115
+ var AxisSensorChainService = class {
4116
+ constructor(registry) {
4117
+ this.registry = registry;
3513
4118
  }
3514
- onModuleInit() {
3515
- const providers = this.discovery.getProviders();
3516
- let totalIntents = 0;
3517
- for (const wrapper of providers) {
3518
- const { instance, metatype } = wrapper;
3519
- if (!instance || !metatype) continue;
3520
- const handlerMeta = Reflect.getMetadata(HANDLER_METADATA_KEY, metatype);
3521
- if (!handlerMeta) continue;
3522
- const handlerName = handlerMeta.intent || metatype.name;
3523
- const proto = Object.getPrototypeOf(instance);
3524
- const methods = this.scanner.getAllMethodNames(proto);
3525
- let registered = 0;
3526
- for (const methodName of methods) {
3527
- const meta = Reflect.getMetadata(
3528
- INTENT_METADATA_KEY,
3529
- proto,
3530
- methodName
3531
- );
3532
- if (!meta?.intent) continue;
3533
- if (!this.router.has(meta.intent)) {
3534
- this.router.register(
3535
- meta.intent,
3536
- instance[methodName].bind(instance)
4119
+ /**
4120
+ * Evaluate all applicable sensors based on phase.
4121
+ */
4122
+ async evaluate(input, phase = "POST_DECODE", baseDecision) {
4123
+ if (phase === "PRE_DECODE") {
4124
+ return this.evaluateSensors(this.registry.getPreDecodeSensors(), input);
4125
+ }
4126
+ if (phase === "BOTH") {
4127
+ const rawPreResult = await this.evaluateSensors(
4128
+ this.registry.getPreDecodeSensors(),
4129
+ input
4130
+ );
4131
+ const preResult = normalizeSensorDecision(rawPreResult);
4132
+ if (!preResult.allow) return rawPreResult;
4133
+ return this.evaluateSensors(
4134
+ this.registry.getPostDecodeSensors(),
4135
+ input,
4136
+ rawPreResult
4137
+ );
4138
+ }
4139
+ return this.evaluateSensors(
4140
+ this.registry.getPostDecodeSensors(),
4141
+ input,
4142
+ baseDecision
4143
+ );
4144
+ }
4145
+ /** Run only pre-decode sensors. */
4146
+ async evaluatePre(input) {
4147
+ return this.evaluateSensors(this.registry.getPreDecodeSensors(), input);
4148
+ }
4149
+ /** Run only post-decode sensors. */
4150
+ async evaluatePost(input, baseDecision) {
4151
+ return this.evaluateSensors(
4152
+ this.registry.getPostDecodeSensors(),
4153
+ input,
4154
+ baseDecision
4155
+ );
4156
+ }
4157
+ async evaluateSensors(sensors, input, baseDecision) {
4158
+ const relevantSensors = sensors.filter(
4159
+ (s) => !s.supports || s.supports(input)
4160
+ );
4161
+ const normalizedBase = baseDecision ? normalizeSensorDecision(baseDecision) : void 0;
4162
+ let riskScore = normalizedBase?.riskScore ?? 0;
4163
+ const reasons = normalizedBase?.reasons ? [...normalizedBase.reasons] : [];
4164
+ const tags = normalizedBase?.tags ? { ...normalizedBase.tags } : {};
4165
+ let expSecondsMax = normalizedBase?.tighten?.expSecondsMax;
4166
+ let constraintsPatch = normalizedBase?.tighten?.constraintsPatch ? { ...normalizedBase.tighten.constraintsPatch } : {};
4167
+ for (const sensor of relevantSensors) {
4168
+ try {
4169
+ const t0 = Date.now();
4170
+ const rawDecision = await sensor.run(input);
4171
+ const elapsed = Date.now() - t0;
4172
+ const decision = normalizeSensorDecision(rawDecision);
4173
+ const obs = input.metadata?.observation;
4174
+ if (obs) {
4175
+ recordSensor(
4176
+ obs,
4177
+ sensor.name,
4178
+ decision.allow,
4179
+ decision.riskScore,
4180
+ elapsed,
4181
+ decision.reasons,
4182
+ decision.allow ? void 0 : decision.code
3537
4183
  );
3538
- registered++;
3539
- totalIntents++;
3540
4184
  }
3541
- this.router.registerIntentMeta(meta.intent, proto, methodName);
3542
- }
3543
- if (registered > 0) {
3544
- this.logger.log(
3545
- `Auto-registered ${registered} intents from ${handlerName}`
3546
- );
4185
+ if (!decision.allow) {
4186
+ return {
4187
+ allow: false,
4188
+ riskScore: Math.min(100, riskScore + decision.riskScore),
4189
+ reasons: [...reasons, ...decision.reasons],
4190
+ tags
4191
+ };
4192
+ }
4193
+ riskScore = Math.min(100, riskScore + decision.riskScore);
4194
+ reasons.push(...decision.reasons);
4195
+ if (decision.tags) {
4196
+ Object.assign(tags, decision.tags);
4197
+ }
4198
+ if (decision.tighten?.expSecondsMax !== void 0) {
4199
+ expSecondsMax = expSecondsMax === void 0 ? decision.tighten.expSecondsMax : Math.min(expSecondsMax, decision.tighten.expSecondsMax);
4200
+ }
4201
+ if (decision.tighten?.constraintsPatch) {
4202
+ constraintsPatch = {
4203
+ ...constraintsPatch,
4204
+ ...decision.tighten.constraintsPatch
4205
+ };
4206
+ }
4207
+ } catch (error) {
4208
+ console.error(`[AXIS][SENSOR] ${sensor.name} failed:`, error);
4209
+ const obs = input.metadata?.observation;
4210
+ if (obs) {
4211
+ recordSensor(obs, sensor.name, false, 100, 0, [
4212
+ `sensor_error:${sensor.name}`
4213
+ ]);
4214
+ }
4215
+ return {
4216
+ allow: false,
4217
+ riskScore: 100,
4218
+ reasons: [`sensor_error:${sensor.name}`]
4219
+ };
3547
4220
  }
3548
4221
  }
3549
- this.logger.log(
3550
- `Handler discovery complete: ${totalIntents} intents auto-registered`
3551
- );
4222
+ const tightenPatch = Object.keys(constraintsPatch).length > 0 ? constraintsPatch : void 0;
4223
+ return {
4224
+ allow: true,
4225
+ riskScore,
4226
+ reasons,
4227
+ tags,
4228
+ tighten: expSecondsMax !== void 0 || tightenPatch ? {
4229
+ expSecondsMax,
4230
+ constraintsPatch: tightenPatch
4231
+ } : void 0
4232
+ };
3552
4233
  }
3553
4234
  };
3554
- HandlerDiscoveryService = __decorateClass([
3555
- (0, import_common7.Injectable)()
3556
- ], HandlerDiscoveryService);
4235
+ AxisSensorChainService = __decorateClass([
4236
+ (0, import_common9.Injectable)()
4237
+ ], AxisSensorChainService);
3557
4238
 
3558
- // src/engine/sensor-bands.ts
3559
- var BAND = {
3560
- /** Pre-decode: raw byte validation, geo, budget, magic */
3561
- WIRE: 0,
3562
- /** Post-decode: identity resolution, capsule, proof */
3563
- IDENTITY: 40,
3564
- /** Post-decode: authorization, signature, rate limiting */
3565
- POLICY: 90,
3566
- /** Post-decode: content validation, TLV, schema, files */
3567
- CONTENT: 140,
3568
- /** Post-decode: business logic sensors, streams, WS */
3569
- BUSINESS: 200,
3570
- /** Post-decode: audit, logging (always last) */
3571
- AUDIT: 900
3572
- };
3573
- var PRE_DECODE_BOUNDARY = 40;
4239
+ // src/cce/index.ts
4240
+ var cce_exports = {};
4241
+ __export(cce_exports, {
4242
+ CCE_AES_KEY_BYTES: () => CCE_AES_KEY_BYTES,
4243
+ CCE_DERIVATION: () => CCE_DERIVATION,
4244
+ CCE_ERROR: () => CCE_ERROR,
4245
+ CCE_IV_BYTES: () => CCE_IV_BYTES,
4246
+ CCE_NONCE_BYTES: () => CCE_NONCE_BYTES,
4247
+ CCE_PROTOCOL_VERSION: () => CCE_PROTOCOL_VERSION,
4248
+ CCE_TAG_BYTES: () => CCE_TAG_BYTES,
4249
+ CceAudienceIntentBindingSensor: () => CceAudienceIntentBindingSensor,
4250
+ CceCapsuleVerificationSensor: () => CceCapsuleVerificationSensor,
4251
+ CceClientSignatureSensor: () => CceClientSignatureSensor,
4252
+ CceEnvelopeValidationSensor: () => CceEnvelopeValidationSensor,
4253
+ CceError: () => CceError,
4254
+ CcePayloadDecryptionSensor: () => CcePayloadDecryptionSensor,
4255
+ CceReplayProtectionSensor: () => CceReplayProtectionSensor,
4256
+ CceTpsWindowSensor: () => CceTpsWindowSensor,
4257
+ InMemoryCceReplayStore: () => InMemoryCceReplayStore,
4258
+ InMemoryCceWitnessStore: () => InMemoryCceWitnessStore,
4259
+ aesGcmDecrypt: () => aesGcmDecrypt,
4260
+ aesGcmEncrypt: () => aesGcmEncrypt,
4261
+ base64UrlDecode: () => base64UrlDecode,
4262
+ base64UrlEncode: () => base64UrlEncode,
4263
+ buildCceErrorResponse: () => buildCceErrorResponse,
4264
+ buildCceResponse: () => buildCceResponse,
4265
+ buildExecutionContext: () => buildExecutionContext,
4266
+ buildWitnessRecord: () => buildWitnessRecord,
4267
+ deriveRequestExecutionKey: () => deriveRequestExecutionKey,
4268
+ deriveResponseExecutionKey: () => deriveResponseExecutionKey,
4269
+ deriveWitnessKey: () => deriveWitnessKey,
4270
+ executeCcePipeline: () => executeCcePipeline,
4271
+ extractVerificationState: () => extractVerificationState,
4272
+ generateAesKey: () => generateAesKey,
4273
+ generateCceNonce: () => generateCceNonce,
4274
+ generateIv: () => generateIv,
4275
+ hashPayload: () => hashPayload,
4276
+ nodeAesGcmProvider: () => nodeAesGcmProvider
4277
+ });
3574
4278
 
3575
- // src/engine/sensor-discovery.service.ts
3576
- var import_common8 = require("@nestjs/common");
3577
- var SensorDiscoveryService = class {
3578
- constructor(discovery, reflector, registry) {
3579
- this.discovery = discovery;
3580
- this.reflector = reflector;
3581
- this.registry = registry;
3582
- this.logger = new import_common8.Logger(SensorDiscoveryService.name);
4279
+ // src/cce/sensors/cce-envelope-validation.sensor.ts
4280
+ var REQUIRED_FIELDS = [
4281
+ "ver",
4282
+ "request_id",
4283
+ "correlation_id",
4284
+ "client_kid",
4285
+ "capsule",
4286
+ "encrypted_key",
4287
+ "encrypted_payload",
4288
+ "request_nonce",
4289
+ "client_sig",
4290
+ "algorithms"
4291
+ ];
4292
+ var CceEnvelopeValidationSensor = class {
4293
+ constructor() {
4294
+ this.name = "cce.envelope.validation";
4295
+ this.order = 5;
4296
+ this.phase = "PRE_DECODE";
3583
4297
  }
3584
- onApplicationBootstrap() {
3585
- const providers = this.discovery.getProviders();
3586
- let count = 0;
3587
- for (const wrapper of providers) {
3588
- const { instance } = wrapper;
3589
- if (!instance || !instance.constructor) continue;
3590
- const meta = this.reflector.get(
3591
- SENSOR_METADATA_KEY,
3592
- instance.constructor
3593
- );
3594
- if (!meta) continue;
3595
- const sensor = instance;
3596
- if (!sensor.name || sensor.order === void 0) {
3597
- this.logger.warn(
3598
- `@Sensor() on ${instance.constructor.name} missing name or order \u2014 skipped`
3599
- );
3600
- continue;
3601
- }
3602
- if (!sensor.phase) {
3603
- const decoratorPhase = meta !== true ? meta.phase : void 0;
3604
- sensor.phase = decoratorPhase ?? (sensor.order < PRE_DECODE_BOUNDARY ? "PRE_DECODE" : "POST_DECODE");
4298
+ supports(input) {
4299
+ return input.metadata?.cce === true || input.metadata?.contentType === "application/axis-cce";
4300
+ }
4301
+ async run(input) {
4302
+ const envelope = input.metadata?.cceEnvelope;
4303
+ if (!envelope) {
4304
+ return {
4305
+ allow: false,
4306
+ riskScore: 100,
4307
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
4308
+ code: CCE_ERROR.INVALID_ENVELOPE
4309
+ };
4310
+ }
4311
+ for (const field of REQUIRED_FIELDS) {
4312
+ if (envelope[field] === void 0 || envelope[field] === null) {
4313
+ return {
4314
+ allow: false,
4315
+ riskScore: 100,
4316
+ reasons: [`${CCE_ERROR.INVALID_ENVELOPE}: missing ${field}`],
4317
+ code: CCE_ERROR.INVALID_ENVELOPE
4318
+ };
3605
4319
  }
3606
- this.registry.register(sensor);
3607
- count++;
3608
4320
  }
3609
- this.logger.log(`Auto-registered ${count} sensors via @Sensor()`);
4321
+ if (envelope.ver !== CCE_PROTOCOL_VERSION) {
4322
+ return {
4323
+ allow: false,
4324
+ riskScore: 100,
4325
+ reasons: [`${CCE_ERROR.UNSUPPORTED_VERSION}: ${envelope.ver}`],
4326
+ code: CCE_ERROR.UNSUPPORTED_VERSION
4327
+ };
4328
+ }
4329
+ if (!/^[0-9a-f]+$/i.test(envelope.request_nonce)) {
4330
+ return {
4331
+ allow: false,
4332
+ riskScore: 100,
4333
+ reasons: [
4334
+ `${CCE_ERROR.INVALID_ENVELOPE}: invalid request_nonce format`
4335
+ ],
4336
+ code: CCE_ERROR.INVALID_ENVELOPE
4337
+ };
4338
+ }
4339
+ if (envelope.request_nonce.length !== CCE_NONCE_BYTES * 2) {
4340
+ return {
4341
+ allow: false,
4342
+ riskScore: 100,
4343
+ reasons: [`${CCE_ERROR.INVALID_ENVELOPE}: request_nonce wrong length`],
4344
+ code: CCE_ERROR.INVALID_ENVELOPE
4345
+ };
4346
+ }
4347
+ const capsule = envelope.capsule;
4348
+ if (!capsule.capsule_id || !capsule.ver || !capsule.sub || !capsule.kid || !capsule.intent || !capsule.aud || !capsule.issuer_sig) {
4349
+ return {
4350
+ allow: false,
4351
+ riskScore: 100,
4352
+ reasons: [`${CCE_ERROR.MISSING_CAPSULE}: incomplete capsule claims`],
4353
+ code: CCE_ERROR.MISSING_CAPSULE
4354
+ };
4355
+ }
4356
+ if (!envelope.encrypted_key.ciphertext || !envelope.encrypted_key.alg) {
4357
+ return {
4358
+ allow: false,
4359
+ riskScore: 100,
4360
+ reasons: [
4361
+ `${CCE_ERROR.MISSING_ENCRYPTED_KEY}: incomplete encrypted_key`
4362
+ ],
4363
+ code: CCE_ERROR.MISSING_ENCRYPTED_KEY
4364
+ };
4365
+ }
4366
+ input.metadata = input.metadata ?? {};
4367
+ input.metadata.cceEnvelopeValid = true;
4368
+ return {
4369
+ decision: "ALLOW" /* ALLOW */,
4370
+ allow: true,
4371
+ riskScore: 0,
4372
+ reasons: []
4373
+ };
3610
4374
  }
3611
4375
  };
3612
- SensorDiscoveryService = __decorateClass([
3613
- (0, import_common8.Injectable)()
3614
- ], SensorDiscoveryService);
3615
4376
 
3616
- // src/engine/registry/sensor.registry.ts
3617
- var import_common9 = require("@nestjs/common");
3618
- var SensorRegistry = class {
3619
- constructor(configService) {
3620
- this.configService = configService;
3621
- this.sensors = [];
3622
- this.logger = new import_common9.Logger(SensorRegistry.name);
4377
+ // src/cce/sensors/cce-client-signature.sensor.ts
4378
+ var CceClientSignatureSensor = class {
4379
+ constructor(keyResolver, signatureVerifier) {
4380
+ this.keyResolver = keyResolver;
4381
+ this.signatureVerifier = signatureVerifier;
4382
+ this.name = "cce.client.signature";
4383
+ this.order = 45;
4384
+ this.phase = "POST_DECODE";
3623
4385
  }
3624
- /**
3625
- * Registers a new sensor in the registry.
3626
- *
3627
- * Validates that:
3628
- * - AxisSensor has a unique name
3629
- * - AxisSensor has an order field
3630
- * - Pre-decode sensors have order < 40
3631
- * - Post-decode sensors have order >= 40
3632
- *
3633
- * @param {AxisSensor} sensor - The sensor instance to register
3634
- * @throws Error if validation fails
3635
- */
3636
- register(sensor) {
3637
- if (!sensor.name) {
3638
- throw new Error("AxisSensor must have a name");
4386
+ supports(input) {
4387
+ return input.metadata?.cceEnvelopeValid === true;
4388
+ }
4389
+ async run(input) {
4390
+ const envelope = input.metadata?.cceEnvelope;
4391
+ if (!envelope) {
4392
+ return {
4393
+ allow: false,
4394
+ riskScore: 100,
4395
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
4396
+ code: CCE_ERROR.INVALID_ENVELOPE
4397
+ };
3639
4398
  }
3640
- const enabledSensorsStr = this.configService.get("ENABLED_SENSORS");
3641
- const disabledSensorsStr = this.configService.get("DISABLED_SENSORS");
3642
- const enabledSensors = enabledSensorsStr ? enabledSensorsStr.split(",").map((s) => s.trim()) : null;
3643
- const disabledSensors = disabledSensorsStr ? disabledSensorsStr.split(",").map((s) => s.trim()) : [];
3644
- if (enabledSensors && !enabledSensors.includes(sensor.name)) {
3645
- this.logger.log(`Skipping disabled sensor (not in ENABLED_SENSORS): ${sensor.name}`);
3646
- return;
4399
+ const keyRecord = await this.keyResolver.resolve(envelope.client_kid);
4400
+ if (!keyRecord) {
4401
+ return {
4402
+ allow: false,
4403
+ riskScore: 100,
4404
+ reasons: [
4405
+ `${CCE_ERROR.CLIENT_KEY_NOT_FOUND}: kid=${envelope.client_kid}`
4406
+ ],
4407
+ code: CCE_ERROR.CLIENT_KEY_NOT_FOUND
4408
+ };
3647
4409
  }
3648
- if (disabledSensors.includes(sensor.name)) {
3649
- this.logger.log(`Skipping disabled sensor (in DISABLED_SENSORS): ${sensor.name}`);
3650
- return;
4410
+ const { client_sig, ...signable } = envelope;
4411
+ const canonical = canonicalize2(signable);
4412
+ const message = new TextEncoder().encode(canonical);
4413
+ const valid = await this.signatureVerifier.verify(
4414
+ message,
4415
+ client_sig.value,
4416
+ keyRecord.publicKeyHex,
4417
+ keyRecord.alg
4418
+ );
4419
+ if (!valid) {
4420
+ return {
4421
+ allow: false,
4422
+ riskScore: 100,
4423
+ reasons: [CCE_ERROR.CLIENT_SIG_INVALID],
4424
+ code: CCE_ERROR.CLIENT_SIG_INVALID
4425
+ };
3651
4426
  }
3652
- if (sensor.order === void 0) {
3653
- throw new Error(`AxisSensor "${sensor.name}" must have an order field`);
4427
+ input.metadata = input.metadata ?? {};
4428
+ input.metadata.cceClientKey = keyRecord;
4429
+ input.metadata.cceClientSigVerified = true;
4430
+ return {
4431
+ decision: "ALLOW" /* ALLOW */,
4432
+ allow: true,
4433
+ riskScore: 0,
4434
+ reasons: [],
4435
+ meta: { kid: envelope.client_kid }
4436
+ };
4437
+ }
4438
+ };
4439
+ function canonicalize2(obj) {
4440
+ if (Array.isArray(obj)) {
4441
+ return "[" + obj.map(canonicalize2).join(",") + "]";
4442
+ }
4443
+ if (obj !== null && typeof obj === "object") {
4444
+ const sorted = Object.keys(obj).sort().map(
4445
+ (k) => JSON.stringify(k) + ":" + canonicalize2(obj[k])
4446
+ );
4447
+ return "{" + sorted.join(",") + "}";
4448
+ }
4449
+ return JSON.stringify(obj);
4450
+ }
4451
+
4452
+ // src/cce/sensors/cce-capsule-verification.sensor.ts
4453
+ var import_blake3 = require("@noble/hashes/blake3.js");
4454
+ var import_utils5 = require("@noble/hashes/utils.js");
4455
+ var CceCapsuleVerificationSensor = class {
4456
+ constructor(issuerKeyResolver, capsuleVerifier) {
4457
+ this.issuerKeyResolver = issuerKeyResolver;
4458
+ this.capsuleVerifier = capsuleVerifier;
4459
+ this.name = "cce.capsule.verification";
4460
+ this.order = 50;
4461
+ this.phase = "POST_DECODE";
4462
+ }
4463
+ supports(input) {
4464
+ return input.metadata?.cceEnvelopeValid === true;
4465
+ }
4466
+ async run(input) {
4467
+ const capsule = input.metadata?.cceEnvelope?.capsule;
4468
+ if (!capsule) {
4469
+ return {
4470
+ allow: false,
4471
+ riskScore: 100,
4472
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
4473
+ code: CCE_ERROR.MISSING_CAPSULE
4474
+ };
3654
4475
  }
3655
- const isPreDecodeSensor = this.isPreDecodeSensor(sensor);
3656
- const isPostDecodeSensor = this.isPostDecodeSensor(sensor);
3657
- if (isPreDecodeSensor && sensor.order >= 40) {
3658
- this.logger.warn(
3659
- `AxisSensor "${sensor.name}" is marked as PRE_DECODE but has order ${sensor.order} (should be < 40)`
4476
+ if (capsule.ver !== CCE_PROTOCOL_VERSION) {
4477
+ return {
4478
+ allow: false,
4479
+ riskScore: 100,
4480
+ reasons: [
4481
+ `${CCE_ERROR.CAPSULE_SIG_INVALID}: wrong version ${capsule.ver}`
4482
+ ],
4483
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
4484
+ };
4485
+ }
4486
+ const { capsule_id, issuer_sig, ...claimsBody } = capsule;
4487
+ const expectedId = computeCceCapsuleId(claimsBody);
4488
+ if (capsule_id !== expectedId) {
4489
+ return {
4490
+ allow: false,
4491
+ riskScore: 100,
4492
+ reasons: [`${CCE_ERROR.CAPSULE_SIG_INVALID}: content hash mismatch`],
4493
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
4494
+ };
4495
+ }
4496
+ const issuerKey = await this.issuerKeyResolver.resolve(
4497
+ capsule.issuer_sig.kid
4498
+ );
4499
+ if (!issuerKey) {
4500
+ return {
4501
+ allow: false,
4502
+ riskScore: 100,
4503
+ reasons: [`${CCE_ERROR.CAPSULE_SIG_INVALID}: issuer key not found`],
4504
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
4505
+ };
4506
+ }
4507
+ const { issuer_sig: sig, ...rest } = capsule;
4508
+ const sigValid = await this.capsuleVerifier.verify(
4509
+ rest,
4510
+ sig,
4511
+ issuerKey.publicKeyHex
4512
+ );
4513
+ if (!sigValid) {
4514
+ return {
4515
+ allow: false,
4516
+ riskScore: 100,
4517
+ reasons: [CCE_ERROR.CAPSULE_SIG_INVALID],
4518
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
4519
+ };
4520
+ }
4521
+ const nowSeconds = Math.floor(Date.now() / 1e3);
4522
+ if (capsule.exp < nowSeconds) {
4523
+ return {
4524
+ allow: false,
4525
+ riskScore: 100,
4526
+ reasons: [`${CCE_ERROR.CAPSULE_EXPIRED}: exp=${capsule.exp}`],
4527
+ code: CCE_ERROR.CAPSULE_EXPIRED
4528
+ };
4529
+ }
4530
+ if (capsule.iat > nowSeconds + 5) {
4531
+ return {
4532
+ allow: false,
4533
+ riskScore: 100,
4534
+ reasons: [`${CCE_ERROR.CAPSULE_NOT_YET_VALID}: iat=${capsule.iat}`],
4535
+ code: CCE_ERROR.CAPSULE_NOT_YET_VALID
4536
+ };
4537
+ }
4538
+ input.metadata = input.metadata ?? {};
4539
+ input.metadata.cceCapsuleVerified = true;
4540
+ input.metadata.cceCapsule = capsule;
4541
+ return {
4542
+ decision: "ALLOW" /* ALLOW */,
4543
+ allow: true,
4544
+ riskScore: 0,
4545
+ reasons: [],
4546
+ meta: { capsule_id: capsule.capsule_id }
4547
+ };
4548
+ }
4549
+ };
4550
+ function canonicalize3(obj) {
4551
+ if (Array.isArray(obj)) {
4552
+ return "[" + obj.map(canonicalize3).join(",") + "]";
4553
+ }
4554
+ if (obj !== null && typeof obj === "object") {
4555
+ const sorted = Object.keys(obj).sort().map(
4556
+ (k) => JSON.stringify(k) + ":" + canonicalize3(obj[k])
4557
+ );
4558
+ return "{" + sorted.join(",") + "}";
4559
+ }
4560
+ return JSON.stringify(obj);
4561
+ }
4562
+ function computeCceCapsuleId(claims) {
4563
+ const canonical = canonicalize3(claims);
4564
+ const hash = (0, import_blake3.blake3)(new TextEncoder().encode(canonical));
4565
+ return "cce_b3_" + (0, import_utils5.bytesToHex)(hash).slice(0, 32);
4566
+ }
4567
+
4568
+ // src/cce/sensors/cce-tps-window.sensor.ts
4569
+ var DEFAULT_SKEW_MS = 5e3;
4570
+ var CceTpsWindowSensor = class {
4571
+ constructor(skewMs = DEFAULT_SKEW_MS) {
4572
+ this.skewMs = skewMs;
4573
+ this.name = "cce.tps.window";
4574
+ this.order = 92;
4575
+ this.phase = "POST_DECODE";
4576
+ }
4577
+ supports(input) {
4578
+ return input.metadata?.cceCapsuleVerified === true;
4579
+ }
4580
+ async run(input) {
4581
+ const capsule = input.metadata?.cceCapsule;
4582
+ if (!capsule) {
4583
+ return {
4584
+ allow: false,
4585
+ riskScore: 100,
4586
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
4587
+ code: CCE_ERROR.MISSING_CAPSULE
4588
+ };
4589
+ }
4590
+ const nowMs = Date.now();
4591
+ if (nowMs > capsule.tps_to + this.skewMs) {
4592
+ return {
4593
+ allow: false,
4594
+ riskScore: 100,
4595
+ reasons: [
4596
+ `${CCE_ERROR.TPS_WINDOW_EXPIRED}: window ended at ${capsule.tps_to}, now=${nowMs}`
4597
+ ],
4598
+ code: CCE_ERROR.TPS_WINDOW_EXPIRED
4599
+ };
4600
+ }
4601
+ if (nowMs < capsule.tps_from - this.skewMs) {
4602
+ return {
4603
+ allow: false,
4604
+ riskScore: 100,
4605
+ reasons: [
4606
+ `${CCE_ERROR.TPS_WINDOW_FUTURE}: window starts at ${capsule.tps_from}, now=${nowMs}`
4607
+ ],
4608
+ code: CCE_ERROR.TPS_WINDOW_FUTURE
4609
+ };
4610
+ }
4611
+ input.metadata = input.metadata ?? {};
4612
+ input.metadata.cceTpsValid = true;
4613
+ return {
4614
+ decision: "ALLOW" /* ALLOW */,
4615
+ allow: true,
4616
+ riskScore: 0,
4617
+ reasons: []
4618
+ };
4619
+ }
4620
+ };
4621
+
4622
+ // src/cce/sensors/cce-audience-intent-binding.sensor.ts
4623
+ var CceAudienceIntentBindingSensor = class {
4624
+ constructor(axisAudience) {
4625
+ this.axisAudience = axisAudience;
4626
+ this.name = "cce.audience.intent.binding";
4627
+ this.order = 95;
4628
+ this.phase = "POST_DECODE";
4629
+ }
4630
+ supports(input) {
4631
+ return input.metadata?.cceCapsuleVerified === true;
4632
+ }
4633
+ async run(input) {
4634
+ const capsule = input.metadata?.cceCapsule;
4635
+ const envelope = input.metadata?.cceEnvelope;
4636
+ if (!capsule || !envelope) {
4637
+ return {
4638
+ allow: false,
4639
+ riskScore: 100,
4640
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
4641
+ code: CCE_ERROR.MISSING_CAPSULE
4642
+ };
4643
+ }
4644
+ if (capsule.aud !== this.axisAudience) {
4645
+ return {
4646
+ allow: false,
4647
+ riskScore: 100,
4648
+ reasons: [
4649
+ `${CCE_ERROR.AUDIENCE_MISMATCH}: capsule.aud=${capsule.aud}, expected=${this.axisAudience}`
4650
+ ],
4651
+ code: CCE_ERROR.AUDIENCE_MISMATCH
4652
+ };
4653
+ }
4654
+ const requestIntent = input.intent ?? input.metadata?.cceRequestIntent;
4655
+ if (requestIntent && capsule.intent !== requestIntent) {
4656
+ return {
4657
+ allow: false,
4658
+ riskScore: 100,
4659
+ reasons: [
4660
+ `${CCE_ERROR.INTENT_MISMATCH}: capsule.intent=${capsule.intent}, request=${requestIntent}`
4661
+ ],
4662
+ code: CCE_ERROR.INTENT_MISMATCH
4663
+ };
4664
+ }
4665
+ if (envelope.client_kid !== capsule.kid) {
4666
+ return {
4667
+ allow: false,
4668
+ riskScore: 100,
4669
+ reasons: [
4670
+ `${CCE_ERROR.INTENT_MISMATCH}: envelope.kid=${envelope.client_kid}, capsule.kid=${capsule.kid}`
4671
+ ],
4672
+ code: CCE_ERROR.INTENT_MISMATCH
4673
+ };
4674
+ }
4675
+ input.metadata = input.metadata ?? {};
4676
+ input.metadata.cceBindingVerified = true;
4677
+ return {
4678
+ decision: "ALLOW" /* ALLOW */,
4679
+ allow: true,
4680
+ riskScore: 0,
4681
+ reasons: []
4682
+ };
4683
+ }
4684
+ };
4685
+
4686
+ // src/cce/sensors/cce-replay-protection.sensor.ts
4687
+ var InMemoryCceReplayStore = class {
4688
+ constructor() {
4689
+ this.nonces = /* @__PURE__ */ new Map();
4690
+ this.consumed = /* @__PURE__ */ new Set();
4691
+ this.revoked = /* @__PURE__ */ new Set();
4692
+ }
4693
+ async checkAndMark(key, ttlMs) {
4694
+ this.cleanup();
4695
+ if (this.nonces.has(key)) return false;
4696
+ this.nonces.set(key, Date.now() + ttlMs);
4697
+ return true;
4698
+ }
4699
+ async isCapsuleConsumed(capsuleId) {
4700
+ return this.consumed.has(capsuleId);
4701
+ }
4702
+ async markCapsuleConsumed(capsuleId, _ttlMs) {
4703
+ this.consumed.add(capsuleId);
4704
+ }
4705
+ async isCapsuleRevoked(capsuleId) {
4706
+ return this.revoked.has(capsuleId);
4707
+ }
4708
+ /** Revoke a capsule (for testing/admin) */
4709
+ revoke(capsuleId) {
4710
+ this.revoked.add(capsuleId);
4711
+ }
4712
+ cleanup() {
4713
+ const now = Date.now();
4714
+ for (const [key, expiresAt] of this.nonces) {
4715
+ if (expiresAt < now) this.nonces.delete(key);
4716
+ }
4717
+ }
4718
+ };
4719
+ var CceReplayProtectionSensor = class {
4720
+ constructor(replayStore, options) {
4721
+ this.replayStore = replayStore;
4722
+ this.name = "cce.replay.protection";
4723
+ this.order = 98;
4724
+ this.phase = "POST_DECODE";
4725
+ this.nonceTtlMs = options?.nonceTtlMs ?? 5 * 60 * 1e3;
4726
+ }
4727
+ supports(input) {
4728
+ return input.metadata?.cceCapsuleVerified === true;
4729
+ }
4730
+ async run(input) {
4731
+ const capsule = input.metadata?.cceCapsule;
4732
+ const envelope = input.metadata?.cceEnvelope;
4733
+ if (!capsule || !envelope) {
4734
+ return {
4735
+ allow: false,
4736
+ riskScore: 100,
4737
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
4738
+ code: CCE_ERROR.MISSING_CAPSULE
4739
+ };
4740
+ }
4741
+ const revoked = await this.replayStore.isCapsuleRevoked(capsule.capsule_id);
4742
+ if (revoked) {
4743
+ return {
4744
+ allow: false,
4745
+ riskScore: 100,
4746
+ reasons: [`${CCE_ERROR.CAPSULE_REVOKED}: ${capsule.capsule_id}`],
4747
+ code: CCE_ERROR.CAPSULE_REVOKED
4748
+ };
4749
+ }
4750
+ if (capsule.mode === "SINGLE_USE") {
4751
+ const consumed = await this.replayStore.isCapsuleConsumed(
4752
+ capsule.capsule_id
3660
4753
  );
4754
+ if (consumed) {
4755
+ return {
4756
+ allow: false,
4757
+ riskScore: 100,
4758
+ reasons: [`${CCE_ERROR.CAPSULE_CONSUMED}: ${capsule.capsule_id}`],
4759
+ code: CCE_ERROR.CAPSULE_CONSUMED
4760
+ };
4761
+ }
3661
4762
  }
3662
- if (isPostDecodeSensor && sensor.order < 40) {
3663
- this.logger.warn(
3664
- `AxisSensor "${sensor.name}" is marked as POST_DECODE but has order ${sensor.order} (should be >= 40)`
4763
+ const nonceKey = `cce:nonce:${capsule.sub}:${capsule.aud}:${capsule.intent}:${envelope.request_nonce}`;
4764
+ const nonceValid = await this.replayStore.checkAndMark(
4765
+ nonceKey,
4766
+ this.nonceTtlMs
4767
+ );
4768
+ if (!nonceValid) {
4769
+ return {
4770
+ allow: false,
4771
+ riskScore: 100,
4772
+ reasons: [
4773
+ `${CCE_ERROR.NONCE_REUSED}: ${envelope.request_nonce.slice(0, 16)}...`
4774
+ ],
4775
+ code: CCE_ERROR.NONCE_REUSED
4776
+ };
4777
+ }
4778
+ if (capsule.mode === "SINGLE_USE") {
4779
+ const capsuleTtl = (capsule.exp - capsule.iat) * 1e3 + 6e4;
4780
+ await this.replayStore.markCapsuleConsumed(
4781
+ capsule.capsule_id,
4782
+ capsuleTtl
4783
+ );
4784
+ }
4785
+ input.metadata = input.metadata ?? {};
4786
+ input.metadata.cceReplayClean = true;
4787
+ return {
4788
+ decision: "ALLOW" /* ALLOW */,
4789
+ allow: true,
4790
+ riskScore: 0,
4791
+ reasons: []
4792
+ };
4793
+ }
4794
+ };
4795
+
4796
+ // src/cce/sensors/cce-payload-decryption.sensor.ts
4797
+ var CcePayloadDecryptionSensor = class {
4798
+ constructor(keyProvider, aesProvider, maxPayloadBytes = 64 * 1024) {
4799
+ this.keyProvider = keyProvider;
4800
+ this.aesProvider = aesProvider;
4801
+ this.maxPayloadBytes = maxPayloadBytes;
4802
+ this.name = "cce.payload.decryption";
4803
+ this.order = 145;
4804
+ this.phase = "POST_DECODE";
4805
+ }
4806
+ supports(input) {
4807
+ return input.metadata?.cceEnvelopeValid === true && input.metadata?.cceClientSigVerified === true && input.metadata?.cceCapsuleVerified === true && input.metadata?.cceReplayClean === true;
4808
+ }
4809
+ async run(input) {
4810
+ const envelope = input.metadata?.cceEnvelope;
4811
+ if (!envelope) {
4812
+ return {
4813
+ allow: false,
4814
+ riskScore: 100,
4815
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
4816
+ code: CCE_ERROR.INVALID_ENVELOPE
4817
+ };
4818
+ }
4819
+ let aesKey;
4820
+ try {
4821
+ aesKey = await this.keyProvider.unwrapKey(
4822
+ envelope.encrypted_key.ciphertext,
4823
+ envelope.encrypted_key.alg,
4824
+ envelope.encrypted_key.axis_kid,
4825
+ envelope.encrypted_key.ephemeral_pk
4826
+ );
4827
+ } catch {
4828
+ return {
4829
+ allow: false,
4830
+ riskScore: 100,
4831
+ reasons: [CCE_ERROR.KEY_UNWRAP_FAILED],
4832
+ code: CCE_ERROR.KEY_UNWRAP_FAILED
4833
+ };
4834
+ }
4835
+ if (!aesKey) {
4836
+ return {
4837
+ allow: false,
4838
+ riskScore: 100,
4839
+ reasons: [CCE_ERROR.KEY_UNWRAP_FAILED],
4840
+ code: CCE_ERROR.KEY_UNWRAP_FAILED
4841
+ };
4842
+ }
4843
+ let iv;
4844
+ let ciphertext;
4845
+ let tag;
4846
+ try {
4847
+ iv = base64UrlDecode2(envelope.encrypted_payload.iv);
4848
+ ciphertext = base64UrlDecode2(envelope.encrypted_payload.ciphertext);
4849
+ tag = base64UrlDecode2(envelope.encrypted_payload.tag);
4850
+ } catch {
4851
+ return {
4852
+ allow: false,
4853
+ riskScore: 100,
4854
+ reasons: [`${CCE_ERROR.DECRYPTION_FAILED}: invalid base64url encoding`],
4855
+ code: CCE_ERROR.DECRYPTION_FAILED
4856
+ };
4857
+ }
4858
+ if (ciphertext.length > this.maxPayloadBytes) {
4859
+ return {
4860
+ allow: false,
4861
+ riskScore: 100,
4862
+ reasons: [
4863
+ `${CCE_ERROR.PAYLOAD_TOO_LARGE}: ${ciphertext.length} > ${this.maxPayloadBytes}`
4864
+ ],
4865
+ code: CCE_ERROR.PAYLOAD_TOO_LARGE
4866
+ };
4867
+ }
4868
+ const aad = buildAad(envelope);
4869
+ let plaintext;
4870
+ try {
4871
+ plaintext = await this.aesProvider.decrypt(
4872
+ aesKey,
4873
+ iv,
4874
+ ciphertext,
4875
+ tag,
4876
+ aad
3665
4877
  );
4878
+ } catch {
4879
+ plaintext = null;
4880
+ } finally {
4881
+ aesKey.fill(0);
4882
+ }
4883
+ if (!plaintext) {
4884
+ return {
4885
+ allow: false,
4886
+ riskScore: 100,
4887
+ reasons: [CCE_ERROR.AEAD_TAG_MISMATCH],
4888
+ code: CCE_ERROR.AEAD_TAG_MISMATCH
4889
+ };
4890
+ }
4891
+ input.metadata = input.metadata ?? {};
4892
+ input.metadata.cceDecryptedPayload = plaintext;
4893
+ input.metadata.cceDecryptionOk = true;
4894
+ return {
4895
+ decision: "ALLOW" /* ALLOW */,
4896
+ allow: true,
4897
+ riskScore: 0,
4898
+ reasons: []
4899
+ };
4900
+ }
4901
+ };
4902
+ function buildAad(envelope) {
4903
+ const parts = [
4904
+ envelope.ver,
4905
+ envelope.request_id,
4906
+ envelope.correlation_id,
4907
+ envelope.client_kid,
4908
+ envelope.capsule.capsule_id,
4909
+ envelope.capsule.intent,
4910
+ envelope.capsule.aud,
4911
+ envelope.request_nonce
4912
+ ];
4913
+ return new TextEncoder().encode(parts.join("|"));
4914
+ }
4915
+ function base64UrlDecode2(input) {
4916
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
4917
+ const padding = "=".repeat((4 - base64.length % 4) % 4);
4918
+ const binary = atob(base64 + padding);
4919
+ const bytes2 = new Uint8Array(binary.length);
4920
+ for (let i = 0; i < binary.length; i++) {
4921
+ bytes2[i] = binary.charCodeAt(i);
4922
+ }
4923
+ return bytes2;
4924
+ }
4925
+
4926
+ // src/core/index.ts
4927
+ var core_exports = {};
4928
+ __export(core_exports, {
4929
+ AXIS_MAGIC: () => import_axis_protocol2.AXIS_MAGIC,
4930
+ AXIS_VERSION: () => import_axis_protocol2.AXIS_VERSION,
4931
+ AxisError: () => AxisError,
4932
+ AxisFrameZ: () => AxisFrameZ,
4933
+ BodyProfile: () => import_axis_protocol2.BodyProfile,
4934
+ ERR_BAD_SIGNATURE: () => import_axis_protocol2.ERR_BAD_SIGNATURE,
4935
+ ERR_CONTRACT_VIOLATION: () => import_axis_protocol2.ERR_CONTRACT_VIOLATION,
4936
+ ERR_INVALID_PACKET: () => import_axis_protocol2.ERR_INVALID_PACKET,
4937
+ ERR_REPLAY_DETECTED: () => import_axis_protocol2.ERR_REPLAY_DETECTED,
4938
+ FLAG_BODY_TLV: () => import_axis_protocol2.FLAG_BODY_TLV,
4939
+ FLAG_CHAIN_REQ: () => import_axis_protocol2.FLAG_CHAIN_REQ,
4940
+ FLAG_HAS_WITNESS: () => import_axis_protocol2.FLAG_HAS_WITNESS,
4941
+ MAX_BODY_LEN: () => import_axis_protocol2.MAX_BODY_LEN,
4942
+ MAX_FRAME_LEN: () => import_axis_protocol2.MAX_FRAME_LEN,
4943
+ MAX_HDR_LEN: () => import_axis_protocol2.MAX_HDR_LEN,
4944
+ MAX_SIG_LEN: () => import_axis_protocol2.MAX_SIG_LEN,
4945
+ NCERT_ALG: () => import_axis_protocol2.NCERT_ALG,
4946
+ NCERT_EXP: () => import_axis_protocol2.NCERT_EXP,
4947
+ NCERT_ISSUER_KID: () => import_axis_protocol2.NCERT_ISSUER_KID,
4948
+ NCERT_KID: () => import_axis_protocol2.NCERT_KID,
4949
+ NCERT_NBF: () => import_axis_protocol2.NCERT_NBF,
4950
+ NCERT_NODE_ID: () => import_axis_protocol2.NCERT_NODE_ID,
4951
+ NCERT_PAYLOAD: () => import_axis_protocol2.NCERT_PAYLOAD,
4952
+ NCERT_PUB: () => import_axis_protocol2.NCERT_PUB,
4953
+ NCERT_SCOPE: () => import_axis_protocol2.NCERT_SCOPE,
4954
+ NCERT_SIG: () => import_axis_protocol2.NCERT_SIG,
4955
+ PROOF_CAPSULE: () => import_axis_protocol2.PROOF_CAPSULE,
4956
+ PROOF_JWT: () => import_axis_protocol2.PROOF_JWT,
4957
+ PROOF_LOOM: () => import_axis_protocol2.PROOF_LOOM,
4958
+ PROOF_MTLS: () => import_axis_protocol2.PROOF_MTLS,
4959
+ PROOF_NONE: () => import_axis_protocol2.PROOF_NONE,
4960
+ PROOF_WITNESS: () => import_axis_protocol2.PROOF_WITNESS,
4961
+ ProofType: () => import_axis_protocol2.ProofType,
4962
+ TLV: () => import_axis_protocol.TLV,
4963
+ TLV_ACTOR_ID: () => import_axis_protocol2.TLV_ACTOR_ID,
4964
+ TLV_AUD: () => import_axis_protocol2.TLV_AUD,
4965
+ TLV_BODY_ARR: () => import_axis_protocol2.TLV_BODY_ARR,
4966
+ TLV_BODY_OBJ: () => import_axis_protocol2.TLV_BODY_OBJ,
4967
+ TLV_CAPSULE: () => import_axis_protocol2.TLV_CAPSULE,
4968
+ TLV_EFFECT: () => import_axis_protocol2.TLV_EFFECT,
4969
+ TLV_ERROR_CODE: () => import_axis_protocol2.TLV_ERROR_CODE,
4970
+ TLV_ERROR_MSG: () => import_axis_protocol2.TLV_ERROR_MSG,
4971
+ TLV_INDEX: () => import_axis_protocol2.TLV_INDEX,
4972
+ TLV_INTENT: () => import_axis_protocol2.TLV_INTENT,
4973
+ TLV_KID: () => import_axis_protocol2.TLV_KID,
4974
+ TLV_LOOM_PRESENCE_ID: () => import_axis_protocol2.TLV_LOOM_PRESENCE_ID,
4975
+ TLV_LOOM_THREAD_HASH: () => import_axis_protocol2.TLV_LOOM_THREAD_HASH,
4976
+ TLV_LOOM_WRIT: () => import_axis_protocol2.TLV_LOOM_WRIT,
4977
+ TLV_NODE: () => import_axis_protocol2.TLV_NODE,
4978
+ TLV_NODE_CERT_HASH: () => import_axis_protocol2.TLV_NODE_CERT_HASH,
4979
+ TLV_NODE_KID: () => import_axis_protocol2.TLV_NODE_KID,
4980
+ TLV_NONCE: () => import_axis_protocol2.TLV_NONCE,
4981
+ TLV_OFFSET: () => import_axis_protocol2.TLV_OFFSET,
4982
+ TLV_OK: () => import_axis_protocol2.TLV_OK,
4983
+ TLV_PID: () => import_axis_protocol2.TLV_PID,
4984
+ TLV_PREV_HASH: () => import_axis_protocol2.TLV_PREV_HASH,
4985
+ TLV_PROOF_REF: () => import_axis_protocol2.TLV_PROOF_REF,
4986
+ TLV_PROOF_TYPE: () => import_axis_protocol2.TLV_PROOF_TYPE,
4987
+ TLV_REALM: () => import_axis_protocol2.TLV_REALM,
4988
+ TLV_RECEIPT_HASH: () => import_axis_protocol2.TLV_RECEIPT_HASH,
4989
+ TLV_RID: () => import_axis_protocol2.TLV_RID,
4990
+ TLV_SHA256_CHUNK: () => import_axis_protocol2.TLV_SHA256_CHUNK,
4991
+ TLV_TRACE_ID: () => import_axis_protocol2.TLV_TRACE_ID,
4992
+ TLV_TS: () => import_axis_protocol2.TLV_TS,
4993
+ TLV_UPLOAD_ID: () => import_axis_protocol2.TLV_UPLOAD_ID,
4994
+ computeReceiptHash: () => computeReceiptHash,
4995
+ computeSignaturePayload: () => computeSignaturePayload,
4996
+ decodeArray: () => import_axis_protocol.decodeArray,
4997
+ decodeFrame: () => decodeFrame,
4998
+ decodeObject: () => import_axis_protocol.decodeObject,
4999
+ decodeTLVs: () => import_axis_protocol.decodeTLVs,
5000
+ decodeTLVsList: () => import_axis_protocol.decodeTLVsList,
5001
+ decodeVarint: () => import_axis_protocol3.decodeVarint,
5002
+ encodeFrame: () => encodeFrame,
5003
+ encodeTLVs: () => import_axis_protocol.encodeTLVs,
5004
+ encodeVarint: () => import_axis_protocol3.encodeVarint,
5005
+ generateEd25519KeyPair: () => generateEd25519KeyPair,
5006
+ getSignTarget: () => getSignTarget,
5007
+ sha256: () => sha2564,
5008
+ signFrame: () => signFrame,
5009
+ varintLength: () => import_axis_protocol3.varintLength,
5010
+ verifyFrameSignature: () => verifyFrameSignature
5011
+ });
5012
+
5013
+ // src/crypto/index.ts
5014
+ var crypto_exports = {};
5015
+ __export(crypto_exports, {
5016
+ ProofVerificationService: () => ProofVerificationService,
5017
+ b64urlDecode: () => b64urlDecode,
5018
+ b64urlDecodeString: () => b64urlDecodeString,
5019
+ b64urlEncode: () => b64urlEncode,
5020
+ b64urlEncodeString: () => b64urlEncodeString,
5021
+ canonicalJson: () => canonicalJson,
5022
+ canonicalJsonExcluding: () => canonicalJsonExcluding
5023
+ });
5024
+
5025
+ // src/crypto/proof-verification.service.ts
5026
+ var import_common10 = require("@nestjs/common");
5027
+ var crypto4 = __toESM(require("crypto"));
5028
+ var nacl = __toESM(require("tweetnacl"));
5029
+ var ProofVerificationService = class {
5030
+ constructor() {
5031
+ this.logger = new import_common10.Logger(ProofVerificationService.name);
5032
+ // Cache of registered device public keys (deviceId -> pubKey)
5033
+ this.deviceKeys = /* @__PURE__ */ new Map();
5034
+ // Cache of trusted mTLS certificate fingerprints
5035
+ this.trustedCerts = /* @__PURE__ */ new Map();
5036
+ }
5037
+ /**
5038
+ * Verifies an authentication proof based on its type.
5039
+ *
5040
+ * **Supported Types:**
5041
+ * - 1 (CAPSULE): Delegated to `verifyCapsuleProof`
5042
+ * - 2 (JWT): Verified by `verifyJWTProof`
5043
+ * - 3 (MTLS_ID): Verified by `verifyMTLSProof`
5044
+ * - 4 (DEVICE_SE): Verified by `verifyDeviceSEProof`
5045
+ *
5046
+ * @param {ProofType} proofType - The numeric AXIS proof type
5047
+ * @param {Uint8Array} proofRef - The binary reference or token for the proof
5048
+ * @param {Object} context - Additional metadata required for specific proof types
5049
+ * @param {Uint8Array} [context.signTarget] - The canonical bytes that were signed (for Ed25519)
5050
+ * @param {Uint8Array} [context.signature] - The signature to verify (for Ed25519)
5051
+ * @param {MTLSContext} [context.mtls] - mTLS certificate data
5052
+ * @param {DeviceSEContext} [context.deviceSE] - Device Secure Element information
5053
+ * @returns {Promise<ProofVerificationResult>} The outcome of the verification
5054
+ */
5055
+ async verifyProof(proofType, proofRef, context) {
5056
+ switch (proofType) {
5057
+ case 1:
5058
+ return this.verifyCapsuleProof(proofRef);
5059
+ case 2:
5060
+ return this.verifyJWTProof(proofRef);
5061
+ case 3:
5062
+ return this.verifyMTLSProof(context.mtls);
5063
+ case 4:
5064
+ return this.verifyDeviceSEProof(
5065
+ context.signTarget,
5066
+ context.signature,
5067
+ context.deviceSE
5068
+ );
5069
+ default:
5070
+ return { valid: false, error: `Unknown proof type: ${proofType}` };
5071
+ }
5072
+ }
5073
+ /**
5074
+ * Verify CAPSULE proof (delegated to CapsuleService)
5075
+ */
5076
+ async verifyCapsuleProof(proofRef) {
5077
+ const capsuleId = new TextDecoder().decode(proofRef);
5078
+ return {
5079
+ valid: true,
5080
+ metadata: { capsuleId, requiresCapsuleValidation: true }
5081
+ };
5082
+ }
5083
+ /**
5084
+ * Verifies a JSON Web Token (JWT) proof.
5085
+ *
5086
+ * **Validation Logic:**
5087
+ * 1. Decodes the token string.
5088
+ * 2. Checks for valid 3-part JWT structure.
5089
+ * 3. Validates `exp` (expiration) and `nbf` (not before) claims.
5090
+ * 4. Extracts `actor_id` or `sub` as the identity.
5091
+ *
5092
+ * @param {Uint8Array} proofRef - Binary representation of the JWT string
5093
+ * @returns {Promise<ProofVerificationResult>} Result including the actor identifier
5094
+ */
5095
+ async verifyJWTProof(proofRef) {
5096
+ try {
5097
+ const token = new TextDecoder().decode(proofRef);
5098
+ const parts = token.split(".");
5099
+ if (parts.length !== 3) {
5100
+ return { valid: false, error: "Invalid JWT format" };
5101
+ }
5102
+ const header = JSON.parse(Buffer.from(parts[0], "base64url").toString());
5103
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
5104
+ if (payload.exp && Date.now() / 1e3 > payload.exp) {
5105
+ return { valid: false, error: "JWT expired" };
5106
+ }
5107
+ if (payload.nbf && Date.now() / 1e3 < payload.nbf) {
5108
+ return { valid: false, error: "JWT not yet valid" };
5109
+ }
5110
+ return {
5111
+ valid: true,
5112
+ actorId: payload.sub || payload.actor_id,
5113
+ metadata: { iss: payload.iss, scope: payload.scope }
5114
+ };
5115
+ } catch (e) {
5116
+ const message = e instanceof Error ? e.message : "Unknown error";
5117
+ return { valid: false, error: `JWT parse error: ${message}` };
5118
+ }
5119
+ }
5120
+ /**
5121
+ * Verify mTLS client certificate proof
5122
+ */
5123
+ async verifyMTLSProof(mtls) {
5124
+ if (!mtls) {
5125
+ return { valid: false, error: "No mTLS context provided" };
5126
+ }
5127
+ if (!mtls.verified) {
5128
+ return { valid: false, error: "mTLS not verified by TLS terminator" };
5129
+ }
5130
+ if (mtls.clientCertFingerprint) {
5131
+ const trusted = this.trustedCerts.get(mtls.clientCertFingerprint);
5132
+ if (trusted) {
5133
+ return {
5134
+ valid: true,
5135
+ actorId: trusted.actorId,
5136
+ metadata: {
5137
+ fingerprint: mtls.clientCertFingerprint,
5138
+ subject: mtls.clientCertSubject
5139
+ }
5140
+ };
5141
+ }
3666
5142
  }
3667
- this.sensors.push(sensor);
3668
- const phaseLabel = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase || "UNKNOWN";
3669
- this.logger.debug(
3670
- `Registered sensor: ${sensor.name} (order: ${sensor.order}, phase: ${phaseLabel})`
3671
- );
3672
- }
3673
- /**
3674
- * Returns all registered sensors, sorted by their execution order.
3675
- *
3676
- * @returns {AxisSensor[]} A sorted array of sensors
3677
- */
3678
- list() {
3679
- return [...this.sensors].sort(
3680
- (a, b) => (a.order ?? 999) - (b.order ?? 999)
3681
- );
5143
+ if (mtls.clientCertSubject) {
5144
+ const cnMatch = mtls.clientCertSubject.match(/CN=([^,]+)/);
5145
+ if (cnMatch) {
5146
+ return {
5147
+ valid: true,
5148
+ actorId: cnMatch[1],
5149
+ metadata: {
5150
+ subject: mtls.clientCertSubject,
5151
+ issuer: mtls.clientCertIssuer
5152
+ }
5153
+ };
5154
+ }
5155
+ }
5156
+ return { valid: false, error: "Could not extract actor from certificate" };
3682
5157
  }
3683
5158
  /**
3684
- * Returns only pre-decode sensors (order < 40).
3685
- * These sensors run in middleware on raw bytes before frame decoding.
3686
- *
3687
- * @returns {AxisPreSensor[]} Pre-decode sensors sorted by order
5159
+ * Verify Device Secure Element signature
3688
5160
  */
3689
- getPreDecodeSensors() {
3690
- return this.list().filter((s) => (s.order ?? 999) < 40);
5161
+ async verifyDeviceSEProof(signTarget, signature, deviceSE) {
5162
+ if (!deviceSE || !signTarget || !signature) {
5163
+ return { valid: false, error: "Missing Device SE context" };
5164
+ }
5165
+ let publicKey = deviceSE.publicKey;
5166
+ const registeredKey = this.deviceKeys.get(deviceSE.deviceId);
5167
+ if (registeredKey) {
5168
+ publicKey = registeredKey;
5169
+ }
5170
+ if (!publicKey || publicKey.length !== 32) {
5171
+ return {
5172
+ valid: false,
5173
+ error: "Invalid or unregistered device public key"
5174
+ };
5175
+ }
5176
+ try {
5177
+ const valid = nacl.sign.detached.verify(signTarget, signature, publicKey);
5178
+ if (!valid) {
5179
+ return { valid: false, error: "Device signature verification failed" };
5180
+ }
5181
+ return {
5182
+ valid: true,
5183
+ actorId: deviceSE.deviceId,
5184
+ metadata: { deviceId: deviceSE.deviceId, proofType: "DEVICE_SE" }
5185
+ };
5186
+ } catch (e) {
5187
+ const message = e instanceof Error ? e.message : "Unknown error";
5188
+ return {
5189
+ valid: false,
5190
+ error: `Signature verification error: ${message}`
5191
+ };
5192
+ }
3691
5193
  }
3692
5194
  /**
3693
- * Returns only post-decode sensors (order >= 40).
3694
- * These sensors run in the controller on fully decoded frames.
5195
+ * Registers a public key for a trusted device.
5196
+ * This key will be used for future `DEVICE_SE` proof verifications.
3695
5197
  *
3696
- * @returns {AxisPostSensor[]} Post-decode sensors sorted by order
5198
+ * @param {string} deviceId - Unique identifier for the device
5199
+ * @param {Uint8Array} publicKey - 32-byte Ed25519 public key
5200
+ * @throws {Error} If the public key is not 32 bytes
3697
5201
  */
3698
- getPostDecodeSensors() {
3699
- return this.list().filter(
3700
- (s) => (s.order ?? 999) >= 40
3701
- );
5202
+ registerDeviceKey(deviceId, publicKey) {
5203
+ if (publicKey.length !== 32) {
5204
+ throw new Error("Device public key must be 32 bytes (Ed25519)");
5205
+ }
5206
+ this.deviceKeys.set(deviceId, publicKey);
5207
+ this.logger.log(`Registered device key for ${deviceId}`);
3702
5208
  }
3703
5209
  /**
3704
- * Helper: Check if a sensor is a pre-decode sensor.
3705
- *
3706
- * @private
3707
- * @param {AxisSensor} sensor - The sensor to check
3708
- * @returns {boolean} True if sensor is pre-decode
5210
+ * Unregister a device
3709
5211
  */
3710
- isPreDecodeSensor(sensor) {
3711
- const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
3712
- return phase === "PRE_DECODE" || (sensor.order ?? 999) < 40;
5212
+ unregisterDevice(deviceId) {
5213
+ return this.deviceKeys.delete(deviceId);
3713
5214
  }
3714
5215
  /**
3715
- * Helper: Check if a sensor is a post-decode sensor.
5216
+ * Registers a trusted mTLS certificate fingerprint and associates it with an actor.
3716
5217
  *
3717
- * @private
3718
- * @param {AxisSensor} sensor - The sensor to check
3719
- * @returns {boolean} True if sensor is post-decode
5218
+ * @param {string} fingerprint - SHA-256 fingerprint of the client certificate
5219
+ * @param {string} actorId - The actor to associate with this certificate
3720
5220
  */
3721
- isPostDecodeSensor(sensor) {
3722
- const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
3723
- return phase === "POST_DECODE" || (sensor.order ?? 999) >= 40;
5221
+ registerMTLSCert(fingerprint, actorId) {
5222
+ this.trustedCerts.set(fingerprint, { actorId, issuedAt: Date.now() });
5223
+ this.logger.log(`Registered mTLS cert ${fingerprint} for actor ${actorId}`);
3724
5224
  }
3725
5225
  /**
3726
- * Returns sensor count by phase.
3727
- * Useful for diagnostics and monitoring.
3728
- *
3729
- * @returns {{preDecodeCount: number, postDecodeCount: number}}
5226
+ * Revoke an mTLS certificate
3730
5227
  */
3731
- getSensorCountByPhase() {
3732
- return {
3733
- preDecodeCount: this.getPreDecodeSensors().length,
3734
- postDecodeCount: this.getPostDecodeSensors().length
3735
- };
5228
+ revokeMTLSCert(fingerprint) {
5229
+ return this.trustedCerts.delete(fingerprint);
3736
5230
  }
3737
5231
  /**
3738
- * Clears all registered sensors.
3739
- * Useful for testing.
3740
- *
3741
- * @internal
5232
+ * Calculate certificate fingerprint (SHA-256)
3742
5233
  */
3743
- clear() {
3744
- this.sensors = [];
5234
+ static calculateFingerprint(certPem) {
5235
+ const der = Buffer.from(
5236
+ certPem.replace(/-----BEGIN CERTIFICATE-----/, "").replace(/-----END CERTIFICATE-----/, "").replace(/\s/g, ""),
5237
+ "base64"
5238
+ );
5239
+ return crypto4.createHash("sha256").update(der).digest("hex");
3745
5240
  }
3746
5241
  };
3747
- SensorRegistry = __decorateClass([
3748
- (0, import_common9.Injectable)()
3749
- ], SensorRegistry);
5242
+ ProofVerificationService = __decorateClass([
5243
+ (0, import_common10.Injectable)()
5244
+ ], ProofVerificationService);
5245
+
5246
+ // src/decorators/index.ts
5247
+ var decorators_exports = {};
5248
+ __export(decorators_exports, {
5249
+ AxisContext: () => AxisContext,
5250
+ AxisDemoPubkey: () => AxisDemoPubkey,
5251
+ AxisFrame: () => AxisFrame3,
5252
+ AxisIp: () => AxisIp,
5253
+ AxisRaw: () => AxisRaw,
5254
+ HANDLER_METADATA_KEY: () => HANDLER_METADATA_KEY,
5255
+ Handler: () => Handler,
5256
+ INTENT_BODY_KEY: () => INTENT_BODY_KEY,
5257
+ INTENT_METADATA_KEY: () => INTENT_METADATA_KEY,
5258
+ INTENT_ROUTES_KEY: () => INTENT_ROUTES_KEY,
5259
+ INTENT_SENSORS_KEY: () => INTENT_SENSORS_KEY,
5260
+ Intent: () => Intent,
5261
+ IntentBody: () => IntentBody,
5262
+ IntentSensors: () => IntentSensors,
5263
+ SENSOR_METADATA_KEY: () => SENSOR_METADATA_KEY,
5264
+ Sensor: () => Sensor,
5265
+ TLV_FIELDS_KEY: () => TLV_FIELDS_KEY,
5266
+ TLV_VALIDATORS_KEY: () => TLV_VALIDATORS_KEY,
5267
+ TlvEnum: () => TlvEnum,
5268
+ TlvField: () => TlvField,
5269
+ TlvMinLen: () => TlvMinLen,
5270
+ TlvRange: () => TlvRange,
5271
+ TlvUtf8Pattern: () => TlvUtf8Pattern,
5272
+ TlvValidate: () => TlvValidate,
5273
+ buildDtoDecoder: () => buildDtoDecoder,
5274
+ extractDtoSchema: () => extractDtoSchema
5275
+ });
5276
+
5277
+ // src/engine/index.ts
5278
+ var engine_exports = {};
5279
+ __export(engine_exports, {
5280
+ BAND: () => BAND,
5281
+ HandlerDiscoveryService: () => HandlerDiscoveryService,
5282
+ IntentRouter: () => IntentRouter,
5283
+ PRE_DECODE_BOUNDARY: () => PRE_DECODE_BOUNDARY,
5284
+ SensorDiscoveryService: () => SensorDiscoveryService,
5285
+ SensorRegistry: () => SensorRegistry,
5286
+ createObservation: () => createObservation,
5287
+ endStage: () => endStage,
5288
+ finalizeObservation: () => finalizeObservation,
5289
+ observation: () => observation_exports,
5290
+ recordSensor: () => recordSensor,
5291
+ startStage: () => startStage
5292
+ });
3750
5293
 
3751
5294
  // src/engine/observation/index.ts
3752
5295
  var observation_exports = {};
@@ -4131,7 +5674,7 @@ var AxisErrorZ = z2.object({
4131
5674
  });
4132
5675
 
4133
5676
  // src/schemas/body-profile.validator.ts
4134
- var import_common10 = require("@nestjs/common");
5677
+ var import_common11 = require("@nestjs/common");
4135
5678
  var BodyProfile2 = /* @__PURE__ */ ((BodyProfile3) => {
4136
5679
  BodyProfile3[BodyProfile3["RAW"] = 0] = "RAW";
4137
5680
  BodyProfile3[BodyProfile3["TLV_MAP"] = 1] = "TLV_MAP";
@@ -4141,7 +5684,7 @@ var BodyProfile2 = /* @__PURE__ */ ((BodyProfile3) => {
4141
5684
  })(BodyProfile2 || {});
4142
5685
  var BodyProfileValidator = class {
4143
5686
  constructor() {
4144
- this.logger = new import_common10.Logger(BodyProfileValidator.name);
5687
+ this.logger = new import_common11.Logger(BodyProfileValidator.name);
4145
5688
  }
4146
5689
  /**
4147
5690
  * Validate body matches declared profile
@@ -4257,12 +5800,13 @@ var BodyProfileValidator = class {
4257
5800
  }
4258
5801
  };
4259
5802
  BodyProfileValidator = __decorateClass([
4260
- (0, import_common10.Injectable)()
5803
+ (0, import_common11.Injectable)()
4261
5804
  ], BodyProfileValidator);
4262
5805
 
4263
5806
  // src/security/index.ts
4264
5807
  var security_exports = {};
4265
5808
  __export(security_exports, {
5809
+ AxisSensorChainService: () => AxisSensorChainService,
4266
5810
  CAPABILITIES: () => CAPABILITIES,
4267
5811
  INTENT_REQUIREMENTS: () => INTENT_REQUIREMENTS,
4268
5812
  PROOF_CAPABILITIES: () => PROOF_CAPABILITIES,
@@ -4295,7 +5839,7 @@ __export(sensors_exports, {
4295
5839
  });
4296
5840
 
4297
5841
  // src/sensors/access-profile-resolver.sensor.ts
4298
- var import_common11 = require("@nestjs/common");
5842
+ var import_common12 = require("@nestjs/common");
4299
5843
  var AccessProfileResolverSensor = class {
4300
5844
  constructor() {
4301
5845
  /** AxisSensor identifier */
@@ -4321,11 +5865,11 @@ var AccessProfileResolverSensor = class {
4321
5865
  };
4322
5866
  AccessProfileResolverSensor = __decorateClass([
4323
5867
  Sensor(),
4324
- (0, import_common11.Injectable)()
5868
+ (0, import_common12.Injectable)()
4325
5869
  ], AccessProfileResolverSensor);
4326
5870
 
4327
5871
  // src/sensors/body-budget.sensor.ts
4328
- var import_common12 = require("@nestjs/common");
5872
+ var import_common13 = require("@nestjs/common");
4329
5873
  var BodyBudgetSensor = class {
4330
5874
  constructor() {
4331
5875
  /** AxisSensor identifier */
@@ -4399,14 +5943,14 @@ var BodyBudgetSensor = class {
4399
5943
  };
4400
5944
  BodyBudgetSensor = __decorateClass([
4401
5945
  Sensor(),
4402
- (0, import_common12.Injectable)()
5946
+ (0, import_common13.Injectable)()
4403
5947
  ], BodyBudgetSensor);
4404
5948
 
4405
5949
  // src/sensors/capability-enforcement.sensor.ts
4406
- var import_common13 = require("@nestjs/common");
5950
+ var import_common14 = require("@nestjs/common");
4407
5951
  var CapabilityEnforcementSensor = class {
4408
5952
  constructor() {
4409
- this.logger = new import_common13.Logger(CapabilityEnforcementSensor.name);
5953
+ this.logger = new import_common14.Logger(CapabilityEnforcementSensor.name);
4410
5954
  /** AxisSensor identifier for logging and registry */
4411
5955
  this.name = "CapabilityEnforcementSensor";
4412
5956
  /**
@@ -4500,12 +6044,12 @@ var CapabilityEnforcementSensor = class {
4500
6044
  };
4501
6045
  CapabilityEnforcementSensor = __decorateClass([
4502
6046
  Sensor(),
4503
- (0, import_common13.Injectable)()
6047
+ (0, import_common14.Injectable)()
4504
6048
  ], CapabilityEnforcementSensor);
4505
6049
 
4506
6050
  // src/sensors/chunk-hash.sensor.ts
4507
- var import_common14 = require("@nestjs/common");
4508
- var import_crypto6 = require("crypto");
6051
+ var import_common15 = require("@nestjs/common");
6052
+ var import_crypto8 = require("crypto");
4509
6053
  var ChunkHashSensor = class {
4510
6054
  constructor() {
4511
6055
  /** Sensor identifier */
@@ -4564,7 +6108,7 @@ var ChunkHashSensor = class {
4564
6108
  reason: "Missing sha256Chunk TLV in header"
4565
6109
  };
4566
6110
  }
4567
- const actual = (0, import_crypto6.createHash)("sha256").update(bodyBytes).digest();
6111
+ const actual = (0, import_crypto8.createHash)("sha256").update(bodyBytes).digest();
4568
6112
  if (!Buffer.from(actual).equals(Buffer.from(expected))) {
4569
6113
  return {
4570
6114
  action: "DENY",
@@ -4577,15 +6121,15 @@ var ChunkHashSensor = class {
4577
6121
  };
4578
6122
  ChunkHashSensor = __decorateClass([
4579
6123
  Sensor(),
4580
- (0, import_common14.Injectable)()
6124
+ (0, import_common15.Injectable)()
4581
6125
  ], ChunkHashSensor);
4582
6126
 
4583
6127
  // src/sensors/entropy.sensor.ts
4584
- var import_common15 = require("@nestjs/common");
4585
- var crypto4 = __toESM(require("crypto"));
6128
+ var import_common16 = require("@nestjs/common");
6129
+ var crypto5 = __toESM(require("crypto"));
4586
6130
  var EntropySensor = class {
4587
6131
  constructor() {
4588
- this.logger = new import_common15.Logger(EntropySensor.name);
6132
+ this.logger = new import_common16.Logger(EntropySensor.name);
4589
6133
  /**
4590
6134
  * Minimum acceptable entropy in bits per byte.
4591
6135
  *
@@ -4750,19 +6294,19 @@ var EntropySensor = class {
4750
6294
  * @returns {Uint8Array} Cryptographically secure random bytes
4751
6295
  */
4752
6296
  static generateSecureRandom(length) {
4753
- return new Uint8Array(crypto4.randomBytes(length));
6297
+ return new Uint8Array(crypto5.randomBytes(length));
4754
6298
  }
4755
6299
  };
4756
6300
  EntropySensor = __decorateClass([
4757
6301
  Sensor(),
4758
- (0, import_common15.Injectable)()
6302
+ (0, import_common16.Injectable)()
4759
6303
  ], EntropySensor);
4760
6304
 
4761
6305
  // src/sensors/execution-timeout.sensor.ts
4762
- var import_common16 = require("@nestjs/common");
6306
+ var import_common17 = require("@nestjs/common");
4763
6307
  var ExecutionTimeoutSensor = class {
4764
6308
  constructor() {
4765
- this.logger = new import_common16.Logger(ExecutionTimeoutSensor.name);
6309
+ this.logger = new import_common17.Logger(ExecutionTimeoutSensor.name);
4766
6310
  /** AxisSensor identifier */
4767
6311
  this.name = "ExecutionTimeoutSensor";
4768
6312
  /**
@@ -4840,11 +6384,11 @@ var ExecutionTimeoutSensor = class {
4840
6384
  };
4841
6385
  ExecutionTimeoutSensor = __decorateClass([
4842
6386
  Sensor(),
4843
- (0, import_common16.Injectable)()
6387
+ (0, import_common17.Injectable)()
4844
6388
  ], ExecutionTimeoutSensor);
4845
6389
 
4846
6390
  // src/sensors/frame-budget.sensor.ts
4847
- var import_common17 = require("@nestjs/common");
6391
+ var import_common18 = require("@nestjs/common");
4848
6392
  var FrameBudgetSensor = class {
4849
6393
  constructor(config) {
4850
6394
  this.config = config;
@@ -4903,11 +6447,11 @@ var FrameBudgetSensor = class {
4903
6447
  };
4904
6448
  FrameBudgetSensor = __decorateClass([
4905
6449
  Sensor({ phase: "PRE_DECODE" }),
4906
- (0, import_common17.Injectable)()
6450
+ (0, import_common18.Injectable)()
4907
6451
  ], FrameBudgetSensor);
4908
6452
 
4909
6453
  // src/sensors/frame-header-sanity.sensor.ts
4910
- var import_common18 = require("@nestjs/common");
6454
+ var import_common19 = require("@nestjs/common");
4911
6455
  var FrameHeaderSanitySensor = class {
4912
6456
  constructor() {
4913
6457
  this.name = "FrameHeaderSanitySensor";
@@ -4951,12 +6495,12 @@ var FrameHeaderSanitySensor = class {
4951
6495
  }
4952
6496
  };
4953
6497
  FrameHeaderSanitySensor = __decorateClass([
4954
- (0, import_common18.Injectable)(),
6498
+ (0, import_common19.Injectable)(),
4955
6499
  Sensor({ phase: "PRE_DECODE" })
4956
6500
  ], FrameHeaderSanitySensor);
4957
6501
 
4958
6502
  // src/sensors/header-tlv-limit.sensor.ts
4959
- var import_common19 = require("@nestjs/common");
6503
+ var import_common20 = require("@nestjs/common");
4960
6504
  var HeaderTLVLimitSensor = class {
4961
6505
  constructor() {
4962
6506
  this.name = "HeaderTLVLimitSensor";
@@ -4988,12 +6532,12 @@ var HeaderTLVLimitSensor = class {
4988
6532
  }
4989
6533
  };
4990
6534
  HeaderTLVLimitSensor = __decorateClass([
4991
- (0, import_common19.Injectable)(),
6535
+ (0, import_common20.Injectable)(),
4992
6536
  Sensor()
4993
6537
  ], HeaderTLVLimitSensor);
4994
6538
 
4995
6539
  // src/sensors/intent-allowlist.sensor.ts
4996
- var import_common20 = require("@nestjs/common");
6540
+ var import_common21 = require("@nestjs/common");
4997
6541
  var PUBLIC_INTENT_ALLOWLIST = [
4998
6542
  "public.",
4999
6543
  "schema.",
@@ -5028,12 +6572,12 @@ var IntentAllowlistSensor = class {
5028
6572
  }
5029
6573
  };
5030
6574
  IntentAllowlistSensor = __decorateClass([
5031
- (0, import_common20.Injectable)(),
6575
+ (0, import_common21.Injectable)(),
5032
6576
  Sensor()
5033
6577
  ], IntentAllowlistSensor);
5034
6578
 
5035
6579
  // src/sensors/intent-registry.sensor.ts
5036
- var import_common21 = require("@nestjs/common");
6580
+ var import_common22 = require("@nestjs/common");
5037
6581
  var IntentRegistrySensor = class {
5038
6582
  constructor(router) {
5039
6583
  this.router = router;
@@ -5056,12 +6600,12 @@ var IntentRegistrySensor = class {
5056
6600
  }
5057
6601
  };
5058
6602
  IntentRegistrySensor = __decorateClass([
5059
- (0, import_common21.Injectable)(),
6603
+ (0, import_common22.Injectable)(),
5060
6604
  Sensor({ phase: "POST_DECODE" })
5061
6605
  ], IntentRegistrySensor);
5062
6606
 
5063
6607
  // src/sensors/proof-presence.sensor.ts
5064
- var import_common22 = require("@nestjs/common");
6608
+ var import_common23 = require("@nestjs/common");
5065
6609
  var ProofPresenceSensor = class {
5066
6610
  constructor() {
5067
6611
  this.name = "ProofPresenceSensor";
@@ -5109,11 +6653,11 @@ var ProofPresenceSensor = class {
5109
6653
  };
5110
6654
  ProofPresenceSensor = __decorateClass([
5111
6655
  Sensor(),
5112
- (0, import_common22.Injectable)()
6656
+ (0, import_common23.Injectable)()
5113
6657
  ], ProofPresenceSensor);
5114
6658
 
5115
6659
  // src/sensors/protocol-strict.sensor.ts
5116
- var import_common23 = require("@nestjs/common");
6660
+ var import_common24 = require("@nestjs/common");
5117
6661
  var VALID_FLAGS = [
5118
6662
  0,
5119
6663
  // No flags
@@ -5131,7 +6675,7 @@ var VALID_FLAGS = [
5131
6675
  var ProtocolStrictSensor = class {
5132
6676
  constructor(config) {
5133
6677
  this.config = config;
5134
- this.logger = new import_common23.Logger(ProtocolStrictSensor.name);
6678
+ this.logger = new import_common24.Logger(ProtocolStrictSensor.name);
5135
6679
  /** Sensor identifier for logging and registry */
5136
6680
  this.name = "ProtocolStrictSensor";
5137
6681
  /**
@@ -5384,11 +6928,11 @@ var ProtocolStrictSensor = class {
5384
6928
  };
5385
6929
  ProtocolStrictSensor = __decorateClass([
5386
6930
  Sensor({ phase: "PRE_DECODE" }),
5387
- (0, import_common23.Injectable)()
6931
+ (0, import_common24.Injectable)()
5388
6932
  ], ProtocolStrictSensor);
5389
6933
 
5390
6934
  // src/sensors/receipt-policy.sensor.ts
5391
- var import_common24 = require("@nestjs/common");
6935
+ var import_common25 = require("@nestjs/common");
5392
6936
  var ReceiptPolicySensor = class {
5393
6937
  constructor() {
5394
6938
  this.name = "ReceiptPolicySensor";
@@ -5402,12 +6946,12 @@ var ReceiptPolicySensor = class {
5402
6946
  }
5403
6947
  };
5404
6948
  ReceiptPolicySensor = __decorateClass([
5405
- (0, import_common24.Injectable)(),
6949
+ (0, import_common25.Injectable)(),
5406
6950
  Sensor()
5407
6951
  ], ReceiptPolicySensor);
5408
6952
 
5409
6953
  // src/sensors/schema-validation.sensor.ts
5410
- var import_common25 = require("@nestjs/common");
6954
+ var import_common26 = require("@nestjs/common");
5411
6955
  function readU64be(b) {
5412
6956
  if (b.length !== 8)
5413
6957
  throw new AxisError("SCHEMA_TYPE_MISMATCH", "u64 must be 8 bytes", 400);
@@ -5582,11 +7126,11 @@ var SchemaValidationSensor = class {
5582
7126
  };
5583
7127
  SchemaValidationSensor = __decorateClass([
5584
7128
  Sensor(),
5585
- (0, import_common25.Injectable)()
7129
+ (0, import_common26.Injectable)()
5586
7130
  ], SchemaValidationSensor);
5587
7131
 
5588
7132
  // src/sensors/stream-scope.sensor.ts
5589
- var import_common26 = require("@nestjs/common");
7133
+ var import_common27 = require("@nestjs/common");
5590
7134
  var StreamScopeSensor = class {
5591
7135
  constructor() {
5592
7136
  /** Sensor identifier */
@@ -5632,11 +7176,11 @@ var StreamScopeSensor = class {
5632
7176
  };
5633
7177
  StreamScopeSensor = __decorateClass([
5634
7178
  Sensor(),
5635
- (0, import_common26.Injectable)()
7179
+ (0, import_common27.Injectable)()
5636
7180
  ], StreamScopeSensor);
5637
7181
 
5638
7182
  // src/sensors/tlv-parse.sensor.ts
5639
- var import_common27 = require("@nestjs/common");
7183
+ var import_common28 = require("@nestjs/common");
5640
7184
  var TLVParseSensor = class {
5641
7185
  constructor() {
5642
7186
  this.name = "TLVParseSensor";
@@ -5738,11 +7282,11 @@ var TLVParseSensor = class {
5738
7282
  };
5739
7283
  TLVParseSensor = __decorateClass([
5740
7284
  Sensor(),
5741
- (0, import_common27.Injectable)()
7285
+ (0, import_common28.Injectable)()
5742
7286
  ], TLVParseSensor);
5743
7287
 
5744
7288
  // src/sensors/varint-hardening.sensor.ts
5745
- var import_common28 = require("@nestjs/common");
7289
+ var import_common29 = require("@nestjs/common");
5746
7290
  var VarintHardeningSensor = class {
5747
7291
  constructor() {
5748
7292
  /** Sensor identifier */
@@ -5805,7 +7349,7 @@ var VarintHardeningSensor = class {
5805
7349
  };
5806
7350
  VarintHardeningSensor = __decorateClass([
5807
7351
  Sensor({ phase: "PRE_DECODE" }),
5808
- (0, import_common28.Injectable)()
7352
+ (0, import_common29.Injectable)()
5809
7353
  ], VarintHardeningSensor);
5810
7354
 
5811
7355
  // src/utils/index.ts
@@ -5877,16 +7421,26 @@ function toBuffer(value) {
5877
7421
  AXIS_UPLOAD_SESSION_STORE,
5878
7422
  AXIS_VERSION,
5879
7423
  Ats1Codec,
7424
+ AxisContext,
7425
+ AxisDemoPubkey,
7426
+ AxisError,
5880
7427
  AxisFilesDownloadHandler,
5881
7428
  AxisFilesFinalizeHandler,
5882
7429
  AxisFrameZ,
5883
7430
  AxisIdDto,
7431
+ AxisIp,
5884
7432
  AxisPacketTags,
5885
7433
  AxisPartialType,
7434
+ AxisRaw,
5886
7435
  AxisResponseDto,
7436
+ AxisSensorChainService,
5887
7437
  AxisTlvDto,
7438
+ BAND,
5888
7439
  BodyProfile,
5889
7440
  CAPABILITIES,
7441
+ CCE_ERROR,
7442
+ CCE_PROTOCOL_VERSION,
7443
+ CceError,
5890
7444
  ContractViolationError,
5891
7445
  DEFAULT_CONTRACTS,
5892
7446
  DEFAULT_TIMEOUT,
@@ -5902,7 +7456,10 @@ function toBuffer(value) {
5902
7456
  FLAG_CHAIN_REQ,
5903
7457
  FLAG_HAS_WITNESS,
5904
7458
  HANDLER_METADATA_KEY,
7459
+ HANDLER_SENSORS_KEY,
5905
7460
  Handler,
7461
+ HandlerDiscoveryService,
7462
+ HandlerSensors,
5906
7463
  INTENT_BODY_KEY,
5907
7464
  INTENT_METADATA_KEY,
5908
7465
  INTENT_REQUIREMENTS,
@@ -5929,6 +7486,7 @@ function toBuffer(value) {
5929
7486
  NCERT_PUB,
5930
7487
  NCERT_SCOPE,
5931
7488
  NCERT_SIG,
7489
+ PRE_DECODE_BOUNDARY,
5932
7490
  PROOF_CAPABILITIES,
5933
7491
  PROOF_CAPSULE,
5934
7492
  PROOF_JWT,
@@ -5943,11 +7501,15 @@ function toBuffer(value) {
5943
7501
  RESPONSE_TAG_UPDATED_AT,
5944
7502
  RESPONSE_TAG_UPDATED_BY,
5945
7503
  RiskDecision,
7504
+ SENSOR_METADATA_KEY,
5946
7505
  Schema2002_PasskeyLoginOptionsRes,
5947
7506
  Schema2011_PasskeyLoginVerifyReq,
5948
7507
  Schema2012_PasskeyLoginVerifyRes,
5949
7508
  Schema2021_PasskeyRegisterOptionsReq,
7509
+ Sensor,
5950
7510
  SensorDecisions,
7511
+ SensorDiscoveryService,
7512
+ SensorRegistry,
5951
7513
  TLV,
5952
7514
  TLV_ACTOR_ID,
5953
7515
  TLV_AUD,
@@ -6005,10 +7567,12 @@ function toBuffer(value) {
6005
7567
  canonicalJson,
6006
7568
  canonicalJsonExcluding,
6007
7569
  canonicalizeObservation,
7570
+ cce,
6008
7571
  classifyIntent,
6009
7572
  computeReceiptHash,
6010
7573
  computeSignaturePayload,
6011
7574
  core,
7575
+ createObservation,
6012
7576
  crypto,
6013
7577
  decodeArray,
6014
7578
  decodeAxis1Frame,
@@ -6025,8 +7589,11 @@ function toBuffer(value) {
6025
7589
  encodeQueueMessage,
6026
7590
  encodeTLVs,
6027
7591
  encodeVarint,
7592
+ endStage,
6028
7593
  engine,
7594
+ executeCcePipeline,
6029
7595
  extractDtoSchema,
7596
+ finalizeObservation,
6030
7597
  generateEd25519KeyPair,
6031
7598
  getSignTarget,
6032
7599
  hasScope,
@@ -6045,6 +7612,7 @@ function toBuffer(value) {
6045
7612
  parseAutoClaimEntries,
6046
7613
  parseScope,
6047
7614
  parseStreamEntries,
7615
+ recordSensor,
6048
7616
  resolveTimeout,
6049
7617
  schemas,
6050
7618
  security,
@@ -6053,6 +7621,7 @@ function toBuffer(value) {
6053
7621
  sha256,
6054
7622
  signFrame,
6055
7623
  stableJsonStringify,
7624
+ startStage,
6056
7625
  tlv,
6057
7626
  u64be,
6058
7627
  unpackPasskeyLoginOptionsReq,