@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.mjs CHANGED
@@ -75,6 +75,22 @@ function IntentSensors(sensors) {
75
75
  };
76
76
  }
77
77
 
78
+ // src/decorators/handler-sensors.decorator.ts
79
+ import "reflect-metadata";
80
+ var HANDLER_SENSORS_KEY = "axis:handler:sensors";
81
+ function HandlerSensors(sensors) {
82
+ return (target) => {
83
+ Reflect.defineMetadata(HANDLER_SENSORS_KEY, sensors, target);
84
+ };
85
+ }
86
+
87
+ // src/decorators/sensor.decorator.ts
88
+ import { SetMetadata as SetMetadata2 } from "@nestjs/common";
89
+ var SENSOR_METADATA_KEY = "axis:sensor";
90
+ function Sensor(options) {
91
+ return SetMetadata2(SENSOR_METADATA_KEY, options ?? true);
92
+ }
93
+
78
94
  // src/decorators/tlv-field.decorator.ts
79
95
  import "reflect-metadata";
80
96
  var TLV_FIELDS_KEY = "axis:tlv:fields";
@@ -389,6 +405,626 @@ var SensorDecisions = {
389
405
  }
390
406
  };
391
407
 
408
+ // src/cce/cce-derivation.service.ts
409
+ import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
410
+ import { hkdf } from "@noble/hashes/hkdf.js";
411
+ import { sha256 } from "@noble/hashes/sha2.js";
412
+
413
+ // src/cce/cce.types.ts
414
+ var CCE_PROTOCOL_VERSION = "cce-v1";
415
+ var CCE_DERIVATION = {
416
+ /** Request execution context */
417
+ REQUEST: "axis:cce:req:v1",
418
+ /** Response execution context */
419
+ RESPONSE: "axis:cce:resp:v1",
420
+ /** Witness binding context */
421
+ WITNESS: "axis:cce:witness:v1"
422
+ };
423
+ var CCE_AES_KEY_BYTES = 32;
424
+ var CCE_IV_BYTES = 12;
425
+ var CCE_TAG_BYTES = 16;
426
+ var CCE_NONCE_BYTES = 32;
427
+ var CCE_ERROR = {
428
+ // Envelope errors
429
+ INVALID_ENVELOPE: "CCE_INVALID_ENVELOPE",
430
+ UNSUPPORTED_VERSION: "CCE_UNSUPPORTED_VERSION",
431
+ MISSING_CAPSULE: "CCE_MISSING_CAPSULE",
432
+ MISSING_ENCRYPTED_KEY: "CCE_MISSING_ENCRYPTED_KEY",
433
+ // Signature errors
434
+ CLIENT_SIG_INVALID: "CCE_CLIENT_SIG_INVALID",
435
+ CLIENT_KEY_NOT_FOUND: "CCE_CLIENT_KEY_NOT_FOUND",
436
+ // Capsule errors
437
+ CAPSULE_SIG_INVALID: "CCE_CAPSULE_SIG_INVALID",
438
+ CAPSULE_EXPIRED: "CCE_CAPSULE_EXPIRED",
439
+ CAPSULE_NOT_YET_VALID: "CCE_CAPSULE_NOT_YET_VALID",
440
+ CAPSULE_REVOKED: "CCE_CAPSULE_REVOKED",
441
+ CAPSULE_CONSUMED: "CCE_CAPSULE_CONSUMED",
442
+ // Binding errors
443
+ AUDIENCE_MISMATCH: "CCE_AUDIENCE_MISMATCH",
444
+ INTENT_MISMATCH: "CCE_INTENT_MISMATCH",
445
+ TPS_WINDOW_EXPIRED: "CCE_TPS_WINDOW_EXPIRED",
446
+ TPS_WINDOW_FUTURE: "CCE_TPS_WINDOW_FUTURE",
447
+ // Replay / nonce errors
448
+ REPLAY_DETECTED: "CCE_REPLAY_DETECTED",
449
+ NONCE_REUSED: "CCE_NONCE_REUSED",
450
+ // Decryption errors
451
+ DECRYPTION_FAILED: "CCE_DECRYPTION_FAILED",
452
+ KEY_UNWRAP_FAILED: "CCE_KEY_UNWRAP_FAILED",
453
+ AEAD_TAG_MISMATCH: "CCE_AEAD_TAG_MISMATCH",
454
+ PAYLOAD_TOO_LARGE: "CCE_PAYLOAD_TOO_LARGE",
455
+ // Schema / validation errors
456
+ PAYLOAD_SCHEMA_INVALID: "CCE_PAYLOAD_SCHEMA_INVALID",
457
+ INTENT_SCHEMA_MISMATCH: "CCE_INTENT_SCHEMA_MISMATCH",
458
+ // Policy errors
459
+ POLICY_DENIED: "CCE_POLICY_DENIED",
460
+ CONSTRAINT_VIOLATED: "CCE_CONSTRAINT_VIOLATED",
461
+ // Handler errors
462
+ HANDLER_NOT_FOUND: "CCE_HANDLER_NOT_FOUND",
463
+ HANDLER_EXECUTION_FAILED: "CCE_HANDLER_EXECUTION_FAILED",
464
+ HANDLER_TIMEOUT: "CCE_HANDLER_TIMEOUT",
465
+ // Response errors
466
+ RESPONSE_ENCRYPTION_FAILED: "CCE_RESPONSE_ENCRYPTION_FAILED"
467
+ };
468
+ var CceError = class extends Error {
469
+ constructor(code, message, metadata) {
470
+ super(`[${code}] ${message}`);
471
+ this.code = code;
472
+ this.metadata = metadata;
473
+ this.name = "CceError";
474
+ }
475
+ /** Whether this error is safe to expose to the client */
476
+ get clientSafe() {
477
+ const internal = [
478
+ CCE_ERROR.DECRYPTION_FAILED,
479
+ CCE_ERROR.KEY_UNWRAP_FAILED,
480
+ CCE_ERROR.AEAD_TAG_MISMATCH,
481
+ CCE_ERROR.HANDLER_EXECUTION_FAILED,
482
+ CCE_ERROR.RESPONSE_ENCRYPTION_FAILED
483
+ ];
484
+ return !internal.includes(this.code);
485
+ }
486
+ /** Get client-safe representation */
487
+ toClientError() {
488
+ if (this.clientSafe) {
489
+ return { code: this.code, message: this.message };
490
+ }
491
+ return {
492
+ code: CCE_ERROR.DECRYPTION_FAILED,
493
+ message: "Request processing failed"
494
+ };
495
+ }
496
+ };
497
+
498
+ // src/cce/cce-derivation.service.ts
499
+ function buildSalt(capsuleId, capsuleNonce, requestNonce) {
500
+ const encoder = new TextEncoder();
501
+ const data = encoder.encode(
502
+ capsuleId + "|" + capsuleNonce + "|" + requestNonce
503
+ );
504
+ return sha256(data);
505
+ }
506
+ function buildInfo(contextPrefix, capsule, extraNonce) {
507
+ const encoder = new TextEncoder();
508
+ const parts = [
509
+ contextPrefix,
510
+ capsule.sub,
511
+ capsule.kid,
512
+ capsule.intent,
513
+ capsule.aud,
514
+ String(capsule.tps_from),
515
+ String(capsule.tps_to),
516
+ capsule.policy_hash ?? "",
517
+ capsule.ver
518
+ ];
519
+ if (extraNonce) {
520
+ parts.push(extraNonce);
521
+ }
522
+ return encoder.encode(parts.join("|"));
523
+ }
524
+ function deriveRequestExecutionKey(input) {
525
+ const ikm = hexToBytes(input.axisLocalSecret);
526
+ const salt = buildSalt(
527
+ input.capsule.capsule_id,
528
+ input.capsule.capsule_nonce,
529
+ input.requestNonce
530
+ );
531
+ const info = buildInfo(CCE_DERIVATION.REQUEST, input.capsule);
532
+ return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
533
+ }
534
+ function deriveResponseExecutionKey(input) {
535
+ const ikm = hexToBytes(input.axisLocalSecret);
536
+ const encoder = new TextEncoder();
537
+ const saltData = encoder.encode(
538
+ input.capsule.capsule_id + "|" + input.capsule.capsule_nonce + "|" + input.requestNonce + "|" + input.responseNonce
539
+ );
540
+ const salt = sha256(saltData);
541
+ const info = buildInfo(
542
+ CCE_DERIVATION.RESPONSE,
543
+ input.capsule,
544
+ input.responseNonce
545
+ );
546
+ return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
547
+ }
548
+ function deriveWitnessKey(input) {
549
+ const ikm = hexToBytes(input.axisLocalSecret);
550
+ const salt = buildSalt(
551
+ input.capsule.capsule_id,
552
+ input.capsule.capsule_nonce,
553
+ input.requestNonce
554
+ );
555
+ const info = buildInfo(CCE_DERIVATION.WITNESS, input.capsule);
556
+ return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
557
+ }
558
+ function buildExecutionContext(input, requestId) {
559
+ const executionKey = deriveRequestExecutionKey(input);
560
+ const keyHash = bytesToHex(sha256(executionKey));
561
+ executionKey.fill(0);
562
+ return {
563
+ execution_key_hash: keyHash,
564
+ request_id: requestId,
565
+ capsule_id: input.capsule.capsule_id,
566
+ sub: input.capsule.sub,
567
+ kid: input.capsule.kid,
568
+ intent: input.capsule.intent,
569
+ aud: input.capsule.aud,
570
+ tps_from: input.capsule.tps_from,
571
+ tps_to: input.capsule.tps_to,
572
+ policy_hash: input.capsule.policy_hash,
573
+ derived_at: Math.floor(Date.now() / 1e3),
574
+ valid: true
575
+ };
576
+ }
577
+ function generateCceNonce() {
578
+ const bytes2 = new Uint8Array(CCE_NONCE_BYTES);
579
+ crypto.getRandomValues(bytes2);
580
+ return bytesToHex(bytes2);
581
+ }
582
+
583
+ // src/cce/cce-response.service.ts
584
+ import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils.js";
585
+ import { randomBytes as randomBytes2 } from "crypto";
586
+
587
+ // src/cce/cce-crypto.ts
588
+ import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
589
+ import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
590
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
591
+ function aesGcmEncrypt(key, plaintext, aad) {
592
+ if (key.length !== CCE_AES_KEY_BYTES) {
593
+ throw new Error(`AES key must be ${CCE_AES_KEY_BYTES} bytes`);
594
+ }
595
+ const iv = randomBytes(CCE_IV_BYTES);
596
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
597
+ if (aad) {
598
+ cipher.setAAD(aad);
599
+ }
600
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
601
+ const tag = cipher.getAuthTag();
602
+ return {
603
+ iv: new Uint8Array(iv),
604
+ ciphertext: new Uint8Array(encrypted),
605
+ tag: new Uint8Array(tag)
606
+ };
607
+ }
608
+ function aesGcmDecrypt(key, iv, ciphertext, tag, aad) {
609
+ if (key.length !== CCE_AES_KEY_BYTES) {
610
+ throw new Error(`AES key must be ${CCE_AES_KEY_BYTES} bytes`);
611
+ }
612
+ if (iv.length !== CCE_IV_BYTES) {
613
+ throw new Error(`IV must be ${CCE_IV_BYTES} bytes`);
614
+ }
615
+ if (tag.length !== CCE_TAG_BYTES) {
616
+ throw new Error(`Tag must be ${CCE_TAG_BYTES} bytes`);
617
+ }
618
+ try {
619
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
620
+ decipher.setAuthTag(tag);
621
+ if (aad) {
622
+ decipher.setAAD(aad);
623
+ }
624
+ const decrypted = Buffer.concat([
625
+ decipher.update(ciphertext),
626
+ decipher.final()
627
+ ]);
628
+ return new Uint8Array(decrypted);
629
+ } catch {
630
+ return null;
631
+ }
632
+ }
633
+ function generateAesKey() {
634
+ return new Uint8Array(randomBytes(CCE_AES_KEY_BYTES));
635
+ }
636
+ function generateIv() {
637
+ return new Uint8Array(randomBytes(CCE_IV_BYTES));
638
+ }
639
+ function base64UrlEncode(bytes2) {
640
+ return Buffer.from(bytes2).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
641
+ }
642
+ function base64UrlDecode(input) {
643
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
644
+ const padding = "=".repeat((4 - base64.length % 4) % 4);
645
+ return new Uint8Array(Buffer.from(base64 + padding, "base64"));
646
+ }
647
+ function hashPayload(payload) {
648
+ return bytesToHex2(sha2562(payload));
649
+ }
650
+ var nodeAesGcmProvider = {
651
+ async decrypt(key, iv, ciphertext, tag, aad) {
652
+ return aesGcmDecrypt(key, iv, ciphertext, tag, aad);
653
+ }
654
+ };
655
+
656
+ // src/cce/cce-response.service.ts
657
+ async function buildCceResponse(options, clientKeyEncryptor, axisSigner) {
658
+ const { request, capsule, status, body, clientPublicKeyHex, witnessRef } = options;
659
+ const responseNonce = bytesToHex3(
660
+ new Uint8Array(randomBytes2(CCE_NONCE_BYTES))
661
+ );
662
+ const responseId = generateResponseId();
663
+ const aesKey = generateAesKey();
664
+ const aad = buildResponseAad(
665
+ request.request_id,
666
+ responseId,
667
+ request.correlation_id,
668
+ capsule.capsule_id,
669
+ responseNonce
670
+ );
671
+ const { iv, ciphertext, tag } = aesGcmEncrypt(aesKey, body, aad);
672
+ const encryptedKey = await clientKeyEncryptor.wrapKey(
673
+ aesKey,
674
+ request.client_kid,
675
+ clientPublicKeyHex
676
+ );
677
+ aesKey.fill(0);
678
+ const encryptedPayload = {
679
+ alg: "AES-256-GCM",
680
+ iv: base64UrlEncode(iv),
681
+ ciphertext: base64UrlEncode(ciphertext),
682
+ tag: base64UrlEncode(tag)
683
+ };
684
+ const algorithms = {
685
+ kem: encryptedKey.alg,
686
+ enc: "AES-256-GCM",
687
+ kdf: "HKDF-SHA256",
688
+ sig: "EdDSA"
689
+ };
690
+ const unsignedResponse = {
691
+ ver: CCE_PROTOCOL_VERSION,
692
+ response_id: responseId,
693
+ request_id: request.request_id,
694
+ correlation_id: request.correlation_id,
695
+ encrypted_key: encryptedKey,
696
+ encrypted_payload: encryptedPayload,
697
+ response_nonce: responseNonce,
698
+ algorithms,
699
+ status,
700
+ ...witnessRef ? { witness_ref: witnessRef } : {}
701
+ };
702
+ const signPayload = new TextEncoder().encode(canonicalize(unsignedResponse));
703
+ const axisSig = await axisSigner.sign(signPayload);
704
+ const envelope = {
705
+ ...unsignedResponse,
706
+ axis_sig: axisSig
707
+ };
708
+ return {
709
+ envelope,
710
+ responsePayloadHash: hashPayload(body)
711
+ };
712
+ }
713
+ function buildCceErrorResponse(requestId, correlationId, status, errorCode, message) {
714
+ return {
715
+ ver: CCE_PROTOCOL_VERSION,
716
+ request_id: requestId,
717
+ correlation_id: correlationId,
718
+ status,
719
+ error: { code: errorCode, message }
720
+ };
721
+ }
722
+ function generateResponseId() {
723
+ const bytes2 = randomBytes2(16);
724
+ return "resp_" + bytesToHex3(new Uint8Array(bytes2)).slice(0, 24);
725
+ }
726
+ function buildResponseAad(requestId, responseId, correlationId, capsuleId, responseNonce) {
727
+ const parts = [
728
+ requestId,
729
+ responseId,
730
+ correlationId,
731
+ capsuleId,
732
+ responseNonce
733
+ ];
734
+ return new TextEncoder().encode(parts.join("|"));
735
+ }
736
+ function canonicalize(obj) {
737
+ if (Array.isArray(obj)) {
738
+ return "[" + obj.map(canonicalize).join(",") + "]";
739
+ }
740
+ if (obj !== null && typeof obj === "object") {
741
+ const sorted = Object.keys(obj).sort().map(
742
+ (k) => JSON.stringify(k) + ":" + canonicalize(obj[k])
743
+ );
744
+ return "{" + sorted.join(",") + "}";
745
+ }
746
+ return JSON.stringify(obj);
747
+ }
748
+
749
+ // src/cce/cce-witness.observer.ts
750
+ import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils.js";
751
+ import { hkdf as hkdf2 } from "@noble/hashes/hkdf.js";
752
+ import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
753
+ var InMemoryCceWitnessStore = class {
754
+ constructor() {
755
+ this.records = [];
756
+ }
757
+ async record(witness) {
758
+ this.records.push(witness);
759
+ }
760
+ getByRequestId(requestId) {
761
+ return this.records.find((w) => w.request_id === requestId);
762
+ }
763
+ getByCapsuleId(capsuleId) {
764
+ return this.records.filter((w) => w.capsule_id === capsuleId);
765
+ }
766
+ };
767
+ function buildWitnessRecord(envelope, capsule, verification, execution, options) {
768
+ const witnessId = generateWitnessId(envelope.request_id, capsule.capsule_id);
769
+ const executionContextHash = computeExecutionContextHash(
770
+ options.axisLocalSecret,
771
+ capsule,
772
+ envelope.request_nonce
773
+ );
774
+ return {
775
+ witness_id: witnessId,
776
+ request_id: envelope.request_id,
777
+ capsule_id: capsule.capsule_id,
778
+ sub: capsule.sub,
779
+ intent: capsule.intent,
780
+ aud: capsule.aud,
781
+ tps_from: capsule.tps_from,
782
+ tps_to: capsule.tps_to,
783
+ timestamp: Math.floor(Date.now() / 1e3),
784
+ verification: {
785
+ client_sig: verification.clientSigVerified,
786
+ capsule_sig: verification.capsuleSigVerified,
787
+ tps_valid: verification.tpsValid,
788
+ audience_match: verification.audienceMatch,
789
+ intent_match: verification.intentMatch,
790
+ replay_clean: verification.replayClean,
791
+ nonce_unique: verification.nonceUnique,
792
+ decryption_ok: verification.decryptionOk
793
+ },
794
+ execution: {
795
+ status: execution.status,
796
+ handler_duration_ms: execution.handlerDurationMs,
797
+ ...execution.effect ? { effect: execution.effect } : {}
798
+ },
799
+ response_encrypted: options.responseEncrypted,
800
+ execution_context_hash: executionContextHash,
801
+ ...options.requestPayload ? { request_payload_hash: hashPayload(options.requestPayload) } : {},
802
+ ...options.responsePayload ? { response_payload_hash: hashPayload(options.responsePayload) } : {}
803
+ };
804
+ }
805
+ function extractVerificationState(metadata) {
806
+ return {
807
+ clientSigVerified: metadata.cceClientSigVerified === true,
808
+ capsuleSigVerified: metadata.cceCapsuleVerified === true,
809
+ tpsValid: metadata.cceTpsValid === true,
810
+ audienceMatch: metadata.cceBindingVerified === true,
811
+ intentMatch: metadata.cceBindingVerified === true,
812
+ replayClean: metadata.cceReplayClean === true,
813
+ nonceUnique: metadata.cceReplayClean === true,
814
+ decryptionOk: metadata.cceDecryptionOk === true
815
+ };
816
+ }
817
+ function generateWitnessId(requestId, capsuleId) {
818
+ const input = `witness:${requestId}:${capsuleId}:${Date.now()}`;
819
+ const hash = sha2563(new TextEncoder().encode(input));
820
+ return "wit_" + bytesToHex4(hash).slice(0, 24);
821
+ }
822
+ function computeExecutionContextHash(axisLocalSecret, capsule, requestNonce) {
823
+ const encoder = new TextEncoder();
824
+ const ikm = hexToBytes2(axisLocalSecret);
825
+ const salt = sha2563(
826
+ encoder.encode(
827
+ capsule.capsule_id + "|" + capsule.capsule_nonce + "|" + requestNonce
828
+ )
829
+ );
830
+ const info = encoder.encode(
831
+ [
832
+ CCE_DERIVATION.WITNESS,
833
+ capsule.sub,
834
+ capsule.kid,
835
+ capsule.intent,
836
+ capsule.aud,
837
+ String(capsule.tps_from),
838
+ String(capsule.tps_to),
839
+ capsule.policy_hash ?? "",
840
+ capsule.ver
841
+ ].join("|")
842
+ );
843
+ const witnessKey = hkdf2(sha2563, ikm, salt, info, 32);
844
+ const hash = bytesToHex4(sha2563(witnessKey));
845
+ witnessKey.fill(0);
846
+ return hash;
847
+ }
848
+ function hexToBytes2(hex) {
849
+ const bytes2 = new Uint8Array(hex.length / 2);
850
+ for (let i = 0; i < bytes2.length; i++) {
851
+ bytes2[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
852
+ }
853
+ return bytes2;
854
+ }
855
+
856
+ // src/cce/cce-pipeline.ts
857
+ async function executeCcePipeline(envelope, config) {
858
+ const startTime = Date.now();
859
+ if (envelope.ver !== CCE_PROTOCOL_VERSION) {
860
+ return {
861
+ ok: false,
862
+ error: {
863
+ code: CCE_ERROR.UNSUPPORTED_VERSION,
864
+ message: `Unsupported version: ${envelope.ver}`
865
+ },
866
+ status: "ERROR"
867
+ };
868
+ }
869
+ const sensorInput = {
870
+ intent: envelope.capsule.intent,
871
+ metadata: {
872
+ cce: true,
873
+ cceEnvelope: envelope,
874
+ contentType: "application/axis-cce"
875
+ }
876
+ };
877
+ const sortedSensors = [...config.sensors].sort(
878
+ (a, b) => (a.order ?? 999) - (b.order ?? 999)
879
+ );
880
+ for (const sensor of sortedSensors) {
881
+ if (sensor.supports && !sensor.supports(sensorInput)) {
882
+ continue;
883
+ }
884
+ let decision;
885
+ try {
886
+ decision = await sensor.run(sensorInput);
887
+ } catch (err) {
888
+ return {
889
+ ok: false,
890
+ error: {
891
+ code: CCE_ERROR.DECRYPTION_FAILED,
892
+ message: `Sensor ${sensor.name} failed`
893
+ },
894
+ status: "ERROR"
895
+ };
896
+ }
897
+ const normalized = normalizeSensorDecision(decision);
898
+ if (!normalized.allow) {
899
+ const code = normalized.reasons[0]?.split(":")[0] ?? CCE_ERROR.DECRYPTION_FAILED;
900
+ return {
901
+ ok: false,
902
+ error: { code, message: normalized.reasons.join("; ") },
903
+ status: "DENIED"
904
+ };
905
+ }
906
+ }
907
+ const capsule = sensorInput.metadata?.cceCapsule;
908
+ const decryptedPayload = sensorInput.metadata?.cceDecryptedPayload;
909
+ const clientKey = sensorInput.metadata?.cceClientKey;
910
+ if (!capsule || !decryptedPayload || !clientKey) {
911
+ return {
912
+ ok: false,
913
+ error: {
914
+ code: CCE_ERROR.DECRYPTION_FAILED,
915
+ message: "Sensor chain did not produce required outputs"
916
+ },
917
+ status: "ERROR"
918
+ };
919
+ }
920
+ const derivationInput = {
921
+ axisLocalSecret: config.axisLocalSecret,
922
+ capsule,
923
+ requestNonce: envelope.request_nonce
924
+ };
925
+ const executionContext = buildExecutionContext(
926
+ derivationInput,
927
+ envelope.request_id
928
+ );
929
+ const handler = config.handlers.get(capsule.intent);
930
+ if (!handler) {
931
+ return {
932
+ ok: false,
933
+ error: {
934
+ code: CCE_ERROR.HANDLER_NOT_FOUND,
935
+ message: `No handler for intent: ${capsule.intent}`
936
+ },
937
+ status: "ERROR"
938
+ };
939
+ }
940
+ const handlerContext = {
941
+ capsule,
942
+ executionContext,
943
+ envelope,
944
+ clientPublicKeyHex: clientKey.publicKeyHex,
945
+ intent: capsule.intent,
946
+ sub: capsule.sub
947
+ };
948
+ let result;
949
+ const handlerStart = Date.now();
950
+ try {
951
+ result = await handler(decryptedPayload, handlerContext);
952
+ } catch (err) {
953
+ const handlerDuration2 = Date.now() - handlerStart;
954
+ const verification2 = extractVerificationState(sensorInput.metadata ?? {});
955
+ const witness2 = buildWitnessRecord(
956
+ envelope,
957
+ capsule,
958
+ verification2,
959
+ { status: "FAILED", handlerDurationMs: handlerDuration2 },
960
+ {
961
+ axisLocalSecret: config.axisLocalSecret,
962
+ requestPayload: decryptedPayload,
963
+ responseEncrypted: false
964
+ }
965
+ );
966
+ await config.witnessStore.record(witness2);
967
+ return {
968
+ ok: false,
969
+ error: {
970
+ code: CCE_ERROR.HANDLER_EXECUTION_FAILED,
971
+ message: "Handler execution failed"
972
+ },
973
+ status: "FAILED"
974
+ };
975
+ }
976
+ const handlerDuration = Date.now() - handlerStart;
977
+ let responseEnvelope;
978
+ let responsePayloadHash;
979
+ try {
980
+ const responseResult = await buildCceResponse(
981
+ {
982
+ request: envelope,
983
+ capsule,
984
+ status: result.status,
985
+ body: result.body,
986
+ clientPublicKeyHex: clientKey.publicKeyHex
987
+ },
988
+ config.clientKeyEncryptor,
989
+ config.axisSigner
990
+ );
991
+ responseEnvelope = responseResult.envelope;
992
+ responsePayloadHash = responseResult.responsePayloadHash;
993
+ } catch (err) {
994
+ return {
995
+ ok: false,
996
+ error: {
997
+ code: CCE_ERROR.RESPONSE_ENCRYPTION_FAILED,
998
+ message: "Response encryption failed"
999
+ },
1000
+ status: "ERROR"
1001
+ };
1002
+ }
1003
+ const verification = extractVerificationState(sensorInput.metadata ?? {});
1004
+ const witness = buildWitnessRecord(
1005
+ envelope,
1006
+ capsule,
1007
+ verification,
1008
+ {
1009
+ status: result.status,
1010
+ handlerDurationMs: handlerDuration,
1011
+ effect: result.effect
1012
+ },
1013
+ {
1014
+ axisLocalSecret: config.axisLocalSecret,
1015
+ requestPayload: decryptedPayload,
1016
+ responsePayload: result.body,
1017
+ responseEncrypted: true
1018
+ }
1019
+ );
1020
+ await config.witnessStore.record(witness);
1021
+ return {
1022
+ ok: true,
1023
+ response: responseEnvelope,
1024
+ witnessId: witness.witness_id
1025
+ };
1026
+ }
1027
+
392
1028
  // src/engine/intent.router.ts
393
1029
  var IntentRouter = class {
394
1030
  constructor(moduleRef) {
@@ -406,6 +1042,10 @@ var IntentRouter = class {
406
1042
  this.intentValidators = /* @__PURE__ */ new Map();
407
1043
  /** Per-intent operation kind */
408
1044
  this.intentKinds = /* @__PURE__ */ new Map();
1045
+ /** CCE handler registry */
1046
+ this.cceHandlers = /* @__PURE__ */ new Map();
1047
+ /** CCE pipeline configuration (set via configureCce) */
1048
+ this.ccePipelineConfig = null;
409
1049
  }
410
1050
  getSchema(intent) {
411
1051
  return this.intentSchemas.get(intent);
@@ -455,6 +1095,7 @@ var IntentRouter = class {
455
1095
  );
456
1096
  const prefix = handlerMeta?.intent || instance.name;
457
1097
  const routes = Reflect.getMetadata(INTENT_ROUTES_KEY, instance.constructor) || [];
1098
+ const handlerSensors = Reflect.getMetadata(HANDLER_SENSORS_KEY, instance.constructor) || [];
458
1099
  for (const route of routes) {
459
1100
  const intentName = route.absolute ? route.action : `${prefix}.${route.action}`;
460
1101
  const fn = instance[route.methodName].bind(instance);
@@ -463,7 +1104,12 @@ var IntentRouter = class {
463
1104
  } else {
464
1105
  this.register(intentName, fn);
465
1106
  }
466
- this.registerIntentMeta(intentName, Object.getPrototypeOf(instance), String(route.methodName));
1107
+ this.registerIntentMeta(
1108
+ intentName,
1109
+ Object.getPrototypeOf(instance),
1110
+ String(route.methodName),
1111
+ handlerSensors
1112
+ );
467
1113
  }
468
1114
  const proto = Object.getPrototypeOf(instance);
469
1115
  for (const key of Object.getOwnPropertyNames(proto)) {
@@ -472,7 +1118,7 @@ var IntentRouter = class {
472
1118
  if (!this.handlers.has(meta.intent)) {
473
1119
  this.register(meta.intent, instance[key].bind(instance));
474
1120
  }
475
- this.registerIntentMeta(meta.intent, proto, key);
1121
+ this.registerIntentMeta(meta.intent, proto, key, handlerSensors);
476
1122
  }
477
1123
  }
478
1124
  /**
@@ -608,14 +1254,22 @@ var IntentRouter = class {
608
1254
  this.logger.warn(`${intent} failed in ${ms}ms - ${error}`);
609
1255
  }
610
1256
  }
611
- registerIntentMeta(intent, proto, methodName) {
1257
+ registerIntentMeta(intent, proto, methodName, handlerSensors) {
612
1258
  const decoder = Reflect.getMetadata(INTENT_BODY_KEY, proto, methodName);
613
1259
  if (decoder) {
614
1260
  this.intentDecoders.set(intent, decoder);
615
1261
  }
616
- const sensors = Reflect.getMetadata(INTENT_SENSORS_KEY, proto, methodName);
617
- if (sensors && Array.isArray(sensors) && sensors.length > 0) {
618
- this.intentSensors.set(intent, sensors);
1262
+ const intentSensors = Reflect.getMetadata(
1263
+ INTENT_SENSORS_KEY,
1264
+ proto,
1265
+ methodName
1266
+ );
1267
+ const combined = [
1268
+ ...handlerSensors || [],
1269
+ ...Array.isArray(intentSensors) ? intentSensors : []
1270
+ ];
1271
+ if (combined.length > 0) {
1272
+ this.intentSensors.set(intent, combined);
619
1273
  }
620
1274
  const meta = Reflect.getMetadata(INTENT_METADATA_KEY, proto, methodName);
621
1275
  if (meta) {
@@ -655,6 +1309,58 @@ var IntentRouter = class {
655
1309
  }
656
1310
  }
657
1311
  }
1312
+ // ===========================================================================
1313
+ // CCE — Capsule-Carried Encryption Support
1314
+ // ===========================================================================
1315
+ /**
1316
+ * Configure the CCE pipeline.
1317
+ * Must be called before routeCce() can process encrypted requests.
1318
+ */
1319
+ configureCce(config) {
1320
+ this.ccePipelineConfig = config;
1321
+ this.logger.log("CCE pipeline configured");
1322
+ }
1323
+ /**
1324
+ * Register a CCE-encrypted intent handler.
1325
+ * CCE handlers receive decrypted payloads and execution context.
1326
+ */
1327
+ registerCceHandler(intent, handler) {
1328
+ this.cceHandlers.set(intent, handler);
1329
+ this.logger.debug(`CCE handler registered: ${intent}`);
1330
+ }
1331
+ /**
1332
+ * Check if a CCE handler exists for the given intent.
1333
+ */
1334
+ hasCceHandler(intent) {
1335
+ return this.cceHandlers.has(intent);
1336
+ }
1337
+ /**
1338
+ * Route a CCE-encrypted request through the full pipeline.
1339
+ *
1340
+ * Steps:
1341
+ * 1. Sensor chain (envelope validation → capsule verification → replay → decrypt)
1342
+ * 2. Execution context derivation
1343
+ * 3. Handler execution
1344
+ * 4. Response encryption
1345
+ * 5. Witness recording
1346
+ */
1347
+ async routeCce(envelope) {
1348
+ if (!this.ccePipelineConfig) {
1349
+ return {
1350
+ ok: false,
1351
+ error: {
1352
+ code: "CCE_NOT_CONFIGURED",
1353
+ message: "CCE pipeline not configured. Call configureCce() first."
1354
+ },
1355
+ status: "ERROR"
1356
+ };
1357
+ }
1358
+ const config = {
1359
+ ...this.ccePipelineConfig,
1360
+ handlers: this.cceHandlers
1361
+ };
1362
+ return executeCcePipeline(envelope, config);
1363
+ }
658
1364
  storeSchema(meta) {
659
1365
  if (meta.dto) {
660
1366
  if (meta.tlv && meta.tlv.length > 0) {
@@ -718,6 +1424,23 @@ IntentRouter = __decorateClass([
718
1424
  __decorateParam(0, Optional())
719
1425
  ], IntentRouter);
720
1426
 
1427
+ // src/engine/sensor-bands.ts
1428
+ var BAND = {
1429
+ /** Pre-decode: raw byte validation, geo, budget, magic */
1430
+ WIRE: 0,
1431
+ /** Post-decode: identity resolution, capsule, proof */
1432
+ IDENTITY: 40,
1433
+ /** Post-decode: authorization, signature, rate limiting */
1434
+ POLICY: 90,
1435
+ /** Post-decode: content validation, TLV, schema, files */
1436
+ CONTENT: 140,
1437
+ /** Post-decode: business logic sensors, streams, WS */
1438
+ BUSINESS: 200,
1439
+ /** Post-decode: audit, logging (always last) */
1440
+ AUDIT: 900
1441
+ };
1442
+ var PRE_DECODE_BOUNDARY = 40;
1443
+
721
1444
  // src/engine/observation/stable-json.ts
722
1445
  function normalize(value) {
723
1446
  if (Array.isArray(value)) {
@@ -992,7 +1715,7 @@ function verifyResponse(ctx, response) {
992
1715
  import { encodeVarint, decodeVarint, varintLength } from "@nextera.one/axis-protocol";
993
1716
 
994
1717
  // src/core/signature.ts
995
- import * as crypto from "crypto";
1718
+ import * as crypto2 from "crypto";
996
1719
 
997
1720
  // src/core/axis-bin.ts
998
1721
  import * as z from "zod";
@@ -1122,19 +1845,19 @@ function signFrame(frame, privateKey) {
1122
1845
  32
1123
1846
  ]);
1124
1847
  const pkcs8Key = Buffer.concat([pkcs8Prefix, privateKey]);
1125
- keyObject = crypto.createPrivateKey({
1848
+ keyObject = crypto2.createPrivateKey({
1126
1849
  key: pkcs8Key,
1127
1850
  format: "der",
1128
1851
  type: "pkcs8"
1129
1852
  });
1130
1853
  } else {
1131
- keyObject = crypto.createPrivateKey({
1854
+ keyObject = crypto2.createPrivateKey({
1132
1855
  key: privateKey,
1133
1856
  format: "der",
1134
1857
  type: "pkcs8"
1135
1858
  });
1136
1859
  }
1137
- const signature = crypto.sign(null, payload, keyObject);
1860
+ const signature = crypto2.sign(null, payload, keyObject);
1138
1861
  if (signature.length !== 64) {
1139
1862
  throw new Error("Ed25519 signature must be 64 bytes");
1140
1863
  }
@@ -1166,19 +1889,19 @@ function verifyFrameSignature(frame, publicKey) {
1166
1889
  0
1167
1890
  ]);
1168
1891
  const spkiKey = Buffer.concat([spkiPrefix, publicKey]);
1169
- keyObject = crypto.createPublicKey({
1892
+ keyObject = crypto2.createPublicKey({
1170
1893
  key: spkiKey,
1171
1894
  format: "der",
1172
1895
  type: "spki"
1173
1896
  });
1174
1897
  } else {
1175
- keyObject = crypto.createPublicKey({
1898
+ keyObject = crypto2.createPublicKey({
1176
1899
  key: publicKey,
1177
1900
  format: "der",
1178
1901
  type: "spki"
1179
1902
  });
1180
1903
  }
1181
- const valid = crypto.verify(
1904
+ const valid = crypto2.verify(
1182
1905
  null,
1183
1906
  payload,
1184
1907
  keyObject,
@@ -1190,17 +1913,17 @@ function verifyFrameSignature(frame, publicKey) {
1190
1913
  }
1191
1914
  }
1192
1915
  function generateEd25519KeyPair() {
1193
- const { privateKey, publicKey } = crypto.generateKeyPairSync("ed25519");
1916
+ const { privateKey, publicKey } = crypto2.generateKeyPairSync("ed25519");
1194
1917
  return {
1195
1918
  privateKey: privateKey.export({ type: "pkcs8", format: "der" }),
1196
1919
  publicKey: publicKey.export({ type: "spki", format: "der" })
1197
1920
  };
1198
1921
  }
1199
- function sha256(data) {
1200
- return crypto.createHash("sha256").update(data).digest();
1922
+ function sha2564(data) {
1923
+ return crypto2.createHash("sha256").update(data).digest();
1201
1924
  }
1202
1925
  function computeReceiptHash(receiptBytes, prevHash) {
1203
- const hasher = crypto.createHash("sha256");
1926
+ const hasher = crypto2.createHash("sha256");
1204
1927
  hasher.update(receiptBytes);
1205
1928
  if (prevHash && prevHash.length > 0) {
1206
1929
  hasher.update(prevHash);
@@ -1258,7 +1981,7 @@ __export(ats1_exports, {
1258
1981
  encodeU64BE: () => encodeU64BE,
1259
1982
  encodeUVarint: () => encodeUVarint,
1260
1983
  logicalBodyToTLVs: () => logicalBodyToTLVs,
1261
- sha256: () => sha2562,
1984
+ sha256: () => sha2565,
1262
1985
  tlvsToLogicalBody: () => tlvsToLogicalBody,
1263
1986
  tlvsToMap: () => tlvsToMap,
1264
1987
  validateTLVsAgainstSchema: () => validateTLVsAgainstSchema
@@ -1312,7 +2035,7 @@ function decodeU64BE(buf) {
1312
2035
  if (buf.length !== 8) throw new Error("decodeU64BE: length must be 8");
1313
2036
  return buf.readBigUInt64BE(0);
1314
2037
  }
1315
- function sha2562(data) {
2038
+ function sha2565(data) {
1316
2039
  return createHash3("sha256").update(data).digest();
1317
2040
  }
1318
2041
  function encodeTLV(tag, value) {
@@ -1629,7 +2352,7 @@ function decodeAxisHeaderFromTLVs(hdrTlvs, limits = DEFAULT_LIMITS) {
1629
2352
  function encodeAxisRequestBinary(schema, req, limits = DEFAULT_LIMITS) {
1630
2353
  const bodyTlvs = logicalBodyToTLVs(schema, req.body, limits);
1631
2354
  const bodyBytes = encodeTLVStreamCanonical(bodyTlvs);
1632
- const bodyHash = sha2562(bodyBytes);
2355
+ const bodyHash = sha2565(bodyBytes);
1633
2356
  const hdr = {
1634
2357
  ...req.hdr,
1635
2358
  schemaId: schema.schemaId,
@@ -1645,7 +2368,7 @@ function decodeAxisRequestBinary(schema, hdrBytes, bodyBytes, limits = DEFAULT_L
1645
2368
  const hdr = decodeAxisHeaderFromTLVs(hdrTlvs, limits);
1646
2369
  if (hdr.schemaId !== schema.schemaId)
1647
2370
  throw new Error("decodeAxisRequestBinary: schemaId mismatch");
1648
- const bh = sha2562(bodyBytes);
2371
+ const bh = sha2565(bodyBytes);
1649
2372
  if (!Buffer.from(hdr.bodyHash).equals(bh))
1650
2373
  throw new Error("decodeAxisRequestBinary: body_hash mismatch");
1651
2374
  const body = tlvsToLogicalBody(schema, bodyTlvs, limits);
@@ -1718,7 +2441,7 @@ function packPasskeyLoginOptionsReq(params) {
1718
2441
  }
1719
2442
  );
1720
2443
  const body = encodeTLVStreamCanonical(bodyTlvs);
1721
- const bodyHash = sha2562(body);
2444
+ const bodyHash = sha2565(body);
1722
2445
  const hdr = buildAts1Hdr({
1723
2446
  intentId: params.intentId,
1724
2447
  schemaId: ATS1_SCHEMA.PASSKEY_LOGIN_OPTIONS_REQ,
@@ -1787,7 +2510,7 @@ function packPasskeyRegisterOptionsReq(params) {
1787
2510
  }
1788
2511
  );
1789
2512
  const body = encodeTLVStreamCanonical(bodyTlvs);
1790
- const bodyHash = sha2562(body);
2513
+ const bodyHash = sha2565(body);
1791
2514
  const hdr = buildAts1Hdr({
1792
2515
  intentId: params.intentId,
1793
2516
  schemaId: ATS1_SCHEMA.PASSKEY_REGISTER_OPTIONS_REQ,
@@ -1818,7 +2541,7 @@ function packPasskeyLoginVerifyReq(params) {
1818
2541
  }
1819
2542
  });
1820
2543
  const body = encodeTLVStreamCanonical(bodyTlvs);
1821
- const bodyHash = sha2562(body);
2544
+ const bodyHash = sha2565(body);
1822
2545
  const hdr = buildAts1Hdr({
1823
2546
  intentId: params.intentId,
1824
2547
  schemaId: ATS1_SCHEMA.PASSKEY_LOGIN_VERIFY_REQ,
@@ -1902,7 +2625,7 @@ function packPasskeyLoginVerifyRes(params) {
1902
2625
  }
1903
2626
 
1904
2627
  // src/codec/tlv.encode.ts
1905
- import { randomBytes as randomBytes2 } from "crypto";
2628
+ import { randomBytes as randomBytes4 } from "crypto";
1906
2629
  function encVarint(x) {
1907
2630
  if (x < 0n) throw new Error("VARINT_NEG");
1908
2631
  const out = [];
@@ -1930,7 +2653,7 @@ function bytes(b) {
1930
2653
  return Buffer.isBuffer(b) ? b : Buffer.from(b);
1931
2654
  }
1932
2655
  function nonce16() {
1933
- return randomBytes2(16);
2656
+ return randomBytes4(16);
1934
2657
  }
1935
2658
  function tlv(type, value) {
1936
2659
  if (!Number.isSafeInteger(type) || type < 0) throw new Error("TLV_BAD_TYPE");
@@ -2658,7 +3381,7 @@ function isTimestampValid(ts, skewSeconds = 120) {
2658
3381
 
2659
3382
  // src/upload/axis-files.handlers.ts
2660
3383
  import { Inject, Injectable as Injectable3, Logger as Logger2, Optional as Optional2 } from "@nestjs/common";
2661
- import * as crypto2 from "crypto";
3384
+ import * as crypto3 from "crypto";
2662
3385
 
2663
3386
  // src/upload/upload.tokens.ts
2664
3387
  var AXIS_UPLOAD_SESSION_STORE = "AXIS_UPLOAD_SESSION_STORE";
@@ -2759,7 +3482,7 @@ var AxisFilesFinalizeHandler = class {
2759
3482
  if (!await this.files.hasTemp(fileId)) {
2760
3483
  throw new Error("CHUNKS_NOT_FOUND");
2761
3484
  }
2762
- const hash = crypto2.createHash("sha256");
3485
+ const hash = crypto3.createHash("sha256");
2763
3486
  const rs = this.files.createTempReadStream(fileId);
2764
3487
  for await (const chunk of rs) {
2765
3488
  hash.update(chunk);
@@ -2883,92 +3606,54 @@ var DiskUploadFileStore = class {
2883
3606
  }
2884
3607
  };
2885
3608
 
2886
- // src/core/index.ts
2887
- var core_exports = {};
2888
- __export(core_exports, {
2889
- AXIS_MAGIC: () => AXIS_MAGIC,
2890
- AXIS_VERSION: () => AXIS_VERSION,
2891
- AxisError: () => AxisError,
2892
- AxisFrameZ: () => AxisFrameZ,
2893
- BodyProfile: () => BodyProfile,
2894
- ERR_BAD_SIGNATURE: () => ERR_BAD_SIGNATURE,
2895
- ERR_CONTRACT_VIOLATION: () => ERR_CONTRACT_VIOLATION,
2896
- ERR_INVALID_PACKET: () => ERR_INVALID_PACKET,
2897
- ERR_REPLAY_DETECTED: () => ERR_REPLAY_DETECTED,
2898
- FLAG_BODY_TLV: () => FLAG_BODY_TLV,
2899
- FLAG_CHAIN_REQ: () => FLAG_CHAIN_REQ,
2900
- FLAG_HAS_WITNESS: () => FLAG_HAS_WITNESS,
2901
- MAX_BODY_LEN: () => MAX_BODY_LEN,
2902
- MAX_FRAME_LEN: () => MAX_FRAME_LEN,
2903
- MAX_HDR_LEN: () => MAX_HDR_LEN,
2904
- MAX_SIG_LEN: () => MAX_SIG_LEN,
2905
- NCERT_ALG: () => NCERT_ALG,
2906
- NCERT_EXP: () => NCERT_EXP,
2907
- NCERT_ISSUER_KID: () => NCERT_ISSUER_KID,
2908
- NCERT_KID: () => NCERT_KID,
2909
- NCERT_NBF: () => NCERT_NBF,
2910
- NCERT_NODE_ID: () => NCERT_NODE_ID,
2911
- NCERT_PAYLOAD: () => NCERT_PAYLOAD,
2912
- NCERT_PUB: () => NCERT_PUB,
2913
- NCERT_SCOPE: () => NCERT_SCOPE,
2914
- NCERT_SIG: () => NCERT_SIG,
2915
- PROOF_CAPSULE: () => PROOF_CAPSULE,
2916
- PROOF_JWT: () => PROOF_JWT,
2917
- PROOF_LOOM: () => PROOF_LOOM,
2918
- PROOF_MTLS: () => PROOF_MTLS,
2919
- PROOF_NONE: () => PROOF_NONE,
2920
- PROOF_WITNESS: () => PROOF_WITNESS,
2921
- ProofType: () => ProofType,
2922
- TLV: () => TLV,
2923
- TLV_ACTOR_ID: () => TLV_ACTOR_ID,
2924
- TLV_AUD: () => TLV_AUD,
2925
- TLV_BODY_ARR: () => TLV_BODY_ARR,
2926
- TLV_BODY_OBJ: () => TLV_BODY_OBJ,
2927
- TLV_CAPSULE: () => TLV_CAPSULE,
2928
- TLV_EFFECT: () => TLV_EFFECT,
2929
- TLV_ERROR_CODE: () => TLV_ERROR_CODE,
2930
- TLV_ERROR_MSG: () => TLV_ERROR_MSG,
2931
- TLV_INDEX: () => TLV_INDEX,
2932
- TLV_INTENT: () => TLV_INTENT,
2933
- TLV_KID: () => TLV_KID,
2934
- TLV_LOOM_PRESENCE_ID: () => TLV_LOOM_PRESENCE_ID,
2935
- TLV_LOOM_THREAD_HASH: () => TLV_LOOM_THREAD_HASH,
2936
- TLV_LOOM_WRIT: () => TLV_LOOM_WRIT,
2937
- TLV_NODE: () => TLV_NODE,
2938
- TLV_NODE_CERT_HASH: () => TLV_NODE_CERT_HASH,
2939
- TLV_NODE_KID: () => TLV_NODE_KID,
2940
- TLV_NONCE: () => TLV_NONCE,
2941
- TLV_OFFSET: () => TLV_OFFSET,
2942
- TLV_OK: () => TLV_OK,
2943
- TLV_PID: () => TLV_PID,
2944
- TLV_PREV_HASH: () => TLV_PREV_HASH,
2945
- TLV_PROOF_REF: () => TLV_PROOF_REF,
2946
- TLV_PROOF_TYPE: () => TLV_PROOF_TYPE,
2947
- TLV_REALM: () => TLV_REALM,
2948
- TLV_RECEIPT_HASH: () => TLV_RECEIPT_HASH,
2949
- TLV_RID: () => TLV_RID,
2950
- TLV_SHA256_CHUNK: () => TLV_SHA256_CHUNK,
2951
- TLV_TRACE_ID: () => TLV_TRACE_ID,
2952
- TLV_TS: () => TLV_TS,
2953
- TLV_UPLOAD_ID: () => TLV_UPLOAD_ID,
2954
- computeReceiptHash: () => computeReceiptHash,
2955
- computeSignaturePayload: () => computeSignaturePayload,
2956
- decodeArray: () => decodeArray,
2957
- decodeFrame: () => decodeFrame,
2958
- decodeObject: () => decodeObject,
2959
- decodeTLVs: () => decodeTLVs,
2960
- decodeTLVsList: () => decodeTLVsList,
2961
- decodeVarint: () => decodeVarint,
2962
- encodeFrame: () => encodeFrame,
2963
- encodeTLVs: () => encodeTLVs,
2964
- encodeVarint: () => encodeVarint,
2965
- generateEd25519KeyPair: () => generateEd25519KeyPair,
2966
- getSignTarget: () => getSignTarget,
2967
- sha256: () => sha256,
2968
- signFrame: () => signFrame,
2969
- varintLength: () => varintLength,
2970
- verifyFrameSignature: () => verifyFrameSignature
2971
- });
3609
+ // src/decorators/axis-request.decorator.ts
3610
+ import { createParamDecorator } from "@nestjs/common";
3611
+ function resolveIp(req) {
3612
+ return req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.headers["x-real-ip"] || req.socket.remoteAddress || void 0;
3613
+ }
3614
+ var AxisRaw = createParamDecorator(
3615
+ (_data, ctx) => {
3616
+ const req = ctx.switchToHttp().getRequest();
3617
+ return req.body;
3618
+ }
3619
+ );
3620
+ var AxisIp = createParamDecorator(
3621
+ (_data, ctx) => {
3622
+ const req = ctx.switchToHttp().getRequest();
3623
+ return resolveIp(req);
3624
+ }
3625
+ );
3626
+ var AxisContext = createParamDecorator(
3627
+ (_data, ctx) => {
3628
+ const req = ctx.switchToHttp().getRequest();
3629
+ const axisData = req.axis || {};
3630
+ return {
3631
+ raw: req.body,
3632
+ ip: resolveIp(req),
3633
+ preDecodeInput: axisData.preDecodeInput,
3634
+ frameBytesCount: axisData.frameBytesCount || 0
3635
+ };
3636
+ }
3637
+ );
3638
+ var AxisDemoPubkey = createParamDecorator(
3639
+ (_data, ctx) => {
3640
+ if (process.env.NODE_ENV !== "development") return void 0;
3641
+ const req = ctx.switchToHttp().getRequest();
3642
+ return req.headers["x-demo-pubkey"];
3643
+ }
3644
+ );
3645
+ var AxisFrame3 = createParamDecorator(
3646
+ (_data, ctx) => {
3647
+ const req = ctx.switchToHttp().getRequest();
3648
+ const decoded = req.axisDecoded;
3649
+ if (!decoded) {
3650
+ throw new Error(
3651
+ "@AxisFrame() requires AxisDecodeInterceptor on the route. Add @UseInterceptors(AxisDecodeInterceptor) to use this decorator."
3652
+ );
3653
+ }
3654
+ return decoded;
3655
+ }
3656
+ );
2972
3657
 
2973
3658
  // src/core/axis-error.ts
2974
3659
  var AxisError = class extends Error {
@@ -2981,348 +3666,246 @@ var AxisError = class extends Error {
2981
3666
  }
2982
3667
  };
2983
3668
 
2984
- // src/crypto/index.ts
2985
- var crypto_exports = {};
2986
- __export(crypto_exports, {
2987
- ProofVerificationService: () => ProofVerificationService,
2988
- b64urlDecode: () => b64urlDecode,
2989
- b64urlDecodeString: () => b64urlDecodeString,
2990
- b64urlEncode: () => b64urlEncode,
2991
- b64urlEncodeString: () => b64urlEncodeString,
2992
- canonicalJson: () => canonicalJson,
2993
- canonicalJsonExcluding: () => canonicalJsonExcluding
2994
- });
2995
-
2996
- // src/crypto/proof-verification.service.ts
3669
+ // src/engine/handler-discovery.service.ts
2997
3670
  import { Injectable as Injectable4, Logger as Logger3 } from "@nestjs/common";
2998
- import * as crypto3 from "crypto";
2999
- import * as nacl from "tweetnacl";
3000
- var ProofVerificationService = class {
3001
- constructor() {
3002
- this.logger = new Logger3(ProofVerificationService.name);
3003
- // Cache of registered device public keys (deviceId -> pubKey)
3004
- this.deviceKeys = /* @__PURE__ */ new Map();
3005
- // Cache of trusted mTLS certificate fingerprints
3006
- this.trustedCerts = /* @__PURE__ */ new Map();
3671
+ var HandlerDiscoveryService = class {
3672
+ constructor(discovery, scanner, router) {
3673
+ this.discovery = discovery;
3674
+ this.scanner = scanner;
3675
+ this.router = router;
3676
+ this.logger = new Logger3(HandlerDiscoveryService.name);
3007
3677
  }
3008
- /**
3009
- * Verifies an authentication proof based on its type.
3010
- *
3011
- * **Supported Types:**
3012
- * - 1 (CAPSULE): Delegated to `verifyCapsuleProof`
3013
- * - 2 (JWT): Verified by `verifyJWTProof`
3014
- * - 3 (MTLS_ID): Verified by `verifyMTLSProof`
3015
- * - 4 (DEVICE_SE): Verified by `verifyDeviceSEProof`
3016
- *
3017
- * @param {ProofType} proofType - The numeric AXIS proof type
3018
- * @param {Uint8Array} proofRef - The binary reference or token for the proof
3019
- * @param {Object} context - Additional metadata required for specific proof types
3020
- * @param {Uint8Array} [context.signTarget] - The canonical bytes that were signed (for Ed25519)
3021
- * @param {Uint8Array} [context.signature] - The signature to verify (for Ed25519)
3022
- * @param {MTLSContext} [context.mtls] - mTLS certificate data
3023
- * @param {DeviceSEContext} [context.deviceSE] - Device Secure Element information
3024
- * @returns {Promise<ProofVerificationResult>} The outcome of the verification
3025
- */
3026
- async verifyProof(proofType, proofRef, context) {
3027
- switch (proofType) {
3028
- case 1:
3029
- return this.verifyCapsuleProof(proofRef);
3030
- case 2:
3031
- return this.verifyJWTProof(proofRef);
3032
- case 3:
3033
- return this.verifyMTLSProof(context.mtls);
3034
- case 4:
3035
- return this.verifyDeviceSEProof(
3036
- context.signTarget,
3037
- context.signature,
3038
- context.deviceSE
3678
+ onModuleInit() {
3679
+ const providers = this.discovery.getProviders();
3680
+ let totalIntents = 0;
3681
+ for (const wrapper of providers) {
3682
+ const { instance, metatype } = wrapper;
3683
+ if (!instance || !metatype) continue;
3684
+ const handlerMeta = Reflect.getMetadata(HANDLER_METADATA_KEY, metatype);
3685
+ if (!handlerMeta) continue;
3686
+ const handlerName = handlerMeta.intent || metatype.name;
3687
+ const proto = Object.getPrototypeOf(instance);
3688
+ const methods = this.scanner.getAllMethodNames(proto);
3689
+ let registered = 0;
3690
+ const handlerSensors = Reflect.getMetadata(HANDLER_SENSORS_KEY, metatype) || [];
3691
+ for (const methodName of methods) {
3692
+ const meta = Reflect.getMetadata(
3693
+ INTENT_METADATA_KEY,
3694
+ proto,
3695
+ methodName
3039
3696
  );
3040
- default:
3041
- return { valid: false, error: `Unknown proof type: ${proofType}` };
3697
+ if (!meta?.intent) continue;
3698
+ if (!this.router.has(meta.intent)) {
3699
+ this.router.register(
3700
+ meta.intent,
3701
+ instance[methodName].bind(instance)
3702
+ );
3703
+ registered++;
3704
+ totalIntents++;
3705
+ }
3706
+ this.router.registerIntentMeta(
3707
+ meta.intent,
3708
+ proto,
3709
+ methodName,
3710
+ handlerSensors
3711
+ );
3712
+ }
3713
+ if (registered > 0) {
3714
+ this.logger.log(
3715
+ `Auto-registered ${registered} intents from ${handlerName}`
3716
+ );
3717
+ }
3042
3718
  }
3719
+ this.logger.log(
3720
+ `Handler discovery complete: ${totalIntents} intents auto-registered`
3721
+ );
3043
3722
  }
3044
- /**
3045
- * Verify CAPSULE proof (delegated to CapsuleService)
3046
- */
3047
- async verifyCapsuleProof(proofRef) {
3048
- const capsuleId = new TextDecoder().decode(proofRef);
3049
- return {
3050
- valid: true,
3051
- metadata: { capsuleId, requiresCapsuleValidation: true }
3052
- };
3723
+ };
3724
+ HandlerDiscoveryService = __decorateClass([
3725
+ Injectable4()
3726
+ ], HandlerDiscoveryService);
3727
+
3728
+ // src/engine/sensor-discovery.service.ts
3729
+ import { Injectable as Injectable5, Logger as Logger4 } from "@nestjs/common";
3730
+ var SensorDiscoveryService = class {
3731
+ constructor(discovery, reflector, registry) {
3732
+ this.discovery = discovery;
3733
+ this.reflector = reflector;
3734
+ this.registry = registry;
3735
+ this.logger = new Logger4(SensorDiscoveryService.name);
3053
3736
  }
3054
- /**
3055
- * Verifies a JSON Web Token (JWT) proof.
3056
- *
3057
- * **Validation Logic:**
3058
- * 1. Decodes the token string.
3059
- * 2. Checks for valid 3-part JWT structure.
3060
- * 3. Validates `exp` (expiration) and `nbf` (not before) claims.
3061
- * 4. Extracts `actor_id` or `sub` as the identity.
3062
- *
3063
- * @param {Uint8Array} proofRef - Binary representation of the JWT string
3064
- * @returns {Promise<ProofVerificationResult>} Result including the actor identifier
3065
- */
3066
- async verifyJWTProof(proofRef) {
3067
- try {
3068
- const token = new TextDecoder().decode(proofRef);
3069
- const parts = token.split(".");
3070
- if (parts.length !== 3) {
3071
- return { valid: false, error: "Invalid JWT format" };
3072
- }
3073
- const header = JSON.parse(Buffer.from(parts[0], "base64url").toString());
3074
- const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
3075
- if (payload.exp && Date.now() / 1e3 > payload.exp) {
3076
- return { valid: false, error: "JWT expired" };
3737
+ onApplicationBootstrap() {
3738
+ const providers = this.discovery.getProviders();
3739
+ let count = 0;
3740
+ for (const wrapper of providers) {
3741
+ const { instance } = wrapper;
3742
+ if (!instance || !instance.constructor) continue;
3743
+ const meta = this.reflector.get(
3744
+ SENSOR_METADATA_KEY,
3745
+ instance.constructor
3746
+ );
3747
+ if (!meta) continue;
3748
+ const sensor = instance;
3749
+ if (!sensor.name || sensor.order === void 0) {
3750
+ this.logger.warn(
3751
+ `@Sensor() on ${instance.constructor.name} missing name or order \u2014 skipped`
3752
+ );
3753
+ continue;
3077
3754
  }
3078
- if (payload.nbf && Date.now() / 1e3 < payload.nbf) {
3079
- return { valid: false, error: "JWT not yet valid" };
3755
+ if (!sensor.phase) {
3756
+ const decoratorPhase = meta !== true ? meta.phase : void 0;
3757
+ sensor.phase = decoratorPhase ?? (sensor.order < PRE_DECODE_BOUNDARY ? "PRE_DECODE" : "POST_DECODE");
3080
3758
  }
3081
- return {
3082
- valid: true,
3083
- actorId: payload.sub || payload.actor_id,
3084
- metadata: { iss: payload.iss, scope: payload.scope }
3085
- };
3086
- } catch (e) {
3087
- const message = e instanceof Error ? e.message : "Unknown error";
3088
- return { valid: false, error: `JWT parse error: ${message}` };
3759
+ this.registry.register(sensor);
3760
+ count++;
3089
3761
  }
3762
+ this.logger.log(`Auto-registered ${count} sensors via @Sensor()`);
3090
3763
  }
3091
- /**
3092
- * Verify mTLS client certificate proof
3093
- */
3094
- async verifyMTLSProof(mtls) {
3095
- if (!mtls) {
3096
- return { valid: false, error: "No mTLS context provided" };
3097
- }
3098
- if (!mtls.verified) {
3099
- return { valid: false, error: "mTLS not verified by TLS terminator" };
3100
- }
3101
- if (mtls.clientCertFingerprint) {
3102
- const trusted = this.trustedCerts.get(mtls.clientCertFingerprint);
3103
- if (trusted) {
3104
- return {
3105
- valid: true,
3106
- actorId: trusted.actorId,
3107
- metadata: {
3108
- fingerprint: mtls.clientCertFingerprint,
3109
- subject: mtls.clientCertSubject
3110
- }
3111
- };
3112
- }
3113
- }
3114
- if (mtls.clientCertSubject) {
3115
- const cnMatch = mtls.clientCertSubject.match(/CN=([^,]+)/);
3116
- if (cnMatch) {
3117
- return {
3118
- valid: true,
3119
- actorId: cnMatch[1],
3120
- metadata: {
3121
- subject: mtls.clientCertSubject,
3122
- issuer: mtls.clientCertIssuer
3123
- }
3124
- };
3125
- }
3126
- }
3127
- return { valid: false, error: "Could not extract actor from certificate" };
3764
+ };
3765
+ SensorDiscoveryService = __decorateClass([
3766
+ Injectable5()
3767
+ ], SensorDiscoveryService);
3768
+
3769
+ // src/engine/registry/sensor.registry.ts
3770
+ import { Injectable as Injectable6, Logger as Logger5 } from "@nestjs/common";
3771
+ var SensorRegistry = class {
3772
+ constructor(configService) {
3773
+ this.configService = configService;
3774
+ this.sensors = [];
3775
+ this.logger = new Logger5(SensorRegistry.name);
3128
3776
  }
3129
3777
  /**
3130
- * Verify Device Secure Element signature
3778
+ * Registers a new sensor in the registry.
3779
+ *
3780
+ * Validates that:
3781
+ * - AxisSensor has a unique name
3782
+ * - AxisSensor has an order field
3783
+ * - Pre-decode sensors have order < 40
3784
+ * - Post-decode sensors have order >= 40
3785
+ *
3786
+ * @param {AxisSensor} sensor - The sensor instance to register
3787
+ * @throws Error if validation fails
3131
3788
  */
3132
- async verifyDeviceSEProof(signTarget, signature, deviceSE) {
3133
- if (!deviceSE || !signTarget || !signature) {
3134
- return { valid: false, error: "Missing Device SE context" };
3789
+ register(sensor) {
3790
+ if (!sensor.name) {
3791
+ throw new Error("AxisSensor must have a name");
3135
3792
  }
3136
- let publicKey = deviceSE.publicKey;
3137
- const registeredKey = this.deviceKeys.get(deviceSE.deviceId);
3138
- if (registeredKey) {
3139
- publicKey = registeredKey;
3793
+ const enabledSensorsStr = this.configService.get("ENABLED_SENSORS");
3794
+ const disabledSensorsStr = this.configService.get("DISABLED_SENSORS");
3795
+ const enabledSensors = enabledSensorsStr ? enabledSensorsStr.split(",").map((s) => s.trim()) : null;
3796
+ const disabledSensors = disabledSensorsStr ? disabledSensorsStr.split(",").map((s) => s.trim()) : [];
3797
+ if (enabledSensors && !enabledSensors.includes(sensor.name)) {
3798
+ this.logger.log(`Skipping disabled sensor (not in ENABLED_SENSORS): ${sensor.name}`);
3799
+ return;
3140
3800
  }
3141
- if (!publicKey || publicKey.length !== 32) {
3142
- return {
3143
- valid: false,
3144
- error: "Invalid or unregistered device public key"
3145
- };
3801
+ if (disabledSensors.includes(sensor.name)) {
3802
+ this.logger.log(`Skipping disabled sensor (in DISABLED_SENSORS): ${sensor.name}`);
3803
+ return;
3146
3804
  }
3147
- try {
3148
- const valid = nacl.sign.detached.verify(signTarget, signature, publicKey);
3149
- if (!valid) {
3150
- return { valid: false, error: "Device signature verification failed" };
3151
- }
3152
- return {
3153
- valid: true,
3154
- actorId: deviceSE.deviceId,
3155
- metadata: { deviceId: deviceSE.deviceId, proofType: "DEVICE_SE" }
3156
- };
3157
- } catch (e) {
3158
- const message = e instanceof Error ? e.message : "Unknown error";
3159
- return {
3160
- valid: false,
3161
- error: `Signature verification error: ${message}`
3162
- };
3805
+ if (sensor.order === void 0) {
3806
+ throw new Error(`AxisSensor "${sensor.name}" must have an order field`);
3807
+ }
3808
+ const isPreDecodeSensor = this.isPreDecodeSensor(sensor);
3809
+ const isPostDecodeSensor = this.isPostDecodeSensor(sensor);
3810
+ if (isPreDecodeSensor && sensor.order >= 40) {
3811
+ this.logger.warn(
3812
+ `AxisSensor "${sensor.name}" is marked as PRE_DECODE but has order ${sensor.order} (should be < 40)`
3813
+ );
3814
+ }
3815
+ if (isPostDecodeSensor && sensor.order < 40) {
3816
+ this.logger.warn(
3817
+ `AxisSensor "${sensor.name}" is marked as POST_DECODE but has order ${sensor.order} (should be >= 40)`
3818
+ );
3163
3819
  }
3820
+ this.sensors.push(sensor);
3821
+ const phaseLabel = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase || "UNKNOWN";
3822
+ this.logger.debug(
3823
+ `Registered sensor: ${sensor.name} (order: ${sensor.order}, phase: ${phaseLabel})`
3824
+ );
3164
3825
  }
3165
3826
  /**
3166
- * Registers a public key for a trusted device.
3167
- * This key will be used for future `DEVICE_SE` proof verifications.
3827
+ * Returns all registered sensors, sorted by their execution order.
3168
3828
  *
3169
- * @param {string} deviceId - Unique identifier for the device
3170
- * @param {Uint8Array} publicKey - 32-byte Ed25519 public key
3171
- * @throws {Error} If the public key is not 32 bytes
3829
+ * @returns {AxisSensor[]} A sorted array of sensors
3172
3830
  */
3173
- registerDeviceKey(deviceId, publicKey) {
3174
- if (publicKey.length !== 32) {
3175
- throw new Error("Device public key must be 32 bytes (Ed25519)");
3176
- }
3177
- this.deviceKeys.set(deviceId, publicKey);
3178
- this.logger.log(`Registered device key for ${deviceId}`);
3831
+ list() {
3832
+ return [...this.sensors].sort(
3833
+ (a, b) => (a.order ?? 999) - (b.order ?? 999)
3834
+ );
3179
3835
  }
3180
3836
  /**
3181
- * Unregister a device
3837
+ * Returns only pre-decode sensors (order < 40).
3838
+ * These sensors run in middleware on raw bytes before frame decoding.
3839
+ *
3840
+ * @returns {AxisPreSensor[]} Pre-decode sensors sorted by order
3182
3841
  */
3183
- unregisterDevice(deviceId) {
3184
- return this.deviceKeys.delete(deviceId);
3842
+ getPreDecodeSensors() {
3843
+ return this.list().filter((s) => (s.order ?? 999) < 40);
3185
3844
  }
3186
3845
  /**
3187
- * Registers a trusted mTLS certificate fingerprint and associates it with an actor.
3846
+ * Returns only post-decode sensors (order >= 40).
3847
+ * These sensors run in the controller on fully decoded frames.
3188
3848
  *
3189
- * @param {string} fingerprint - SHA-256 fingerprint of the client certificate
3190
- * @param {string} actorId - The actor to associate with this certificate
3849
+ * @returns {AxisPostSensor[]} Post-decode sensors sorted by order
3191
3850
  */
3192
- registerMTLSCert(fingerprint, actorId) {
3193
- this.trustedCerts.set(fingerprint, { actorId, issuedAt: Date.now() });
3194
- this.logger.log(`Registered mTLS cert ${fingerprint} for actor ${actorId}`);
3851
+ getPostDecodeSensors() {
3852
+ return this.list().filter(
3853
+ (s) => (s.order ?? 999) >= 40
3854
+ );
3195
3855
  }
3196
3856
  /**
3197
- * Revoke an mTLS certificate
3857
+ * Helper: Check if a sensor is a pre-decode sensor.
3858
+ *
3859
+ * @private
3860
+ * @param {AxisSensor} sensor - The sensor to check
3861
+ * @returns {boolean} True if sensor is pre-decode
3198
3862
  */
3199
- revokeMTLSCert(fingerprint) {
3200
- return this.trustedCerts.delete(fingerprint);
3863
+ isPreDecodeSensor(sensor) {
3864
+ const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
3865
+ return phase === "PRE_DECODE" || (sensor.order ?? 999) < 40;
3201
3866
  }
3202
3867
  /**
3203
- * Calculate certificate fingerprint (SHA-256)
3868
+ * Helper: Check if a sensor is a post-decode sensor.
3869
+ *
3870
+ * @private
3871
+ * @param {AxisSensor} sensor - The sensor to check
3872
+ * @returns {boolean} True if sensor is post-decode
3204
3873
  */
3205
- static calculateFingerprint(certPem) {
3206
- const der = Buffer.from(
3207
- certPem.replace(/-----BEGIN CERTIFICATE-----/, "").replace(/-----END CERTIFICATE-----/, "").replace(/\s/g, ""),
3208
- "base64"
3209
- );
3210
- return crypto3.createHash("sha256").update(der).digest("hex");
3211
- }
3212
- };
3213
- ProofVerificationService = __decorateClass([
3214
- Injectable4()
3215
- ], ProofVerificationService);
3216
-
3217
- // src/decorators/index.ts
3218
- var decorators_exports = {};
3219
- __export(decorators_exports, {
3220
- AxisContext: () => AxisContext,
3221
- AxisDemoPubkey: () => AxisDemoPubkey,
3222
- AxisFrame: () => AxisFrame3,
3223
- AxisIp: () => AxisIp,
3224
- AxisRaw: () => AxisRaw,
3225
- HANDLER_METADATA_KEY: () => HANDLER_METADATA_KEY,
3226
- Handler: () => Handler,
3227
- INTENT_BODY_KEY: () => INTENT_BODY_KEY,
3228
- INTENT_METADATA_KEY: () => INTENT_METADATA_KEY,
3229
- INTENT_ROUTES_KEY: () => INTENT_ROUTES_KEY,
3230
- INTENT_SENSORS_KEY: () => INTENT_SENSORS_KEY,
3231
- Intent: () => Intent,
3232
- IntentBody: () => IntentBody,
3233
- IntentSensors: () => IntentSensors,
3234
- SENSOR_METADATA_KEY: () => SENSOR_METADATA_KEY,
3235
- Sensor: () => Sensor,
3236
- TLV_FIELDS_KEY: () => TLV_FIELDS_KEY,
3237
- TLV_VALIDATORS_KEY: () => TLV_VALIDATORS_KEY,
3238
- TlvEnum: () => TlvEnum,
3239
- TlvField: () => TlvField,
3240
- TlvMinLen: () => TlvMinLen,
3241
- TlvRange: () => TlvRange,
3242
- TlvUtf8Pattern: () => TlvUtf8Pattern,
3243
- TlvValidate: () => TlvValidate,
3244
- buildDtoDecoder: () => buildDtoDecoder,
3245
- extractDtoSchema: () => extractDtoSchema
3246
- });
3247
-
3248
- // src/decorators/axis-request.decorator.ts
3249
- import { createParamDecorator } from "@nestjs/common";
3250
- function resolveIp(req) {
3251
- return req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.headers["x-real-ip"] || req.socket.remoteAddress || void 0;
3252
- }
3253
- var AxisRaw = createParamDecorator(
3254
- (_data, ctx) => {
3255
- const req = ctx.switchToHttp().getRequest();
3256
- return req.body;
3257
- }
3258
- );
3259
- var AxisIp = createParamDecorator(
3260
- (_data, ctx) => {
3261
- const req = ctx.switchToHttp().getRequest();
3262
- return resolveIp(req);
3874
+ isPostDecodeSensor(sensor) {
3875
+ const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
3876
+ return phase === "POST_DECODE" || (sensor.order ?? 999) >= 40;
3263
3877
  }
3264
- );
3265
- var AxisContext = createParamDecorator(
3266
- (_data, ctx) => {
3267
- const req = ctx.switchToHttp().getRequest();
3268
- const axisData = req.axis || {};
3878
+ /**
3879
+ * Returns sensor count by phase.
3880
+ * Useful for diagnostics and monitoring.
3881
+ *
3882
+ * @returns {{preDecodeCount: number, postDecodeCount: number}}
3883
+ */
3884
+ getSensorCountByPhase() {
3269
3885
  return {
3270
- raw: req.body,
3271
- ip: resolveIp(req),
3272
- preDecodeInput: axisData.preDecodeInput,
3273
- frameBytesCount: axisData.frameBytesCount || 0
3886
+ preDecodeCount: this.getPreDecodeSensors().length,
3887
+ postDecodeCount: this.getPostDecodeSensors().length
3274
3888
  };
3275
3889
  }
3276
- );
3277
- var AxisDemoPubkey = createParamDecorator(
3278
- (_data, ctx) => {
3279
- if (process.env.NODE_ENV !== "development") return void 0;
3280
- const req = ctx.switchToHttp().getRequest();
3281
- return req.headers["x-demo-pubkey"];
3282
- }
3283
- );
3284
- var AxisFrame3 = createParamDecorator(
3285
- (_data, ctx) => {
3286
- const req = ctx.switchToHttp().getRequest();
3287
- const decoded = req.axisDecoded;
3288
- if (!decoded) {
3289
- throw new Error(
3290
- "@AxisFrame() requires AxisDecodeInterceptor on the route. Add @UseInterceptors(AxisDecodeInterceptor) to use this decorator."
3291
- );
3292
- }
3293
- return decoded;
3890
+ /**
3891
+ * Clears all registered sensors.
3892
+ * Useful for testing.
3893
+ *
3894
+ * @internal
3895
+ */
3896
+ clear() {
3897
+ this.sensors = [];
3294
3898
  }
3295
- );
3296
-
3297
- // src/decorators/sensor.decorator.ts
3298
- import { SetMetadata as SetMetadata2 } from "@nestjs/common";
3299
- var SENSOR_METADATA_KEY = "axis:sensor";
3300
- function Sensor(options) {
3301
- return SetMetadata2(SENSOR_METADATA_KEY, options ?? true);
3302
- }
3303
-
3304
- // src/engine/index.ts
3305
- var engine_exports = {};
3306
- __export(engine_exports, {
3307
- BAND: () => BAND,
3308
- HandlerDiscoveryService: () => HandlerDiscoveryService,
3309
- IntentRouter: () => IntentRouter,
3310
- PRE_DECODE_BOUNDARY: () => PRE_DECODE_BOUNDARY,
3311
- SensorDiscoveryService: () => SensorDiscoveryService,
3312
- SensorRegistry: () => SensorRegistry,
3313
- createObservation: () => createObservation,
3314
- endStage: () => endStage,
3315
- finalizeObservation: () => finalizeObservation,
3316
- observation: () => observation_exports,
3317
- recordSensor: () => recordSensor,
3318
- startStage: () => startStage
3319
- });
3899
+ };
3900
+ SensorRegistry = __decorateClass([
3901
+ Injectable6()
3902
+ ], SensorRegistry);
3320
3903
 
3321
3904
  // src/engine/axis-observation.ts
3322
- import { randomBytes as randomBytes3 } from "crypto";
3905
+ import { randomBytes as randomBytes5 } from "crypto";
3323
3906
  function createObservation(transport, ip) {
3324
3907
  return {
3325
- id: randomBytes3(16).toString("hex"),
3908
+ id: randomBytes5(16).toString("hex"),
3326
3909
  startMs: Date.now(),
3327
3910
  transport,
3328
3911
  ip,
@@ -3354,251 +3937,1186 @@ function finalizeObservation(obs, decision, statusCode, resultCode) {
3354
3937
  if (resultCode) obs.resultCode = resultCode;
3355
3938
  }
3356
3939
 
3357
- // src/engine/handler-discovery.service.ts
3358
- import { Injectable as Injectable5, Logger as Logger4 } from "@nestjs/common";
3359
- var HandlerDiscoveryService = class {
3360
- constructor(discovery, scanner, router) {
3361
- this.discovery = discovery;
3362
- this.scanner = scanner;
3363
- this.router = router;
3364
- this.logger = new Logger4(HandlerDiscoveryService.name);
3940
+ // src/security/axis-sensor-chain.service.ts
3941
+ import { Injectable as Injectable7 } from "@nestjs/common";
3942
+ var AxisSensorChainService = class {
3943
+ constructor(registry) {
3944
+ this.registry = registry;
3365
3945
  }
3366
- onModuleInit() {
3367
- const providers = this.discovery.getProviders();
3368
- let totalIntents = 0;
3369
- for (const wrapper of providers) {
3370
- const { instance, metatype } = wrapper;
3371
- if (!instance || !metatype) continue;
3372
- const handlerMeta = Reflect.getMetadata(HANDLER_METADATA_KEY, metatype);
3373
- if (!handlerMeta) continue;
3374
- const handlerName = handlerMeta.intent || metatype.name;
3375
- const proto = Object.getPrototypeOf(instance);
3376
- const methods = this.scanner.getAllMethodNames(proto);
3377
- let registered = 0;
3378
- for (const methodName of methods) {
3379
- const meta = Reflect.getMetadata(
3380
- INTENT_METADATA_KEY,
3381
- proto,
3382
- methodName
3383
- );
3384
- if (!meta?.intent) continue;
3385
- if (!this.router.has(meta.intent)) {
3386
- this.router.register(
3387
- meta.intent,
3388
- instance[methodName].bind(instance)
3946
+ /**
3947
+ * Evaluate all applicable sensors based on phase.
3948
+ */
3949
+ async evaluate(input, phase = "POST_DECODE", baseDecision) {
3950
+ if (phase === "PRE_DECODE") {
3951
+ return this.evaluateSensors(this.registry.getPreDecodeSensors(), input);
3952
+ }
3953
+ if (phase === "BOTH") {
3954
+ const rawPreResult = await this.evaluateSensors(
3955
+ this.registry.getPreDecodeSensors(),
3956
+ input
3957
+ );
3958
+ const preResult = normalizeSensorDecision(rawPreResult);
3959
+ if (!preResult.allow) return rawPreResult;
3960
+ return this.evaluateSensors(
3961
+ this.registry.getPostDecodeSensors(),
3962
+ input,
3963
+ rawPreResult
3964
+ );
3965
+ }
3966
+ return this.evaluateSensors(
3967
+ this.registry.getPostDecodeSensors(),
3968
+ input,
3969
+ baseDecision
3970
+ );
3971
+ }
3972
+ /** Run only pre-decode sensors. */
3973
+ async evaluatePre(input) {
3974
+ return this.evaluateSensors(this.registry.getPreDecodeSensors(), input);
3975
+ }
3976
+ /** Run only post-decode sensors. */
3977
+ async evaluatePost(input, baseDecision) {
3978
+ return this.evaluateSensors(
3979
+ this.registry.getPostDecodeSensors(),
3980
+ input,
3981
+ baseDecision
3982
+ );
3983
+ }
3984
+ async evaluateSensors(sensors, input, baseDecision) {
3985
+ const relevantSensors = sensors.filter(
3986
+ (s) => !s.supports || s.supports(input)
3987
+ );
3988
+ const normalizedBase = baseDecision ? normalizeSensorDecision(baseDecision) : void 0;
3989
+ let riskScore = normalizedBase?.riskScore ?? 0;
3990
+ const reasons = normalizedBase?.reasons ? [...normalizedBase.reasons] : [];
3991
+ const tags = normalizedBase?.tags ? { ...normalizedBase.tags } : {};
3992
+ let expSecondsMax = normalizedBase?.tighten?.expSecondsMax;
3993
+ let constraintsPatch = normalizedBase?.tighten?.constraintsPatch ? { ...normalizedBase.tighten.constraintsPatch } : {};
3994
+ for (const sensor of relevantSensors) {
3995
+ try {
3996
+ const t0 = Date.now();
3997
+ const rawDecision = await sensor.run(input);
3998
+ const elapsed = Date.now() - t0;
3999
+ const decision = normalizeSensorDecision(rawDecision);
4000
+ const obs = input.metadata?.observation;
4001
+ if (obs) {
4002
+ recordSensor(
4003
+ obs,
4004
+ sensor.name,
4005
+ decision.allow,
4006
+ decision.riskScore,
4007
+ elapsed,
4008
+ decision.reasons,
4009
+ decision.allow ? void 0 : decision.code
3389
4010
  );
3390
- registered++;
3391
- totalIntents++;
3392
4011
  }
3393
- this.router.registerIntentMeta(meta.intent, proto, methodName);
3394
- }
3395
- if (registered > 0) {
3396
- this.logger.log(
3397
- `Auto-registered ${registered} intents from ${handlerName}`
3398
- );
4012
+ if (!decision.allow) {
4013
+ return {
4014
+ allow: false,
4015
+ riskScore: Math.min(100, riskScore + decision.riskScore),
4016
+ reasons: [...reasons, ...decision.reasons],
4017
+ tags
4018
+ };
4019
+ }
4020
+ riskScore = Math.min(100, riskScore + decision.riskScore);
4021
+ reasons.push(...decision.reasons);
4022
+ if (decision.tags) {
4023
+ Object.assign(tags, decision.tags);
4024
+ }
4025
+ if (decision.tighten?.expSecondsMax !== void 0) {
4026
+ expSecondsMax = expSecondsMax === void 0 ? decision.tighten.expSecondsMax : Math.min(expSecondsMax, decision.tighten.expSecondsMax);
4027
+ }
4028
+ if (decision.tighten?.constraintsPatch) {
4029
+ constraintsPatch = {
4030
+ ...constraintsPatch,
4031
+ ...decision.tighten.constraintsPatch
4032
+ };
4033
+ }
4034
+ } catch (error) {
4035
+ console.error(`[AXIS][SENSOR] ${sensor.name} failed:`, error);
4036
+ const obs = input.metadata?.observation;
4037
+ if (obs) {
4038
+ recordSensor(obs, sensor.name, false, 100, 0, [
4039
+ `sensor_error:${sensor.name}`
4040
+ ]);
4041
+ }
4042
+ return {
4043
+ allow: false,
4044
+ riskScore: 100,
4045
+ reasons: [`sensor_error:${sensor.name}`]
4046
+ };
3399
4047
  }
3400
4048
  }
3401
- this.logger.log(
3402
- `Handler discovery complete: ${totalIntents} intents auto-registered`
3403
- );
4049
+ const tightenPatch = Object.keys(constraintsPatch).length > 0 ? constraintsPatch : void 0;
4050
+ return {
4051
+ allow: true,
4052
+ riskScore,
4053
+ reasons,
4054
+ tags,
4055
+ tighten: expSecondsMax !== void 0 || tightenPatch ? {
4056
+ expSecondsMax,
4057
+ constraintsPatch: tightenPatch
4058
+ } : void 0
4059
+ };
3404
4060
  }
3405
4061
  };
3406
- HandlerDiscoveryService = __decorateClass([
3407
- Injectable5()
3408
- ], HandlerDiscoveryService);
4062
+ AxisSensorChainService = __decorateClass([
4063
+ Injectable7()
4064
+ ], AxisSensorChainService);
3409
4065
 
3410
- // src/engine/sensor-bands.ts
3411
- var BAND = {
3412
- /** Pre-decode: raw byte validation, geo, budget, magic */
3413
- WIRE: 0,
3414
- /** Post-decode: identity resolution, capsule, proof */
3415
- IDENTITY: 40,
3416
- /** Post-decode: authorization, signature, rate limiting */
3417
- POLICY: 90,
3418
- /** Post-decode: content validation, TLV, schema, files */
3419
- CONTENT: 140,
3420
- /** Post-decode: business logic sensors, streams, WS */
3421
- BUSINESS: 200,
3422
- /** Post-decode: audit, logging (always last) */
3423
- AUDIT: 900
3424
- };
3425
- var PRE_DECODE_BOUNDARY = 40;
4066
+ // src/cce/index.ts
4067
+ var cce_exports = {};
4068
+ __export(cce_exports, {
4069
+ CCE_AES_KEY_BYTES: () => CCE_AES_KEY_BYTES,
4070
+ CCE_DERIVATION: () => CCE_DERIVATION,
4071
+ CCE_ERROR: () => CCE_ERROR,
4072
+ CCE_IV_BYTES: () => CCE_IV_BYTES,
4073
+ CCE_NONCE_BYTES: () => CCE_NONCE_BYTES,
4074
+ CCE_PROTOCOL_VERSION: () => CCE_PROTOCOL_VERSION,
4075
+ CCE_TAG_BYTES: () => CCE_TAG_BYTES,
4076
+ CceAudienceIntentBindingSensor: () => CceAudienceIntentBindingSensor,
4077
+ CceCapsuleVerificationSensor: () => CceCapsuleVerificationSensor,
4078
+ CceClientSignatureSensor: () => CceClientSignatureSensor,
4079
+ CceEnvelopeValidationSensor: () => CceEnvelopeValidationSensor,
4080
+ CceError: () => CceError,
4081
+ CcePayloadDecryptionSensor: () => CcePayloadDecryptionSensor,
4082
+ CceReplayProtectionSensor: () => CceReplayProtectionSensor,
4083
+ CceTpsWindowSensor: () => CceTpsWindowSensor,
4084
+ InMemoryCceReplayStore: () => InMemoryCceReplayStore,
4085
+ InMemoryCceWitnessStore: () => InMemoryCceWitnessStore,
4086
+ aesGcmDecrypt: () => aesGcmDecrypt,
4087
+ aesGcmEncrypt: () => aesGcmEncrypt,
4088
+ base64UrlDecode: () => base64UrlDecode,
4089
+ base64UrlEncode: () => base64UrlEncode,
4090
+ buildCceErrorResponse: () => buildCceErrorResponse,
4091
+ buildCceResponse: () => buildCceResponse,
4092
+ buildExecutionContext: () => buildExecutionContext,
4093
+ buildWitnessRecord: () => buildWitnessRecord,
4094
+ deriveRequestExecutionKey: () => deriveRequestExecutionKey,
4095
+ deriveResponseExecutionKey: () => deriveResponseExecutionKey,
4096
+ deriveWitnessKey: () => deriveWitnessKey,
4097
+ executeCcePipeline: () => executeCcePipeline,
4098
+ extractVerificationState: () => extractVerificationState,
4099
+ generateAesKey: () => generateAesKey,
4100
+ generateCceNonce: () => generateCceNonce,
4101
+ generateIv: () => generateIv,
4102
+ hashPayload: () => hashPayload,
4103
+ nodeAesGcmProvider: () => nodeAesGcmProvider
4104
+ });
3426
4105
 
3427
- // src/engine/sensor-discovery.service.ts
3428
- import { Injectable as Injectable6, Logger as Logger5 } from "@nestjs/common";
3429
- var SensorDiscoveryService = class {
3430
- constructor(discovery, reflector, registry) {
3431
- this.discovery = discovery;
3432
- this.reflector = reflector;
3433
- this.registry = registry;
3434
- this.logger = new Logger5(SensorDiscoveryService.name);
4106
+ // src/cce/sensors/cce-envelope-validation.sensor.ts
4107
+ var REQUIRED_FIELDS = [
4108
+ "ver",
4109
+ "request_id",
4110
+ "correlation_id",
4111
+ "client_kid",
4112
+ "capsule",
4113
+ "encrypted_key",
4114
+ "encrypted_payload",
4115
+ "request_nonce",
4116
+ "client_sig",
4117
+ "algorithms"
4118
+ ];
4119
+ var CceEnvelopeValidationSensor = class {
4120
+ constructor() {
4121
+ this.name = "cce.envelope.validation";
4122
+ this.order = 5;
4123
+ this.phase = "PRE_DECODE";
3435
4124
  }
3436
- onApplicationBootstrap() {
3437
- const providers = this.discovery.getProviders();
3438
- let count = 0;
3439
- for (const wrapper of providers) {
3440
- const { instance } = wrapper;
3441
- if (!instance || !instance.constructor) continue;
3442
- const meta = this.reflector.get(
3443
- SENSOR_METADATA_KEY,
3444
- instance.constructor
3445
- );
3446
- if (!meta) continue;
3447
- const sensor = instance;
3448
- if (!sensor.name || sensor.order === void 0) {
3449
- this.logger.warn(
3450
- `@Sensor() on ${instance.constructor.name} missing name or order \u2014 skipped`
3451
- );
3452
- continue;
3453
- }
3454
- if (!sensor.phase) {
3455
- const decoratorPhase = meta !== true ? meta.phase : void 0;
3456
- sensor.phase = decoratorPhase ?? (sensor.order < PRE_DECODE_BOUNDARY ? "PRE_DECODE" : "POST_DECODE");
4125
+ supports(input) {
4126
+ return input.metadata?.cce === true || input.metadata?.contentType === "application/axis-cce";
4127
+ }
4128
+ async run(input) {
4129
+ const envelope = input.metadata?.cceEnvelope;
4130
+ if (!envelope) {
4131
+ return {
4132
+ allow: false,
4133
+ riskScore: 100,
4134
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
4135
+ code: CCE_ERROR.INVALID_ENVELOPE
4136
+ };
4137
+ }
4138
+ for (const field of REQUIRED_FIELDS) {
4139
+ if (envelope[field] === void 0 || envelope[field] === null) {
4140
+ return {
4141
+ allow: false,
4142
+ riskScore: 100,
4143
+ reasons: [`${CCE_ERROR.INVALID_ENVELOPE}: missing ${field}`],
4144
+ code: CCE_ERROR.INVALID_ENVELOPE
4145
+ };
3457
4146
  }
3458
- this.registry.register(sensor);
3459
- count++;
3460
4147
  }
3461
- this.logger.log(`Auto-registered ${count} sensors via @Sensor()`);
3462
- }
3463
- };
3464
- SensorDiscoveryService = __decorateClass([
3465
- Injectable6()
3466
- ], SensorDiscoveryService);
3467
-
3468
- // src/engine/registry/sensor.registry.ts
3469
- import { Injectable as Injectable7, Logger as Logger6 } from "@nestjs/common";
3470
- var SensorRegistry = class {
3471
- constructor(configService) {
3472
- this.configService = configService;
3473
- this.sensors = [];
3474
- this.logger = new Logger6(SensorRegistry.name);
3475
- }
3476
- /**
3477
- * Registers a new sensor in the registry.
3478
- *
3479
- * Validates that:
3480
- * - AxisSensor has a unique name
3481
- * - AxisSensor has an order field
3482
- * - Pre-decode sensors have order < 40
3483
- * - Post-decode sensors have order >= 40
3484
- *
3485
- * @param {AxisSensor} sensor - The sensor instance to register
3486
- * @throws Error if validation fails
3487
- */
3488
- register(sensor) {
3489
- if (!sensor.name) {
3490
- throw new Error("AxisSensor must have a name");
4148
+ if (envelope.ver !== CCE_PROTOCOL_VERSION) {
4149
+ return {
4150
+ allow: false,
4151
+ riskScore: 100,
4152
+ reasons: [`${CCE_ERROR.UNSUPPORTED_VERSION}: ${envelope.ver}`],
4153
+ code: CCE_ERROR.UNSUPPORTED_VERSION
4154
+ };
3491
4155
  }
3492
- const enabledSensorsStr = this.configService.get("ENABLED_SENSORS");
3493
- const disabledSensorsStr = this.configService.get("DISABLED_SENSORS");
3494
- const enabledSensors = enabledSensorsStr ? enabledSensorsStr.split(",").map((s) => s.trim()) : null;
3495
- const disabledSensors = disabledSensorsStr ? disabledSensorsStr.split(",").map((s) => s.trim()) : [];
3496
- if (enabledSensors && !enabledSensors.includes(sensor.name)) {
3497
- this.logger.log(`Skipping disabled sensor (not in ENABLED_SENSORS): ${sensor.name}`);
3498
- return;
4156
+ if (!/^[0-9a-f]+$/i.test(envelope.request_nonce)) {
4157
+ return {
4158
+ allow: false,
4159
+ riskScore: 100,
4160
+ reasons: [
4161
+ `${CCE_ERROR.INVALID_ENVELOPE}: invalid request_nonce format`
4162
+ ],
4163
+ code: CCE_ERROR.INVALID_ENVELOPE
4164
+ };
3499
4165
  }
3500
- if (disabledSensors.includes(sensor.name)) {
3501
- this.logger.log(`Skipping disabled sensor (in DISABLED_SENSORS): ${sensor.name}`);
3502
- return;
4166
+ if (envelope.request_nonce.length !== CCE_NONCE_BYTES * 2) {
4167
+ return {
4168
+ allow: false,
4169
+ riskScore: 100,
4170
+ reasons: [`${CCE_ERROR.INVALID_ENVELOPE}: request_nonce wrong length`],
4171
+ code: CCE_ERROR.INVALID_ENVELOPE
4172
+ };
3503
4173
  }
3504
- if (sensor.order === void 0) {
3505
- throw new Error(`AxisSensor "${sensor.name}" must have an order field`);
4174
+ const capsule = envelope.capsule;
4175
+ if (!capsule.capsule_id || !capsule.ver || !capsule.sub || !capsule.kid || !capsule.intent || !capsule.aud || !capsule.issuer_sig) {
4176
+ return {
4177
+ allow: false,
4178
+ riskScore: 100,
4179
+ reasons: [`${CCE_ERROR.MISSING_CAPSULE}: incomplete capsule claims`],
4180
+ code: CCE_ERROR.MISSING_CAPSULE
4181
+ };
3506
4182
  }
3507
- const isPreDecodeSensor = this.isPreDecodeSensor(sensor);
3508
- const isPostDecodeSensor = this.isPostDecodeSensor(sensor);
3509
- if (isPreDecodeSensor && sensor.order >= 40) {
3510
- this.logger.warn(
3511
- `AxisSensor "${sensor.name}" is marked as PRE_DECODE but has order ${sensor.order} (should be < 40)`
3512
- );
4183
+ if (!envelope.encrypted_key.ciphertext || !envelope.encrypted_key.alg) {
4184
+ return {
4185
+ allow: false,
4186
+ riskScore: 100,
4187
+ reasons: [
4188
+ `${CCE_ERROR.MISSING_ENCRYPTED_KEY}: incomplete encrypted_key`
4189
+ ],
4190
+ code: CCE_ERROR.MISSING_ENCRYPTED_KEY
4191
+ };
3513
4192
  }
3514
- if (isPostDecodeSensor && sensor.order < 40) {
3515
- this.logger.warn(
3516
- `AxisSensor "${sensor.name}" is marked as POST_DECODE but has order ${sensor.order} (should be >= 40)`
3517
- );
4193
+ input.metadata = input.metadata ?? {};
4194
+ input.metadata.cceEnvelopeValid = true;
4195
+ return {
4196
+ decision: "ALLOW" /* ALLOW */,
4197
+ allow: true,
4198
+ riskScore: 0,
4199
+ reasons: []
4200
+ };
4201
+ }
4202
+ };
4203
+
4204
+ // src/cce/sensors/cce-client-signature.sensor.ts
4205
+ var CceClientSignatureSensor = class {
4206
+ constructor(keyResolver, signatureVerifier) {
4207
+ this.keyResolver = keyResolver;
4208
+ this.signatureVerifier = signatureVerifier;
4209
+ this.name = "cce.client.signature";
4210
+ this.order = 45;
4211
+ this.phase = "POST_DECODE";
4212
+ }
4213
+ supports(input) {
4214
+ return input.metadata?.cceEnvelopeValid === true;
4215
+ }
4216
+ async run(input) {
4217
+ const envelope = input.metadata?.cceEnvelope;
4218
+ if (!envelope) {
4219
+ return {
4220
+ allow: false,
4221
+ riskScore: 100,
4222
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
4223
+ code: CCE_ERROR.INVALID_ENVELOPE
4224
+ };
3518
4225
  }
3519
- this.sensors.push(sensor);
3520
- const phaseLabel = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase || "UNKNOWN";
3521
- this.logger.debug(
3522
- `Registered sensor: ${sensor.name} (order: ${sensor.order}, phase: ${phaseLabel})`
4226
+ const keyRecord = await this.keyResolver.resolve(envelope.client_kid);
4227
+ if (!keyRecord) {
4228
+ return {
4229
+ allow: false,
4230
+ riskScore: 100,
4231
+ reasons: [
4232
+ `${CCE_ERROR.CLIENT_KEY_NOT_FOUND}: kid=${envelope.client_kid}`
4233
+ ],
4234
+ code: CCE_ERROR.CLIENT_KEY_NOT_FOUND
4235
+ };
4236
+ }
4237
+ const { client_sig, ...signable } = envelope;
4238
+ const canonical = canonicalize2(signable);
4239
+ const message = new TextEncoder().encode(canonical);
4240
+ const valid = await this.signatureVerifier.verify(
4241
+ message,
4242
+ client_sig.value,
4243
+ keyRecord.publicKeyHex,
4244
+ keyRecord.alg
3523
4245
  );
4246
+ if (!valid) {
4247
+ return {
4248
+ allow: false,
4249
+ riskScore: 100,
4250
+ reasons: [CCE_ERROR.CLIENT_SIG_INVALID],
4251
+ code: CCE_ERROR.CLIENT_SIG_INVALID
4252
+ };
4253
+ }
4254
+ input.metadata = input.metadata ?? {};
4255
+ input.metadata.cceClientKey = keyRecord;
4256
+ input.metadata.cceClientSigVerified = true;
4257
+ return {
4258
+ decision: "ALLOW" /* ALLOW */,
4259
+ allow: true,
4260
+ riskScore: 0,
4261
+ reasons: [],
4262
+ meta: { kid: envelope.client_kid }
4263
+ };
4264
+ }
4265
+ };
4266
+ function canonicalize2(obj) {
4267
+ if (Array.isArray(obj)) {
4268
+ return "[" + obj.map(canonicalize2).join(",") + "]";
4269
+ }
4270
+ if (obj !== null && typeof obj === "object") {
4271
+ const sorted = Object.keys(obj).sort().map(
4272
+ (k) => JSON.stringify(k) + ":" + canonicalize2(obj[k])
4273
+ );
4274
+ return "{" + sorted.join(",") + "}";
4275
+ }
4276
+ return JSON.stringify(obj);
4277
+ }
4278
+
4279
+ // src/cce/sensors/cce-capsule-verification.sensor.ts
4280
+ import { blake3 } from "@noble/hashes/blake3.js";
4281
+ import { bytesToHex as bytesToHex5 } from "@noble/hashes/utils.js";
4282
+ var CceCapsuleVerificationSensor = class {
4283
+ constructor(issuerKeyResolver, capsuleVerifier) {
4284
+ this.issuerKeyResolver = issuerKeyResolver;
4285
+ this.capsuleVerifier = capsuleVerifier;
4286
+ this.name = "cce.capsule.verification";
4287
+ this.order = 50;
4288
+ this.phase = "POST_DECODE";
4289
+ }
4290
+ supports(input) {
4291
+ return input.metadata?.cceEnvelopeValid === true;
4292
+ }
4293
+ async run(input) {
4294
+ const capsule = input.metadata?.cceEnvelope?.capsule;
4295
+ if (!capsule) {
4296
+ return {
4297
+ allow: false,
4298
+ riskScore: 100,
4299
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
4300
+ code: CCE_ERROR.MISSING_CAPSULE
4301
+ };
4302
+ }
4303
+ if (capsule.ver !== CCE_PROTOCOL_VERSION) {
4304
+ return {
4305
+ allow: false,
4306
+ riskScore: 100,
4307
+ reasons: [
4308
+ `${CCE_ERROR.CAPSULE_SIG_INVALID}: wrong version ${capsule.ver}`
4309
+ ],
4310
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
4311
+ };
4312
+ }
4313
+ const { capsule_id, issuer_sig, ...claimsBody } = capsule;
4314
+ const expectedId = computeCceCapsuleId(claimsBody);
4315
+ if (capsule_id !== expectedId) {
4316
+ return {
4317
+ allow: false,
4318
+ riskScore: 100,
4319
+ reasons: [`${CCE_ERROR.CAPSULE_SIG_INVALID}: content hash mismatch`],
4320
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
4321
+ };
4322
+ }
4323
+ const issuerKey = await this.issuerKeyResolver.resolve(
4324
+ capsule.issuer_sig.kid
4325
+ );
4326
+ if (!issuerKey) {
4327
+ return {
4328
+ allow: false,
4329
+ riskScore: 100,
4330
+ reasons: [`${CCE_ERROR.CAPSULE_SIG_INVALID}: issuer key not found`],
4331
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
4332
+ };
4333
+ }
4334
+ const { issuer_sig: sig, ...rest } = capsule;
4335
+ const sigValid = await this.capsuleVerifier.verify(
4336
+ rest,
4337
+ sig,
4338
+ issuerKey.publicKeyHex
4339
+ );
4340
+ if (!sigValid) {
4341
+ return {
4342
+ allow: false,
4343
+ riskScore: 100,
4344
+ reasons: [CCE_ERROR.CAPSULE_SIG_INVALID],
4345
+ code: CCE_ERROR.CAPSULE_SIG_INVALID
4346
+ };
4347
+ }
4348
+ const nowSeconds = Math.floor(Date.now() / 1e3);
4349
+ if (capsule.exp < nowSeconds) {
4350
+ return {
4351
+ allow: false,
4352
+ riskScore: 100,
4353
+ reasons: [`${CCE_ERROR.CAPSULE_EXPIRED}: exp=${capsule.exp}`],
4354
+ code: CCE_ERROR.CAPSULE_EXPIRED
4355
+ };
4356
+ }
4357
+ if (capsule.iat > nowSeconds + 5) {
4358
+ return {
4359
+ allow: false,
4360
+ riskScore: 100,
4361
+ reasons: [`${CCE_ERROR.CAPSULE_NOT_YET_VALID}: iat=${capsule.iat}`],
4362
+ code: CCE_ERROR.CAPSULE_NOT_YET_VALID
4363
+ };
4364
+ }
4365
+ input.metadata = input.metadata ?? {};
4366
+ input.metadata.cceCapsuleVerified = true;
4367
+ input.metadata.cceCapsule = capsule;
4368
+ return {
4369
+ decision: "ALLOW" /* ALLOW */,
4370
+ allow: true,
4371
+ riskScore: 0,
4372
+ reasons: [],
4373
+ meta: { capsule_id: capsule.capsule_id }
4374
+ };
4375
+ }
4376
+ };
4377
+ function canonicalize3(obj) {
4378
+ if (Array.isArray(obj)) {
4379
+ return "[" + obj.map(canonicalize3).join(",") + "]";
4380
+ }
4381
+ if (obj !== null && typeof obj === "object") {
4382
+ const sorted = Object.keys(obj).sort().map(
4383
+ (k) => JSON.stringify(k) + ":" + canonicalize3(obj[k])
4384
+ );
4385
+ return "{" + sorted.join(",") + "}";
4386
+ }
4387
+ return JSON.stringify(obj);
4388
+ }
4389
+ function computeCceCapsuleId(claims) {
4390
+ const canonical = canonicalize3(claims);
4391
+ const hash = blake3(new TextEncoder().encode(canonical));
4392
+ return "cce_b3_" + bytesToHex5(hash).slice(0, 32);
4393
+ }
4394
+
4395
+ // src/cce/sensors/cce-tps-window.sensor.ts
4396
+ var DEFAULT_SKEW_MS = 5e3;
4397
+ var CceTpsWindowSensor = class {
4398
+ constructor(skewMs = DEFAULT_SKEW_MS) {
4399
+ this.skewMs = skewMs;
4400
+ this.name = "cce.tps.window";
4401
+ this.order = 92;
4402
+ this.phase = "POST_DECODE";
4403
+ }
4404
+ supports(input) {
4405
+ return input.metadata?.cceCapsuleVerified === true;
4406
+ }
4407
+ async run(input) {
4408
+ const capsule = input.metadata?.cceCapsule;
4409
+ if (!capsule) {
4410
+ return {
4411
+ allow: false,
4412
+ riskScore: 100,
4413
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
4414
+ code: CCE_ERROR.MISSING_CAPSULE
4415
+ };
4416
+ }
4417
+ const nowMs = Date.now();
4418
+ if (nowMs > capsule.tps_to + this.skewMs) {
4419
+ return {
4420
+ allow: false,
4421
+ riskScore: 100,
4422
+ reasons: [
4423
+ `${CCE_ERROR.TPS_WINDOW_EXPIRED}: window ended at ${capsule.tps_to}, now=${nowMs}`
4424
+ ],
4425
+ code: CCE_ERROR.TPS_WINDOW_EXPIRED
4426
+ };
4427
+ }
4428
+ if (nowMs < capsule.tps_from - this.skewMs) {
4429
+ return {
4430
+ allow: false,
4431
+ riskScore: 100,
4432
+ reasons: [
4433
+ `${CCE_ERROR.TPS_WINDOW_FUTURE}: window starts at ${capsule.tps_from}, now=${nowMs}`
4434
+ ],
4435
+ code: CCE_ERROR.TPS_WINDOW_FUTURE
4436
+ };
4437
+ }
4438
+ input.metadata = input.metadata ?? {};
4439
+ input.metadata.cceTpsValid = true;
4440
+ return {
4441
+ decision: "ALLOW" /* ALLOW */,
4442
+ allow: true,
4443
+ riskScore: 0,
4444
+ reasons: []
4445
+ };
4446
+ }
4447
+ };
4448
+
4449
+ // src/cce/sensors/cce-audience-intent-binding.sensor.ts
4450
+ var CceAudienceIntentBindingSensor = class {
4451
+ constructor(axisAudience) {
4452
+ this.axisAudience = axisAudience;
4453
+ this.name = "cce.audience.intent.binding";
4454
+ this.order = 95;
4455
+ this.phase = "POST_DECODE";
4456
+ }
4457
+ supports(input) {
4458
+ return input.metadata?.cceCapsuleVerified === true;
4459
+ }
4460
+ async run(input) {
4461
+ const capsule = input.metadata?.cceCapsule;
4462
+ const envelope = input.metadata?.cceEnvelope;
4463
+ if (!capsule || !envelope) {
4464
+ return {
4465
+ allow: false,
4466
+ riskScore: 100,
4467
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
4468
+ code: CCE_ERROR.MISSING_CAPSULE
4469
+ };
4470
+ }
4471
+ if (capsule.aud !== this.axisAudience) {
4472
+ return {
4473
+ allow: false,
4474
+ riskScore: 100,
4475
+ reasons: [
4476
+ `${CCE_ERROR.AUDIENCE_MISMATCH}: capsule.aud=${capsule.aud}, expected=${this.axisAudience}`
4477
+ ],
4478
+ code: CCE_ERROR.AUDIENCE_MISMATCH
4479
+ };
4480
+ }
4481
+ const requestIntent = input.intent ?? input.metadata?.cceRequestIntent;
4482
+ if (requestIntent && capsule.intent !== requestIntent) {
4483
+ return {
4484
+ allow: false,
4485
+ riskScore: 100,
4486
+ reasons: [
4487
+ `${CCE_ERROR.INTENT_MISMATCH}: capsule.intent=${capsule.intent}, request=${requestIntent}`
4488
+ ],
4489
+ code: CCE_ERROR.INTENT_MISMATCH
4490
+ };
4491
+ }
4492
+ if (envelope.client_kid !== capsule.kid) {
4493
+ return {
4494
+ allow: false,
4495
+ riskScore: 100,
4496
+ reasons: [
4497
+ `${CCE_ERROR.INTENT_MISMATCH}: envelope.kid=${envelope.client_kid}, capsule.kid=${capsule.kid}`
4498
+ ],
4499
+ code: CCE_ERROR.INTENT_MISMATCH
4500
+ };
4501
+ }
4502
+ input.metadata = input.metadata ?? {};
4503
+ input.metadata.cceBindingVerified = true;
4504
+ return {
4505
+ decision: "ALLOW" /* ALLOW */,
4506
+ allow: true,
4507
+ riskScore: 0,
4508
+ reasons: []
4509
+ };
4510
+ }
4511
+ };
4512
+
4513
+ // src/cce/sensors/cce-replay-protection.sensor.ts
4514
+ var InMemoryCceReplayStore = class {
4515
+ constructor() {
4516
+ this.nonces = /* @__PURE__ */ new Map();
4517
+ this.consumed = /* @__PURE__ */ new Set();
4518
+ this.revoked = /* @__PURE__ */ new Set();
4519
+ }
4520
+ async checkAndMark(key, ttlMs) {
4521
+ this.cleanup();
4522
+ if (this.nonces.has(key)) return false;
4523
+ this.nonces.set(key, Date.now() + ttlMs);
4524
+ return true;
4525
+ }
4526
+ async isCapsuleConsumed(capsuleId) {
4527
+ return this.consumed.has(capsuleId);
4528
+ }
4529
+ async markCapsuleConsumed(capsuleId, _ttlMs) {
4530
+ this.consumed.add(capsuleId);
4531
+ }
4532
+ async isCapsuleRevoked(capsuleId) {
4533
+ return this.revoked.has(capsuleId);
4534
+ }
4535
+ /** Revoke a capsule (for testing/admin) */
4536
+ revoke(capsuleId) {
4537
+ this.revoked.add(capsuleId);
4538
+ }
4539
+ cleanup() {
4540
+ const now = Date.now();
4541
+ for (const [key, expiresAt] of this.nonces) {
4542
+ if (expiresAt < now) this.nonces.delete(key);
4543
+ }
4544
+ }
4545
+ };
4546
+ var CceReplayProtectionSensor = class {
4547
+ constructor(replayStore, options) {
4548
+ this.replayStore = replayStore;
4549
+ this.name = "cce.replay.protection";
4550
+ this.order = 98;
4551
+ this.phase = "POST_DECODE";
4552
+ this.nonceTtlMs = options?.nonceTtlMs ?? 5 * 60 * 1e3;
4553
+ }
4554
+ supports(input) {
4555
+ return input.metadata?.cceCapsuleVerified === true;
4556
+ }
4557
+ async run(input) {
4558
+ const capsule = input.metadata?.cceCapsule;
4559
+ const envelope = input.metadata?.cceEnvelope;
4560
+ if (!capsule || !envelope) {
4561
+ return {
4562
+ allow: false,
4563
+ riskScore: 100,
4564
+ reasons: [CCE_ERROR.MISSING_CAPSULE],
4565
+ code: CCE_ERROR.MISSING_CAPSULE
4566
+ };
4567
+ }
4568
+ const revoked = await this.replayStore.isCapsuleRevoked(capsule.capsule_id);
4569
+ if (revoked) {
4570
+ return {
4571
+ allow: false,
4572
+ riskScore: 100,
4573
+ reasons: [`${CCE_ERROR.CAPSULE_REVOKED}: ${capsule.capsule_id}`],
4574
+ code: CCE_ERROR.CAPSULE_REVOKED
4575
+ };
4576
+ }
4577
+ if (capsule.mode === "SINGLE_USE") {
4578
+ const consumed = await this.replayStore.isCapsuleConsumed(
4579
+ capsule.capsule_id
4580
+ );
4581
+ if (consumed) {
4582
+ return {
4583
+ allow: false,
4584
+ riskScore: 100,
4585
+ reasons: [`${CCE_ERROR.CAPSULE_CONSUMED}: ${capsule.capsule_id}`],
4586
+ code: CCE_ERROR.CAPSULE_CONSUMED
4587
+ };
4588
+ }
4589
+ }
4590
+ const nonceKey = `cce:nonce:${capsule.sub}:${capsule.aud}:${capsule.intent}:${envelope.request_nonce}`;
4591
+ const nonceValid = await this.replayStore.checkAndMark(
4592
+ nonceKey,
4593
+ this.nonceTtlMs
4594
+ );
4595
+ if (!nonceValid) {
4596
+ return {
4597
+ allow: false,
4598
+ riskScore: 100,
4599
+ reasons: [
4600
+ `${CCE_ERROR.NONCE_REUSED}: ${envelope.request_nonce.slice(0, 16)}...`
4601
+ ],
4602
+ code: CCE_ERROR.NONCE_REUSED
4603
+ };
4604
+ }
4605
+ if (capsule.mode === "SINGLE_USE") {
4606
+ const capsuleTtl = (capsule.exp - capsule.iat) * 1e3 + 6e4;
4607
+ await this.replayStore.markCapsuleConsumed(
4608
+ capsule.capsule_id,
4609
+ capsuleTtl
4610
+ );
4611
+ }
4612
+ input.metadata = input.metadata ?? {};
4613
+ input.metadata.cceReplayClean = true;
4614
+ return {
4615
+ decision: "ALLOW" /* ALLOW */,
4616
+ allow: true,
4617
+ riskScore: 0,
4618
+ reasons: []
4619
+ };
4620
+ }
4621
+ };
4622
+
4623
+ // src/cce/sensors/cce-payload-decryption.sensor.ts
4624
+ var CcePayloadDecryptionSensor = class {
4625
+ constructor(keyProvider, aesProvider, maxPayloadBytes = 64 * 1024) {
4626
+ this.keyProvider = keyProvider;
4627
+ this.aesProvider = aesProvider;
4628
+ this.maxPayloadBytes = maxPayloadBytes;
4629
+ this.name = "cce.payload.decryption";
4630
+ this.order = 145;
4631
+ this.phase = "POST_DECODE";
4632
+ }
4633
+ supports(input) {
4634
+ return input.metadata?.cceEnvelopeValid === true && input.metadata?.cceClientSigVerified === true && input.metadata?.cceCapsuleVerified === true && input.metadata?.cceReplayClean === true;
4635
+ }
4636
+ async run(input) {
4637
+ const envelope = input.metadata?.cceEnvelope;
4638
+ if (!envelope) {
4639
+ return {
4640
+ allow: false,
4641
+ riskScore: 100,
4642
+ reasons: [CCE_ERROR.INVALID_ENVELOPE],
4643
+ code: CCE_ERROR.INVALID_ENVELOPE
4644
+ };
4645
+ }
4646
+ let aesKey;
4647
+ try {
4648
+ aesKey = await this.keyProvider.unwrapKey(
4649
+ envelope.encrypted_key.ciphertext,
4650
+ envelope.encrypted_key.alg,
4651
+ envelope.encrypted_key.axis_kid,
4652
+ envelope.encrypted_key.ephemeral_pk
4653
+ );
4654
+ } catch {
4655
+ return {
4656
+ allow: false,
4657
+ riskScore: 100,
4658
+ reasons: [CCE_ERROR.KEY_UNWRAP_FAILED],
4659
+ code: CCE_ERROR.KEY_UNWRAP_FAILED
4660
+ };
4661
+ }
4662
+ if (!aesKey) {
4663
+ return {
4664
+ allow: false,
4665
+ riskScore: 100,
4666
+ reasons: [CCE_ERROR.KEY_UNWRAP_FAILED],
4667
+ code: CCE_ERROR.KEY_UNWRAP_FAILED
4668
+ };
4669
+ }
4670
+ let iv;
4671
+ let ciphertext;
4672
+ let tag;
4673
+ try {
4674
+ iv = base64UrlDecode2(envelope.encrypted_payload.iv);
4675
+ ciphertext = base64UrlDecode2(envelope.encrypted_payload.ciphertext);
4676
+ tag = base64UrlDecode2(envelope.encrypted_payload.tag);
4677
+ } catch {
4678
+ return {
4679
+ allow: false,
4680
+ riskScore: 100,
4681
+ reasons: [`${CCE_ERROR.DECRYPTION_FAILED}: invalid base64url encoding`],
4682
+ code: CCE_ERROR.DECRYPTION_FAILED
4683
+ };
4684
+ }
4685
+ if (ciphertext.length > this.maxPayloadBytes) {
4686
+ return {
4687
+ allow: false,
4688
+ riskScore: 100,
4689
+ reasons: [
4690
+ `${CCE_ERROR.PAYLOAD_TOO_LARGE}: ${ciphertext.length} > ${this.maxPayloadBytes}`
4691
+ ],
4692
+ code: CCE_ERROR.PAYLOAD_TOO_LARGE
4693
+ };
4694
+ }
4695
+ const aad = buildAad(envelope);
4696
+ let plaintext;
4697
+ try {
4698
+ plaintext = await this.aesProvider.decrypt(
4699
+ aesKey,
4700
+ iv,
4701
+ ciphertext,
4702
+ tag,
4703
+ aad
4704
+ );
4705
+ } catch {
4706
+ plaintext = null;
4707
+ } finally {
4708
+ aesKey.fill(0);
4709
+ }
4710
+ if (!plaintext) {
4711
+ return {
4712
+ allow: false,
4713
+ riskScore: 100,
4714
+ reasons: [CCE_ERROR.AEAD_TAG_MISMATCH],
4715
+ code: CCE_ERROR.AEAD_TAG_MISMATCH
4716
+ };
4717
+ }
4718
+ input.metadata = input.metadata ?? {};
4719
+ input.metadata.cceDecryptedPayload = plaintext;
4720
+ input.metadata.cceDecryptionOk = true;
4721
+ return {
4722
+ decision: "ALLOW" /* ALLOW */,
4723
+ allow: true,
4724
+ riskScore: 0,
4725
+ reasons: []
4726
+ };
4727
+ }
4728
+ };
4729
+ function buildAad(envelope) {
4730
+ const parts = [
4731
+ envelope.ver,
4732
+ envelope.request_id,
4733
+ envelope.correlation_id,
4734
+ envelope.client_kid,
4735
+ envelope.capsule.capsule_id,
4736
+ envelope.capsule.intent,
4737
+ envelope.capsule.aud,
4738
+ envelope.request_nonce
4739
+ ];
4740
+ return new TextEncoder().encode(parts.join("|"));
4741
+ }
4742
+ function base64UrlDecode2(input) {
4743
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
4744
+ const padding = "=".repeat((4 - base64.length % 4) % 4);
4745
+ const binary = atob(base64 + padding);
4746
+ const bytes2 = new Uint8Array(binary.length);
4747
+ for (let i = 0; i < binary.length; i++) {
4748
+ bytes2[i] = binary.charCodeAt(i);
4749
+ }
4750
+ return bytes2;
4751
+ }
4752
+
4753
+ // src/core/index.ts
4754
+ var core_exports = {};
4755
+ __export(core_exports, {
4756
+ AXIS_MAGIC: () => AXIS_MAGIC,
4757
+ AXIS_VERSION: () => AXIS_VERSION,
4758
+ AxisError: () => AxisError,
4759
+ AxisFrameZ: () => AxisFrameZ,
4760
+ BodyProfile: () => BodyProfile,
4761
+ ERR_BAD_SIGNATURE: () => ERR_BAD_SIGNATURE,
4762
+ ERR_CONTRACT_VIOLATION: () => ERR_CONTRACT_VIOLATION,
4763
+ ERR_INVALID_PACKET: () => ERR_INVALID_PACKET,
4764
+ ERR_REPLAY_DETECTED: () => ERR_REPLAY_DETECTED,
4765
+ FLAG_BODY_TLV: () => FLAG_BODY_TLV,
4766
+ FLAG_CHAIN_REQ: () => FLAG_CHAIN_REQ,
4767
+ FLAG_HAS_WITNESS: () => FLAG_HAS_WITNESS,
4768
+ MAX_BODY_LEN: () => MAX_BODY_LEN,
4769
+ MAX_FRAME_LEN: () => MAX_FRAME_LEN,
4770
+ MAX_HDR_LEN: () => MAX_HDR_LEN,
4771
+ MAX_SIG_LEN: () => MAX_SIG_LEN,
4772
+ NCERT_ALG: () => NCERT_ALG,
4773
+ NCERT_EXP: () => NCERT_EXP,
4774
+ NCERT_ISSUER_KID: () => NCERT_ISSUER_KID,
4775
+ NCERT_KID: () => NCERT_KID,
4776
+ NCERT_NBF: () => NCERT_NBF,
4777
+ NCERT_NODE_ID: () => NCERT_NODE_ID,
4778
+ NCERT_PAYLOAD: () => NCERT_PAYLOAD,
4779
+ NCERT_PUB: () => NCERT_PUB,
4780
+ NCERT_SCOPE: () => NCERT_SCOPE,
4781
+ NCERT_SIG: () => NCERT_SIG,
4782
+ PROOF_CAPSULE: () => PROOF_CAPSULE,
4783
+ PROOF_JWT: () => PROOF_JWT,
4784
+ PROOF_LOOM: () => PROOF_LOOM,
4785
+ PROOF_MTLS: () => PROOF_MTLS,
4786
+ PROOF_NONE: () => PROOF_NONE,
4787
+ PROOF_WITNESS: () => PROOF_WITNESS,
4788
+ ProofType: () => ProofType,
4789
+ TLV: () => TLV,
4790
+ TLV_ACTOR_ID: () => TLV_ACTOR_ID,
4791
+ TLV_AUD: () => TLV_AUD,
4792
+ TLV_BODY_ARR: () => TLV_BODY_ARR,
4793
+ TLV_BODY_OBJ: () => TLV_BODY_OBJ,
4794
+ TLV_CAPSULE: () => TLV_CAPSULE,
4795
+ TLV_EFFECT: () => TLV_EFFECT,
4796
+ TLV_ERROR_CODE: () => TLV_ERROR_CODE,
4797
+ TLV_ERROR_MSG: () => TLV_ERROR_MSG,
4798
+ TLV_INDEX: () => TLV_INDEX,
4799
+ TLV_INTENT: () => TLV_INTENT,
4800
+ TLV_KID: () => TLV_KID,
4801
+ TLV_LOOM_PRESENCE_ID: () => TLV_LOOM_PRESENCE_ID,
4802
+ TLV_LOOM_THREAD_HASH: () => TLV_LOOM_THREAD_HASH,
4803
+ TLV_LOOM_WRIT: () => TLV_LOOM_WRIT,
4804
+ TLV_NODE: () => TLV_NODE,
4805
+ TLV_NODE_CERT_HASH: () => TLV_NODE_CERT_HASH,
4806
+ TLV_NODE_KID: () => TLV_NODE_KID,
4807
+ TLV_NONCE: () => TLV_NONCE,
4808
+ TLV_OFFSET: () => TLV_OFFSET,
4809
+ TLV_OK: () => TLV_OK,
4810
+ TLV_PID: () => TLV_PID,
4811
+ TLV_PREV_HASH: () => TLV_PREV_HASH,
4812
+ TLV_PROOF_REF: () => TLV_PROOF_REF,
4813
+ TLV_PROOF_TYPE: () => TLV_PROOF_TYPE,
4814
+ TLV_REALM: () => TLV_REALM,
4815
+ TLV_RECEIPT_HASH: () => TLV_RECEIPT_HASH,
4816
+ TLV_RID: () => TLV_RID,
4817
+ TLV_SHA256_CHUNK: () => TLV_SHA256_CHUNK,
4818
+ TLV_TRACE_ID: () => TLV_TRACE_ID,
4819
+ TLV_TS: () => TLV_TS,
4820
+ TLV_UPLOAD_ID: () => TLV_UPLOAD_ID,
4821
+ computeReceiptHash: () => computeReceiptHash,
4822
+ computeSignaturePayload: () => computeSignaturePayload,
4823
+ decodeArray: () => decodeArray,
4824
+ decodeFrame: () => decodeFrame,
4825
+ decodeObject: () => decodeObject,
4826
+ decodeTLVs: () => decodeTLVs,
4827
+ decodeTLVsList: () => decodeTLVsList,
4828
+ decodeVarint: () => decodeVarint,
4829
+ encodeFrame: () => encodeFrame,
4830
+ encodeTLVs: () => encodeTLVs,
4831
+ encodeVarint: () => encodeVarint,
4832
+ generateEd25519KeyPair: () => generateEd25519KeyPair,
4833
+ getSignTarget: () => getSignTarget,
4834
+ sha256: () => sha2564,
4835
+ signFrame: () => signFrame,
4836
+ varintLength: () => varintLength,
4837
+ verifyFrameSignature: () => verifyFrameSignature
4838
+ });
4839
+
4840
+ // src/crypto/index.ts
4841
+ var crypto_exports = {};
4842
+ __export(crypto_exports, {
4843
+ ProofVerificationService: () => ProofVerificationService,
4844
+ b64urlDecode: () => b64urlDecode,
4845
+ b64urlDecodeString: () => b64urlDecodeString,
4846
+ b64urlEncode: () => b64urlEncode,
4847
+ b64urlEncodeString: () => b64urlEncodeString,
4848
+ canonicalJson: () => canonicalJson,
4849
+ canonicalJsonExcluding: () => canonicalJsonExcluding
4850
+ });
4851
+
4852
+ // src/crypto/proof-verification.service.ts
4853
+ import { Injectable as Injectable8, Logger as Logger7 } from "@nestjs/common";
4854
+ import * as crypto4 from "crypto";
4855
+ import * as nacl from "tweetnacl";
4856
+ var ProofVerificationService = class {
4857
+ constructor() {
4858
+ this.logger = new Logger7(ProofVerificationService.name);
4859
+ // Cache of registered device public keys (deviceId -> pubKey)
4860
+ this.deviceKeys = /* @__PURE__ */ new Map();
4861
+ // Cache of trusted mTLS certificate fingerprints
4862
+ this.trustedCerts = /* @__PURE__ */ new Map();
4863
+ }
4864
+ /**
4865
+ * Verifies an authentication proof based on its type.
4866
+ *
4867
+ * **Supported Types:**
4868
+ * - 1 (CAPSULE): Delegated to `verifyCapsuleProof`
4869
+ * - 2 (JWT): Verified by `verifyJWTProof`
4870
+ * - 3 (MTLS_ID): Verified by `verifyMTLSProof`
4871
+ * - 4 (DEVICE_SE): Verified by `verifyDeviceSEProof`
4872
+ *
4873
+ * @param {ProofType} proofType - The numeric AXIS proof type
4874
+ * @param {Uint8Array} proofRef - The binary reference or token for the proof
4875
+ * @param {Object} context - Additional metadata required for specific proof types
4876
+ * @param {Uint8Array} [context.signTarget] - The canonical bytes that were signed (for Ed25519)
4877
+ * @param {Uint8Array} [context.signature] - The signature to verify (for Ed25519)
4878
+ * @param {MTLSContext} [context.mtls] - mTLS certificate data
4879
+ * @param {DeviceSEContext} [context.deviceSE] - Device Secure Element information
4880
+ * @returns {Promise<ProofVerificationResult>} The outcome of the verification
4881
+ */
4882
+ async verifyProof(proofType, proofRef, context) {
4883
+ switch (proofType) {
4884
+ case 1:
4885
+ return this.verifyCapsuleProof(proofRef);
4886
+ case 2:
4887
+ return this.verifyJWTProof(proofRef);
4888
+ case 3:
4889
+ return this.verifyMTLSProof(context.mtls);
4890
+ case 4:
4891
+ return this.verifyDeviceSEProof(
4892
+ context.signTarget,
4893
+ context.signature,
4894
+ context.deviceSE
4895
+ );
4896
+ default:
4897
+ return { valid: false, error: `Unknown proof type: ${proofType}` };
4898
+ }
4899
+ }
4900
+ /**
4901
+ * Verify CAPSULE proof (delegated to CapsuleService)
4902
+ */
4903
+ async verifyCapsuleProof(proofRef) {
4904
+ const capsuleId = new TextDecoder().decode(proofRef);
4905
+ return {
4906
+ valid: true,
4907
+ metadata: { capsuleId, requiresCapsuleValidation: true }
4908
+ };
4909
+ }
4910
+ /**
4911
+ * Verifies a JSON Web Token (JWT) proof.
4912
+ *
4913
+ * **Validation Logic:**
4914
+ * 1. Decodes the token string.
4915
+ * 2. Checks for valid 3-part JWT structure.
4916
+ * 3. Validates `exp` (expiration) and `nbf` (not before) claims.
4917
+ * 4. Extracts `actor_id` or `sub` as the identity.
4918
+ *
4919
+ * @param {Uint8Array} proofRef - Binary representation of the JWT string
4920
+ * @returns {Promise<ProofVerificationResult>} Result including the actor identifier
4921
+ */
4922
+ async verifyJWTProof(proofRef) {
4923
+ try {
4924
+ const token = new TextDecoder().decode(proofRef);
4925
+ const parts = token.split(".");
4926
+ if (parts.length !== 3) {
4927
+ return { valid: false, error: "Invalid JWT format" };
4928
+ }
4929
+ const header = JSON.parse(Buffer.from(parts[0], "base64url").toString());
4930
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
4931
+ if (payload.exp && Date.now() / 1e3 > payload.exp) {
4932
+ return { valid: false, error: "JWT expired" };
4933
+ }
4934
+ if (payload.nbf && Date.now() / 1e3 < payload.nbf) {
4935
+ return { valid: false, error: "JWT not yet valid" };
4936
+ }
4937
+ return {
4938
+ valid: true,
4939
+ actorId: payload.sub || payload.actor_id,
4940
+ metadata: { iss: payload.iss, scope: payload.scope }
4941
+ };
4942
+ } catch (e) {
4943
+ const message = e instanceof Error ? e.message : "Unknown error";
4944
+ return { valid: false, error: `JWT parse error: ${message}` };
4945
+ }
3524
4946
  }
3525
4947
  /**
3526
- * Returns all registered sensors, sorted by their execution order.
3527
- *
3528
- * @returns {AxisSensor[]} A sorted array of sensors
4948
+ * Verify mTLS client certificate proof
3529
4949
  */
3530
- list() {
3531
- return [...this.sensors].sort(
3532
- (a, b) => (a.order ?? 999) - (b.order ?? 999)
3533
- );
4950
+ async verifyMTLSProof(mtls) {
4951
+ if (!mtls) {
4952
+ return { valid: false, error: "No mTLS context provided" };
4953
+ }
4954
+ if (!mtls.verified) {
4955
+ return { valid: false, error: "mTLS not verified by TLS terminator" };
4956
+ }
4957
+ if (mtls.clientCertFingerprint) {
4958
+ const trusted = this.trustedCerts.get(mtls.clientCertFingerprint);
4959
+ if (trusted) {
4960
+ return {
4961
+ valid: true,
4962
+ actorId: trusted.actorId,
4963
+ metadata: {
4964
+ fingerprint: mtls.clientCertFingerprint,
4965
+ subject: mtls.clientCertSubject
4966
+ }
4967
+ };
4968
+ }
4969
+ }
4970
+ if (mtls.clientCertSubject) {
4971
+ const cnMatch = mtls.clientCertSubject.match(/CN=([^,]+)/);
4972
+ if (cnMatch) {
4973
+ return {
4974
+ valid: true,
4975
+ actorId: cnMatch[1],
4976
+ metadata: {
4977
+ subject: mtls.clientCertSubject,
4978
+ issuer: mtls.clientCertIssuer
4979
+ }
4980
+ };
4981
+ }
4982
+ }
4983
+ return { valid: false, error: "Could not extract actor from certificate" };
3534
4984
  }
3535
4985
  /**
3536
- * Returns only pre-decode sensors (order < 40).
3537
- * These sensors run in middleware on raw bytes before frame decoding.
3538
- *
3539
- * @returns {AxisPreSensor[]} Pre-decode sensors sorted by order
4986
+ * Verify Device Secure Element signature
3540
4987
  */
3541
- getPreDecodeSensors() {
3542
- return this.list().filter((s) => (s.order ?? 999) < 40);
4988
+ async verifyDeviceSEProof(signTarget, signature, deviceSE) {
4989
+ if (!deviceSE || !signTarget || !signature) {
4990
+ return { valid: false, error: "Missing Device SE context" };
4991
+ }
4992
+ let publicKey = deviceSE.publicKey;
4993
+ const registeredKey = this.deviceKeys.get(deviceSE.deviceId);
4994
+ if (registeredKey) {
4995
+ publicKey = registeredKey;
4996
+ }
4997
+ if (!publicKey || publicKey.length !== 32) {
4998
+ return {
4999
+ valid: false,
5000
+ error: "Invalid or unregistered device public key"
5001
+ };
5002
+ }
5003
+ try {
5004
+ const valid = nacl.sign.detached.verify(signTarget, signature, publicKey);
5005
+ if (!valid) {
5006
+ return { valid: false, error: "Device signature verification failed" };
5007
+ }
5008
+ return {
5009
+ valid: true,
5010
+ actorId: deviceSE.deviceId,
5011
+ metadata: { deviceId: deviceSE.deviceId, proofType: "DEVICE_SE" }
5012
+ };
5013
+ } catch (e) {
5014
+ const message = e instanceof Error ? e.message : "Unknown error";
5015
+ return {
5016
+ valid: false,
5017
+ error: `Signature verification error: ${message}`
5018
+ };
5019
+ }
3543
5020
  }
3544
5021
  /**
3545
- * Returns only post-decode sensors (order >= 40).
3546
- * These sensors run in the controller on fully decoded frames.
5022
+ * Registers a public key for a trusted device.
5023
+ * This key will be used for future `DEVICE_SE` proof verifications.
3547
5024
  *
3548
- * @returns {AxisPostSensor[]} Post-decode sensors sorted by order
5025
+ * @param {string} deviceId - Unique identifier for the device
5026
+ * @param {Uint8Array} publicKey - 32-byte Ed25519 public key
5027
+ * @throws {Error} If the public key is not 32 bytes
3549
5028
  */
3550
- getPostDecodeSensors() {
3551
- return this.list().filter(
3552
- (s) => (s.order ?? 999) >= 40
3553
- );
5029
+ registerDeviceKey(deviceId, publicKey) {
5030
+ if (publicKey.length !== 32) {
5031
+ throw new Error("Device public key must be 32 bytes (Ed25519)");
5032
+ }
5033
+ this.deviceKeys.set(deviceId, publicKey);
5034
+ this.logger.log(`Registered device key for ${deviceId}`);
3554
5035
  }
3555
5036
  /**
3556
- * Helper: Check if a sensor is a pre-decode sensor.
3557
- *
3558
- * @private
3559
- * @param {AxisSensor} sensor - The sensor to check
3560
- * @returns {boolean} True if sensor is pre-decode
5037
+ * Unregister a device
3561
5038
  */
3562
- isPreDecodeSensor(sensor) {
3563
- const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
3564
- return phase === "PRE_DECODE" || (sensor.order ?? 999) < 40;
5039
+ unregisterDevice(deviceId) {
5040
+ return this.deviceKeys.delete(deviceId);
3565
5041
  }
3566
5042
  /**
3567
- * Helper: Check if a sensor is a post-decode sensor.
5043
+ * Registers a trusted mTLS certificate fingerprint and associates it with an actor.
3568
5044
  *
3569
- * @private
3570
- * @param {AxisSensor} sensor - The sensor to check
3571
- * @returns {boolean} True if sensor is post-decode
5045
+ * @param {string} fingerprint - SHA-256 fingerprint of the client certificate
5046
+ * @param {string} actorId - The actor to associate with this certificate
3572
5047
  */
3573
- isPostDecodeSensor(sensor) {
3574
- const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
3575
- return phase === "POST_DECODE" || (sensor.order ?? 999) >= 40;
5048
+ registerMTLSCert(fingerprint, actorId) {
5049
+ this.trustedCerts.set(fingerprint, { actorId, issuedAt: Date.now() });
5050
+ this.logger.log(`Registered mTLS cert ${fingerprint} for actor ${actorId}`);
3576
5051
  }
3577
5052
  /**
3578
- * Returns sensor count by phase.
3579
- * Useful for diagnostics and monitoring.
3580
- *
3581
- * @returns {{preDecodeCount: number, postDecodeCount: number}}
5053
+ * Revoke an mTLS certificate
3582
5054
  */
3583
- getSensorCountByPhase() {
3584
- return {
3585
- preDecodeCount: this.getPreDecodeSensors().length,
3586
- postDecodeCount: this.getPostDecodeSensors().length
3587
- };
5055
+ revokeMTLSCert(fingerprint) {
5056
+ return this.trustedCerts.delete(fingerprint);
3588
5057
  }
3589
5058
  /**
3590
- * Clears all registered sensors.
3591
- * Useful for testing.
3592
- *
3593
- * @internal
5059
+ * Calculate certificate fingerprint (SHA-256)
3594
5060
  */
3595
- clear() {
3596
- this.sensors = [];
5061
+ static calculateFingerprint(certPem) {
5062
+ const der = Buffer.from(
5063
+ certPem.replace(/-----BEGIN CERTIFICATE-----/, "").replace(/-----END CERTIFICATE-----/, "").replace(/\s/g, ""),
5064
+ "base64"
5065
+ );
5066
+ return crypto4.createHash("sha256").update(der).digest("hex");
3597
5067
  }
3598
5068
  };
3599
- SensorRegistry = __decorateClass([
3600
- Injectable7()
3601
- ], SensorRegistry);
5069
+ ProofVerificationService = __decorateClass([
5070
+ Injectable8()
5071
+ ], ProofVerificationService);
5072
+
5073
+ // src/decorators/index.ts
5074
+ var decorators_exports = {};
5075
+ __export(decorators_exports, {
5076
+ AxisContext: () => AxisContext,
5077
+ AxisDemoPubkey: () => AxisDemoPubkey,
5078
+ AxisFrame: () => AxisFrame3,
5079
+ AxisIp: () => AxisIp,
5080
+ AxisRaw: () => AxisRaw,
5081
+ HANDLER_METADATA_KEY: () => HANDLER_METADATA_KEY,
5082
+ Handler: () => Handler,
5083
+ INTENT_BODY_KEY: () => INTENT_BODY_KEY,
5084
+ INTENT_METADATA_KEY: () => INTENT_METADATA_KEY,
5085
+ INTENT_ROUTES_KEY: () => INTENT_ROUTES_KEY,
5086
+ INTENT_SENSORS_KEY: () => INTENT_SENSORS_KEY,
5087
+ Intent: () => Intent,
5088
+ IntentBody: () => IntentBody,
5089
+ IntentSensors: () => IntentSensors,
5090
+ SENSOR_METADATA_KEY: () => SENSOR_METADATA_KEY,
5091
+ Sensor: () => Sensor,
5092
+ TLV_FIELDS_KEY: () => TLV_FIELDS_KEY,
5093
+ TLV_VALIDATORS_KEY: () => TLV_VALIDATORS_KEY,
5094
+ TlvEnum: () => TlvEnum,
5095
+ TlvField: () => TlvField,
5096
+ TlvMinLen: () => TlvMinLen,
5097
+ TlvRange: () => TlvRange,
5098
+ TlvUtf8Pattern: () => TlvUtf8Pattern,
5099
+ TlvValidate: () => TlvValidate,
5100
+ buildDtoDecoder: () => buildDtoDecoder,
5101
+ extractDtoSchema: () => extractDtoSchema
5102
+ });
5103
+
5104
+ // src/engine/index.ts
5105
+ var engine_exports = {};
5106
+ __export(engine_exports, {
5107
+ BAND: () => BAND,
5108
+ HandlerDiscoveryService: () => HandlerDiscoveryService,
5109
+ IntentRouter: () => IntentRouter,
5110
+ PRE_DECODE_BOUNDARY: () => PRE_DECODE_BOUNDARY,
5111
+ SensorDiscoveryService: () => SensorDiscoveryService,
5112
+ SensorRegistry: () => SensorRegistry,
5113
+ createObservation: () => createObservation,
5114
+ endStage: () => endStage,
5115
+ finalizeObservation: () => finalizeObservation,
5116
+ observation: () => observation_exports,
5117
+ recordSensor: () => recordSensor,
5118
+ startStage: () => startStage
5119
+ });
3602
5120
 
3603
5121
  // src/engine/observation/index.ts
3604
5122
  var observation_exports = {};
@@ -3983,7 +5501,7 @@ var AxisErrorZ = z2.object({
3983
5501
  });
3984
5502
 
3985
5503
  // src/schemas/body-profile.validator.ts
3986
- import { Injectable as Injectable8, Logger as Logger7 } from "@nestjs/common";
5504
+ import { Injectable as Injectable9, Logger as Logger8 } from "@nestjs/common";
3987
5505
  var BodyProfile2 = /* @__PURE__ */ ((BodyProfile3) => {
3988
5506
  BodyProfile3[BodyProfile3["RAW"] = 0] = "RAW";
3989
5507
  BodyProfile3[BodyProfile3["TLV_MAP"] = 1] = "TLV_MAP";
@@ -3993,7 +5511,7 @@ var BodyProfile2 = /* @__PURE__ */ ((BodyProfile3) => {
3993
5511
  })(BodyProfile2 || {});
3994
5512
  var BodyProfileValidator = class {
3995
5513
  constructor() {
3996
- this.logger = new Logger7(BodyProfileValidator.name);
5514
+ this.logger = new Logger8(BodyProfileValidator.name);
3997
5515
  }
3998
5516
  /**
3999
5517
  * Validate body matches declared profile
@@ -4109,12 +5627,13 @@ var BodyProfileValidator = class {
4109
5627
  }
4110
5628
  };
4111
5629
  BodyProfileValidator = __decorateClass([
4112
- Injectable8()
5630
+ Injectable9()
4113
5631
  ], BodyProfileValidator);
4114
5632
 
4115
5633
  // src/security/index.ts
4116
5634
  var security_exports = {};
4117
5635
  __export(security_exports, {
5636
+ AxisSensorChainService: () => AxisSensorChainService,
4118
5637
  CAPABILITIES: () => CAPABILITIES,
4119
5638
  INTENT_REQUIREMENTS: () => INTENT_REQUIREMENTS,
4120
5639
  PROOF_CAPABILITIES: () => PROOF_CAPABILITIES,
@@ -4147,7 +5666,7 @@ __export(sensors_exports, {
4147
5666
  });
4148
5667
 
4149
5668
  // src/sensors/access-profile-resolver.sensor.ts
4150
- import { Injectable as Injectable9 } from "@nestjs/common";
5669
+ import { Injectable as Injectable10 } from "@nestjs/common";
4151
5670
  var AccessProfileResolverSensor = class {
4152
5671
  constructor() {
4153
5672
  /** AxisSensor identifier */
@@ -4173,11 +5692,11 @@ var AccessProfileResolverSensor = class {
4173
5692
  };
4174
5693
  AccessProfileResolverSensor = __decorateClass([
4175
5694
  Sensor(),
4176
- Injectable9()
5695
+ Injectable10()
4177
5696
  ], AccessProfileResolverSensor);
4178
5697
 
4179
5698
  // src/sensors/body-budget.sensor.ts
4180
- import { Injectable as Injectable10 } from "@nestjs/common";
5699
+ import { Injectable as Injectable11 } from "@nestjs/common";
4181
5700
  var BodyBudgetSensor = class {
4182
5701
  constructor() {
4183
5702
  /** AxisSensor identifier */
@@ -4251,14 +5770,14 @@ var BodyBudgetSensor = class {
4251
5770
  };
4252
5771
  BodyBudgetSensor = __decorateClass([
4253
5772
  Sensor(),
4254
- Injectable10()
5773
+ Injectable11()
4255
5774
  ], BodyBudgetSensor);
4256
5775
 
4257
5776
  // src/sensors/capability-enforcement.sensor.ts
4258
- import { Injectable as Injectable11, Logger as Logger8 } from "@nestjs/common";
5777
+ import { Injectable as Injectable12, Logger as Logger9 } from "@nestjs/common";
4259
5778
  var CapabilityEnforcementSensor = class {
4260
5779
  constructor() {
4261
- this.logger = new Logger8(CapabilityEnforcementSensor.name);
5780
+ this.logger = new Logger9(CapabilityEnforcementSensor.name);
4262
5781
  /** AxisSensor identifier for logging and registry */
4263
5782
  this.name = "CapabilityEnforcementSensor";
4264
5783
  /**
@@ -4352,11 +5871,11 @@ var CapabilityEnforcementSensor = class {
4352
5871
  };
4353
5872
  CapabilityEnforcementSensor = __decorateClass([
4354
5873
  Sensor(),
4355
- Injectable11()
5874
+ Injectable12()
4356
5875
  ], CapabilityEnforcementSensor);
4357
5876
 
4358
5877
  // src/sensors/chunk-hash.sensor.ts
4359
- import { Injectable as Injectable12 } from "@nestjs/common";
5878
+ import { Injectable as Injectable13 } from "@nestjs/common";
4360
5879
  import { createHash as createHash7 } from "crypto";
4361
5880
  var ChunkHashSensor = class {
4362
5881
  constructor() {
@@ -4429,15 +5948,15 @@ var ChunkHashSensor = class {
4429
5948
  };
4430
5949
  ChunkHashSensor = __decorateClass([
4431
5950
  Sensor(),
4432
- Injectable12()
5951
+ Injectable13()
4433
5952
  ], ChunkHashSensor);
4434
5953
 
4435
5954
  // src/sensors/entropy.sensor.ts
4436
- import { Injectable as Injectable13, Logger as Logger9 } from "@nestjs/common";
4437
- import * as crypto4 from "crypto";
5955
+ import { Injectable as Injectable14, Logger as Logger10 } from "@nestjs/common";
5956
+ import * as crypto5 from "crypto";
4438
5957
  var EntropySensor = class {
4439
5958
  constructor() {
4440
- this.logger = new Logger9(EntropySensor.name);
5959
+ this.logger = new Logger10(EntropySensor.name);
4441
5960
  /**
4442
5961
  * Minimum acceptable entropy in bits per byte.
4443
5962
  *
@@ -4602,19 +6121,19 @@ var EntropySensor = class {
4602
6121
  * @returns {Uint8Array} Cryptographically secure random bytes
4603
6122
  */
4604
6123
  static generateSecureRandom(length) {
4605
- return new Uint8Array(crypto4.randomBytes(length));
6124
+ return new Uint8Array(crypto5.randomBytes(length));
4606
6125
  }
4607
6126
  };
4608
6127
  EntropySensor = __decorateClass([
4609
6128
  Sensor(),
4610
- Injectable13()
6129
+ Injectable14()
4611
6130
  ], EntropySensor);
4612
6131
 
4613
6132
  // src/sensors/execution-timeout.sensor.ts
4614
- import { Injectable as Injectable14, Logger as Logger10 } from "@nestjs/common";
6133
+ import { Injectable as Injectable15, Logger as Logger11 } from "@nestjs/common";
4615
6134
  var ExecutionTimeoutSensor = class {
4616
6135
  constructor() {
4617
- this.logger = new Logger10(ExecutionTimeoutSensor.name);
6136
+ this.logger = new Logger11(ExecutionTimeoutSensor.name);
4618
6137
  /** AxisSensor identifier */
4619
6138
  this.name = "ExecutionTimeoutSensor";
4620
6139
  /**
@@ -4692,11 +6211,11 @@ var ExecutionTimeoutSensor = class {
4692
6211
  };
4693
6212
  ExecutionTimeoutSensor = __decorateClass([
4694
6213
  Sensor(),
4695
- Injectable14()
6214
+ Injectable15()
4696
6215
  ], ExecutionTimeoutSensor);
4697
6216
 
4698
6217
  // src/sensors/frame-budget.sensor.ts
4699
- import { Injectable as Injectable15 } from "@nestjs/common";
6218
+ import { Injectable as Injectable16 } from "@nestjs/common";
4700
6219
  var FrameBudgetSensor = class {
4701
6220
  constructor(config) {
4702
6221
  this.config = config;
@@ -4755,11 +6274,11 @@ var FrameBudgetSensor = class {
4755
6274
  };
4756
6275
  FrameBudgetSensor = __decorateClass([
4757
6276
  Sensor({ phase: "PRE_DECODE" }),
4758
- Injectable15()
6277
+ Injectable16()
4759
6278
  ], FrameBudgetSensor);
4760
6279
 
4761
6280
  // src/sensors/frame-header-sanity.sensor.ts
4762
- import { Injectable as Injectable16 } from "@nestjs/common";
6281
+ import { Injectable as Injectable17 } from "@nestjs/common";
4763
6282
  var FrameHeaderSanitySensor = class {
4764
6283
  constructor() {
4765
6284
  this.name = "FrameHeaderSanitySensor";
@@ -4803,12 +6322,12 @@ var FrameHeaderSanitySensor = class {
4803
6322
  }
4804
6323
  };
4805
6324
  FrameHeaderSanitySensor = __decorateClass([
4806
- Injectable16(),
6325
+ Injectable17(),
4807
6326
  Sensor({ phase: "PRE_DECODE" })
4808
6327
  ], FrameHeaderSanitySensor);
4809
6328
 
4810
6329
  // src/sensors/header-tlv-limit.sensor.ts
4811
- import { Injectable as Injectable17 } from "@nestjs/common";
6330
+ import { Injectable as Injectable18 } from "@nestjs/common";
4812
6331
  var HeaderTLVLimitSensor = class {
4813
6332
  constructor() {
4814
6333
  this.name = "HeaderTLVLimitSensor";
@@ -4840,12 +6359,12 @@ var HeaderTLVLimitSensor = class {
4840
6359
  }
4841
6360
  };
4842
6361
  HeaderTLVLimitSensor = __decorateClass([
4843
- Injectable17(),
6362
+ Injectable18(),
4844
6363
  Sensor()
4845
6364
  ], HeaderTLVLimitSensor);
4846
6365
 
4847
6366
  // src/sensors/intent-allowlist.sensor.ts
4848
- import { Injectable as Injectable18 } from "@nestjs/common";
6367
+ import { Injectable as Injectable19 } from "@nestjs/common";
4849
6368
  var PUBLIC_INTENT_ALLOWLIST = [
4850
6369
  "public.",
4851
6370
  "schema.",
@@ -4880,12 +6399,12 @@ var IntentAllowlistSensor = class {
4880
6399
  }
4881
6400
  };
4882
6401
  IntentAllowlistSensor = __decorateClass([
4883
- Injectable18(),
6402
+ Injectable19(),
4884
6403
  Sensor()
4885
6404
  ], IntentAllowlistSensor);
4886
6405
 
4887
6406
  // src/sensors/intent-registry.sensor.ts
4888
- import { Injectable as Injectable19 } from "@nestjs/common";
6407
+ import { Injectable as Injectable20 } from "@nestjs/common";
4889
6408
  var IntentRegistrySensor = class {
4890
6409
  constructor(router) {
4891
6410
  this.router = router;
@@ -4908,12 +6427,12 @@ var IntentRegistrySensor = class {
4908
6427
  }
4909
6428
  };
4910
6429
  IntentRegistrySensor = __decorateClass([
4911
- Injectable19(),
6430
+ Injectable20(),
4912
6431
  Sensor({ phase: "POST_DECODE" })
4913
6432
  ], IntentRegistrySensor);
4914
6433
 
4915
6434
  // src/sensors/proof-presence.sensor.ts
4916
- import { Injectable as Injectable20 } from "@nestjs/common";
6435
+ import { Injectable as Injectable21 } from "@nestjs/common";
4917
6436
  var ProofPresenceSensor = class {
4918
6437
  constructor() {
4919
6438
  this.name = "ProofPresenceSensor";
@@ -4961,11 +6480,11 @@ var ProofPresenceSensor = class {
4961
6480
  };
4962
6481
  ProofPresenceSensor = __decorateClass([
4963
6482
  Sensor(),
4964
- Injectable20()
6483
+ Injectable21()
4965
6484
  ], ProofPresenceSensor);
4966
6485
 
4967
6486
  // src/sensors/protocol-strict.sensor.ts
4968
- import { Injectable as Injectable21, Logger as Logger11 } from "@nestjs/common";
6487
+ import { Injectable as Injectable22, Logger as Logger12 } from "@nestjs/common";
4969
6488
  var VALID_FLAGS = [
4970
6489
  0,
4971
6490
  // No flags
@@ -4983,7 +6502,7 @@ var VALID_FLAGS = [
4983
6502
  var ProtocolStrictSensor = class {
4984
6503
  constructor(config) {
4985
6504
  this.config = config;
4986
- this.logger = new Logger11(ProtocolStrictSensor.name);
6505
+ this.logger = new Logger12(ProtocolStrictSensor.name);
4987
6506
  /** Sensor identifier for logging and registry */
4988
6507
  this.name = "ProtocolStrictSensor";
4989
6508
  /**
@@ -5236,11 +6755,11 @@ var ProtocolStrictSensor = class {
5236
6755
  };
5237
6756
  ProtocolStrictSensor = __decorateClass([
5238
6757
  Sensor({ phase: "PRE_DECODE" }),
5239
- Injectable21()
6758
+ Injectable22()
5240
6759
  ], ProtocolStrictSensor);
5241
6760
 
5242
6761
  // src/sensors/receipt-policy.sensor.ts
5243
- import { Injectable as Injectable22 } from "@nestjs/common";
6762
+ import { Injectable as Injectable23 } from "@nestjs/common";
5244
6763
  var ReceiptPolicySensor = class {
5245
6764
  constructor() {
5246
6765
  this.name = "ReceiptPolicySensor";
@@ -5254,12 +6773,12 @@ var ReceiptPolicySensor = class {
5254
6773
  }
5255
6774
  };
5256
6775
  ReceiptPolicySensor = __decorateClass([
5257
- Injectable22(),
6776
+ Injectable23(),
5258
6777
  Sensor()
5259
6778
  ], ReceiptPolicySensor);
5260
6779
 
5261
6780
  // src/sensors/schema-validation.sensor.ts
5262
- import { Injectable as Injectable23 } from "@nestjs/common";
6781
+ import { Injectable as Injectable24 } from "@nestjs/common";
5263
6782
  function readU64be(b) {
5264
6783
  if (b.length !== 8)
5265
6784
  throw new AxisError("SCHEMA_TYPE_MISMATCH", "u64 must be 8 bytes", 400);
@@ -5434,11 +6953,11 @@ var SchemaValidationSensor = class {
5434
6953
  };
5435
6954
  SchemaValidationSensor = __decorateClass([
5436
6955
  Sensor(),
5437
- Injectable23()
6956
+ Injectable24()
5438
6957
  ], SchemaValidationSensor);
5439
6958
 
5440
6959
  // src/sensors/stream-scope.sensor.ts
5441
- import { Injectable as Injectable24 } from "@nestjs/common";
6960
+ import { Injectable as Injectable25 } from "@nestjs/common";
5442
6961
  var StreamScopeSensor = class {
5443
6962
  constructor() {
5444
6963
  /** Sensor identifier */
@@ -5484,11 +7003,11 @@ var StreamScopeSensor = class {
5484
7003
  };
5485
7004
  StreamScopeSensor = __decorateClass([
5486
7005
  Sensor(),
5487
- Injectable24()
7006
+ Injectable25()
5488
7007
  ], StreamScopeSensor);
5489
7008
 
5490
7009
  // src/sensors/tlv-parse.sensor.ts
5491
- import { Injectable as Injectable25 } from "@nestjs/common";
7010
+ import { Injectable as Injectable26 } from "@nestjs/common";
5492
7011
  var TLVParseSensor = class {
5493
7012
  constructor() {
5494
7013
  this.name = "TLVParseSensor";
@@ -5590,11 +7109,11 @@ var TLVParseSensor = class {
5590
7109
  };
5591
7110
  TLVParseSensor = __decorateClass([
5592
7111
  Sensor(),
5593
- Injectable25()
7112
+ Injectable26()
5594
7113
  ], TLVParseSensor);
5595
7114
 
5596
7115
  // src/sensors/varint-hardening.sensor.ts
5597
- import { Injectable as Injectable26 } from "@nestjs/common";
7116
+ import { Injectable as Injectable27 } from "@nestjs/common";
5598
7117
  var VarintHardeningSensor = class {
5599
7118
  constructor() {
5600
7119
  /** Sensor identifier */
@@ -5657,7 +7176,7 @@ var VarintHardeningSensor = class {
5657
7176
  };
5658
7177
  VarintHardeningSensor = __decorateClass([
5659
7178
  Sensor({ phase: "PRE_DECODE" }),
5660
- Injectable26()
7179
+ Injectable27()
5661
7180
  ], VarintHardeningSensor);
5662
7181
 
5663
7182
  // src/utils/index.ts
@@ -5728,16 +7247,26 @@ export {
5728
7247
  AXIS_UPLOAD_SESSION_STORE,
5729
7248
  AXIS_VERSION,
5730
7249
  ats1_exports as Ats1Codec,
7250
+ AxisContext,
7251
+ AxisDemoPubkey,
7252
+ AxisError,
5731
7253
  AxisFilesDownloadHandler,
5732
7254
  AxisFilesFinalizeHandler,
5733
7255
  AxisFrameZ,
5734
7256
  AxisIdDto,
7257
+ AxisIp,
5735
7258
  T as AxisPacketTags,
5736
7259
  AxisPartialType,
7260
+ AxisRaw,
5737
7261
  AxisResponseDto,
7262
+ AxisSensorChainService,
5738
7263
  AxisTlvDto,
7264
+ BAND,
5739
7265
  BodyProfile,
5740
7266
  CAPABILITIES,
7267
+ CCE_ERROR,
7268
+ CCE_PROTOCOL_VERSION,
7269
+ CceError,
5741
7270
  ContractViolationError,
5742
7271
  DEFAULT_CONTRACTS,
5743
7272
  DEFAULT_TIMEOUT,
@@ -5753,7 +7282,10 @@ export {
5753
7282
  FLAG_CHAIN_REQ,
5754
7283
  FLAG_HAS_WITNESS,
5755
7284
  HANDLER_METADATA_KEY,
7285
+ HANDLER_SENSORS_KEY,
5756
7286
  Handler,
7287
+ HandlerDiscoveryService,
7288
+ HandlerSensors,
5757
7289
  INTENT_BODY_KEY,
5758
7290
  INTENT_METADATA_KEY,
5759
7291
  INTENT_REQUIREMENTS,
@@ -5780,6 +7312,7 @@ export {
5780
7312
  NCERT_PUB,
5781
7313
  NCERT_SCOPE,
5782
7314
  NCERT_SIG,
7315
+ PRE_DECODE_BOUNDARY,
5783
7316
  PROOF_CAPABILITIES,
5784
7317
  PROOF_CAPSULE,
5785
7318
  PROOF_JWT,
@@ -5794,11 +7327,15 @@ export {
5794
7327
  RESPONSE_TAG_UPDATED_AT,
5795
7328
  RESPONSE_TAG_UPDATED_BY,
5796
7329
  RiskDecision,
7330
+ SENSOR_METADATA_KEY,
5797
7331
  Schema2002_PasskeyLoginOptionsRes,
5798
7332
  Schema2011_PasskeyLoginVerifyReq,
5799
7333
  Schema2012_PasskeyLoginVerifyRes,
5800
7334
  Schema2021_PasskeyRegisterOptionsReq,
7335
+ Sensor,
5801
7336
  SensorDecisions,
7337
+ SensorDiscoveryService,
7338
+ SensorRegistry,
5802
7339
  TLV,
5803
7340
  TLV_ACTOR_ID,
5804
7341
  TLV_AUD,
@@ -5856,10 +7393,12 @@ export {
5856
7393
  canonicalJson,
5857
7394
  canonicalJsonExcluding,
5858
7395
  canonicalizeObservation,
7396
+ cce_exports as cce,
5859
7397
  classifyIntent,
5860
7398
  computeReceiptHash,
5861
7399
  computeSignaturePayload,
5862
7400
  core_exports as core,
7401
+ createObservation,
5863
7402
  crypto_exports as crypto,
5864
7403
  decodeArray,
5865
7404
  decodeAxis1Frame,
@@ -5876,8 +7415,11 @@ export {
5876
7415
  encodeQueueMessage,
5877
7416
  encodeTLVs,
5878
7417
  encodeVarint,
7418
+ endStage,
5879
7419
  engine_exports as engine,
7420
+ executeCcePipeline,
5880
7421
  extractDtoSchema,
7422
+ finalizeObservation,
5881
7423
  generateEd25519KeyPair,
5882
7424
  getSignTarget,
5883
7425
  hasScope,
@@ -5896,14 +7438,16 @@ export {
5896
7438
  parseAutoClaimEntries,
5897
7439
  parseScope,
5898
7440
  parseStreamEntries,
7441
+ recordSensor,
5899
7442
  resolveTimeout,
5900
7443
  schemas_exports as schemas,
5901
7444
  security_exports as security,
5902
7445
  sensitivityName,
5903
7446
  sensors_exports as sensors,
5904
- sha256,
7447
+ sha2564 as sha256,
5905
7448
  signFrame,
5906
7449
  stableJsonStringify,
7450
+ startStage,
5907
7451
  tlv,
5908
7452
  u64be,
5909
7453
  unpackPasskeyLoginOptionsReq,