@nextera.one/axis-server-sdk 1.2.1 → 1.3.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
@@ -2678,6 +2678,2826 @@ var DiskUploadFileStore = class {
2678
2678
  return fs.createReadStream(tempPath);
2679
2679
  }
2680
2680
  };
2681
+
2682
+ // src/core/index.ts
2683
+ var core_exports = {};
2684
+ __export(core_exports, {
2685
+ AXIS_MAGIC: () => AXIS_MAGIC,
2686
+ AXIS_VERSION: () => AXIS_VERSION,
2687
+ AxisError: () => AxisError,
2688
+ AxisFrameZ: () => AxisFrameZ,
2689
+ BodyProfile: () => BodyProfile,
2690
+ ERR_BAD_SIGNATURE: () => ERR_BAD_SIGNATURE,
2691
+ ERR_CONTRACT_VIOLATION: () => ERR_CONTRACT_VIOLATION,
2692
+ ERR_INVALID_PACKET: () => ERR_INVALID_PACKET,
2693
+ ERR_REPLAY_DETECTED: () => ERR_REPLAY_DETECTED,
2694
+ FLAG_BODY_TLV: () => FLAG_BODY_TLV,
2695
+ FLAG_CHAIN_REQ: () => FLAG_CHAIN_REQ,
2696
+ FLAG_HAS_WITNESS: () => FLAG_HAS_WITNESS,
2697
+ MAX_BODY_LEN: () => MAX_BODY_LEN,
2698
+ MAX_FRAME_LEN: () => MAX_FRAME_LEN,
2699
+ MAX_HDR_LEN: () => MAX_HDR_LEN,
2700
+ MAX_SIG_LEN: () => MAX_SIG_LEN,
2701
+ NCERT_ALG: () => NCERT_ALG,
2702
+ NCERT_EXP: () => NCERT_EXP,
2703
+ NCERT_ISSUER_KID: () => NCERT_ISSUER_KID,
2704
+ NCERT_KID: () => NCERT_KID,
2705
+ NCERT_NBF: () => NCERT_NBF,
2706
+ NCERT_NODE_ID: () => NCERT_NODE_ID,
2707
+ NCERT_PAYLOAD: () => NCERT_PAYLOAD,
2708
+ NCERT_PUB: () => NCERT_PUB,
2709
+ NCERT_SCOPE: () => NCERT_SCOPE,
2710
+ NCERT_SIG: () => NCERT_SIG,
2711
+ PROOF_CAPSULE: () => PROOF_CAPSULE,
2712
+ PROOF_JWT: () => PROOF_JWT,
2713
+ PROOF_LOOM: () => PROOF_LOOM,
2714
+ PROOF_MTLS: () => PROOF_MTLS,
2715
+ PROOF_NONE: () => PROOF_NONE,
2716
+ PROOF_WITNESS: () => PROOF_WITNESS,
2717
+ ProofType: () => ProofType,
2718
+ TLV: () => TLV,
2719
+ TLV_ACTOR_ID: () => TLV_ACTOR_ID,
2720
+ TLV_AUD: () => TLV_AUD,
2721
+ TLV_BODY_ARR: () => TLV_BODY_ARR,
2722
+ TLV_BODY_OBJ: () => TLV_BODY_OBJ,
2723
+ TLV_CAPSULE: () => TLV_CAPSULE,
2724
+ TLV_EFFECT: () => TLV_EFFECT,
2725
+ TLV_ERROR_CODE: () => TLV_ERROR_CODE,
2726
+ TLV_ERROR_MSG: () => TLV_ERROR_MSG,
2727
+ TLV_INDEX: () => TLV_INDEX,
2728
+ TLV_INTENT: () => TLV_INTENT,
2729
+ TLV_KID: () => TLV_KID,
2730
+ TLV_LOOM_PRESENCE_ID: () => TLV_LOOM_PRESENCE_ID,
2731
+ TLV_LOOM_THREAD_HASH: () => TLV_LOOM_THREAD_HASH,
2732
+ TLV_LOOM_WRIT: () => TLV_LOOM_WRIT,
2733
+ TLV_NODE: () => TLV_NODE,
2734
+ TLV_NODE_CERT_HASH: () => TLV_NODE_CERT_HASH,
2735
+ TLV_NODE_KID: () => TLV_NODE_KID,
2736
+ TLV_NONCE: () => TLV_NONCE,
2737
+ TLV_OFFSET: () => TLV_OFFSET,
2738
+ TLV_OK: () => TLV_OK,
2739
+ TLV_PID: () => TLV_PID,
2740
+ TLV_PREV_HASH: () => TLV_PREV_HASH,
2741
+ TLV_PROOF_REF: () => TLV_PROOF_REF,
2742
+ TLV_PROOF_TYPE: () => TLV_PROOF_TYPE,
2743
+ TLV_REALM: () => TLV_REALM,
2744
+ TLV_RECEIPT_HASH: () => TLV_RECEIPT_HASH,
2745
+ TLV_RID: () => TLV_RID,
2746
+ TLV_SHA256_CHUNK: () => TLV_SHA256_CHUNK,
2747
+ TLV_TRACE_ID: () => TLV_TRACE_ID,
2748
+ TLV_TS: () => TLV_TS,
2749
+ TLV_UPLOAD_ID: () => TLV_UPLOAD_ID,
2750
+ computeReceiptHash: () => computeReceiptHash,
2751
+ computeSignaturePayload: () => computeSignaturePayload,
2752
+ decodeArray: () => decodeArray,
2753
+ decodeFrame: () => decodeFrame,
2754
+ decodeObject: () => decodeObject,
2755
+ decodeTLVs: () => decodeTLVs,
2756
+ decodeTLVsList: () => decodeTLVsList,
2757
+ decodeVarint: () => decodeVarint,
2758
+ encodeFrame: () => encodeFrame,
2759
+ encodeTLVs: () => encodeTLVs,
2760
+ encodeVarint: () => encodeVarint,
2761
+ generateEd25519KeyPair: () => generateEd25519KeyPair,
2762
+ getSignTarget: () => getSignTarget,
2763
+ sha256: () => sha256,
2764
+ signFrame: () => signFrame,
2765
+ varintLength: () => varintLength,
2766
+ verifyFrameSignature: () => verifyFrameSignature
2767
+ });
2768
+
2769
+ // src/core/axis-error.ts
2770
+ var AxisError = class extends Error {
2771
+ constructor(code, message, httpStatus = 400, details) {
2772
+ super(message);
2773
+ this.code = code;
2774
+ this.httpStatus = httpStatus;
2775
+ this.details = details;
2776
+ this.name = "AxisError";
2777
+ }
2778
+ };
2779
+
2780
+ // src/crypto/index.ts
2781
+ var crypto_exports = {};
2782
+ __export(crypto_exports, {
2783
+ ProofVerificationService: () => ProofVerificationService,
2784
+ b64urlDecode: () => b64urlDecode,
2785
+ b64urlDecodeString: () => b64urlDecodeString,
2786
+ b64urlEncode: () => b64urlEncode,
2787
+ b64urlEncodeString: () => b64urlEncodeString,
2788
+ canonicalJson: () => canonicalJson,
2789
+ canonicalJsonExcluding: () => canonicalJsonExcluding
2790
+ });
2791
+
2792
+ // src/crypto/proof-verification.service.ts
2793
+ import { Injectable as Injectable4, Logger as Logger3 } from "@nestjs/common";
2794
+ import * as crypto3 from "crypto";
2795
+ import * as nacl from "tweetnacl";
2796
+ var ProofVerificationService = class {
2797
+ constructor() {
2798
+ this.logger = new Logger3(ProofVerificationService.name);
2799
+ // Cache of registered device public keys (deviceId -> pubKey)
2800
+ this.deviceKeys = /* @__PURE__ */ new Map();
2801
+ // Cache of trusted mTLS certificate fingerprints
2802
+ this.trustedCerts = /* @__PURE__ */ new Map();
2803
+ }
2804
+ /**
2805
+ * Verifies an authentication proof based on its type.
2806
+ *
2807
+ * **Supported Types:**
2808
+ * - 1 (CAPSULE): Delegated to `verifyCapsuleProof`
2809
+ * - 2 (JWT): Verified by `verifyJWTProof`
2810
+ * - 3 (MTLS_ID): Verified by `verifyMTLSProof`
2811
+ * - 4 (DEVICE_SE): Verified by `verifyDeviceSEProof`
2812
+ *
2813
+ * @param {ProofType} proofType - The numeric AXIS proof type
2814
+ * @param {Uint8Array} proofRef - The binary reference or token for the proof
2815
+ * @param {Object} context - Additional metadata required for specific proof types
2816
+ * @param {Uint8Array} [context.signTarget] - The canonical bytes that were signed (for Ed25519)
2817
+ * @param {Uint8Array} [context.signature] - The signature to verify (for Ed25519)
2818
+ * @param {MTLSContext} [context.mtls] - mTLS certificate data
2819
+ * @param {DeviceSEContext} [context.deviceSE] - Device Secure Element information
2820
+ * @returns {Promise<ProofVerificationResult>} The outcome of the verification
2821
+ */
2822
+ async verifyProof(proofType, proofRef, context) {
2823
+ switch (proofType) {
2824
+ case 1:
2825
+ return this.verifyCapsuleProof(proofRef);
2826
+ case 2:
2827
+ return this.verifyJWTProof(proofRef);
2828
+ case 3:
2829
+ return this.verifyMTLSProof(context.mtls);
2830
+ case 4:
2831
+ return this.verifyDeviceSEProof(
2832
+ context.signTarget,
2833
+ context.signature,
2834
+ context.deviceSE
2835
+ );
2836
+ default:
2837
+ return { valid: false, error: `Unknown proof type: ${proofType}` };
2838
+ }
2839
+ }
2840
+ /**
2841
+ * Verify CAPSULE proof (delegated to CapsuleService)
2842
+ */
2843
+ async verifyCapsuleProof(proofRef) {
2844
+ const capsuleId = new TextDecoder().decode(proofRef);
2845
+ return {
2846
+ valid: true,
2847
+ metadata: { capsuleId, requiresCapsuleValidation: true }
2848
+ };
2849
+ }
2850
+ /**
2851
+ * Verifies a JSON Web Token (JWT) proof.
2852
+ *
2853
+ * **Validation Logic:**
2854
+ * 1. Decodes the token string.
2855
+ * 2. Checks for valid 3-part JWT structure.
2856
+ * 3. Validates `exp` (expiration) and `nbf` (not before) claims.
2857
+ * 4. Extracts `actor_id` or `sub` as the identity.
2858
+ *
2859
+ * @param {Uint8Array} proofRef - Binary representation of the JWT string
2860
+ * @returns {Promise<ProofVerificationResult>} Result including the actor identifier
2861
+ */
2862
+ async verifyJWTProof(proofRef) {
2863
+ try {
2864
+ const token = new TextDecoder().decode(proofRef);
2865
+ const parts = token.split(".");
2866
+ if (parts.length !== 3) {
2867
+ return { valid: false, error: "Invalid JWT format" };
2868
+ }
2869
+ const header = JSON.parse(Buffer.from(parts[0], "base64url").toString());
2870
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
2871
+ if (payload.exp && Date.now() / 1e3 > payload.exp) {
2872
+ return { valid: false, error: "JWT expired" };
2873
+ }
2874
+ if (payload.nbf && Date.now() / 1e3 < payload.nbf) {
2875
+ return { valid: false, error: "JWT not yet valid" };
2876
+ }
2877
+ return {
2878
+ valid: true,
2879
+ actorId: payload.sub || payload.actor_id,
2880
+ metadata: { iss: payload.iss, scope: payload.scope }
2881
+ };
2882
+ } catch (e) {
2883
+ const message = e instanceof Error ? e.message : "Unknown error";
2884
+ return { valid: false, error: `JWT parse error: ${message}` };
2885
+ }
2886
+ }
2887
+ /**
2888
+ * Verify mTLS client certificate proof
2889
+ */
2890
+ async verifyMTLSProof(mtls) {
2891
+ if (!mtls) {
2892
+ return { valid: false, error: "No mTLS context provided" };
2893
+ }
2894
+ if (!mtls.verified) {
2895
+ return { valid: false, error: "mTLS not verified by TLS terminator" };
2896
+ }
2897
+ if (mtls.clientCertFingerprint) {
2898
+ const trusted = this.trustedCerts.get(mtls.clientCertFingerprint);
2899
+ if (trusted) {
2900
+ return {
2901
+ valid: true,
2902
+ actorId: trusted.actorId,
2903
+ metadata: {
2904
+ fingerprint: mtls.clientCertFingerprint,
2905
+ subject: mtls.clientCertSubject
2906
+ }
2907
+ };
2908
+ }
2909
+ }
2910
+ if (mtls.clientCertSubject) {
2911
+ const cnMatch = mtls.clientCertSubject.match(/CN=([^,]+)/);
2912
+ if (cnMatch) {
2913
+ return {
2914
+ valid: true,
2915
+ actorId: cnMatch[1],
2916
+ metadata: {
2917
+ subject: mtls.clientCertSubject,
2918
+ issuer: mtls.clientCertIssuer
2919
+ }
2920
+ };
2921
+ }
2922
+ }
2923
+ return { valid: false, error: "Could not extract actor from certificate" };
2924
+ }
2925
+ /**
2926
+ * Verify Device Secure Element signature
2927
+ */
2928
+ async verifyDeviceSEProof(signTarget, signature, deviceSE) {
2929
+ if (!deviceSE || !signTarget || !signature) {
2930
+ return { valid: false, error: "Missing Device SE context" };
2931
+ }
2932
+ let publicKey = deviceSE.publicKey;
2933
+ const registeredKey = this.deviceKeys.get(deviceSE.deviceId);
2934
+ if (registeredKey) {
2935
+ publicKey = registeredKey;
2936
+ }
2937
+ if (!publicKey || publicKey.length !== 32) {
2938
+ return {
2939
+ valid: false,
2940
+ error: "Invalid or unregistered device public key"
2941
+ };
2942
+ }
2943
+ try {
2944
+ const valid = nacl.sign.detached.verify(signTarget, signature, publicKey);
2945
+ if (!valid) {
2946
+ return { valid: false, error: "Device signature verification failed" };
2947
+ }
2948
+ return {
2949
+ valid: true,
2950
+ actorId: deviceSE.deviceId,
2951
+ metadata: { deviceId: deviceSE.deviceId, proofType: "DEVICE_SE" }
2952
+ };
2953
+ } catch (e) {
2954
+ const message = e instanceof Error ? e.message : "Unknown error";
2955
+ return {
2956
+ valid: false,
2957
+ error: `Signature verification error: ${message}`
2958
+ };
2959
+ }
2960
+ }
2961
+ /**
2962
+ * Registers a public key for a trusted device.
2963
+ * This key will be used for future `DEVICE_SE` proof verifications.
2964
+ *
2965
+ * @param {string} deviceId - Unique identifier for the device
2966
+ * @param {Uint8Array} publicKey - 32-byte Ed25519 public key
2967
+ * @throws {Error} If the public key is not 32 bytes
2968
+ */
2969
+ registerDeviceKey(deviceId, publicKey) {
2970
+ if (publicKey.length !== 32) {
2971
+ throw new Error("Device public key must be 32 bytes (Ed25519)");
2972
+ }
2973
+ this.deviceKeys.set(deviceId, publicKey);
2974
+ this.logger.log(`Registered device key for ${deviceId}`);
2975
+ }
2976
+ /**
2977
+ * Unregister a device
2978
+ */
2979
+ unregisterDevice(deviceId) {
2980
+ return this.deviceKeys.delete(deviceId);
2981
+ }
2982
+ /**
2983
+ * Registers a trusted mTLS certificate fingerprint and associates it with an actor.
2984
+ *
2985
+ * @param {string} fingerprint - SHA-256 fingerprint of the client certificate
2986
+ * @param {string} actorId - The actor to associate with this certificate
2987
+ */
2988
+ registerMTLSCert(fingerprint, actorId) {
2989
+ this.trustedCerts.set(fingerprint, { actorId, issuedAt: Date.now() });
2990
+ this.logger.log(`Registered mTLS cert ${fingerprint} for actor ${actorId}`);
2991
+ }
2992
+ /**
2993
+ * Revoke an mTLS certificate
2994
+ */
2995
+ revokeMTLSCert(fingerprint) {
2996
+ return this.trustedCerts.delete(fingerprint);
2997
+ }
2998
+ /**
2999
+ * Calculate certificate fingerprint (SHA-256)
3000
+ */
3001
+ static calculateFingerprint(certPem) {
3002
+ const der = Buffer.from(
3003
+ certPem.replace(/-----BEGIN CERTIFICATE-----/, "").replace(/-----END CERTIFICATE-----/, "").replace(/\s/g, ""),
3004
+ "base64"
3005
+ );
3006
+ return crypto3.createHash("sha256").update(der).digest("hex");
3007
+ }
3008
+ };
3009
+ ProofVerificationService = __decorateClass([
3010
+ Injectable4()
3011
+ ], ProofVerificationService);
3012
+
3013
+ // src/decorators/index.ts
3014
+ var decorators_exports = {};
3015
+ __export(decorators_exports, {
3016
+ AxisContext: () => AxisContext,
3017
+ AxisDemoPubkey: () => AxisDemoPubkey,
3018
+ AxisFrame: () => AxisFrame3,
3019
+ AxisIp: () => AxisIp,
3020
+ AxisRaw: () => AxisRaw,
3021
+ HANDLER_METADATA_KEY: () => HANDLER_METADATA_KEY,
3022
+ Handler: () => Handler,
3023
+ INTENT_BODY_KEY: () => INTENT_BODY_KEY,
3024
+ INTENT_METADATA_KEY: () => INTENT_METADATA_KEY,
3025
+ INTENT_ROUTES_KEY: () => INTENT_ROUTES_KEY,
3026
+ INTENT_SENSORS_KEY: () => INTENT_SENSORS_KEY,
3027
+ Intent: () => Intent,
3028
+ IntentBody: () => IntentBody,
3029
+ IntentSensors: () => IntentSensors,
3030
+ SENSOR_METADATA_KEY: () => SENSOR_METADATA_KEY,
3031
+ Sensor: () => Sensor,
3032
+ TLV_FIELDS_KEY: () => TLV_FIELDS_KEY,
3033
+ TLV_VALIDATORS_KEY: () => TLV_VALIDATORS_KEY,
3034
+ TlvEnum: () => TlvEnum,
3035
+ TlvField: () => TlvField,
3036
+ TlvMinLen: () => TlvMinLen,
3037
+ TlvRange: () => TlvRange,
3038
+ TlvUtf8Pattern: () => TlvUtf8Pattern,
3039
+ TlvValidate: () => TlvValidate,
3040
+ buildDtoDecoder: () => buildDtoDecoder,
3041
+ extractDtoSchema: () => extractDtoSchema
3042
+ });
3043
+
3044
+ // src/decorators/axis-request.decorator.ts
3045
+ import { createParamDecorator } from "@nestjs/common";
3046
+ function resolveIp(req) {
3047
+ return req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.headers["x-real-ip"] || req.socket.remoteAddress || void 0;
3048
+ }
3049
+ var AxisRaw = createParamDecorator(
3050
+ (_data, ctx) => {
3051
+ const req = ctx.switchToHttp().getRequest();
3052
+ return req.body;
3053
+ }
3054
+ );
3055
+ var AxisIp = createParamDecorator(
3056
+ (_data, ctx) => {
3057
+ const req = ctx.switchToHttp().getRequest();
3058
+ return resolveIp(req);
3059
+ }
3060
+ );
3061
+ var AxisContext = createParamDecorator(
3062
+ (_data, ctx) => {
3063
+ const req = ctx.switchToHttp().getRequest();
3064
+ const axisData = req.axis || {};
3065
+ return {
3066
+ raw: req.body,
3067
+ ip: resolveIp(req),
3068
+ preDecodeInput: axisData.preDecodeInput,
3069
+ frameBytesCount: axisData.frameBytesCount || 0
3070
+ };
3071
+ }
3072
+ );
3073
+ var AxisDemoPubkey = createParamDecorator(
3074
+ (_data, ctx) => {
3075
+ if (process.env.NODE_ENV !== "development") return void 0;
3076
+ const req = ctx.switchToHttp().getRequest();
3077
+ return req.headers["x-demo-pubkey"];
3078
+ }
3079
+ );
3080
+ var AxisFrame3 = createParamDecorator(
3081
+ (_data, ctx) => {
3082
+ const req = ctx.switchToHttp().getRequest();
3083
+ const decoded = req.axisDecoded;
3084
+ if (!decoded) {
3085
+ throw new Error(
3086
+ "@AxisFrame() requires AxisDecodeInterceptor on the route. Add @UseInterceptors(AxisDecodeInterceptor) to use this decorator."
3087
+ );
3088
+ }
3089
+ return decoded;
3090
+ }
3091
+ );
3092
+
3093
+ // src/decorators/sensor.decorator.ts
3094
+ import { SetMetadata as SetMetadata2 } from "@nestjs/common";
3095
+ var SENSOR_METADATA_KEY = "axis:sensor";
3096
+ function Sensor(options) {
3097
+ return SetMetadata2(SENSOR_METADATA_KEY, options ?? true);
3098
+ }
3099
+
3100
+ // src/engine/index.ts
3101
+ var engine_exports = {};
3102
+ __export(engine_exports, {
3103
+ BAND: () => BAND,
3104
+ HandlerDiscoveryService: () => HandlerDiscoveryService,
3105
+ IntentRouter: () => IntentRouter,
3106
+ PRE_DECODE_BOUNDARY: () => PRE_DECODE_BOUNDARY,
3107
+ SensorDiscoveryService: () => SensorDiscoveryService,
3108
+ SensorRegistry: () => SensorRegistry,
3109
+ createObservation: () => createObservation,
3110
+ endStage: () => endStage,
3111
+ finalizeObservation: () => finalizeObservation,
3112
+ recordSensor: () => recordSensor,
3113
+ startStage: () => startStage
3114
+ });
3115
+
3116
+ // src/engine/axis-observation.ts
3117
+ import { randomBytes as randomBytes3 } from "crypto";
3118
+ function createObservation(transport, ip) {
3119
+ return {
3120
+ id: randomBytes3(16).toString("hex"),
3121
+ startMs: Date.now(),
3122
+ transport,
3123
+ ip,
3124
+ stages: [],
3125
+ sensors: [],
3126
+ facts: {}
3127
+ };
3128
+ }
3129
+ function startStage(obs, name) {
3130
+ const stage = { name, status: "ok", startMs: Date.now() };
3131
+ obs.stages.push(stage);
3132
+ return stage;
3133
+ }
3134
+ function endStage(stage, status = "ok", reason, code) {
3135
+ stage.endMs = Date.now();
3136
+ stage.durationMs = stage.endMs - stage.startMs;
3137
+ stage.status = status;
3138
+ if (reason) stage.reason = reason;
3139
+ if (code) stage.code = code;
3140
+ }
3141
+ function recordSensor(obs, name, allowed, riskScore, durationMs, reasons, code) {
3142
+ obs.sensors.push({ name, allowed, riskScore, durationMs, reasons, code });
3143
+ }
3144
+ function finalizeObservation(obs, decision, statusCode, resultCode) {
3145
+ obs.endMs = Date.now();
3146
+ obs.durationMs = obs.endMs - obs.startMs;
3147
+ obs.decision = decision;
3148
+ obs.statusCode = statusCode;
3149
+ if (resultCode) obs.resultCode = resultCode;
3150
+ }
3151
+
3152
+ // src/engine/handler-discovery.service.ts
3153
+ import { Injectable as Injectable5, Logger as Logger4 } from "@nestjs/common";
3154
+ var HandlerDiscoveryService = class {
3155
+ constructor(discovery, scanner, router) {
3156
+ this.discovery = discovery;
3157
+ this.scanner = scanner;
3158
+ this.router = router;
3159
+ this.logger = new Logger4(HandlerDiscoveryService.name);
3160
+ }
3161
+ onModuleInit() {
3162
+ const providers = this.discovery.getProviders();
3163
+ let totalIntents = 0;
3164
+ for (const wrapper of providers) {
3165
+ const { instance, metatype } = wrapper;
3166
+ if (!instance || !metatype) continue;
3167
+ const handlerMeta = Reflect.getMetadata(HANDLER_METADATA_KEY, metatype);
3168
+ if (!handlerMeta) continue;
3169
+ const handlerName = handlerMeta.intent || metatype.name;
3170
+ const proto = Object.getPrototypeOf(instance);
3171
+ const methods = this.scanner.getAllMethodNames(proto);
3172
+ let registered = 0;
3173
+ for (const methodName of methods) {
3174
+ const meta = Reflect.getMetadata(
3175
+ INTENT_METADATA_KEY,
3176
+ proto,
3177
+ methodName
3178
+ );
3179
+ if (!meta?.intent) continue;
3180
+ if (!this.router.has(meta.intent)) {
3181
+ this.router.register(
3182
+ meta.intent,
3183
+ instance[methodName].bind(instance)
3184
+ );
3185
+ registered++;
3186
+ totalIntents++;
3187
+ }
3188
+ this.router.registerIntentMeta(meta.intent, proto, methodName);
3189
+ }
3190
+ if (registered > 0) {
3191
+ this.logger.log(
3192
+ `Auto-registered ${registered} intents from ${handlerName}`
3193
+ );
3194
+ }
3195
+ }
3196
+ this.logger.log(
3197
+ `Handler discovery complete: ${totalIntents} intents auto-registered`
3198
+ );
3199
+ }
3200
+ };
3201
+ HandlerDiscoveryService = __decorateClass([
3202
+ Injectable5()
3203
+ ], HandlerDiscoveryService);
3204
+
3205
+ // src/engine/sensor-bands.ts
3206
+ var BAND = {
3207
+ /** Pre-decode: raw byte validation, geo, budget, magic */
3208
+ WIRE: 0,
3209
+ /** Post-decode: identity resolution, capsule, proof */
3210
+ IDENTITY: 40,
3211
+ /** Post-decode: authorization, signature, rate limiting */
3212
+ POLICY: 90,
3213
+ /** Post-decode: content validation, TLV, schema, files */
3214
+ CONTENT: 140,
3215
+ /** Post-decode: business logic sensors, streams, WS */
3216
+ BUSINESS: 200,
3217
+ /** Post-decode: audit, logging (always last) */
3218
+ AUDIT: 900
3219
+ };
3220
+ var PRE_DECODE_BOUNDARY = 40;
3221
+
3222
+ // src/engine/sensor-discovery.service.ts
3223
+ import { Injectable as Injectable6, Logger as Logger5 } from "@nestjs/common";
3224
+ var SensorDiscoveryService = class {
3225
+ constructor(discovery, reflector, registry) {
3226
+ this.discovery = discovery;
3227
+ this.reflector = reflector;
3228
+ this.registry = registry;
3229
+ this.logger = new Logger5(SensorDiscoveryService.name);
3230
+ }
3231
+ onApplicationBootstrap() {
3232
+ const providers = this.discovery.getProviders();
3233
+ let count = 0;
3234
+ for (const wrapper of providers) {
3235
+ const { instance } = wrapper;
3236
+ if (!instance || !instance.constructor) continue;
3237
+ const meta = this.reflector.get(
3238
+ SENSOR_METADATA_KEY,
3239
+ instance.constructor
3240
+ );
3241
+ if (!meta) continue;
3242
+ const sensor = instance;
3243
+ if (!sensor.name || sensor.order === void 0) {
3244
+ this.logger.warn(
3245
+ `@Sensor() on ${instance.constructor.name} missing name or order \u2014 skipped`
3246
+ );
3247
+ continue;
3248
+ }
3249
+ if (!sensor.phase) {
3250
+ const decoratorPhase = meta !== true ? meta.phase : void 0;
3251
+ sensor.phase = decoratorPhase ?? (sensor.order < PRE_DECODE_BOUNDARY ? "PRE_DECODE" : "POST_DECODE");
3252
+ }
3253
+ this.registry.register(sensor);
3254
+ count++;
3255
+ }
3256
+ this.logger.log(`Auto-registered ${count} sensors via @Sensor()`);
3257
+ }
3258
+ };
3259
+ SensorDiscoveryService = __decorateClass([
3260
+ Injectable6()
3261
+ ], SensorDiscoveryService);
3262
+
3263
+ // src/engine/registry/sensor.registry.ts
3264
+ import { Injectable as Injectable7, Logger as Logger6 } from "@nestjs/common";
3265
+ var SensorRegistry = class {
3266
+ constructor(configService) {
3267
+ this.configService = configService;
3268
+ this.sensors = [];
3269
+ this.logger = new Logger6(SensorRegistry.name);
3270
+ }
3271
+ /**
3272
+ * Registers a new sensor in the registry.
3273
+ *
3274
+ * Validates that:
3275
+ * - AxisSensor has a unique name
3276
+ * - AxisSensor has an order field
3277
+ * - Pre-decode sensors have order < 40
3278
+ * - Post-decode sensors have order >= 40
3279
+ *
3280
+ * @param {AxisSensor} sensor - The sensor instance to register
3281
+ * @throws Error if validation fails
3282
+ */
3283
+ register(sensor) {
3284
+ if (!sensor.name) {
3285
+ throw new Error("AxisSensor must have a name");
3286
+ }
3287
+ const enabledSensorsStr = this.configService.get("ENABLED_SENSORS");
3288
+ const disabledSensorsStr = this.configService.get("DISABLED_SENSORS");
3289
+ const enabledSensors = enabledSensorsStr ? enabledSensorsStr.split(",").map((s) => s.trim()) : null;
3290
+ const disabledSensors = disabledSensorsStr ? disabledSensorsStr.split(",").map((s) => s.trim()) : [];
3291
+ if (enabledSensors && !enabledSensors.includes(sensor.name)) {
3292
+ this.logger.log(`Skipping disabled sensor (not in ENABLED_SENSORS): ${sensor.name}`);
3293
+ return;
3294
+ }
3295
+ if (disabledSensors.includes(sensor.name)) {
3296
+ this.logger.log(`Skipping disabled sensor (in DISABLED_SENSORS): ${sensor.name}`);
3297
+ return;
3298
+ }
3299
+ if (sensor.order === void 0) {
3300
+ throw new Error(`AxisSensor "${sensor.name}" must have an order field`);
3301
+ }
3302
+ const isPreDecodeSensor = this.isPreDecodeSensor(sensor);
3303
+ const isPostDecodeSensor = this.isPostDecodeSensor(sensor);
3304
+ if (isPreDecodeSensor && sensor.order >= 40) {
3305
+ this.logger.warn(
3306
+ `AxisSensor "${sensor.name}" is marked as PRE_DECODE but has order ${sensor.order} (should be < 40)`
3307
+ );
3308
+ }
3309
+ if (isPostDecodeSensor && sensor.order < 40) {
3310
+ this.logger.warn(
3311
+ `AxisSensor "${sensor.name}" is marked as POST_DECODE but has order ${sensor.order} (should be >= 40)`
3312
+ );
3313
+ }
3314
+ this.sensors.push(sensor);
3315
+ const phaseLabel = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase || "UNKNOWN";
3316
+ this.logger.debug(
3317
+ `Registered sensor: ${sensor.name} (order: ${sensor.order}, phase: ${phaseLabel})`
3318
+ );
3319
+ }
3320
+ /**
3321
+ * Returns all registered sensors, sorted by their execution order.
3322
+ *
3323
+ * @returns {AxisSensor[]} A sorted array of sensors
3324
+ */
3325
+ list() {
3326
+ return [...this.sensors].sort(
3327
+ (a, b) => (a.order ?? 999) - (b.order ?? 999)
3328
+ );
3329
+ }
3330
+ /**
3331
+ * Returns only pre-decode sensors (order < 40).
3332
+ * These sensors run in middleware on raw bytes before frame decoding.
3333
+ *
3334
+ * @returns {AxisPreSensor[]} Pre-decode sensors sorted by order
3335
+ */
3336
+ getPreDecodeSensors() {
3337
+ return this.list().filter((s) => (s.order ?? 999) < 40);
3338
+ }
3339
+ /**
3340
+ * Returns only post-decode sensors (order >= 40).
3341
+ * These sensors run in the controller on fully decoded frames.
3342
+ *
3343
+ * @returns {AxisPostSensor[]} Post-decode sensors sorted by order
3344
+ */
3345
+ getPostDecodeSensors() {
3346
+ return this.list().filter(
3347
+ (s) => (s.order ?? 999) >= 40
3348
+ );
3349
+ }
3350
+ /**
3351
+ * Helper: Check if a sensor is a pre-decode sensor.
3352
+ *
3353
+ * @private
3354
+ * @param {AxisSensor} sensor - The sensor to check
3355
+ * @returns {boolean} True if sensor is pre-decode
3356
+ */
3357
+ isPreDecodeSensor(sensor) {
3358
+ const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
3359
+ return phase === "PRE_DECODE" || (sensor.order ?? 999) < 40;
3360
+ }
3361
+ /**
3362
+ * Helper: Check if a sensor is a post-decode sensor.
3363
+ *
3364
+ * @private
3365
+ * @param {AxisSensor} sensor - The sensor to check
3366
+ * @returns {boolean} True if sensor is post-decode
3367
+ */
3368
+ isPostDecodeSensor(sensor) {
3369
+ const phase = typeof sensor.phase === "string" ? sensor.phase : sensor.phase?.phase;
3370
+ return phase === "POST_DECODE" || (sensor.order ?? 999) >= 40;
3371
+ }
3372
+ /**
3373
+ * Returns sensor count by phase.
3374
+ * Useful for diagnostics and monitoring.
3375
+ *
3376
+ * @returns {{preDecodeCount: number, postDecodeCount: number}}
3377
+ */
3378
+ getSensorCountByPhase() {
3379
+ return {
3380
+ preDecodeCount: this.getPreDecodeSensors().length,
3381
+ postDecodeCount: this.getPostDecodeSensors().length
3382
+ };
3383
+ }
3384
+ /**
3385
+ * Clears all registered sensors.
3386
+ * Useful for testing.
3387
+ *
3388
+ * @internal
3389
+ */
3390
+ clear() {
3391
+ this.sensors = [];
3392
+ }
3393
+ };
3394
+ SensorRegistry = __decorateClass([
3395
+ Injectable7()
3396
+ ], SensorRegistry);
3397
+
3398
+ // src/loom/index.ts
3399
+ var loom_exports = {};
3400
+ __export(loom_exports, {
3401
+ PROOF_LOOM: () => PROOF_LOOM,
3402
+ TLV_PRESENCE_ID: () => TLV_LOOM_PRESENCE_ID,
3403
+ TLV_THREAD_HASH: () => TLV_LOOM_THREAD_HASH,
3404
+ TLV_WRIT: () => TLV_LOOM_WRIT,
3405
+ canonicalizeGrant: () => canonicalizeGrant,
3406
+ canonicalizeWrit: () => canonicalizeWrit,
3407
+ deriveAnchorReflection: () => deriveAnchorReflection
3408
+ });
3409
+
3410
+ // src/loom/loom.types.ts
3411
+ function deriveAnchorReflection(softid, context = "openlogs", scope = "loom") {
3412
+ return `ar:${context}:${scope}:${softid}`;
3413
+ }
3414
+ function canonicalizeWrit(writ) {
3415
+ const ordered = {
3416
+ head: { tid: writ.head.tid, seq: writ.head.seq },
3417
+ body: {
3418
+ who: writ.body.who,
3419
+ act: writ.body.act,
3420
+ res: writ.body.res,
3421
+ law: writ.body.law
3422
+ },
3423
+ meta: { iat: writ.meta.iat, exp: writ.meta.exp, prev: writ.meta.prev }
3424
+ };
3425
+ return JSON.stringify(ordered);
3426
+ }
3427
+ function canonicalizeGrant(grant) {
3428
+ const ordered = {
3429
+ grant_id: grant.grant_id,
3430
+ issuer: grant.issuer,
3431
+ subject: grant.subject,
3432
+ grant_type: grant.grant_type,
3433
+ caps: grant.caps,
3434
+ meta: grant.meta
3435
+ };
3436
+ return JSON.stringify(ordered);
3437
+ }
3438
+
3439
+ // src/schemas/index.ts
3440
+ var schemas_exports = {};
3441
+ __export(schemas_exports, {
3442
+ AccessProfileZ: () => AccessProfileZ,
3443
+ AxisContextZ: () => AxisContextZ,
3444
+ AxisErrorZ: () => AxisErrorZ,
3445
+ BodyBudgetInputZ: () => BodyBudgetInputZ,
3446
+ BodyBudgetPolicyZ: () => BodyBudgetPolicyZ,
3447
+ BodyProfile: () => BodyProfile2,
3448
+ BodyProfileValidator: () => BodyProfileValidator,
3449
+ BodyProfileZ: () => BodyProfileZ,
3450
+ CapsuleClaimsZ: () => CapsuleClaimsZ,
3451
+ CapsuleValidationResultZ: () => CapsuleValidationResultZ,
3452
+ CapsuleVerifyResultZ: () => CapsuleVerifyResultZ,
3453
+ CapsuleVerifySensorInputZ: () => CapsuleVerifySensorInputZ,
3454
+ CapsuleZ: () => CapsuleZ,
3455
+ ChunkHashInputZ: () => ChunkHashInputZ,
3456
+ CountryBlockDecisionZ: () => CountryBlockDecisionZ,
3457
+ CountryBlockSensorInputZ: () => CountryBlockSensorInputZ,
3458
+ EntropySensorInputZ: () => EntropySensorInputZ,
3459
+ ExecutionMetricsZ: () => ExecutionMetricsZ,
3460
+ IPReputationInputZ: () => IPReputationInputZ,
3461
+ IPReputationZ: () => IPReputationZ,
3462
+ IntentPolicyDecisionZ: () => IntentPolicyDecisionZ,
3463
+ IntentPolicySensorInputZ: () => IntentPolicySensorInputZ,
3464
+ IntentPolicyZ: () => IntentPolicyZ,
3465
+ IntentSchemaZ: () => IntentSchemaZ,
3466
+ PassportZ: () => PassportZ,
3467
+ ProofKindZ: () => ProofKindZ,
3468
+ ProofPresenceInputZ: () => ProofPresenceInputZ,
3469
+ ProofType: () => ProofType2,
3470
+ ProtocolStrictInputZ: () => ProtocolStrictInputZ,
3471
+ RateLimitConfigZ: () => RateLimitConfigZ,
3472
+ RateLimitInputZ: () => RateLimitInputZ,
3473
+ RateLimitProfileZ: () => RateLimitProfileZ,
3474
+ ScanBurstDecisionZ: () => ScanBurstDecisionZ,
3475
+ ScanBurstSensorInputZ: () => ScanBurstSensorInputZ,
3476
+ SchemaFieldKindZ: () => SchemaFieldKindZ,
3477
+ SchemaFieldZ: () => SchemaFieldZ,
3478
+ ScopeZ: () => ScopeZ,
3479
+ SensitivityLevelZ: () => SensitivityLevelZ,
3480
+ SensorChainInputZ: () => SensorChainInputZ,
3481
+ SensorDecisionWithMetadataZ: () => SensorDecisionWithMetadataZ,
3482
+ SensorDecisionZ: () => SensorDecisionZ,
3483
+ SensorResultZ: () => SensorResultZ,
3484
+ UploadSessionZ: () => UploadSessionZ,
3485
+ UploadStatusZ: () => UploadStatusZ,
3486
+ WsHandshakeDecisionZ: () => WsHandshakeDecisionZ,
3487
+ WsHandshakeInputZ: () => WsHandshakeInputZ
3488
+ });
3489
+
3490
+ // src/schemas/axis-schemas.ts
3491
+ import * as z2 from "zod";
3492
+ var SensorDecisionZ = z2.union([
3493
+ z2.object({ action: z2.literal("ALLOW"), meta: z2.any().optional() }),
3494
+ z2.object({
3495
+ action: z2.literal("DENY"),
3496
+ code: z2.string(),
3497
+ reason: z2.string().optional(),
3498
+ meta: z2.any().optional()
3499
+ })
3500
+ ]);
3501
+ var SensorDecisionWithMetadataZ = z2.union([
3502
+ z2.object({ action: z2.literal("ALLOW"), meta: z2.any().optional() }),
3503
+ z2.object({
3504
+ action: z2.literal("DENY"),
3505
+ code: z2.string(),
3506
+ reason: z2.string().optional(),
3507
+ retryAfterMs: z2.number().int().positive().optional(),
3508
+ meta: z2.any().optional()
3509
+ })
3510
+ ]);
3511
+ var CountryBlockSensorInputZ = z2.object({
3512
+ ip: z2.string().min(1),
3513
+ country: z2.string().length(2).toUpperCase().optional()
3514
+ });
3515
+ var CountryBlockDecisionZ = SensorDecisionZ;
3516
+ var ScanBurstSensorInputZ = z2.object({
3517
+ ip: z2.string().min(1),
3518
+ isFailure: z2.boolean().optional()
3519
+ });
3520
+ var ScanBurstDecisionZ = SensorDecisionWithMetadataZ;
3521
+ var ProofKindZ = z2.enum([
3522
+ "NONE",
3523
+ "CAPSULE",
3524
+ "PASSPORT",
3525
+ "MTLS",
3526
+ "JWT"
3527
+ ]);
3528
+ var AccessProfileZ = z2.enum(["PUBLIC", "PARTNER", "INTERNAL", "NODE"]);
3529
+ var ProofPresenceInputZ = z2.object({
3530
+ profile: AccessProfileZ,
3531
+ visibility: z2.enum(["PUBLIC", "GUARDED"]),
3532
+ requiredProof: z2.array(ProofKindZ).min(1),
3533
+ hasCapsule: z2.boolean(),
3534
+ hasPassportSignature: z2.boolean(),
3535
+ intent: z2.string().min(1)
3536
+ });
3537
+ var SensitivityLevelZ = z2.enum(["LOW", "MEDIUM", "HIGH", "CRITICAL"]);
3538
+ var IntentPolicyZ = z2.object({
3539
+ intent: z2.string().min(1),
3540
+ sensitivity: SensitivityLevelZ,
3541
+ maxFrameBytes: z2.number().int().positive(),
3542
+ maxHeaderBytes: z2.number().int().positive(),
3543
+ maxBodyBytes: z2.number().int().positive(),
3544
+ maxSigBytes: z2.number().int().positive().optional(),
3545
+ rateLimitPerMinute: z2.number().int().positive().optional(),
3546
+ rateLimitPerHour: z2.number().int().positive().optional(),
3547
+ requiresSignature: z2.boolean(),
3548
+ requiresCapsule: z2.boolean(),
3549
+ timeoutMs: z2.number().int().positive()
3550
+ });
3551
+ var IntentPolicySensorInputZ = z2.object({
3552
+ frame: AxisFrameZ,
3553
+ intent: z2.string().min(1),
3554
+ rawFrameSize: z2.number().int().positive()
3555
+ });
3556
+ var IntentPolicyDecisionZ = z2.union([
3557
+ z2.object({
3558
+ action: z2.literal("ALLOW"),
3559
+ policy: IntentPolicyZ
3560
+ }),
3561
+ z2.object({
3562
+ action: z2.literal("DENY"),
3563
+ reason: z2.string()
3564
+ })
3565
+ ]);
3566
+ var CapsuleClaimsZ = z2.object({
3567
+ capsuleId: z2.string().min(8),
3568
+ allowIntents: z2.array(z2.string()).min(1),
3569
+ limits: z2.object({
3570
+ maxBodyBytes: z2.number().int().positive().optional()
3571
+ }).optional(),
3572
+ scopes: z2.record(z2.string(), z2.any()).optional()
3573
+ });
3574
+ var CapsuleZ = z2.object({
3575
+ id: z2.string(),
3576
+ claims: CapsuleClaimsZ,
3577
+ issuedAt: z2.number().int(),
3578
+ expiresAt: z2.number().int(),
3579
+ tier: z2.enum(["FREE", "STANDARD", "PREMIUM"])
3580
+ });
3581
+ var CapsuleValidationResultZ = z2.object({
3582
+ valid: z2.boolean(),
3583
+ capsule: CapsuleZ.optional(),
3584
+ reason: z2.string().optional(),
3585
+ requiresStepUp: z2.boolean().optional()
3586
+ });
3587
+ var CapsuleVerifySensorInputZ = z2.object({
3588
+ headers: z2.map(
3589
+ z2.number(),
3590
+ z2.custom((v) => v instanceof Uint8Array)
3591
+ ),
3592
+ intent: z2.string().min(1),
3593
+ ctx: z2.any()
3594
+ // AxisContext - avoid circular dependency
3595
+ });
3596
+ var CapsuleVerifyResultZ = z2.object({
3597
+ ok: z2.literal(true),
3598
+ capsule: CapsuleZ
3599
+ });
3600
+ var RateLimitProfileZ = z2.enum([
3601
+ "PUBLIC",
3602
+ "PARTNER",
3603
+ "INTERNAL",
3604
+ "NODE"
3605
+ ]);
3606
+ var RateLimitInputZ = z2.object({
3607
+ ip: z2.string().min(1),
3608
+ userAgent: z2.string().optional(),
3609
+ actorId: z2.string().optional(),
3610
+ capsuleId: z2.string().optional(),
3611
+ intent: z2.string().min(1),
3612
+ profile: RateLimitProfileZ
3613
+ });
3614
+ var RateLimitConfigZ = z2.object({
3615
+ windowSec: z2.number().int().positive(),
3616
+ max: z2.number().int().positive(),
3617
+ key: z2.enum(["ip_fingerprint", "actor_capsule"])
3618
+ });
3619
+ var SensorResultZ = z2.object({
3620
+ ok: z2.literal(true)
3621
+ });
3622
+ var PassportZ = z2.object({
3623
+ id: z2.string(),
3624
+ public_key: z2.custom((v) => Buffer.isBuffer(v)),
3625
+ status: z2.enum(["ACTIVE", "REVOKED", "EXPIRED", "PENDING"]),
3626
+ issuedAt: z2.number().int(),
3627
+ expiresAt: z2.number().int().optional()
3628
+ });
3629
+ var ExecutionMetricsZ = z2.object({
3630
+ dbWrites: z2.number().int(),
3631
+ dbReads: z2.number().int(),
3632
+ externalCalls: z2.number().int(),
3633
+ elapsedMs: z2.number().int().optional()
3634
+ });
3635
+ var SensorChainInputZ = z2.object({
3636
+ ip: z2.string().min(1),
3637
+ path: z2.string().min(1),
3638
+ contentLength: z2.number().int().nonnegative(),
3639
+ peek: z2.instanceof(Uint8Array),
3640
+ country: z2.string().optional()
3641
+ });
3642
+ var EntropySensorInputZ = z2.object({
3643
+ pid: z2.custom((v) => Buffer.isBuffer(v)).optional(),
3644
+ nonce: z2.custom((v) => Buffer.isBuffer(v)).optional(),
3645
+ ip: z2.string().min(1)
3646
+ });
3647
+ var ProtocolStrictInputZ = z2.object({
3648
+ rawBytes: z2.union([z2.custom((v) => Buffer.isBuffer(v)), z2.instanceof(Uint8Array)]).optional(),
3649
+ ip: z2.string().min(1),
3650
+ path: z2.string().min(1),
3651
+ contentLength: z2.number().int().nonnegative(),
3652
+ peek: z2.instanceof(Uint8Array),
3653
+ country: z2.string().optional(),
3654
+ contentType: z2.string().optional()
3655
+ });
3656
+ var SchemaFieldKindZ = z2.enum([
3657
+ "utf8",
3658
+ "u64",
3659
+ "bytes",
3660
+ "bytes16",
3661
+ "bool",
3662
+ "obj",
3663
+ "arr"
3664
+ ]);
3665
+ var ScopeZ = z2.enum(["header", "body"]);
3666
+ var SchemaFieldZ = z2.object({
3667
+ name: z2.string().min(1),
3668
+ tlv: z2.number().int().positive(),
3669
+ kind: SchemaFieldKindZ,
3670
+ required: z2.boolean().optional(),
3671
+ maxLen: z2.number().int().positive().optional(),
3672
+ max: z2.string().optional(),
3673
+ scope: ScopeZ.optional()
3674
+ });
3675
+ var BodyProfileZ = z2.enum(["TLV_MAP", "RAW", "TLV_OBJ", "TLV_ARR"]);
3676
+ var IntentSchemaZ = z2.object({
3677
+ intent: z2.string().min(1),
3678
+ version: z2.number().int().positive(),
3679
+ bodyProfile: BodyProfileZ,
3680
+ fields: z2.array(SchemaFieldZ).min(1)
3681
+ });
3682
+ var WsHandshakeInputZ = z2.object({
3683
+ clientId: z2.string().min(1),
3684
+ isWs: z2.boolean(),
3685
+ ip: z2.string().min(1)
3686
+ });
3687
+ var WsHandshakeDecisionZ = z2.union([
3688
+ z2.object({ action: z2.literal("ALLOW") }),
3689
+ z2.object({ action: z2.literal("DENY"), code: z2.string() })
3690
+ ]);
3691
+ var IPReputationInputZ = z2.object({
3692
+ ip: z2.string().min(1)
3693
+ });
3694
+ var IPReputationZ = z2.object({
3695
+ score: z2.number().min(-100).max(100),
3696
+ lastUpdated: z2.number().int(),
3697
+ totalRequests: z2.number().int().nonnegative(),
3698
+ failedRequests: z2.number().int().nonnegative(),
3699
+ blockedRequests: z2.number().int().nonnegative(),
3700
+ tags: z2.array(z2.string())
3701
+ });
3702
+ var UploadStatusZ = z2.enum([
3703
+ "INIT",
3704
+ "UPLOADING",
3705
+ "FINALIZING",
3706
+ "DONE",
3707
+ "ABORTED"
3708
+ ]);
3709
+ var UploadSessionZ = z2.object({
3710
+ uploadIdHex: z2.string().min(1),
3711
+ fileName: z2.string().min(1),
3712
+ totalSize: z2.number().int().positive(),
3713
+ chunkSize: z2.number().int().positive(),
3714
+ totalChunks: z2.number().int().positive(),
3715
+ receivedCount: z2.number().int().nonnegative(),
3716
+ status: UploadStatusZ
3717
+ });
3718
+ var BodyBudgetInputZ = z2.object({
3719
+ intent: z2.string().min(1),
3720
+ headerLen: z2.number().int().nonnegative(),
3721
+ bodyLen: z2.number().int().nonnegative()
3722
+ });
3723
+ var BodyBudgetPolicyZ = z2.object({
3724
+ maxHeaderBytes: z2.number().int().positive(),
3725
+ maxBodyBytes: z2.number().int().positive()
3726
+ });
3727
+ var ChunkHashInputZ = z2.object({
3728
+ headerTLVs: z2.any(),
3729
+ // Map<number, Uint8Array> - flexible validation for compatibility
3730
+ bodyBytes: z2.any(),
3731
+ // Uint8Array - flexible validation for compatibility
3732
+ intent: z2.string().min(1)
3733
+ });
3734
+ var ProofType2 = /* @__PURE__ */ ((ProofType3) => {
3735
+ ProofType3[ProofType3["CAPSULE"] = 1] = "CAPSULE";
3736
+ ProofType3[ProofType3["JWT"] = 2] = "JWT";
3737
+ ProofType3[ProofType3["MTLS_ID"] = 3] = "MTLS_ID";
3738
+ ProofType3[ProofType3["DEVICE_SE"] = 4] = "DEVICE_SE";
3739
+ ProofType3[ProofType3["WITNESS_SIG"] = 5] = "WITNESS_SIG";
3740
+ return ProofType3;
3741
+ })(ProofType2 || {});
3742
+ var AxisContextZ = z2.object({
3743
+ pid: z2.custom((v) => Buffer.isBuffer(v)),
3744
+ // Process ID
3745
+ ts: z2.bigint(),
3746
+ // Timestamp
3747
+ intent: z2.string().min(1),
3748
+ actorId: z2.custom((v) => Buffer.isBuffer(v)),
3749
+ proofType: z2.enum(ProofType2),
3750
+ proofRef: z2.custom((v) => Buffer.isBuffer(v)),
3751
+ nonce: z2.custom((v) => Buffer.isBuffer(v)),
3752
+ ip: z2.string().min(1),
3753
+ nodeCertHash: z2.string().optional(),
3754
+ capsule: CapsuleZ.optional(),
3755
+ passport: PassportZ.optional(),
3756
+ meter: z2.any().optional()
3757
+ // ExecutionMeter instance - any to avoid circular dependency and allow class instance
3758
+ });
3759
+ var AxisErrorZ = z2.object({
3760
+ code: z2.string(),
3761
+ message: z2.string(),
3762
+ httpStatus: z2.number().int()
3763
+ });
3764
+
3765
+ // src/schemas/body-profile.validator.ts
3766
+ import { Injectable as Injectable8, Logger as Logger7 } from "@nestjs/common";
3767
+ var BodyProfile2 = /* @__PURE__ */ ((BodyProfile3) => {
3768
+ BodyProfile3[BodyProfile3["RAW"] = 0] = "RAW";
3769
+ BodyProfile3[BodyProfile3["TLV_MAP"] = 1] = "TLV_MAP";
3770
+ BodyProfile3[BodyProfile3["OBJ"] = 2] = "OBJ";
3771
+ BodyProfile3[BodyProfile3["ARR"] = 3] = "ARR";
3772
+ return BodyProfile3;
3773
+ })(BodyProfile2 || {});
3774
+ var BodyProfileValidator = class {
3775
+ constructor() {
3776
+ this.logger = new Logger7(BodyProfileValidator.name);
3777
+ }
3778
+ /**
3779
+ * Validate body matches declared profile
3780
+ */
3781
+ validate(body, profile) {
3782
+ switch (profile) {
3783
+ case 0 /* RAW */:
3784
+ return this.validateRaw(body);
3785
+ case 1 /* TLV_MAP */:
3786
+ return this.validateTlvMap(body);
3787
+ case 2 /* OBJ */:
3788
+ return this.validateObj(body);
3789
+ case 3 /* ARR */:
3790
+ return this.validateArr(body);
3791
+ default:
3792
+ return {
3793
+ valid: false,
3794
+ error: `Unknown body profile: ${profile}`,
3795
+ profile
3796
+ };
3797
+ }
3798
+ }
3799
+ /**
3800
+ * RAW profile - no validation, any bytes accepted
3801
+ */
3802
+ validateRaw(body) {
3803
+ return {
3804
+ valid: true,
3805
+ profile: 0 /* RAW */
3806
+ };
3807
+ }
3808
+ /**
3809
+ * TLV_MAP profile - flat TLV list (no nested structures)
3810
+ */
3811
+ validateTlvMap(body) {
3812
+ try {
3813
+ const tlvs = decodeTLVsList(body);
3814
+ for (const tlv2 of tlvs) {
3815
+ if (tlv2.type === 254 || tlv2.type === 255) {
3816
+ return {
3817
+ valid: false,
3818
+ error: "TLV_MAP profile cannot contain nested OBJ/ARR types",
3819
+ profile: 1 /* TLV_MAP */
3820
+ };
3821
+ }
3822
+ }
3823
+ return {
3824
+ valid: true,
3825
+ profile: 1 /* TLV_MAP */
3826
+ };
3827
+ } catch (error) {
3828
+ const message = error instanceof Error ? error.message : "Unknown error";
3829
+ return {
3830
+ valid: false,
3831
+ error: `TLV_MAP decode failed: ${message}`,
3832
+ profile: 1 /* TLV_MAP */
3833
+ };
3834
+ }
3835
+ }
3836
+ /**
3837
+ * OBJ profile - must be valid nested object
3838
+ */
3839
+ validateObj(body) {
3840
+ try {
3841
+ const tlvs = decodeTLVsList(body);
3842
+ const hasObj = tlvs.some((t) => t.type === 254);
3843
+ if (!hasObj && tlvs.length > 0) {
3844
+ return {
3845
+ valid: false,
3846
+ error: "OBJ profile must contain OBJ type (254)",
3847
+ profile: 2 /* OBJ */
3848
+ };
3849
+ }
3850
+ return {
3851
+ valid: true,
3852
+ profile: 2 /* OBJ */
3853
+ };
3854
+ } catch (error) {
3855
+ const message = error instanceof Error ? error.message : "Unknown error";
3856
+ return {
3857
+ valid: false,
3858
+ error: `OBJ decode failed: ${message}`,
3859
+ profile: 2 /* OBJ */
3860
+ };
3861
+ }
3862
+ }
3863
+ /**
3864
+ * ARR profile - must be valid array
3865
+ */
3866
+ validateArr(body) {
3867
+ try {
3868
+ const tlvs = decodeTLVsList(body);
3869
+ const hasArr = tlvs.some((t) => t.type === 255);
3870
+ if (!hasArr && tlvs.length > 0) {
3871
+ return {
3872
+ valid: false,
3873
+ error: "ARR profile must contain ARR type (255)",
3874
+ profile: 3 /* ARR */
3875
+ };
3876
+ }
3877
+ return {
3878
+ valid: true,
3879
+ profile: 3 /* ARR */
3880
+ };
3881
+ } catch (error) {
3882
+ const message = error instanceof Error ? error.message : "Unknown error";
3883
+ return {
3884
+ valid: false,
3885
+ error: `ARR decode failed: ${message}`,
3886
+ profile: 3 /* ARR */
3887
+ };
3888
+ }
3889
+ }
3890
+ };
3891
+ BodyProfileValidator = __decorateClass([
3892
+ Injectable8()
3893
+ ], BodyProfileValidator);
3894
+
3895
+ // src/security/index.ts
3896
+ var security_exports = {};
3897
+ __export(security_exports, {
3898
+ CAPABILITIES: () => CAPABILITIES,
3899
+ INTENT_REQUIREMENTS: () => INTENT_REQUIREMENTS,
3900
+ PROOF_CAPABILITIES: () => PROOF_CAPABILITIES,
3901
+ canAccessResource: () => canAccessResource,
3902
+ hasScope: () => hasScope,
3903
+ parseScope: () => parseScope
3904
+ });
3905
+
3906
+ // src/sensors/index.ts
3907
+ var sensors_exports = {};
3908
+ __export(sensors_exports, {
3909
+ AccessProfileResolverSensor: () => AccessProfileResolverSensor,
3910
+ BodyBudgetSensor: () => BodyBudgetSensor,
3911
+ CapabilityEnforcementSensor: () => CapabilityEnforcementSensor,
3912
+ ChunkHashSensor: () => ChunkHashSensor,
3913
+ EntropySensor: () => EntropySensor,
3914
+ ExecutionTimeoutSensor: () => ExecutionTimeoutSensor,
3915
+ FrameBudgetSensor: () => FrameBudgetSensor,
3916
+ FrameHeaderSanitySensor: () => FrameHeaderSanitySensor,
3917
+ HeaderTLVLimitSensor: () => HeaderTLVLimitSensor,
3918
+ IntentAllowlistSensor: () => IntentAllowlistSensor,
3919
+ IntentRegistrySensor: () => IntentRegistrySensor,
3920
+ ProofPresenceSensor: () => ProofPresenceSensor,
3921
+ ProtocolStrictSensor: () => ProtocolStrictSensor,
3922
+ ReceiptPolicySensor: () => ReceiptPolicySensor,
3923
+ SchemaValidationSensor: () => SchemaValidationSensor,
3924
+ StreamScopeSensor: () => StreamScopeSensor,
3925
+ TLVParseSensor: () => TLVParseSensor,
3926
+ VarintHardeningSensor: () => VarintHardeningSensor
3927
+ });
3928
+
3929
+ // src/sensors/access-profile-resolver.sensor.ts
3930
+ import { Injectable as Injectable9 } from "@nestjs/common";
3931
+ var AccessProfileResolverSensor = class {
3932
+ constructor() {
3933
+ /** AxisSensor identifier */
3934
+ this.name = "AccessProfileResolverSensor";
3935
+ /**
3936
+ * Execution order - runs early to establish the access profile
3937
+ * for downstream sensors.
3938
+ */
3939
+ this.order = BAND.IDENTITY + 10;
3940
+ }
3941
+ supports() {
3942
+ return true;
3943
+ }
3944
+ async run(input) {
3945
+ const hasCapsule = !!input.metadata?.capsuleId;
3946
+ const hasPassport = !!input.metadata?.passportSig;
3947
+ const hasMTLS = !!input.metadata?.mtlsId;
3948
+ const profile = hasCapsule || hasPassport || hasMTLS ? "GUARDED" : "PUBLIC";
3949
+ if (!input.metadata) input.metadata = {};
3950
+ input.metadata.profile = profile;
3951
+ return { action: "ALLOW" };
3952
+ }
3953
+ };
3954
+ AccessProfileResolverSensor = __decorateClass([
3955
+ Sensor(),
3956
+ Injectable9()
3957
+ ], AccessProfileResolverSensor);
3958
+
3959
+ // src/sensors/body-budget.sensor.ts
3960
+ import { Injectable as Injectable10 } from "@nestjs/common";
3961
+ var BodyBudgetSensor = class {
3962
+ constructor() {
3963
+ /** AxisSensor identifier */
3964
+ this.name = "BodyBudgetSensor";
3965
+ /**
3966
+ * Execution order - after authentication
3967
+ *
3968
+ * Order 150 ensures:
3969
+ * - Authentication complete
3970
+ * - Runs before full body read
3971
+ * - Before schema validation (170)
3972
+ */
3973
+ this.order = BAND.CONTENT + 10;
3974
+ }
3975
+ /**
3976
+ * Determines if this sensor should process the given input.
3977
+ *
3978
+ * Requires at least 8 bytes of peeked data to read headers.
3979
+ *
3980
+ * @param {SensorInput} input - Incoming request
3981
+ * @returns {boolean} True if sufficient peek data available
3982
+ */
3983
+ supports(input) {
3984
+ return !!input.peek && input.peek.length >= 8;
3985
+ }
3986
+ /**
3987
+ * Validates header and body lengths against configured limits.
3988
+ *
3989
+ * **Frame Parsing:**
3990
+ * - Skip magic (5 bytes)
3991
+ * - Skip version (1 byte)
3992
+ * - Skip flags (1 byte)
3993
+ * - Read HDR_LEN varint
3994
+ * - Read BODY_LEN varint
3995
+ * - Compare against MAX_HDR_LEN and MAX_BODY_LEN
3996
+ *
3997
+ * @param {SensorInput} input - Request with peek data
3998
+ * @returns {Promise<SensorDecision>} ALLOW or DENY based on size limits
3999
+ */
4000
+ async run(input) {
4001
+ const { peek } = input;
4002
+ if (!peek || peek.length < 8) {
4003
+ return { action: "ALLOW" };
4004
+ }
4005
+ try {
4006
+ let offset = 5;
4007
+ offset += 1;
4008
+ offset += 1;
4009
+ const { value: hdrLen, length: hdrBytes } = decodeVarint(peek, offset);
4010
+ offset += hdrBytes;
4011
+ const { value: bodyLen } = decodeVarint(peek, offset);
4012
+ if (hdrLen > MAX_HDR_LEN) {
4013
+ return {
4014
+ action: "DENY",
4015
+ code: "HEADER_TOO_LARGE",
4016
+ reason: `Header size ${hdrLen} exceeds limit ${MAX_HDR_LEN}`
4017
+ };
4018
+ }
4019
+ if (bodyLen > MAX_BODY_LEN) {
4020
+ return {
4021
+ action: "DENY",
4022
+ code: "BODY_TOO_LARGE",
4023
+ reason: `Body size ${bodyLen} exceeds limit ${MAX_BODY_LEN}`
4024
+ };
4025
+ }
4026
+ return { action: "ALLOW" };
4027
+ } catch (e) {
4028
+ return { action: "ALLOW" };
4029
+ }
4030
+ }
4031
+ };
4032
+ BodyBudgetSensor = __decorateClass([
4033
+ Sensor(),
4034
+ Injectable10()
4035
+ ], BodyBudgetSensor);
4036
+
4037
+ // src/sensors/capability-enforcement.sensor.ts
4038
+ import { Injectable as Injectable11, Logger as Logger8 } from "@nestjs/common";
4039
+ var CapabilityEnforcementSensor = class {
4040
+ constructor() {
4041
+ this.logger = new Logger8(CapabilityEnforcementSensor.name);
4042
+ /** AxisSensor identifier for logging and registry */
4043
+ this.name = "CapabilityEnforcementSensor";
4044
+ /**
4045
+ * Execution order - runs after authentication
4046
+ *
4047
+ * Order 100 ensures:
4048
+ * - Capsule is verified (CapsuleVerifySensor @ 80)
4049
+ * - Signature is verified (SigVerifySensor @ 90)
4050
+ * - We know the proof type for capability lookup
4051
+ */
4052
+ this.order = BAND.POLICY + 10;
4053
+ }
4054
+ /**
4055
+ * Determines if this sensor should process the given input.
4056
+ *
4057
+ * Only activates when an intent is present.
4058
+ *
4059
+ * @param {SensorInput} input - Incoming AXIS request
4060
+ * @returns {boolean} True if intent is present
4061
+ */
4062
+ supports(input) {
4063
+ return !!input.intent;
4064
+ }
4065
+ /**
4066
+ * Enforces capability requirements for the requested intent.
4067
+ *
4068
+ * **Processing Flow:**
4069
+ * 1. Extract proof type from packet (default: 0/NONE)
4070
+ * 2. Look up capabilities granted by this proof type
4071
+ * 3. Look up capabilities required by the intent
4072
+ * 4. If no requirements, ALLOW
4073
+ * 5. Check if all required capabilities are granted
4074
+ * 6. If missing capabilities, DENY with details
4075
+ * 7. Otherwise, ALLOW
4076
+ *
4077
+ * @param {SensorInput} input - Request with intent and packet
4078
+ * @returns {Promise<SensorDecision>} ALLOW or DENY based on capabilities
4079
+ */
4080
+ async run(input) {
4081
+ const { intent, packet } = input;
4082
+ if (!intent) {
4083
+ return { action: "ALLOW" };
4084
+ }
4085
+ const proofType = packet?.proofType ?? 0;
4086
+ const grantedCapabilities = PROOF_CAPABILITIES[proofType] || [];
4087
+ const requiredCapabilities = this.getRequiredCapabilities(intent);
4088
+ if (requiredCapabilities.length === 0) {
4089
+ return { action: "ALLOW" };
4090
+ }
4091
+ const missingCapabilities = requiredCapabilities.filter(
4092
+ (cap) => !grantedCapabilities.includes(cap)
4093
+ );
4094
+ if (missingCapabilities.length > 0) {
4095
+ this.logger.warn(
4096
+ `Capability denied for ${intent}: missing ${missingCapabilities.join(", ")} (has: ${grantedCapabilities.join(", ")})`
4097
+ );
4098
+ return {
4099
+ action: "DENY",
4100
+ code: "CAPABILITY_DENIED",
4101
+ reason: `Missing capabilities: ${missingCapabilities.join(", ")}`
4102
+ };
4103
+ }
4104
+ return { action: "ALLOW" };
4105
+ }
4106
+ /**
4107
+ * Gets required capabilities for an intent.
4108
+ *
4109
+ * **Lookup Strategy:**
4110
+ * 1. Check for exact intent match
4111
+ * 2. Check for prefix pattern match (*.suffix)
4112
+ * 3. Default to 'execute' for unknown intents
4113
+ *
4114
+ * @private
4115
+ * @param {string} intent - Intent name to look up
4116
+ * @returns {Capability[]} Array of required capabilities
4117
+ */
4118
+ getRequiredCapabilities(intent) {
4119
+ if (INTENT_REQUIREMENTS[intent]) {
4120
+ return INTENT_REQUIREMENTS[intent];
4121
+ }
4122
+ for (const [pattern, caps] of Object.entries(INTENT_REQUIREMENTS)) {
4123
+ if (pattern.endsWith(".*")) {
4124
+ const prefix = pattern.slice(0, -1);
4125
+ if (intent.startsWith(prefix)) {
4126
+ return caps;
4127
+ }
4128
+ }
4129
+ }
4130
+ return ["execute"];
4131
+ }
4132
+ };
4133
+ CapabilityEnforcementSensor = __decorateClass([
4134
+ Sensor(),
4135
+ Injectable11()
4136
+ ], CapabilityEnforcementSensor);
4137
+
4138
+ // src/sensors/chunk-hash.sensor.ts
4139
+ import { Injectable as Injectable12 } from "@nestjs/common";
4140
+ import { createHash as createHash6 } from "crypto";
4141
+ var ChunkHashSensor = class {
4142
+ constructor() {
4143
+ /** Sensor identifier */
4144
+ this.name = "ChunkHashSensor";
4145
+ /**
4146
+ * Execution order - after session validation
4147
+ *
4148
+ * Order 190 ensures:
4149
+ * - Session validated (180)
4150
+ * - Chunk parameters verified
4151
+ * - Hash check before storage
4152
+ */
4153
+ this.order = BAND.CONTENT + 50;
4154
+ }
4155
+ /**
4156
+ * Determines if this sensor should process the given input.
4157
+ *
4158
+ * Only processes file.chunk intents.
4159
+ *
4160
+ * @param {SensorInput} input - Incoming request
4161
+ * @returns {boolean} True if intent is 'file.chunk'
4162
+ */
4163
+ supports(input) {
4164
+ return input.intent === "file.chunk";
4165
+ }
4166
+ /**
4167
+ * Validates chunk data against declared SHA-256 hash.
4168
+ *
4169
+ * **Processing Flow:**
4170
+ * 1. Check for required headerTLVs and body
4171
+ * 2. Extract expected hash from TLV 73
4172
+ * 3. Verify hash is exactly 32 bytes
4173
+ * 4. Compute SHA-256 of body
4174
+ * 5. Compare bytes (timing-safe)
4175
+ * 6. DENY on mismatch
4176
+ *
4177
+ * @param {SensorInput} input - Request with chunk body
4178
+ * @returns {Promise<SensorDecision>} ALLOW if hash matches, DENY otherwise
4179
+ */
4180
+ async run(input) {
4181
+ const headerTLVs = input.headerTLVs;
4182
+ const bodyBytes = input.body;
4183
+ if (!headerTLVs || !bodyBytes) {
4184
+ return {
4185
+ action: "DENY",
4186
+ code: "SENSOR_INVALID_INPUT",
4187
+ reason: "Missing headerTLVs or body"
4188
+ };
4189
+ }
4190
+ const TLV_SHA256_CHUNK2 = 73;
4191
+ const expected = headerTLVs.get(TLV_SHA256_CHUNK2);
4192
+ if (!expected || expected.length !== 32) {
4193
+ return {
4194
+ action: "DENY",
4195
+ code: "FILE_CHUNK_HASH_MISSING",
4196
+ reason: "Missing sha256Chunk TLV in header"
4197
+ };
4198
+ }
4199
+ const actual = createHash6("sha256").update(bodyBytes).digest();
4200
+ if (!Buffer.from(actual).equals(Buffer.from(expected))) {
4201
+ return {
4202
+ action: "DENY",
4203
+ code: "FILE_CHUNK_HASH_MISMATCH",
4204
+ reason: "Chunk hash mismatch - data corrupted"
4205
+ };
4206
+ }
4207
+ return { action: "ALLOW" };
4208
+ }
4209
+ };
4210
+ ChunkHashSensor = __decorateClass([
4211
+ Sensor(),
4212
+ Injectable12()
4213
+ ], ChunkHashSensor);
4214
+
4215
+ // src/sensors/entropy.sensor.ts
4216
+ import { Injectable as Injectable13, Logger as Logger9 } from "@nestjs/common";
4217
+ import * as crypto4 from "crypto";
4218
+ var EntropySensor = class {
4219
+ constructor() {
4220
+ this.logger = new Logger9(EntropySensor.name);
4221
+ /**
4222
+ * Minimum acceptable entropy in bits per byte.
4223
+ *
4224
+ * 3.0 bits/byte is a conservative threshold:
4225
+ * - Random data: ~7.9 bits/byte
4226
+ * - English text: ~4.5 bits/byte
4227
+ * - Sequential data: ~0-2 bits/byte
4228
+ */
4229
+ this.MIN_ENTROPY_THRESHOLD = 3;
4230
+ /** AxisSensor identifier */
4231
+ this.name = "EntropySensor";
4232
+ /**
4233
+ * Execution order - anomaly detection phase
4234
+ *
4235
+ * Order 130 ensures:
4236
+ * - Replay protection done (120)
4237
+ * - Runs before expensive policy lookups
4238
+ */
4239
+ this.order = BAND.POLICY + 35;
4240
+ }
4241
+ /**
4242
+ * Calculates Shannon entropy of a byte array.
4243
+ *
4244
+ * **Algorithm:**
4245
+ * 1. Count frequency of each byte value (0-255)
4246
+ * 2. Calculate probability p = count / total
4247
+ * 3. Sum: -Σ(p * log2(p))
4248
+ *
4249
+ * @private
4250
+ * @param {Uint8Array} data - Bytes to analyze
4251
+ * @returns {number} Entropy in bits per byte (0-8 scale)
4252
+ */
4253
+ calculateEntropy(data) {
4254
+ if (data.length === 0) return 0;
4255
+ const freq = /* @__PURE__ */ new Map();
4256
+ for (const byte of data) {
4257
+ freq.set(byte, (freq.get(byte) || 0) + 1);
4258
+ }
4259
+ let entropy = 0;
4260
+ const len = data.length;
4261
+ for (const count of freq.values()) {
4262
+ const p = count / len;
4263
+ entropy -= p * Math.log2(p);
4264
+ }
4265
+ return entropy;
4266
+ }
4267
+ /**
4268
+ * Checks for sequential patterns in data.
4269
+ *
4270
+ * Detects sequences like [1,2,3,4...] or [10,9,8,7...].
4271
+ * More than 50% sequential is considered suspicious.
4272
+ *
4273
+ * @private
4274
+ * @param {Uint8Array} data - Bytes to analyze
4275
+ * @returns {boolean} True if sequential pattern detected
4276
+ */
4277
+ hasSequentialPattern(data) {
4278
+ if (data.length < 4) return false;
4279
+ let ascending = 0;
4280
+ let descending = 0;
4281
+ for (let i = 1; i < data.length; i++) {
4282
+ if (data[i] === data[i - 1] + 1) ascending++;
4283
+ if (data[i] === data[i - 1] - 1) descending++;
4284
+ }
4285
+ return ascending > data.length / 2 || descending > data.length / 2;
4286
+ }
4287
+ /**
4288
+ * Checks for repeated patterns in data.
4289
+ *
4290
+ * Detects patterns like [0xAB, 0xCD, 0xAB, 0xCD...].
4291
+ * Checks for 2, 4, and 8 byte repeating patterns.
4292
+ *
4293
+ * @private
4294
+ * @param {Uint8Array} data - Bytes to analyze
4295
+ * @returns {boolean} True if repeated pattern detected
4296
+ */
4297
+ hasRepeatedPattern(data) {
4298
+ if (data.length < 8) return false;
4299
+ for (const patternLen of [2, 4, 8]) {
4300
+ if (data.length % patternLen !== 0) continue;
4301
+ let matches = 0;
4302
+ for (let i = patternLen; i < data.length; i++) {
4303
+ if (data[i] === data[i % patternLen]) matches++;
4304
+ }
4305
+ if (matches > (data.length - patternLen) * 0.9) {
4306
+ return true;
4307
+ }
4308
+ }
4309
+ return false;
4310
+ }
4311
+ /**
4312
+ * Analyzes entropy of PID and nonce in request headers.
4313
+ *
4314
+ * **Processing Flow:**
4315
+ * 1. Extract PID and nonce from header TLVs
4316
+ * 2. Calculate entropy for each
4317
+ * 3. Check for sequential patterns
4318
+ * 4. Check for repeated patterns
4319
+ * 5. Accumulate issues and score delta
4320
+ * 6. Return FLAG if issues found, ALLOW otherwise
4321
+ *
4322
+ * @param {SensorInput} input - Request with header TLVs
4323
+ * @returns {Promise<SensorDecision>} ALLOW or FLAG based on entropy analysis
4324
+ */
4325
+ async run(input) {
4326
+ const headers = input.headerTLVs;
4327
+ if (!headers) {
4328
+ return { action: "ALLOW" };
4329
+ }
4330
+ const pid = headers.get(TLV_PID);
4331
+ const nonce = headers.get(TLV_NONCE);
4332
+ const issues = [];
4333
+ let totalDelta = 0;
4334
+ if (pid && pid.length > 0) {
4335
+ const pidEntropy = this.calculateEntropy(pid);
4336
+ if (pidEntropy < this.MIN_ENTROPY_THRESHOLD) {
4337
+ issues.push(`pid_low_entropy:${pidEntropy.toFixed(2)}`);
4338
+ totalDelta -= 3;
4339
+ }
4340
+ if (this.hasSequentialPattern(pid)) {
4341
+ issues.push("pid_sequential");
4342
+ totalDelta -= 5;
4343
+ }
4344
+ if (this.hasRepeatedPattern(pid)) {
4345
+ issues.push("pid_repeated");
4346
+ totalDelta -= 5;
4347
+ }
4348
+ }
4349
+ if (nonce && nonce.length > 0) {
4350
+ const nonceEntropy = this.calculateEntropy(nonce);
4351
+ if (nonceEntropy < this.MIN_ENTROPY_THRESHOLD) {
4352
+ issues.push(`nonce_low_entropy:${nonceEntropy.toFixed(2)}`);
4353
+ totalDelta -= 3;
4354
+ }
4355
+ if (this.hasSequentialPattern(nonce)) {
4356
+ issues.push("nonce_sequential");
4357
+ totalDelta -= 5;
4358
+ }
4359
+ if (this.hasRepeatedPattern(nonce)) {
4360
+ issues.push("nonce_repeated");
4361
+ totalDelta -= 5;
4362
+ }
4363
+ }
4364
+ if (issues.length > 0) {
4365
+ this.logger.warn(`Entropy issues from ${input.ip}: ${issues.join(", ")}`);
4366
+ return {
4367
+ action: "FLAG",
4368
+ scoreDelta: totalDelta,
4369
+ reasons: issues
4370
+ };
4371
+ }
4372
+ return { action: "ALLOW" };
4373
+ }
4374
+ /**
4375
+ * Generates cryptographically secure random bytes.
4376
+ *
4377
+ * Utility method for SDK/client code to ensure proper entropy.
4378
+ * Uses Node.js crypto.randomBytes for secure PRNG.
4379
+ *
4380
+ * @static
4381
+ * @param {number} length - Number of random bytes
4382
+ * @returns {Uint8Array} Cryptographically secure random bytes
4383
+ */
4384
+ static generateSecureRandom(length) {
4385
+ return new Uint8Array(crypto4.randomBytes(length));
4386
+ }
4387
+ };
4388
+ EntropySensor = __decorateClass([
4389
+ Sensor(),
4390
+ Injectable13()
4391
+ ], EntropySensor);
4392
+
4393
+ // src/sensors/execution-timeout.sensor.ts
4394
+ import { Injectable as Injectable14, Logger as Logger10 } from "@nestjs/common";
4395
+ var ExecutionTimeoutSensor = class {
4396
+ constructor() {
4397
+ this.logger = new Logger10(ExecutionTimeoutSensor.name);
4398
+ /** AxisSensor identifier */
4399
+ this.name = "ExecutionTimeoutSensor";
4400
+ /**
4401
+ * Execution order - late, near handler execution
4402
+ *
4403
+ * Order 210 ensures:
4404
+ * - All validation complete
4405
+ * - Deadline set just before handler
4406
+ */
4407
+ this.order = BAND.BUSINESS + 10;
4408
+ }
4409
+ /**
4410
+ * Determines if this sensor should process the given input.
4411
+ *
4412
+ * @param {SensorInput} input - Incoming request
4413
+ * @returns {boolean} True if intent is present
4414
+ */
4415
+ supports(input) {
4416
+ return !!input.intent;
4417
+ }
4418
+ /**
4419
+ * Sets execution deadline in the request context.
4420
+ *
4421
+ * **Processing Flow:**
4422
+ * 1. Look up timeout for intent
4423
+ * 2. Calculate absolute deadline
4424
+ * 3. Store in context for handler use
4425
+ * 4. Return ALLOW
4426
+ *
4427
+ * @param {SensorInput} input - Request with intent
4428
+ * @returns {Promise<SensorDecision>} Always ALLOW
4429
+ */
4430
+ async run(input) {
4431
+ const { intent, context } = input;
4432
+ if (!intent) {
4433
+ return { action: "ALLOW" };
4434
+ }
4435
+ const timeout = resolveTimeout(intent);
4436
+ const deadline = Date.now() + timeout;
4437
+ if (context) {
4438
+ context.deadline = deadline;
4439
+ context.timeoutMs = timeout;
4440
+ }
4441
+ this.logger.debug(
4442
+ `Set ${timeout}ms timeout for ${intent} (deadline: ${new Date(deadline).toISOString()})`
4443
+ );
4444
+ return { action: "ALLOW" };
4445
+ }
4446
+ /**
4447
+ * Checks if a deadline has been exceeded.
4448
+ *
4449
+ * Utility method for handler code.
4450
+ *
4451
+ * @static
4452
+ * @param {object} ctx - Context with deadline
4453
+ * @returns {boolean} True if deadline passed
4454
+ */
4455
+ static isExpired(ctx) {
4456
+ if (!ctx.deadline) return false;
4457
+ return Date.now() > ctx.deadline;
4458
+ }
4459
+ /**
4460
+ * Gets remaining time until deadline.
4461
+ *
4462
+ * Utility method for handler code.
4463
+ *
4464
+ * @static
4465
+ * @param {object} ctx - Context with deadline
4466
+ * @returns {number} Remaining milliseconds (0 if expired, Infinity if no deadline)
4467
+ */
4468
+ static getRemainingMs(ctx) {
4469
+ if (!ctx.deadline) return Infinity;
4470
+ return Math.max(0, ctx.deadline - Date.now());
4471
+ }
4472
+ };
4473
+ ExecutionTimeoutSensor = __decorateClass([
4474
+ Sensor(),
4475
+ Injectable14()
4476
+ ], ExecutionTimeoutSensor);
4477
+
4478
+ // src/sensors/frame-budget.sensor.ts
4479
+ import { Injectable as Injectable15 } from "@nestjs/common";
4480
+ var FrameBudgetSensor = class {
4481
+ constructor(config) {
4482
+ this.config = config;
4483
+ /** AxisSensor identifier for logging and registry */
4484
+ this.name = "FrameBudgetSensor";
4485
+ /**
4486
+ * Execution order - runs after protocol validation
4487
+ *
4488
+ * Order 20 ensures:
4489
+ * - Protocol is valid (ProtocolStrictSensor @ 10)
4490
+ * - Size checked before expensive processing
4491
+ */
4492
+ this.order = BAND.WIRE + 20;
4493
+ }
4494
+ /**
4495
+ * Determines if this sensor should process the given input.
4496
+ *
4497
+ * Only activates when Content-Length header is available.
4498
+ * WebSocket frames may not have Content-Length; they use different size tracking.
4499
+ *
4500
+ * @param {SensorInput} input - Incoming AXIS request
4501
+ * @returns {boolean} True if Content-Length is present
4502
+ */
4503
+ supports(input) {
4504
+ return typeof input.contentLength === "number";
4505
+ }
4506
+ /**
4507
+ * Validates frame size against configured limits.
4508
+ *
4509
+ * **Current Implementation:** Stub that always allows.
4510
+ *
4511
+ * **TODO:** Full implementation should:
4512
+ * 1. Load intent policy for the request
4513
+ * 2. Get maxFrameBytes from policy
4514
+ * 3. Compare against contentLength
4515
+ * 4. DENY if exceeded
4516
+ *
4517
+ * @param {SensorInput} input - Request with contentLength
4518
+ * @returns {Promise<SensorDecision>} ALLOW or DENY based on size
4519
+ */
4520
+ async run(input) {
4521
+ const maxBytes = this.config.get("AXIS_MAX_FRAME_SIZE") || 50 * 1024 * 1024;
4522
+ const contentLength = input.contentLength;
4523
+ if (typeof contentLength !== "number") {
4524
+ return { action: "ALLOW" };
4525
+ }
4526
+ if (contentLength > maxBytes) {
4527
+ return {
4528
+ action: "DENY",
4529
+ code: "FRAME_TOO_LARGE",
4530
+ reason: `Frame size ${contentLength} exceeds limit ${maxBytes}`
4531
+ };
4532
+ }
4533
+ return { action: "ALLOW" };
4534
+ }
4535
+ };
4536
+ FrameBudgetSensor = __decorateClass([
4537
+ Sensor({ phase: "PRE_DECODE" }),
4538
+ Injectable15()
4539
+ ], FrameBudgetSensor);
4540
+
4541
+ // src/sensors/frame-header-sanity.sensor.ts
4542
+ import { Injectable as Injectable16 } from "@nestjs/common";
4543
+ var FrameHeaderSanitySensor = class {
4544
+ constructor() {
4545
+ this.name = "FrameHeaderSanitySensor";
4546
+ this.order = BAND.WIRE + 30;
4547
+ }
4548
+ supports(input) {
4549
+ return !!input.peek && input.peek.length >= 7;
4550
+ }
4551
+ async run(input) {
4552
+ const peek = input.peek;
4553
+ const contentLen = input.contentLength || 0;
4554
+ if (peek.length < 5 || !this.bufferEqual(peek.slice(0, 5), AXIS_MAGIC)) {
4555
+ return {
4556
+ action: "DENY",
4557
+ code: "INVALID_MAGIC",
4558
+ reason: "Frame magic is not AXIS1"
4559
+ };
4560
+ }
4561
+ if (peek[5] !== AXIS_VERSION) {
4562
+ return {
4563
+ action: "DENY",
4564
+ code: "UNSUPPORTED_VERSION",
4565
+ reason: `Unsupported version: ${peek[5]}`
4566
+ };
4567
+ }
4568
+ if (contentLen > MAX_FRAME_LEN) {
4569
+ return {
4570
+ action: "DENY",
4571
+ code: "FRAME_TOO_LARGE",
4572
+ reason: `Frame size ${contentLen} exceeds max ${MAX_FRAME_LEN}`
4573
+ };
4574
+ }
4575
+ return { action: "ALLOW" };
4576
+ }
4577
+ bufferEqual(a, b) {
4578
+ if (a.length !== b.length) return false;
4579
+ for (let i = 0; i < a.length; i++) {
4580
+ if (a[i] !== b[i]) return false;
4581
+ }
4582
+ return true;
4583
+ }
4584
+ };
4585
+ FrameHeaderSanitySensor = __decorateClass([
4586
+ Injectable16(),
4587
+ Sensor({ phase: "PRE_DECODE" })
4588
+ ], FrameHeaderSanitySensor);
4589
+
4590
+ // src/sensors/header-tlv-limit.sensor.ts
4591
+ import { Injectable as Injectable17 } from "@nestjs/common";
4592
+ var HeaderTLVLimitSensor = class {
4593
+ constructor() {
4594
+ this.name = "HeaderTLVLimitSensor";
4595
+ this.order = BAND.CONTENT + 0;
4596
+ this.MAX_TLVS = 64;
4597
+ }
4598
+ supports(input) {
4599
+ return !!input.headerTLVs || !!input.packet;
4600
+ }
4601
+ async run(input) {
4602
+ if (input.headerTLVs && input.headerTLVs.size > this.MAX_TLVS) {
4603
+ return {
4604
+ action: "DENY",
4605
+ code: "TOO_MANY_TLVS",
4606
+ reason: `Header TLVs (${input.headerTLVs.size}) exceed max (${this.MAX_TLVS})`
4607
+ };
4608
+ }
4609
+ if (input.packet && input.packet.headerBytes) {
4610
+ const hdrLen = input.packet.headerBytes.length;
4611
+ if (hdrLen > MAX_HDR_LEN) {
4612
+ return {
4613
+ action: "DENY",
4614
+ code: "HEADER_TOO_LARGE",
4615
+ reason: `Header size ${hdrLen} exceeds max ${MAX_HDR_LEN}`
4616
+ };
4617
+ }
4618
+ }
4619
+ return { action: "ALLOW" };
4620
+ }
4621
+ };
4622
+ HeaderTLVLimitSensor = __decorateClass([
4623
+ Injectable17(),
4624
+ Sensor()
4625
+ ], HeaderTLVLimitSensor);
4626
+
4627
+ // src/sensors/intent-allowlist.sensor.ts
4628
+ import { Injectable as Injectable18 } from "@nestjs/common";
4629
+ var PUBLIC_INTENT_ALLOWLIST = [
4630
+ "public.",
4631
+ "schema.",
4632
+ "catalog.",
4633
+ "health.",
4634
+ "system."
4635
+ ];
4636
+ var IntentAllowlistSensor = class {
4637
+ constructor() {
4638
+ this.name = "IntentAllowlistSensor";
4639
+ this.order = BAND.IDENTITY + 20;
4640
+ }
4641
+ supports(input) {
4642
+ return !!input.intent;
4643
+ }
4644
+ async run(input) {
4645
+ const profile = input.metadata?.profile || "PUBLIC";
4646
+ const intent = input.intent || "";
4647
+ if (profile === "PUBLIC") {
4648
+ const isAllowed = PUBLIC_INTENT_ALLOWLIST.some(
4649
+ (prefix) => intent.startsWith(prefix)
4650
+ );
4651
+ if (!isAllowed) {
4652
+ return {
4653
+ action: "DENY",
4654
+ code: "INTENT_NOT_ALLOWED",
4655
+ reason: `Intent '${intent}' not in public allowlist`
4656
+ };
4657
+ }
4658
+ }
4659
+ return { action: "ALLOW" };
4660
+ }
4661
+ };
4662
+ IntentAllowlistSensor = __decorateClass([
4663
+ Injectable18(),
4664
+ Sensor()
4665
+ ], IntentAllowlistSensor);
4666
+
4667
+ // src/sensors/intent-registry.sensor.ts
4668
+ import { Injectable as Injectable19 } from "@nestjs/common";
4669
+ var IntentRegistrySensor = class {
4670
+ constructor(router) {
4671
+ this.router = router;
4672
+ this.name = "IntentRegistrySensor";
4673
+ this.order = BAND.IDENTITY + 25;
4674
+ }
4675
+ supports(input) {
4676
+ return !!input.intent;
4677
+ }
4678
+ async run(input) {
4679
+ const intent = input.intent;
4680
+ if (this.router.has(intent)) {
4681
+ return { action: "ALLOW" };
4682
+ }
4683
+ return {
4684
+ action: "DENY",
4685
+ code: "INTENT_NOT_REGISTERED",
4686
+ reason: `Intent '${intent}' is not registered`
4687
+ };
4688
+ }
4689
+ };
4690
+ IntentRegistrySensor = __decorateClass([
4691
+ Injectable19(),
4692
+ Sensor({ phase: "POST_DECODE" })
4693
+ ], IntentRegistrySensor);
4694
+
4695
+ // src/sensors/proof-presence.sensor.ts
4696
+ import { Injectable as Injectable20 } from "@nestjs/common";
4697
+ var ProofPresenceSensor = class {
4698
+ constructor() {
4699
+ this.name = "ProofPresenceSensor";
4700
+ this.order = BAND.IDENTITY + 30;
4701
+ }
4702
+ supports(input) {
4703
+ return !!input.profile && !!input.visibility;
4704
+ }
4705
+ async run(input) {
4706
+ const validatedInput = ProofPresenceInputZ.safeParse(input);
4707
+ if (!validatedInput.success) {
4708
+ throw new AxisError(
4709
+ "SENSOR_INVALID_INPUT",
4710
+ `Input validation failed: ${validatedInput.error.message}`,
4711
+ 400
4712
+ );
4713
+ }
4714
+ const {
4715
+ visibility,
4716
+ requiredProof,
4717
+ hasCapsule,
4718
+ hasPassportSignature,
4719
+ profile,
4720
+ intent
4721
+ } = validatedInput.data;
4722
+ if (visibility === "PUBLIC") {
4723
+ return { action: "ALLOW" };
4724
+ }
4725
+ if (requiredProof.includes("NONE")) {
4726
+ return { action: "ALLOW" };
4727
+ }
4728
+ const hasCapsuleProof = requiredProof.includes("CAPSULE") && hasCapsule;
4729
+ const hasPassportProof = requiredProof.includes("PASSPORT") && hasPassportSignature;
4730
+ const hasNodeProof = requiredProof.includes("MTLS") && profile === "NODE";
4731
+ const satisfied = hasCapsuleProof || hasPassportProof || hasNodeProof;
4732
+ if (!satisfied) {
4733
+ throw new AxisError(
4734
+ "SENSOR_PROOF_REQUIRED",
4735
+ `Proof required for guarded intent: ${intent}`,
4736
+ 403
4737
+ );
4738
+ }
4739
+ return { action: "ALLOW" };
4740
+ }
4741
+ };
4742
+ ProofPresenceSensor = __decorateClass([
4743
+ Sensor(),
4744
+ Injectable20()
4745
+ ], ProofPresenceSensor);
4746
+
4747
+ // src/sensors/protocol-strict.sensor.ts
4748
+ import { Injectable as Injectable21, Logger as Logger11 } from "@nestjs/common";
4749
+ var VALID_FLAGS = [
4750
+ 0,
4751
+ // No flags
4752
+ FLAG_BODY_TLV,
4753
+ // Body contains TLVs
4754
+ FLAG_CHAIN_REQ,
4755
+ // Requires receipt chaining
4756
+ FLAG_HAS_WITNESS,
4757
+ // Has witness signatures
4758
+ FLAG_BODY_TLV | FLAG_CHAIN_REQ,
4759
+ FLAG_BODY_TLV | FLAG_HAS_WITNESS,
4760
+ FLAG_CHAIN_REQ | FLAG_HAS_WITNESS,
4761
+ FLAG_BODY_TLV | FLAG_CHAIN_REQ | FLAG_HAS_WITNESS
4762
+ ];
4763
+ var ProtocolStrictSensor = class {
4764
+ constructor(config) {
4765
+ this.config = config;
4766
+ this.logger = new Logger11(ProtocolStrictSensor.name);
4767
+ /** Sensor identifier for logging and registry */
4768
+ this.name = "ProtocolStrictSensor";
4769
+ /**
4770
+ * Execution order - FIRST sensor in the chain
4771
+ *
4772
+ * Order 10 ensures:
4773
+ * - Runs before any other processing
4774
+ * - Invalid frames rejected immediately
4775
+ * - Protects all downstream sensors from malformed input
4776
+ */
4777
+ this.order = BAND.WIRE + 10;
4778
+ this.protocolMagic = AXIS_MAGIC;
4779
+ this.protocolVersion = AXIS_VERSION;
4780
+ }
4781
+ /**
4782
+ * Static validation for streaming middleware (Fast Check)
4783
+ */
4784
+ static validateMagic(chunk, expected) {
4785
+ if (chunk.length < expected.length) return { valid: true };
4786
+ const actual = chunk.subarray(0, expected.length);
4787
+ const valid = Buffer.from(actual).equals(Buffer.from(expected));
4788
+ return {
4789
+ valid,
4790
+ actual: valid ? void 0 : new TextDecoder().decode(actual)
4791
+ };
4792
+ }
4793
+ static validateVersion(version, expected) {
4794
+ return version === expected;
4795
+ }
4796
+ /**
4797
+ * Lifecycle hook: Registers this sensor in the chain on module initialization.
4798
+ */
4799
+ onModuleInit() {
4800
+ const magicStr = this.config.get("AXIS_PROTOCOL_MAGIC");
4801
+ this.protocolMagic = magicStr ? Buffer.from(magicStr, "ascii") : AXIS_MAGIC;
4802
+ this.protocolVersion = this.config.get("AXIS_PROTOCOL_VERSION") || AXIS_VERSION;
4803
+ }
4804
+ /**
4805
+ * Evaluate protocol strictness
4806
+ */
4807
+ async run(input) {
4808
+ const validatedInput = ProtocolStrictInputZ.safeParse(input);
4809
+ if (!validatedInput.success) {
4810
+ this.logger.error(
4811
+ `Invalid input: ${validatedInput.error.message}`,
4812
+ validatedInput.error.issues
4813
+ );
4814
+ return {
4815
+ action: "DENY",
4816
+ code: "INVALID_INPUT",
4817
+ reason: "Protocol validation input failed"
4818
+ };
4819
+ }
4820
+ const { contentType, peek } = validatedInput.data;
4821
+ const issues = [];
4822
+ if (peek.length >= 8) {
4823
+ const hex = Buffer.from(peek.subarray(0, 10)).toString("hex");
4824
+ this.logger.debug(`Raw Frame Header (Hex): ${hex} (IP: ${input.ip})`);
4825
+ }
4826
+ if (contentType !== void 0) {
4827
+ if (!this.isValidContentType(contentType)) {
4828
+ issues.push(`invalid_content_type:${contentType}`);
4829
+ }
4830
+ }
4831
+ if (peek.length < 9) {
4832
+ return {
4833
+ action: "DENY",
4834
+ code: "FRAME_TOO_SHORT",
4835
+ reason: "Frame too short for protocol header"
4836
+ };
4837
+ }
4838
+ const magicCheck = ProtocolStrictSensor.validateMagic(
4839
+ peek,
4840
+ this.protocolMagic
4841
+ );
4842
+ if (!magicCheck.valid) {
4843
+ return {
4844
+ action: "DENY",
4845
+ code: "INVALID_MAGIC",
4846
+ reason: `Expected ${new TextDecoder().decode(this.protocolMagic)} magic, got ${magicCheck.actual}`
4847
+ };
4848
+ }
4849
+ const version = peek[5];
4850
+ if (!ProtocolStrictSensor.validateVersion(version, this.protocolVersion)) {
4851
+ issues.push(`unsupported_version:${version}`);
4852
+ }
4853
+ const flags = peek[6];
4854
+ if (!this.isValidFlags(flags)) {
4855
+ issues.push(`invalid_flags:0x${flags.toString(16)}`);
4856
+ }
4857
+ if (peek.length >= 10) {
4858
+ const lengthCheck = this.checkVarintEncoding(peek.subarray(7));
4859
+ if (!lengthCheck.valid) {
4860
+ issues.push(`non_minimal_varint:${lengthCheck.reason}`);
4861
+ }
4862
+ }
4863
+ if (peek.length >= 20) {
4864
+ const tlvCheck = this.checkTLVOrdering(peek);
4865
+ if (!tlvCheck.valid) {
4866
+ issues.push(`tlv_not_canonical:${tlvCheck.reason}`);
4867
+ }
4868
+ const hasClientVersion = await this.checkForClientVersion(peek);
4869
+ if (!hasClientVersion) {
4870
+ issues.push("missing_client_version");
4871
+ }
4872
+ }
4873
+ if (issues.length > 0) {
4874
+ const critical = issues.some(
4875
+ (i) => i.startsWith("invalid_magic") || i.startsWith("unsupported_version")
4876
+ );
4877
+ if (critical) {
4878
+ return {
4879
+ action: "DENY",
4880
+ code: "PROTOCOL_VIOLATION",
4881
+ reason: issues.join(", ")
4882
+ };
4883
+ }
4884
+ this.logger.warn(
4885
+ `Protocol issues from ${input.ip}: ${issues.join(", ")}`
4886
+ );
4887
+ return {
4888
+ action: "FLAG",
4889
+ scoreDelta: -issues.length * 2,
4890
+ reasons: issues
4891
+ };
4892
+ }
4893
+ return { action: "ALLOW" };
4894
+ }
4895
+ /**
4896
+ * Compare two buffers for equality
4897
+ */
4898
+ buffersEqual(a, b) {
4899
+ if (a.length !== b.length) return false;
4900
+ for (let i = 0; i < a.length; i++) {
4901
+ if (a[i] !== b[i]) return false;
4902
+ }
4903
+ return true;
4904
+ }
4905
+ /**
4906
+ * Check if Content-Type is valid for AXIS
4907
+ */
4908
+ isValidContentType(contentType) {
4909
+ const valid = [
4910
+ "application/axis-bin",
4911
+ "application/octet-stream",
4912
+ "application/x-axis"
4913
+ ];
4914
+ return valid.some((v) => contentType.toLowerCase().includes(v));
4915
+ }
4916
+ /**
4917
+ * Check if flags are a valid combination
4918
+ */
4919
+ isValidFlags(flags) {
4920
+ return VALID_FLAGS.includes(flags);
4921
+ }
4922
+ /**
4923
+ * Check varint encoding is minimal (no leading zeros)
4924
+ */
4925
+ checkVarintEncoding(data) {
4926
+ try {
4927
+ const { value, length: bytesRead } = decodeVarint(data, 0);
4928
+ if (value < 128 && bytesRead > 1) {
4929
+ return { valid: false, reason: "non-minimal-small-value" };
4930
+ }
4931
+ if (value < 16384 && bytesRead > 2) {
4932
+ return { valid: false, reason: "non-minimal-medium-value" };
4933
+ }
4934
+ return { valid: true };
4935
+ } catch {
4936
+ return { valid: false, reason: "varint-decode-error" };
4937
+ }
4938
+ }
4939
+ /**
4940
+ * Check TLV ordering is canonical (sorted by type, no duplicates)
4941
+ */
4942
+ checkTLVOrdering(data) {
4943
+ try {
4944
+ let offset = 7;
4945
+ const { value: hdrLen, length: hdrBytes } = decodeVarint(data, offset);
4946
+ offset += hdrBytes;
4947
+ const { length: bodyBytes } = decodeVarint(data, offset);
4948
+ offset += bodyBytes;
4949
+ const { length: sigBytes } = decodeVarint(data, offset);
4950
+ offset += sigBytes;
4951
+ const hdrStart = offset;
4952
+ const hdrEnd = hdrStart + Number(hdrLen);
4953
+ if (hdrEnd > data.length) {
4954
+ return { valid: true };
4955
+ }
4956
+ let lastType = -1;
4957
+ let pos = hdrStart;
4958
+ while (pos < hdrEnd && pos < data.length - 2) {
4959
+ const { value: type, length: typeBytes } = decodeVarint(data, pos);
4960
+ pos += typeBytes;
4961
+ if (pos >= hdrEnd) break;
4962
+ const { value: len, length: lenBytes } = decodeVarint(data, pos);
4963
+ pos += lenBytes;
4964
+ if (Number(type) <= lastType) {
4965
+ return {
4966
+ valid: false,
4967
+ reason: `type-${type}-after-${lastType}`
4968
+ };
4969
+ }
4970
+ lastType = Number(type);
4971
+ pos += Number(len);
4972
+ }
4973
+ return { valid: true };
4974
+ } catch {
4975
+ return { valid: true };
4976
+ }
4977
+ }
4978
+ /**
4979
+ * Check if TLV 100 (Client Version) exists in the headers
4980
+ */
4981
+ async checkForClientVersion(data) {
4982
+ try {
4983
+ let offset = 7;
4984
+ const { value: hdrLen, length: hdrBytes } = decodeVarint(data, offset);
4985
+ offset += hdrBytes;
4986
+ const { length: bodyBytes } = decodeVarint(data, offset);
4987
+ offset += bodyBytes;
4988
+ const { length: sigBytes } = decodeVarint(data, offset);
4989
+ offset += sigBytes;
4990
+ const hdrEnd = offset + Number(hdrLen);
4991
+ let pos = offset;
4992
+ while (pos < hdrEnd && pos < data.length) {
4993
+ const { value: type, length: typeBytes } = decodeVarint(data, pos);
4994
+ pos += typeBytes;
4995
+ const { length: lenBytes } = decodeVarint(data, pos);
4996
+ pos += lenBytes;
4997
+ const { value: valLen, length: valLenBytes } = decodeVarint(
4998
+ data,
4999
+ pos - lenBytes
5000
+ );
5001
+ }
5002
+ pos = offset;
5003
+ while (pos < hdrEnd && pos < data.length) {
5004
+ const t = decodeVarint(data, pos);
5005
+ pos += t.length;
5006
+ const l = decodeVarint(data, pos);
5007
+ pos += l.length;
5008
+ if (t.value === 100) return true;
5009
+ pos += Number(l.value);
5010
+ }
5011
+ return false;
5012
+ } catch {
5013
+ return false;
5014
+ }
5015
+ }
5016
+ };
5017
+ ProtocolStrictSensor = __decorateClass([
5018
+ Sensor({ phase: "PRE_DECODE" }),
5019
+ Injectable21()
5020
+ ], ProtocolStrictSensor);
5021
+
5022
+ // src/sensors/receipt-policy.sensor.ts
5023
+ import { Injectable as Injectable22 } from "@nestjs/common";
5024
+ var ReceiptPolicySensor = class {
5025
+ constructor() {
5026
+ this.name = "ReceiptPolicySensor";
5027
+ this.order = BAND.BUSINESS + 20;
5028
+ }
5029
+ supports() {
5030
+ return true;
5031
+ }
5032
+ async run() {
5033
+ return { action: "ALLOW" };
5034
+ }
5035
+ };
5036
+ ReceiptPolicySensor = __decorateClass([
5037
+ Injectable22(),
5038
+ Sensor()
5039
+ ], ReceiptPolicySensor);
5040
+
5041
+ // src/sensors/schema-validation.sensor.ts
5042
+ import { Injectable as Injectable23 } from "@nestjs/common";
5043
+ function readU64be(b) {
5044
+ if (b.length !== 8)
5045
+ throw new AxisError("SCHEMA_TYPE_MISMATCH", "u64 must be 8 bytes", 400);
5046
+ let x = 0n;
5047
+ for (const by of b) x = x << 8n | BigInt(by);
5048
+ return x;
5049
+ }
5050
+ var SchemaValidationSensor = class {
5051
+ constructor() {
5052
+ /** Sensor identifier for logging and registry */
5053
+ this.name = "SchemaValidationSensor";
5054
+ /**
5055
+ * Execution order - runs late in the pipeline
5056
+ *
5057
+ * Order 170 ensures:
5058
+ * - All authentication complete
5059
+ * - All policy checks complete
5060
+ * - Data validated before handler execution
5061
+ */
5062
+ this.order = BAND.CONTENT + 35;
5063
+ }
5064
+ /**
5065
+ * Determines if this sensor should process the given input.
5066
+ *
5067
+ * Only activates when a schema is provided for the intent (post-decode phase).
5068
+ *
5069
+ * @param {any} input - Sensor input
5070
+ * @returns {boolean} True if schema exists in metadata
5071
+ */
5072
+ supports(input) {
5073
+ return !!input.metadata?.schema;
5074
+ }
5075
+ /**
5076
+ * Validates TLV fields against the schema definition.
5077
+ *
5078
+ * **Validation Steps:**
5079
+ * 1. Validate the schema itself using Zod
5080
+ * 2. Iterate through each field definition
5081
+ * 3. Check required fields are present
5082
+ * 4. Validate size limits (maxLen)
5083
+ * 5. Validate type-specific rules
5084
+ *
5085
+ * @param {any} input - Standard SensorInput
5086
+ * @returns {{ action: 'ALLOW' } | { action: 'DENY', code: string, reason: string }} Decision
5087
+ */
5088
+ async run(input) {
5089
+ const schema = input.metadata?.schema;
5090
+ const headerTLVs = input.headerTLVs;
5091
+ const bodyTLVs = input.bodyTLVs;
5092
+ if (!schema) {
5093
+ return { action: "ALLOW" };
5094
+ }
5095
+ const validatedSchema = IntentSchemaZ.safeParse(schema);
5096
+ if (!validatedSchema.success) {
5097
+ return {
5098
+ action: "DENY",
5099
+ code: "SCHEMA_INVALID",
5100
+ reason: `Schema validation failed: ${validatedSchema.error.message}`
5101
+ };
5102
+ }
5103
+ try {
5104
+ for (const field of schema.fields) {
5105
+ const scope = field.scope ?? "body";
5106
+ const map3 = scope === "header" ? headerTLVs : bodyTLVs;
5107
+ const val = map3?.get(field.tlv);
5108
+ if (field.required && !val) {
5109
+ throw new AxisError(
5110
+ "SCHEMA_FIELD_MISSING",
5111
+ `Missing required field: ${field.name} (TLV ${field.tlv})`,
5112
+ 400
5113
+ );
5114
+ }
5115
+ if (!val) continue;
5116
+ if (typeof field.maxLen === "number" && val.length > field.maxLen) {
5117
+ throw new AxisError(
5118
+ "SCHEMA_LIMIT_EXCEEDED",
5119
+ `Field ${field.name} too large (${val.length} > ${field.maxLen})`,
5120
+ 413
5121
+ // Payload Too Large
5122
+ );
5123
+ }
5124
+ switch (field.kind) {
5125
+ case "utf8":
5126
+ try {
5127
+ new TextDecoder("utf-8", { fatal: true }).decode(val);
5128
+ } catch {
5129
+ throw new AxisError(
5130
+ "SCHEMA_TYPE_MISMATCH",
5131
+ `Invalid UTF-8 in ${field.name}`,
5132
+ 400
5133
+ );
5134
+ }
5135
+ break;
5136
+ case "bool":
5137
+ if (val.length !== 1 || val[0] !== 0 && val[0] !== 1) {
5138
+ throw new AxisError(
5139
+ "SCHEMA_TYPE_MISMATCH",
5140
+ `Invalid bool: ${field.name}`,
5141
+ 400
5142
+ );
5143
+ }
5144
+ break;
5145
+ case "u64": {
5146
+ const x = readU64be(val);
5147
+ if (field.max) {
5148
+ const mx = BigInt(field.max);
5149
+ if (x > mx) {
5150
+ throw new AxisError(
5151
+ "SCHEMA_LIMIT_EXCEEDED",
5152
+ `u64 ${field.name} exceeds max (${x} > ${mx})`,
5153
+ 400
5154
+ );
5155
+ }
5156
+ }
5157
+ break;
5158
+ }
5159
+ case "bytes16":
5160
+ if (val.length !== 16) {
5161
+ throw new AxisError(
5162
+ "SCHEMA_TYPE_MISMATCH",
5163
+ `bytes16 required for ${field.name}`,
5164
+ 400
5165
+ );
5166
+ }
5167
+ break;
5168
+ case "bytes":
5169
+ break;
5170
+ case "obj":
5171
+ case "arr":
5172
+ break;
5173
+ default:
5174
+ throw new AxisError(
5175
+ "SCHEMA_TYPE_MISMATCH",
5176
+ `Unknown schema kind: ${field.kind}`,
5177
+ 500
5178
+ );
5179
+ }
5180
+ }
5181
+ const validators = input.metadata?.validators;
5182
+ if (validators && validators.size > 0) {
5183
+ for (const field of schema.fields) {
5184
+ const fns = validators.get(field.tlv);
5185
+ if (!fns || fns.length === 0) continue;
5186
+ const scope = field.scope ?? "body";
5187
+ const map3 = scope === "header" ? headerTLVs : bodyTLVs;
5188
+ const val = map3?.get(field.tlv);
5189
+ if (!val) continue;
5190
+ for (const fn of fns) {
5191
+ const error = fn(val, field.name);
5192
+ if (error) {
5193
+ throw new AxisError(
5194
+ "SCHEMA_VALIDATION_FAILED",
5195
+ `${field.name} (TLV ${field.tlv}): ${error}`,
5196
+ 400
5197
+ );
5198
+ }
5199
+ }
5200
+ }
5201
+ }
5202
+ } catch (err) {
5203
+ if (err instanceof AxisError) {
5204
+ return {
5205
+ action: "DENY",
5206
+ code: err.code,
5207
+ reason: err.message
5208
+ };
5209
+ }
5210
+ throw err;
5211
+ }
5212
+ return { action: "ALLOW" };
5213
+ }
5214
+ };
5215
+ SchemaValidationSensor = __decorateClass([
5216
+ Sensor(),
5217
+ Injectable23()
5218
+ ], SchemaValidationSensor);
5219
+
5220
+ // src/sensors/stream-scope.sensor.ts
5221
+ import { Injectable as Injectable24 } from "@nestjs/common";
5222
+ var StreamScopeSensor = class {
5223
+ constructor() {
5224
+ /** Sensor identifier */
5225
+ this.name = "StreamScopeSensor";
5226
+ /**
5227
+ * Execution order - near handler execution
5228
+ *
5229
+ * Order 200 ensures:
5230
+ * - All authentication complete
5231
+ * - All policy checks complete
5232
+ * - Stream-specific check right before subscription
5233
+ */
5234
+ this.order = BAND.BUSINESS + 0;
5235
+ }
5236
+ /**
5237
+ * Determines if this sensor should process the given input.
5238
+ *
5239
+ * Currently processes all inputs.
5240
+ *
5241
+ * @returns {boolean} Always true
5242
+ */
5243
+ supports() {
5244
+ return true;
5245
+ }
5246
+ /**
5247
+ * Validates stream topic access permissions.
5248
+ *
5249
+ * **Current Implementation:** Stub that always allows.
5250
+ *
5251
+ * **TODO:** Full implementation should:
5252
+ * 1. Check if intent is stream.subscribe or stream.publish
5253
+ * 2. Extract topic from body TLVs
5254
+ * 3. Parse topic into owner/resource pattern
5255
+ * 4. Look up topic ACL from database/cache
5256
+ * 5. Check if actor has required permission (read/write)
5257
+ * 6. DENY if unauthorized
5258
+ *
5259
+ * @returns {Promise<SensorDecision>} ALLOW (stub implementation)
5260
+ */
5261
+ async run() {
5262
+ return { action: "ALLOW" };
5263
+ }
5264
+ };
5265
+ StreamScopeSensor = __decorateClass([
5266
+ Sensor(),
5267
+ Injectable24()
5268
+ ], StreamScopeSensor);
5269
+
5270
+ // src/sensors/tlv-parse.sensor.ts
5271
+ import { Injectable as Injectable25 } from "@nestjs/common";
5272
+ var TLVParseSensor = class {
5273
+ constructor() {
5274
+ this.name = "TLVParseSensor";
5275
+ this.order = BAND.CONTENT + 20;
5276
+ }
5277
+ supports(input) {
5278
+ return !!input.packet;
5279
+ }
5280
+ async run(input) {
5281
+ const packet = input.packet;
5282
+ if (!packet) return { action: "ALLOW" };
5283
+ const hdrBytes = packet.hdrBytes ?? packet.headerBytes;
5284
+ if (hdrBytes && hdrBytes.length > 0) {
5285
+ const result = this.validateCanonicalTLV(hdrBytes, "header");
5286
+ if (result) return result;
5287
+ }
5288
+ const bodyBytes = packet.bodyBytes ?? input.body;
5289
+ const bodyIsTlv = packet.flags !== void 0 ? (packet.flags & 1) !== 0 : false;
5290
+ const bodyProfile = input.metadata?.schema?.bodyProfile;
5291
+ const skipBody = bodyProfile === "RAW";
5292
+ if (!skipBody && bodyIsTlv && bodyBytes && bodyBytes.length > 0) {
5293
+ const result = this.validateCanonicalTLV(bodyBytes, "body");
5294
+ if (result) return result;
5295
+ }
5296
+ return { action: "ALLOW" };
5297
+ }
5298
+ /**
5299
+ * Validates a TLV buffer for canonical ordering, no duplicates,
5300
+ * valid bounds, and minimal varint encoding.
5301
+ */
5302
+ validateCanonicalTLV(buf, section) {
5303
+ let offset = 0;
5304
+ let lastType = -1;
5305
+ let count = 0;
5306
+ const maxItems = 512;
5307
+ while (offset < buf.length) {
5308
+ if (count >= maxItems) {
5309
+ return {
5310
+ action: "DENY",
5311
+ code: "TLV_LIMIT",
5312
+ reason: `Too many TLVs in ${section}`
5313
+ };
5314
+ }
5315
+ let type;
5316
+ let typeLen;
5317
+ try {
5318
+ const r = decodeVarint(buf, offset);
5319
+ type = r.value;
5320
+ typeLen = r.length;
5321
+ } catch {
5322
+ return {
5323
+ action: "DENY",
5324
+ code: "TLV_PARSE_ERROR",
5325
+ reason: `Malformed type varint in ${section} at offset ${offset}`
5326
+ };
5327
+ }
5328
+ offset += typeLen;
5329
+ if (type <= 0) {
5330
+ return {
5331
+ action: "DENY",
5332
+ code: "TLV_INVALID_TAG",
5333
+ reason: `Invalid tag ${type} in ${section}`
5334
+ };
5335
+ }
5336
+ if (type <= lastType) {
5337
+ return {
5338
+ action: "DENY",
5339
+ code: "TLV_NOT_CANONICAL",
5340
+ reason: `Non-canonical tag order in ${section}: ${type} after ${lastType}`
5341
+ };
5342
+ }
5343
+ lastType = type;
5344
+ let len;
5345
+ let lenLen;
5346
+ try {
5347
+ const r = decodeVarint(buf, offset);
5348
+ len = r.value;
5349
+ lenLen = r.length;
5350
+ } catch {
5351
+ return {
5352
+ action: "DENY",
5353
+ code: "TLV_PARSE_ERROR",
5354
+ reason: `Malformed length varint in ${section}`
5355
+ };
5356
+ }
5357
+ offset += lenLen;
5358
+ if (offset + len > buf.length) {
5359
+ return {
5360
+ action: "DENY",
5361
+ code: "TLV_TRUNCATED",
5362
+ reason: `TLV value truncated in ${section}`
5363
+ };
5364
+ }
5365
+ offset += len;
5366
+ count++;
5367
+ }
5368
+ return null;
5369
+ }
5370
+ };
5371
+ TLVParseSensor = __decorateClass([
5372
+ Sensor(),
5373
+ Injectable25()
5374
+ ], TLVParseSensor);
5375
+
5376
+ // src/sensors/varint-hardening.sensor.ts
5377
+ import { Injectable as Injectable26 } from "@nestjs/common";
5378
+ var VarintHardeningSensor = class {
5379
+ constructor() {
5380
+ /** Sensor identifier */
5381
+ this.name = "VarintHardeningSensor";
5382
+ /**
5383
+ * Execution order - early detection
5384
+ *
5385
+ * Order 40 ensures:
5386
+ * - After protocol magic check
5387
+ * - Before length-based parsing
5388
+ */
5389
+ this.order = BAND.WIRE + 35;
5390
+ /** Maximum allowed bytes for a single varint */
5391
+ this.MAX_VARINT_BYTES = 5;
5392
+ }
5393
+ /**
5394
+ * Determines if this sensor should process the given input.
5395
+ *
5396
+ * Requires at least 7 bytes of peeked data.
5397
+ *
5398
+ * @param {SensorInput} input - Incoming request
5399
+ * @returns {boolean} True if sufficient peek data
5400
+ */
5401
+ supports(input) {
5402
+ return !!input.peek && input.peek.length >= 7;
5403
+ }
5404
+ /**
5405
+ * Validates varint lengths in frame header.
5406
+ *
5407
+ * **Processing Flow:**
5408
+ * 1. Skip to varint section (offset 7)
5409
+ * 2. Scan for continuation bytes (MSB = 1)
5410
+ * 3. Count consecutive continuation bytes
5411
+ * 4. DENY if count exceeds MAX_VARINT_BYTES
5412
+ *
5413
+ * @param {SensorInput} input - Request with peek data
5414
+ * @returns {Promise<SensorDecision>} ALLOW or DENY based on varint length
5415
+ */
5416
+ async run(input) {
5417
+ const peek = input.peek;
5418
+ const offset = 7;
5419
+ const maxOffset = Math.min(offset + 15, peek.length);
5420
+ let continuationCount = 0;
5421
+ for (let i = offset; i < maxOffset; i++) {
5422
+ if ((peek[i] & 128) !== 0) {
5423
+ continuationCount++;
5424
+ if (continuationCount > this.MAX_VARINT_BYTES) {
5425
+ return {
5426
+ action: "DENY",
5427
+ code: "VARINT_OVERFLOW",
5428
+ reason: `Varint exceeds ${this.MAX_VARINT_BYTES} bytes`
5429
+ };
5430
+ }
5431
+ } else {
5432
+ continuationCount = 0;
5433
+ }
5434
+ }
5435
+ return { action: "ALLOW" };
5436
+ }
5437
+ };
5438
+ VarintHardeningSensor = __decorateClass([
5439
+ Sensor({ phase: "PRE_DECODE" }),
5440
+ Injectable26()
5441
+ ], VarintHardeningSensor);
5442
+
5443
+ // src/utils/index.ts
5444
+ var utils_exports = {};
5445
+ __export(utils_exports, {
5446
+ encodeAxisTlvDto: () => encodeAxisTlvDto
5447
+ });
5448
+
5449
+ // src/utils/axis-tlv-codec.ts
5450
+ function encodeAxisTlvDto(dtoClass, data) {
5451
+ const schema = extractDtoSchema(dtoClass);
5452
+ const items = schema.fields.flatMap((field) => {
5453
+ const value = data[field.name];
5454
+ if (value === void 0 || value === null) {
5455
+ if (field.required) {
5456
+ throw new Error(`Missing required TLV response field: ${field.name}`);
5457
+ }
5458
+ return [];
5459
+ }
5460
+ return [{ type: field.tag, value: encodeField(field, value) }];
5461
+ });
5462
+ return buildTLVs(items);
5463
+ }
5464
+ function encodeField(field, value) {
5465
+ switch (field.kind) {
5466
+ case "utf8":
5467
+ return Buffer.from(String(value), "utf8");
5468
+ case "u64":
5469
+ return encodeU64(value);
5470
+ case "bytes":
5471
+ case "bytes16":
5472
+ return toBuffer(value);
5473
+ case "bool":
5474
+ return Buffer.from([value ? 1 : 0]);
5475
+ case "obj":
5476
+ case "arr":
5477
+ return Buffer.from(JSON.stringify(value), "utf8");
5478
+ default:
5479
+ return toBuffer(value);
5480
+ }
5481
+ }
5482
+ function encodeU64(value) {
5483
+ const encoded = Buffer.alloc(8);
5484
+ encoded.writeBigUInt64BE(
5485
+ typeof value === "bigint" ? value : BigInt(value)
5486
+ );
5487
+ return encoded;
5488
+ }
5489
+ function toBuffer(value) {
5490
+ if (Buffer.isBuffer(value)) {
5491
+ return value;
5492
+ }
5493
+ if (value instanceof Uint8Array) {
5494
+ return Buffer.from(value);
5495
+ }
5496
+ if (typeof value === "string") {
5497
+ return Buffer.from(value, "utf8");
5498
+ }
5499
+ throw new Error(`Unsupported TLV bytes value: ${typeof value}`);
5500
+ }
2681
5501
  export {
2682
5502
  ATS1_HDR,
2683
5503
  ATS1_SCHEMA,
@@ -2816,6 +5636,8 @@ export {
2816
5636
  classifyIntent,
2817
5637
  computeReceiptHash,
2818
5638
  computeSignaturePayload,
5639
+ core_exports as core,
5640
+ crypto_exports as crypto,
2819
5641
  decodeArray,
2820
5642
  decodeAxis1Frame,
2821
5643
  decodeFrame,
@@ -2823,11 +5645,13 @@ export {
2823
5645
  decodeTLVs,
2824
5646
  decodeTLVsList,
2825
5647
  decodeVarint,
5648
+ decorators_exports as decorators,
2826
5649
  encVarint,
2827
5650
  encodeAxis1Frame,
2828
5651
  encodeFrame,
2829
5652
  encodeTLVs,
2830
5653
  encodeVarint,
5654
+ engine_exports as engine,
2831
5655
  extractDtoSchema,
2832
5656
  generateEd25519KeyPair,
2833
5657
  getSignTarget,
@@ -2835,6 +5659,7 @@ export {
2835
5659
  isAdminOpcode,
2836
5660
  isKnownOpcode,
2837
5661
  isTimestampValid,
5662
+ loom_exports as loom,
2838
5663
  nonce16,
2839
5664
  normalizeSensorDecision,
2840
5665
  packPasskeyLoginOptionsReq,
@@ -2844,7 +5669,10 @@ export {
2844
5669
  packPasskeyRegisterOptionsReq,
2845
5670
  parseScope,
2846
5671
  resolveTimeout,
5672
+ schemas_exports as schemas,
5673
+ security_exports as security,
2847
5674
  sensitivityName,
5675
+ sensors_exports as sensors,
2848
5676
  sha256,
2849
5677
  signFrame,
2850
5678
  tlv,
@@ -2853,6 +5681,7 @@ export {
2853
5681
  unpackPasskeyLoginVerifyReq,
2854
5682
  unpackPasskeyRegisterOptionsReq,
2855
5683
  utf8,
5684
+ utils_exports as utils,
2856
5685
  validateFrameShape,
2857
5686
  varintLength,
2858
5687
  varintU,