@nextera.one/axis-server-sdk 1.3.0 → 1.4.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
@@ -718,6 +718,165 @@ IntentRouter = __decorateClass([
718
718
  __decorateParam(0, Optional())
719
719
  ], IntentRouter);
720
720
 
721
+ // src/engine/observation/stable-json.ts
722
+ function normalize(value) {
723
+ if (Array.isArray(value)) {
724
+ return value.map((item) => normalize(item));
725
+ }
726
+ if (value && typeof value === "object") {
727
+ const entries = Object.entries(value).filter(([, nested]) => nested !== void 0).sort(([left], [right]) => left.localeCompare(right));
728
+ const normalized = {};
729
+ for (const [key, nested] of entries) {
730
+ normalized[key] = normalize(nested);
731
+ }
732
+ return normalized;
733
+ }
734
+ return value;
735
+ }
736
+ function stableJsonStringify(value) {
737
+ return JSON.stringify(normalize(value));
738
+ }
739
+
740
+ // src/engine/observation/observation-queue.codec.ts
741
+ function buildQueueMessage(observation, sourceNodeId, previous, lastError) {
742
+ const now = Date.now();
743
+ return {
744
+ v: 1,
745
+ observation,
746
+ attempts: previous ? previous.attempts + 1 : 0,
747
+ firstEnqueuedAt: previous?.firstEnqueuedAt ?? now,
748
+ lastEnqueuedAt: now,
749
+ sourceNodeId,
750
+ lastError
751
+ };
752
+ }
753
+ function encodeQueueMessage(message) {
754
+ return JSON.stringify(message);
755
+ }
756
+ function decodeQueueMessage(raw) {
757
+ try {
758
+ const parsed = JSON.parse(raw);
759
+ if (!parsed || parsed.v !== 1 || !parsed.observation?.id) {
760
+ return null;
761
+ }
762
+ return parsed;
763
+ } catch {
764
+ return null;
765
+ }
766
+ }
767
+ function parseStreamEntries(raw) {
768
+ if (!Array.isArray(raw)) {
769
+ return [];
770
+ }
771
+ const entries = [];
772
+ for (const streamRow of raw) {
773
+ if (!Array.isArray(streamRow) || streamRow.length < 2) {
774
+ continue;
775
+ }
776
+ const messageRows = streamRow[1];
777
+ if (!Array.isArray(messageRows)) {
778
+ continue;
779
+ }
780
+ for (const row of messageRows) {
781
+ if (!Array.isArray(row) || row.length < 2) {
782
+ continue;
783
+ }
784
+ const id = String(row[0]);
785
+ const fields = Array.isArray(row[1]) ? row[1] : [];
786
+ const fieldMap = fieldsToMap(fields);
787
+ const payload = fieldMap.get("payload");
788
+ if (!payload) {
789
+ continue;
790
+ }
791
+ const message = decodeQueueMessage(payload);
792
+ if (!message) {
793
+ continue;
794
+ }
795
+ entries.push({ id, message });
796
+ }
797
+ }
798
+ return entries;
799
+ }
800
+ function parseAutoClaimEntries(raw) {
801
+ if (!Array.isArray(raw) || raw.length < 2) {
802
+ return [];
803
+ }
804
+ const rows = Array.isArray(raw[1]) ? raw[1] : [];
805
+ return parseStreamEntries([["stream", rows]]);
806
+ }
807
+ function fieldsToMap(fields) {
808
+ const map3 = /* @__PURE__ */ new Map();
809
+ for (let i = 0; i < fields.length; i += 2) {
810
+ const key = fields[i];
811
+ const value = fields[i + 1];
812
+ if (key !== void 0 && value !== void 0) {
813
+ map3.set(String(key), String(value));
814
+ }
815
+ }
816
+ return map3;
817
+ }
818
+
819
+ // src/engine/observation/observation-hash.ts
820
+ import { createHash } from "crypto";
821
+ function canonicalizeObservation(obs) {
822
+ const obj = {
823
+ id: obs.id,
824
+ startMs: obs.startMs,
825
+ endMs: obs.endMs,
826
+ transport: obs.transport,
827
+ ip: obs.ip,
828
+ intent: obs.intent,
829
+ actorId: obs.actorId,
830
+ capsuleId: obs.capsuleId,
831
+ decision: obs.decision,
832
+ resultCode: obs.resultCode,
833
+ statusCode: obs.statusCode,
834
+ durationMs: obs.durationMs,
835
+ stages: obs.stages.map((s) => ({
836
+ name: s.name,
837
+ status: s.status,
838
+ startMs: s.startMs,
839
+ endMs: s.endMs,
840
+ durationMs: s.durationMs,
841
+ reason: s.reason,
842
+ code: s.code
843
+ })),
844
+ sensors: obs.sensors.map((s) => ({
845
+ name: s.name,
846
+ allowed: s.allowed,
847
+ riskScore: s.riskScore,
848
+ durationMs: s.durationMs,
849
+ reasons: s.reasons,
850
+ code: s.code
851
+ }))
852
+ };
853
+ return stableJsonStringify(obj);
854
+ }
855
+ function hashObservation(obs) {
856
+ const canonical = canonicalizeObservation(obs);
857
+ return createHash("sha256").update(canonical).digest("hex");
858
+ }
859
+ function buildUnsignedWitness(obs) {
860
+ if (!obs.decision || !obs.endMs) {
861
+ return null;
862
+ }
863
+ return {
864
+ v: 1,
865
+ observationId: obs.id,
866
+ payloadHash: hashObservation(obs),
867
+ sealedAt: Date.now(),
868
+ summary: {
869
+ intent: obs.intent,
870
+ actorId: obs.actorId,
871
+ decision: obs.decision,
872
+ statusCode: obs.statusCode,
873
+ durationMs: obs.durationMs,
874
+ sensorCount: obs.sensors.length,
875
+ stageCount: obs.stages.length
876
+ }
877
+ };
878
+ }
879
+
721
880
  // src/core/constants.ts
722
881
  import {
723
882
  AXIS_MAGIC,
@@ -784,6 +943,51 @@ import {
784
943
  ERR_CONTRACT_VIOLATION
785
944
  } from "@nextera.one/axis-protocol";
786
945
 
946
+ // src/engine/observation/response-observer.ts
947
+ var SENSITIVE_RESPONSE_TAGS = [4, 5, 6];
948
+ function verifyResponse(ctx, response) {
949
+ if (!response.effect || typeof response.effect !== "string") {
950
+ return {
951
+ passed: false,
952
+ code: "OBSERVER_INVALID_EFFECT",
953
+ reason: "Response effect is missing or invalid"
954
+ };
955
+ }
956
+ if (response.ok && (!response.body || response.body.length === 0)) {
957
+ return {
958
+ passed: false,
959
+ code: "OBSERVER_EMPTY_BODY",
960
+ reason: "Successful response must contain a body"
961
+ };
962
+ }
963
+ if (response.body && response.body.length > MAX_BODY_LEN) {
964
+ return {
965
+ passed: false,
966
+ code: "OBSERVER_BODY_OVERFLOW",
967
+ reason: `Response body exceeds ${MAX_BODY_LEN} bytes`
968
+ };
969
+ }
970
+ if (response.headers) {
971
+ for (const tag of SENSITIVE_RESPONSE_TAGS) {
972
+ if (response.headers.has(tag)) {
973
+ return {
974
+ passed: false,
975
+ code: "OBSERVER_DATA_LEAK",
976
+ reason: `Response must not contain sensitive TLV tag ${tag}`
977
+ };
978
+ }
979
+ }
980
+ }
981
+ if (response.effect.includes("Error:") || response.effect.includes("stack") || response.effect.includes("at /")) {
982
+ return {
983
+ passed: false,
984
+ code: "OBSERVER_INFO_LEAK",
985
+ reason: "Response effect may contain internal error details"
986
+ };
987
+ }
988
+ return { passed: true };
989
+ }
990
+
787
991
  // src/core/varint.ts
788
992
  import { encodeVarint, decodeVarint, varintLength } from "@nextera.one/axis-protocol";
789
993
 
@@ -1059,7 +1263,7 @@ __export(ats1_exports, {
1059
1263
  tlvsToMap: () => tlvsToMap,
1060
1264
  validateTLVsAgainstSchema: () => validateTLVsAgainstSchema
1061
1265
  });
1062
- import { createHash as createHash2 } from "crypto";
1266
+ import { createHash as createHash3 } from "crypto";
1063
1267
  var DEFAULT_LIMITS = {
1064
1268
  maxVarintBytes: 10,
1065
1269
  maxTlvCount: 512,
@@ -1109,7 +1313,7 @@ function decodeU64BE(buf) {
1109
1313
  return buf.readBigUInt64BE(0);
1110
1314
  }
1111
1315
  function sha2562(data) {
1112
- return createHash2("sha256").update(data).digest();
1316
+ return createHash3("sha256").update(data).digest();
1113
1317
  }
1114
1318
  function encodeTLV(tag, value) {
1115
1319
  if (!Number.isInteger(tag) || tag <= 0)
@@ -2272,9 +2476,9 @@ function isAdminOpcode(op) {
2272
2476
  }
2273
2477
 
2274
2478
  // src/core/receipt.ts
2275
- import { createHash as createHash3 } from "crypto";
2479
+ import { createHash as createHash4 } from "crypto";
2276
2480
  function buildReceiptHash(prevHash, pid, actorId, intent, effect, ts) {
2277
- const h = createHash3("sha256");
2481
+ const h = createHash4("sha256");
2278
2482
  if (prevHash) h.update(prevHash);
2279
2483
  h.update(pid);
2280
2484
  h.update(Buffer.from(actorId, "utf8"));
@@ -3109,6 +3313,7 @@ __export(engine_exports, {
3109
3313
  createObservation: () => createObservation,
3110
3314
  endStage: () => endStage,
3111
3315
  finalizeObservation: () => finalizeObservation,
3316
+ observation: () => observation_exports,
3112
3317
  recordSensor: () => recordSensor,
3113
3318
  startStage: () => startStage
3114
3319
  });
@@ -3395,6 +3600,21 @@ SensorRegistry = __decorateClass([
3395
3600
  Injectable7()
3396
3601
  ], SensorRegistry);
3397
3602
 
3603
+ // src/engine/observation/index.ts
3604
+ var observation_exports = {};
3605
+ __export(observation_exports, {
3606
+ buildQueueMessage: () => buildQueueMessage,
3607
+ buildUnsignedWitness: () => buildUnsignedWitness,
3608
+ canonicalizeObservation: () => canonicalizeObservation,
3609
+ decodeQueueMessage: () => decodeQueueMessage,
3610
+ encodeQueueMessage: () => encodeQueueMessage,
3611
+ hashObservation: () => hashObservation,
3612
+ parseAutoClaimEntries: () => parseAutoClaimEntries,
3613
+ parseStreamEntries: () => parseStreamEntries,
3614
+ stableJsonStringify: () => stableJsonStringify,
3615
+ verifyResponse: () => verifyResponse
3616
+ });
3617
+
3398
3618
  // src/loom/index.ts
3399
3619
  var loom_exports = {};
3400
3620
  __export(loom_exports, {
@@ -4137,7 +4357,7 @@ CapabilityEnforcementSensor = __decorateClass([
4137
4357
 
4138
4358
  // src/sensors/chunk-hash.sensor.ts
4139
4359
  import { Injectable as Injectable12 } from "@nestjs/common";
4140
- import { createHash as createHash6 } from "crypto";
4360
+ import { createHash as createHash7 } from "crypto";
4141
4361
  var ChunkHashSensor = class {
4142
4362
  constructor() {
4143
4363
  /** Sensor identifier */
@@ -4196,7 +4416,7 @@ var ChunkHashSensor = class {
4196
4416
  reason: "Missing sha256Chunk TLV in header"
4197
4417
  };
4198
4418
  }
4199
- const actual = createHash6("sha256").update(bodyBytes).digest();
4419
+ const actual = createHash7("sha256").update(bodyBytes).digest();
4200
4420
  if (!Buffer.from(actual).equals(Buffer.from(expected))) {
4201
4421
  return {
4202
4422
  action: "DENY",
@@ -5627,12 +5847,15 @@ export {
5627
5847
  buildAts1Hdr,
5628
5848
  buildDtoDecoder,
5629
5849
  buildPacket,
5850
+ buildQueueMessage,
5630
5851
  buildReceiptHash,
5631
5852
  buildTLVs,
5853
+ buildUnsignedWitness,
5632
5854
  bytes,
5633
5855
  canAccessResource,
5634
5856
  canonicalJson,
5635
5857
  canonicalJsonExcluding,
5858
+ canonicalizeObservation,
5636
5859
  classifyIntent,
5637
5860
  computeReceiptHash,
5638
5861
  computeSignaturePayload,
@@ -5642,6 +5865,7 @@ export {
5642
5865
  decodeAxis1Frame,
5643
5866
  decodeFrame,
5644
5867
  decodeObject,
5868
+ decodeQueueMessage,
5645
5869
  decodeTLVs,
5646
5870
  decodeTLVsList,
5647
5871
  decodeVarint,
@@ -5649,6 +5873,7 @@ export {
5649
5873
  encVarint,
5650
5874
  encodeAxis1Frame,
5651
5875
  encodeFrame,
5876
+ encodeQueueMessage,
5652
5877
  encodeTLVs,
5653
5878
  encodeVarint,
5654
5879
  engine_exports as engine,
@@ -5656,6 +5881,7 @@ export {
5656
5881
  generateEd25519KeyPair,
5657
5882
  getSignTarget,
5658
5883
  hasScope,
5884
+ hashObservation,
5659
5885
  isAdminOpcode,
5660
5886
  isKnownOpcode,
5661
5887
  isTimestampValid,
@@ -5667,7 +5893,9 @@ export {
5667
5893
  packPasskeyLoginVerifyReq,
5668
5894
  packPasskeyLoginVerifyRes,
5669
5895
  packPasskeyRegisterOptionsReq,
5896
+ parseAutoClaimEntries,
5670
5897
  parseScope,
5898
+ parseStreamEntries,
5671
5899
  resolveTimeout,
5672
5900
  schemas_exports as schemas,
5673
5901
  security_exports as security,
@@ -5675,6 +5903,7 @@ export {
5675
5903
  sensors_exports as sensors,
5676
5904
  sha256,
5677
5905
  signFrame,
5906
+ stableJsonStringify,
5678
5907
  tlv,
5679
5908
  u64be,
5680
5909
  unpackPasskeyLoginOptionsReq,
@@ -5685,6 +5914,7 @@ export {
5685
5914
  validateFrameShape,
5686
5915
  varintLength,
5687
5916
  varintU,
5688
- verifyFrameSignature
5917
+ verifyFrameSignature,
5918
+ verifyResponse
5689
5919
  };
5690
5920
  //# sourceMappingURL=index.mjs.map