@camstack/system 1.2.383 → 1.2.385

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/auth/auth-audit-log.d.ts +2 -2
  6. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  7. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  9. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  10. package/dist/builtins/alerts/alerts.addon.js +1 -1
  11. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  12. package/dist/builtins/autotrack/index.js +1 -1
  13. package/dist/builtins/autotrack/index.mjs +1 -1
  14. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  15. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  16. package/dist/builtins/calendars/calendars.addon.js +1 -1
  17. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  18. package/dist/builtins/camera-grid/index.js +1 -1
  19. package/dist/builtins/camera-grid/index.mjs +1 -1
  20. package/dist/builtins/composer/composer.addon.js +2 -2
  21. package/dist/builtins/composer/composer.addon.mjs +2 -2
  22. package/dist/builtins/console-logging/index.js +1 -1
  23. package/dist/builtins/console-logging/index.mjs +1 -1
  24. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  25. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  26. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  27. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  28. package/dist/builtins/hub-forwarder/index.js +1 -1
  29. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  30. package/dist/builtins/keychain/index.js +3 -3
  31. package/dist/builtins/keychain/index.mjs +3 -3
  32. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  33. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  34. package/dist/builtins/local-auth/local-auth.addon.js +5 -4
  35. package/dist/builtins/local-auth/local-auth.addon.mjs +5 -4
  36. package/dist/builtins/local-network/local-network.addon.js +1 -1
  37. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  38. package/dist/builtins/loki-logging/index.js +1 -1
  39. package/dist/builtins/loki-logging/index.mjs +1 -1
  40. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  41. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  42. package/dist/builtins/platform-probe/index.js +1 -1
  43. package/dist/builtins/platform-probe/index.mjs +1 -1
  44. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  45. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  46. package/dist/builtins/snapshot/index.js +1 -1
  47. package/dist/builtins/snapshot/index.mjs +1 -1
  48. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  49. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  50. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  51. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  52. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  53. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  54. package/dist/builtins/system-config/system-config.addon.d.ts +13 -1
  55. package/dist/builtins/system-config/system-config.addon.js +201 -8
  56. package/dist/builtins/system-config/system-config.addon.mjs +195 -9
  57. package/dist/builtins/winston-logging/index.js +1 -1
  58. package/dist/builtins/winston-logging/index.mjs +1 -1
  59. package/dist/{child-cap-dispatch-C68N2a6Y.mjs → child-cap-dispatch-B1gyE35b.mjs} +1 -1
  60. package/dist/{child-cap-dispatch-BD8fhJ1b.js → child-cap-dispatch-nURMuVTX.js} +1 -1
  61. package/dist/{composition-sources-ChpT_Yw4.js → composition-sources-wdAxs-uY.js} +1 -1
  62. package/dist/{composition-sources-DM7Ft_tp.mjs → composition-sources-xqhm2Ygr.mjs} +1 -1
  63. package/dist/{constants-CDpry1_T.js → constants-Ca-Sgcl8.js} +1 -1
  64. package/dist/{constants-e3mCa1jG.mjs → constants-D5z4EwY8.mjs} +1 -1
  65. package/dist/{dist-CUNGH0fs.js → dist-8LQ4lDEB.js} +9 -1
  66. package/dist/{dist-osl3bkF0.mjs → dist-Biqn3TEn.mjs} +9 -1
  67. package/dist/{event-loop-stall-monitor-BeeWMnyT.mjs → event-loop-stall-monitor-BtK_0Pjc.mjs} +1 -1
  68. package/dist/{event-loop-stall-monitor-Dk28EF8B.js → event-loop-stall-monitor-CNj73d4D.js} +1 -1
  69. package/dist/index.js +13 -6
  70. package/dist/index.mjs +8 -8
  71. package/dist/{jwt-signing-key-WI6z7JBo.js → jwt-signing-key-DxV2ucvJ.js} +1 -1
  72. package/dist/{jwt-signing-key-MQLxLfgF.mjs → jwt-signing-key-SnDgvVUQ.mjs} +1 -1
  73. package/dist/kernel/index.d.ts +2 -2
  74. package/dist/kernel/moleculer/mesh-mtls/index.d.ts +1 -0
  75. package/dist/kernel/moleculer/mesh-mtls/mesh-clear-port.d.ts +56 -0
  76. package/dist/kernel/moleculer/mesh-mtls/tls-tcp-transporter.d.ts +33 -0
  77. package/dist/{restore-archive-CZRtI1k9.mjs → restore-archive-D4cmLoNe.mjs} +1 -1
  78. package/dist/{restore-archive-C5f9yX90.js → restore-archive-Do_Yu5rC.js} +1 -1
  79. package/dist/{retired-settings-keys-LuDd4y3M.mjs → retired-settings-keys-BsgVxh8-.mjs} +1 -1
  80. package/dist/{retired-settings-keys-WLrbzdPR.js → retired-settings-keys-Cyn3OD0K.js} +1 -1
  81. package/package.json +1 -1
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CUNGH0fs.js");
6
+ const require_dist = require("../../dist-8LQ4lDEB.js");
7
7
  const require_ms$1 = require("../../ms-t68_FaqE.js");
8
- const require_constants$8 = require("../../constants-CDpry1_T.js");
8
+ const require_constants$8 = require("../../constants-Ca-Sgcl8.js");
9
9
  const require_x509_es = require("../../x509.es-BhTzcFhZ.js");
10
10
  let zod = require("zod");
11
11
  let node_crypto = require("node:crypto");
@@ -280,6 +280,9 @@ async function createMeshCsrPem(keys, commonName) {
280
280
  async function generateMeshKeyPair() {
281
281
  return node_crypto.webcrypto.subtle.generateKey(MESH_KEY_ALGORITHM, true, ["sign", "verify"]);
282
282
  }
283
+ async function exportMeshPrivateKeyPem(key) {
284
+ return exportKeyPem(key);
285
+ }
283
286
  //#endregion
284
287
  //#region src/kernel/moleculer/mesh-mtls/mesh-ca-keeper.ts
285
288
  var SLOT$2 = Symbol.for("camstack.mesh.ca");
@@ -465,6 +468,69 @@ function processMeshCrl() {
465
468
  return crl;
466
469
  }
467
470
  //#endregion
471
+ //#region src/kernel/moleculer/mesh-mtls/mesh-clear-port.ts
472
+ /** The field key inside the `cluster` section. */
473
+ var MESH_CLEAR_PORT_FIELD = "meshClearPort";
474
+ function isMeshClearPortMode(value) {
475
+ return value === "on" || value === "off";
476
+ }
477
+ /** A stored value that is not a mode reads as `on`: never cut the mesh on a typo. */
478
+ function parseMeshClearPortMode(value) {
479
+ return isMeshClearPortMode(value) ? value : "on";
480
+ }
481
+ /** Pure: may the clear port be switched off right now? */
482
+ function decideMeshClearPortOff(control) {
483
+ if (control === null) return {
484
+ allowed: false,
485
+ blocking: [],
486
+ error: "The clear mesh port can only be switched off on the hub (no mesh transport here)."
487
+ };
488
+ if (!control.isTlsListening()) return {
489
+ allowed: false,
490
+ blocking: [],
491
+ error: "Refusing to close the clear mesh port: the TLS listener (:6443) is not up yet, so no node could reach the hub."
492
+ };
493
+ const blocking = control.listOnlineAgents().filter((agent) => agent.transport !== "tls").map((agent) => agent.nodeId).toSorted();
494
+ if (blocking.length > 0) return {
495
+ allowed: false,
496
+ blocking,
497
+ error: `Refusing to close the clear mesh port: ${blocking.join(", ")} still ${blocking.length === 1 ? "connects" : "connect"} in clear. Enroll ${blocking.length === 1 ? "it" : "each"} with a join token first (System → Cluster → Add agent).`
498
+ };
499
+ return { allowed: true };
500
+ }
501
+ var CONTROL_SLOT = Symbol.for("camstack.cluster.mesh-clear-port-control");
502
+ /** Published by the hub's broker owner; cleared on shutdown. */
503
+ function setProcessMeshClearPortControl(control) {
504
+ if (control === null) Reflect.deleteProperty(globalThis, CONTROL_SLOT);
505
+ else Reflect.set(globalThis, CONTROL_SLOT, control);
506
+ }
507
+ function isMeshClearPortControl(value) {
508
+ return value !== null && typeof value === "object" && typeof Reflect.get(value, "listOnlineAgents") === "function" && typeof Reflect.get(value, "isTlsListening") === "function" && typeof Reflect.get(value, "apply") === "function" && typeof Reflect.get(value, "transportOf") === "function";
509
+ }
510
+ function getProcessMeshClearPortControl() {
511
+ const value = Reflect.get(globalThis, CONTROL_SLOT);
512
+ return isMeshClearPortControl(value) ? value : null;
513
+ }
514
+ /** `tls` / `clear` for an agent as the hub sees it; `undefined` off the hub. */
515
+ function getProcessNodeMeshTransport(nodeId) {
516
+ return getProcessMeshClearPortControl()?.transportOf(nodeId);
517
+ }
518
+ /**
519
+ * Validate a requested mode against the live mesh. `on` is always allowed; `off`
520
+ * only when {@link decideMeshClearPortOff} allows. Does NOT apply it.
521
+ */
522
+ function checkProcessMeshClearPort(mode) {
523
+ if (mode === "on") return { allowed: true };
524
+ return decideMeshClearPortOff(getProcessMeshClearPortControl());
525
+ }
526
+ /** Apply a mode that {@link checkProcessMeshClearPort} allowed. No-op where nobody can. */
527
+ async function applyProcessMeshClearPort(mode) {
528
+ const control = getProcessMeshClearPortControl();
529
+ if (control === null) return false;
530
+ await control.apply(mode);
531
+ return true;
532
+ }
533
+ //#endregion
468
534
  //#region ../../node_modules/jose/dist/webapi/lib/buffer_utils.js
469
535
  var encoder = new TextEncoder(), decoder = new TextDecoder(), strictDecoder = new TextDecoder("utf-8", { fatal: !0 });
470
536
  function concat(...buffers) {
@@ -87309,6 +87375,9 @@ var TlsTcpTransporter = class extends TcpTransporter {
87309
87375
  meshTls;
87310
87376
  crl;
87311
87377
  tlsSockets = /* @__PURE__ */ new Set();
87378
+ /** Node id (certificate CN) → serial of every peer that completed a mutual-TLS handshake. */
87379
+ tlsPeers = /* @__PURE__ */ new Map();
87380
+ clearDisabled = false;
87312
87381
  constructor(opts = {}) {
87313
87382
  super({ ...opts });
87314
87383
  this.meshTls = opts.meshTls ?? null;
@@ -87401,6 +87470,54 @@ var TlsTcpTransporter = class extends TcpTransporter {
87401
87470
  this.meshTls = material;
87402
87471
  await this.startTlsServerIfReady();
87403
87472
  }
87473
+ /**
87474
+ * How `nodeId` reaches this transporter: `tls` once it completed a mutual-TLS
87475
+ * handshake (certificate CN = node id, issued by the mesh CA — so this cannot
87476
+ * be claimed by a clear-text peer), otherwise `clear`. Sticky until
87477
+ * {@link forgetPeer}: Moleculer closes idle sockets while the node is still
87478
+ * connected, and a closed socket is not a downgrade.
87479
+ */
87480
+ peerTransport(nodeId) {
87481
+ return this.tlsPeers.has(nodeId) ? "tls" : "clear";
87482
+ }
87483
+ /**
87484
+ * The node disconnected: its next connection decides its transport again —
87485
+ * unless it already re-handshook (the disconnect can be noticed after the
87486
+ * restarted node is back), in which case its live TLS socket keeps it `tls`.
87487
+ */
87488
+ forgetPeer(nodeId) {
87489
+ for (const socket of this.tlsSockets) if (socket.meshPeerCn === nodeId && !socket.destroyed) return;
87490
+ this.tlsPeers.delete(nodeId);
87491
+ }
87492
+ /** True once the mutual-TLS listener is bound. */
87493
+ isTlsListening() {
87494
+ return this.tlsServer !== null;
87495
+ }
87496
+ isClearTransportEnabled() {
87497
+ return !this.clearDisabled;
87498
+ }
87499
+ /**
87500
+ * Cutover: stop listening on the clear TCP port, drop the clear-text sockets
87501
+ * already open on it and stop UDP discovery. TLS sockets (own listener, own
87502
+ * port) are untouched. Idempotent.
87503
+ */
87504
+ disableClearTransport() {
87505
+ this.clearDisabled = true;
87506
+ const reader = this.reader;
87507
+ if (reader?.server?.listening) reader.server.close();
87508
+ for (const socket of [...reader?.sockets ?? []]) if (!isTlsSocket(socket)) socket.destroy();
87509
+ this.udpServer?.close();
87510
+ this.udpServer = null;
87511
+ this.logger.info("Mesh clear transport disabled (clear TCP reader and UDP discovery stopped).");
87512
+ }
87513
+ /** The switch back: listen on the clear port again and restart UDP discovery. Idempotent. */
87514
+ async enableClearTransport() {
87515
+ if (!this.clearDisabled) return;
87516
+ this.clearDisabled = false;
87517
+ if (this.reader !== null && this.reader.server?.listening !== true) await this.reader.listen();
87518
+ if (this.udpServer === null) await this.startUdpServer();
87519
+ this.logger.info("Mesh clear transport re-enabled.");
87520
+ }
87404
87521
  /** Close sockets whose peer serial was just revoked. */
87405
87522
  dropRevokedSockets() {
87406
87523
  for (const socket of [...this.tlsSockets]) {
@@ -87423,6 +87540,7 @@ var TlsTcpTransporter = class extends TcpTransporter {
87423
87540
  s.destroy();
87424
87541
  } catch {}
87425
87542
  this.tlsSockets.clear();
87543
+ this.tlsPeers.clear();
87426
87544
  return super.disconnect();
87427
87545
  }
87428
87546
  onIncomingMessage(type, message, socket) {
@@ -87499,6 +87617,7 @@ var TlsTcpTransporter = class extends TcpTransporter {
87499
87617
  socket.meshPeerCn = identity.cn;
87500
87618
  socket.meshPeerSerial = identity.serial;
87501
87619
  this.tlsSockets.add(socket);
87620
+ this.tlsPeers.set(identity.cn, identity.serial);
87502
87621
  const reader = this.reader;
87503
87622
  socket.on("close", () => {
87504
87623
  this.tlsSockets.delete(socket);
@@ -87757,7 +87876,8 @@ var KEY_TO_SECTION = {
87757
87876
  trustedProxies: "auth",
87758
87877
  trustedNetworks: "auth",
87759
87878
  [CLUSTER_SECRET_FIELD]: "cluster",
87760
- [MESH_AUTH_MODE_FIELD]: "cluster"
87879
+ [MESH_AUTH_MODE_FIELD]: "cluster",
87880
+ [MESH_CLEAR_PORT_FIELD]: "cluster"
87761
87881
  };
87762
87882
  /** Bytes of a generated cluster secret (D802) — 256 bits, base64url (43 chars). */
87763
87883
  var GENERATED_SECRET_BYTES = 32;
@@ -87810,10 +87930,11 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
87810
87930
  onReady: (ca) => this.onMeshCaReady(ca)
87811
87931
  });
87812
87932
  await this.meshCaKeeper.start();
87933
+ await this.applyStoredClearPort();
87813
87934
  this.ctx.logger.info("Initialized — exposes yml-backed sections via getGlobalSettings");
87814
87935
  return {
87815
87936
  customActions: systemConfigMeshActions,
87816
- actionHandlers: { mintJoinToken: async (input) => this.mintJoinToken(input ?? {}) }
87937
+ actionHandlers: { mintJoinToken: async (input) => this.mintJoinToken(parseMintJoinTokenInput(input)) }
87817
87938
  };
87818
87939
  }
87819
87940
  async onShutdown() {
@@ -87823,6 +87944,20 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
87823
87944
  this.clusterSecretKeeper?.stop();
87824
87945
  this.clusterSecretKeeper = null;
87825
87946
  }
87947
+ /**
87948
+ * Boot: a stored `meshClearPort: off` is the operator's standing choice, so it
87949
+ * is applied WITHOUT the agents gate (nobody is connected yet to be checked).
87950
+ * Default `on` changes nothing.
87951
+ */
87952
+ async applyStoredClearPort() {
87953
+ try {
87954
+ const stored = (await this.ctx.settings?.getSection("cluster"))?.[MESH_CLEAR_PORT_FIELD];
87955
+ if (parseMeshClearPortMode(stored) !== "off") return;
87956
+ if (await applyProcessMeshClearPort("off")) this.ctx.logger.info("Mesh clear port is OFF (cluster.meshClearPort)");
87957
+ } catch (err) {
87958
+ this.ctx.logger.warn("could not apply cluster.meshClearPort at boot", { meta: { error: require_dist.errMsg(err) } });
87959
+ }
87960
+ }
87826
87961
  async onMeshCaReady(ca) {
87827
87962
  const hubLeaf = await issueMeshLeafForNode(ca, "hub");
87828
87963
  await requestProcessMeshTlsEnable({
@@ -87854,7 +87989,7 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
87854
87989
  }
87855
87990
  async resolveHubUrl() {
87856
87991
  try {
87857
- const book = await this.ctx.api?.localNetwork?.getAddresses?.({ scheme: "https" });
87992
+ const book = await this.ctx.api?.localNetwork?.getAddresses?.query({ scheme: "https" });
87858
87993
  const url = book?.roles?.publicEndpoint?.resolved ?? book?.roles?.internal?.resolved ?? null;
87859
87994
  if (typeof url === "string" && url.length > 0) return url;
87860
87995
  } catch {}
@@ -87864,7 +87999,7 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
87864
87999
  }
87865
88000
  async resolveHubMeshAddress() {
87866
88001
  try {
87867
- const base = (await this.ctx.api?.localNetwork?.getAddresses?.({}))?.roles?.internal?.resolved;
88002
+ const base = (await this.ctx.api?.localNetwork?.getAddresses?.query({}))?.roles?.internal?.resolved;
87868
88003
  if (typeof base === "string" && base.length > 0) return `${new URL(base).hostname}:${require_constants$8.DEFAULT_HUB_TCP_PORT}`;
87869
88004
  } catch {}
87870
88005
  const host = (await this.ctx.settings?.getSection("server"))?.["host"];
@@ -87985,6 +88120,20 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
87985
88120
  label: "Enforced — refuse"
87986
88121
  }]
87987
88122
  },
88123
+ {
88124
+ type: "select",
88125
+ key: MESH_CLEAR_PORT_FIELD,
88126
+ label: "Clear mesh port (:6000)",
88127
+ description: "On keeps the clear TCP port and UDP discovery next to the TLS port (:6443). Off closes both — refused while any connected agent has not enrolled with a join token (the Cluster page shows TLS / clear per node).",
88128
+ default: "on",
88129
+ options: [{
88130
+ value: "on",
88131
+ label: "On — accept clear agents"
88132
+ }, {
88133
+ value: "off",
88134
+ label: "Off — mutual TLS only"
88135
+ }]
88136
+ },
87988
88137
  {
87989
88138
  type: "info",
87990
88139
  key: "meshAuthStatus",
@@ -88067,7 +88216,8 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
88067
88216
  * is REFUSED — never written inline (D787, P5). `meshAuth` is a plain value.
88068
88217
  */
88069
88218
  async writeClusterSection(patch) {
88070
- const { [MESH_AUTH_MODE_FIELD]: mode, ...secretPatch } = patch;
88219
+ const { [MESH_AUTH_MODE_FIELD]: mode, [MESH_CLEAR_PORT_FIELD]: clearPort, ...secretPatch } = patch;
88220
+ if (clearPort !== void 0) await this.writeMeshClearPort(clearPort);
88071
88221
  if (mode !== void 0) {
88072
88222
  if (!isMeshAuthMode(mode)) throw new Error(`system-config: meshAuth must be 'transition' or 'enforced'`);
88073
88223
  await this.writeMeshAuthMode(mode);
@@ -88078,6 +88228,27 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
88078
88228
  this.ctx.logger.info("Cluster secret updated");
88079
88229
  await this.clusterSecretKeeper?.reload();
88080
88230
  }
88231
+ /**
88232
+ * `cluster.meshClearPort`: `off` is REFUSED while any online agent still
88233
+ * connects in clear (or the TLS listener is not up); the refusal names them.
88234
+ * Persist, then apply — an `on` save always passes.
88235
+ */
88236
+ async writeMeshClearPort(value) {
88237
+ if (!isMeshClearPortMode(value)) throw new Error(`system-config: meshClearPort must be 'on' or 'off'`);
88238
+ const stored = (await this.ctx.settings.getSection("cluster"))[MESH_CLEAR_PORT_FIELD];
88239
+ if (parseMeshClearPortMode(stored) === value) return;
88240
+ const decision = checkProcessMeshClearPort(value);
88241
+ if (!decision.allowed) {
88242
+ this.ctx.logger.warn("mesh clear port switch-off refused", { meta: {
88243
+ blocking: decision.blocking,
88244
+ error: decision.error
88245
+ } });
88246
+ throw new Error(decision.error);
88247
+ }
88248
+ await this.ctx.settings.setSection("cluster", { [MESH_CLEAR_PORT_FIELD]: value });
88249
+ await applyProcessMeshClearPort(value);
88250
+ this.ctx.logger.info("Mesh clear port switched", { meta: { mode: value } });
88251
+ }
88081
88252
  /** `cluster.meshAuth` (D802), then republish it. */
88082
88253
  async writeMeshAuthMode(mode) {
88083
88254
  await this.ctx.settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: mode });
@@ -88122,18 +88293,34 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
88122
88293
  if (orphaned.length > 0) this.ctx.logger.warn("an old cluster secret value could not be deleted from the keychain", { meta: { refs: orphaned } });
88123
88294
  }
88124
88295
  };
88296
+ function parseMintJoinTokenInput(input) {
88297
+ if (input === null || input === void 0 || typeof input !== "object") return {};
88298
+ const out = {};
88299
+ const nodeId = Reflect.get(input, "nodeId");
88300
+ const hubUrl = Reflect.get(input, "hubUrl");
88301
+ const hubMeshAddress = Reflect.get(input, "hubMeshAddress");
88302
+ const ttlSec = Reflect.get(input, "ttlSec");
88303
+ if (typeof nodeId === "string") out.nodeId = nodeId;
88304
+ if (typeof hubUrl === "string") out.hubUrl = hubUrl;
88305
+ if (typeof hubMeshAddress === "string") out.hubMeshAddress = hubMeshAddress;
88306
+ if (typeof ttlSec === "number") out.ttlSec = ttlSec;
88307
+ return out;
88308
+ }
88125
88309
  /** Read `exp` from an unencrypted JWT payload (no verify — already minted here). */
88126
88310
  function readJwtExp(token) {
88127
88311
  const parts = token.split(".");
88128
88312
  if (parts.length < 2 || parts[1] === void 0) return 0;
88129
88313
  const json = Buffer.from(parts[1], "base64url").toString("utf8");
88130
88314
  const payload = JSON.parse(json);
88131
- return typeof payload.exp === "number" ? payload.exp : 0;
88315
+ if (payload === null || typeof payload !== "object") return 0;
88316
+ const exp = Reflect.get(payload, "exp");
88317
+ return typeof exp === "number" ? exp : 0;
88132
88318
  }
88133
88319
  //#endregion
88134
88320
  exports.Errors = Errors;
88135
88321
  exports.JoinTokenJtiStore = JoinTokenJtiStore;
88136
88322
  exports.MESH_AUTH_MODE_FIELD = MESH_AUTH_MODE_FIELD;
88323
+ exports.MESH_CLEAR_PORT_FIELD = MESH_CLEAR_PORT_FIELD;
88137
88324
  exports.MeshCaKeeper = MeshCaKeeper;
88138
88325
  exports.MeshCrl = MeshCrl;
88139
88326
  exports.SYSTEM_CONFIG_CLUSTER_SECTION_ID = SYSTEM_CONFIG_CLUSTER_SECTION_ID;
@@ -88141,16 +88328,21 @@ exports.ServiceBroker = ServiceBroker;
88141
88328
  exports.SystemConfigAddon = SystemConfigAddon;
88142
88329
  exports.default = SystemConfigAddon;
88143
88330
  exports.TlsTcpTransporter = TlsTcpTransporter;
88331
+ exports.applyProcessMeshClearPort = applyProcessMeshClearPort;
88144
88332
  exports.caFingerprint = caFingerprint;
88145
88333
  exports.caFingerprintCompact = caFingerprintCompact;
88334
+ exports.checkProcessMeshClearPort = checkProcessMeshClearPort;
88146
88335
  exports.createCidrMatcher = createCidrMatcher;
88147
88336
  exports.createMeshCsrPem = createMeshCsrPem;
88337
+ exports.decideMeshClearPortOff = decideMeshClearPortOff;
88148
88338
  exports.enrollMeshNode = enrollMeshNode;
88339
+ exports.exportMeshPrivateKeyPem = exportMeshPrivateKeyPem;
88149
88340
  exports.generateMeshCa = generateMeshCa;
88150
88341
  exports.generateMeshKeyPair = generateMeshKeyPair;
88151
88342
  exports.getProcessMeshAuthMode = getProcessMeshAuthMode;
88152
88343
  exports.getProcessMeshAuthStatus = getProcessMeshAuthStatus;
88153
88344
  exports.getProcessMeshCaState = getProcessMeshCaState;
88345
+ exports.getProcessNodeMeshTransport = getProcessNodeMeshTransport;
88154
88346
  exports.isMeshAuthMode = isMeshAuthMode;
88155
88347
  exports.issueMeshLeafForNode = issueMeshLeafForNode;
88156
88348
  exports.issueMeshLeafFromCsr = issueMeshLeafFromCsr;
@@ -88166,6 +88358,7 @@ exports.requestProcessMeshEnforcement = requestProcessMeshEnforcement;
88166
88358
  exports.requireProcessMeshCa = requireProcessMeshCa;
88167
88359
  exports.setProcessMeshAuthMode = setProcessMeshAuthMode;
88168
88360
  exports.setProcessMeshAuthStatusSource = setProcessMeshAuthStatusSource;
88361
+ exports.setProcessMeshClearPortControl = setProcessMeshClearPortControl;
88169
88362
  exports.setProcessMeshEnforcer = setProcessMeshEnforcer;
88170
88363
  exports.setProcessMeshTlsEnabler = setProcessMeshTlsEnabler;
88171
88364
  exports.systemConfigMeshActions = systemConfigMeshActions;
@@ -1,7 +1,7 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM, r as __exportAll, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Dr as defineCustomActions, Er as customAction, Ir as isSecretClear, Lr as isSecretMarker, Mr as hydrateSchema, Zr as secretTreeFromSchema, lr as errMsg, ti as transformSecretWrite, ur as BaseAddon, zr as makeSecretMarker } from "../../dist-osl3bkF0.mjs";
2
+ import { Dr as defineCustomActions, Er as customAction, Ir as isSecretClear, Lr as isSecretMarker, Mr as hydrateSchema, Zr as secretTreeFromSchema, lr as errMsg, ti as transformSecretWrite, ur as BaseAddon, zr as makeSecretMarker } from "../../dist-Biqn3TEn.mjs";
3
3
  import { n as require_safe_buffer, t as require_ms } from "../../ms-DlIWNkkk.mjs";
4
- import { a as MESH_CA_FIELD, c as MESH_CA_VALIDITY_DAYS, f as CLUSTER_SECRET_REF, i as MESH_CA_COMMON_NAME, l as MESH_JOIN_TOKEN_MAX_TTL_SEC, n as DEFAULT_HUB_TCP_PORT, o as MESH_CA_KEY_REF, r as DEFAULT_HUB_TLS_TCP_PORT, s as MESH_CA_SETTINGS_KEY, t as CLIENT_AUTH_OID, u as SERVER_AUTH_OID, x as setProcessClusterSecretGate, y as hashClusterSecret } from "../../constants-e3mCa1jG.mjs";
4
+ import { a as MESH_CA_FIELD, c as MESH_CA_VALIDITY_DAYS, f as CLUSTER_SECRET_REF, i as MESH_CA_COMMON_NAME, l as MESH_JOIN_TOKEN_MAX_TTL_SEC, n as DEFAULT_HUB_TCP_PORT, o as MESH_CA_KEY_REF, r as DEFAULT_HUB_TLS_TCP_PORT, s as MESH_CA_SETTINGS_KEY, t as CLIENT_AUTH_OID, u as SERVER_AUTH_OID, x as setProcessClusterSecretGate, y as hashClusterSecret } from "../../constants-D5z4EwY8.mjs";
5
5
  import { a as KeyUsagesExtension, c as Pkcs10CertificateRequestGenerator, d as X509Certificate$1, f as X509CertificateGenerator, i as KeyUsageFlags, n as BasicConstraintsExtension, o as PemConverter, p as cryptoProvider, r as ExtendedKeyUsageExtension, s as Pkcs10CertificateRequest, t as AuthorityKeyIdentifierExtension, u as SubjectKeyIdentifierExtension } from "../../x509.es-Xf3K_brW.mjs";
6
6
  import { z } from "zod";
7
7
  import { X509Certificate, createHash, randomBytes, webcrypto } from "node:crypto";
@@ -271,6 +271,9 @@ async function createMeshCsrPem(keys, commonName) {
271
271
  async function generateMeshKeyPair() {
272
272
  return webcrypto.subtle.generateKey(MESH_KEY_ALGORITHM, true, ["sign", "verify"]);
273
273
  }
274
+ async function exportMeshPrivateKeyPem(key) {
275
+ return exportKeyPem(key);
276
+ }
274
277
  //#endregion
275
278
  //#region src/kernel/moleculer/mesh-mtls/mesh-ca-keeper.ts
276
279
  var SLOT$2 = Symbol.for("camstack.mesh.ca");
@@ -456,6 +459,69 @@ function processMeshCrl() {
456
459
  return crl;
457
460
  }
458
461
  //#endregion
462
+ //#region src/kernel/moleculer/mesh-mtls/mesh-clear-port.ts
463
+ /** The field key inside the `cluster` section. */
464
+ var MESH_CLEAR_PORT_FIELD = "meshClearPort";
465
+ function isMeshClearPortMode(value) {
466
+ return value === "on" || value === "off";
467
+ }
468
+ /** A stored value that is not a mode reads as `on`: never cut the mesh on a typo. */
469
+ function parseMeshClearPortMode(value) {
470
+ return isMeshClearPortMode(value) ? value : "on";
471
+ }
472
+ /** Pure: may the clear port be switched off right now? */
473
+ function decideMeshClearPortOff(control) {
474
+ if (control === null) return {
475
+ allowed: false,
476
+ blocking: [],
477
+ error: "The clear mesh port can only be switched off on the hub (no mesh transport here)."
478
+ };
479
+ if (!control.isTlsListening()) return {
480
+ allowed: false,
481
+ blocking: [],
482
+ error: "Refusing to close the clear mesh port: the TLS listener (:6443) is not up yet, so no node could reach the hub."
483
+ };
484
+ const blocking = control.listOnlineAgents().filter((agent) => agent.transport !== "tls").map((agent) => agent.nodeId).toSorted();
485
+ if (blocking.length > 0) return {
486
+ allowed: false,
487
+ blocking,
488
+ error: `Refusing to close the clear mesh port: ${blocking.join(", ")} still ${blocking.length === 1 ? "connects" : "connect"} in clear. Enroll ${blocking.length === 1 ? "it" : "each"} with a join token first (System → Cluster → Add agent).`
489
+ };
490
+ return { allowed: true };
491
+ }
492
+ var CONTROL_SLOT = Symbol.for("camstack.cluster.mesh-clear-port-control");
493
+ /** Published by the hub's broker owner; cleared on shutdown. */
494
+ function setProcessMeshClearPortControl(control) {
495
+ if (control === null) Reflect.deleteProperty(globalThis, CONTROL_SLOT);
496
+ else Reflect.set(globalThis, CONTROL_SLOT, control);
497
+ }
498
+ function isMeshClearPortControl(value) {
499
+ return value !== null && typeof value === "object" && typeof Reflect.get(value, "listOnlineAgents") === "function" && typeof Reflect.get(value, "isTlsListening") === "function" && typeof Reflect.get(value, "apply") === "function" && typeof Reflect.get(value, "transportOf") === "function";
500
+ }
501
+ function getProcessMeshClearPortControl() {
502
+ const value = Reflect.get(globalThis, CONTROL_SLOT);
503
+ return isMeshClearPortControl(value) ? value : null;
504
+ }
505
+ /** `tls` / `clear` for an agent as the hub sees it; `undefined` off the hub. */
506
+ function getProcessNodeMeshTransport(nodeId) {
507
+ return getProcessMeshClearPortControl()?.transportOf(nodeId);
508
+ }
509
+ /**
510
+ * Validate a requested mode against the live mesh. `on` is always allowed; `off`
511
+ * only when {@link decideMeshClearPortOff} allows. Does NOT apply it.
512
+ */
513
+ function checkProcessMeshClearPort(mode) {
514
+ if (mode === "on") return { allowed: true };
515
+ return decideMeshClearPortOff(getProcessMeshClearPortControl());
516
+ }
517
+ /** Apply a mode that {@link checkProcessMeshClearPort} allowed. No-op where nobody can. */
518
+ async function applyProcessMeshClearPort(mode) {
519
+ const control = getProcessMeshClearPortControl();
520
+ if (control === null) return false;
521
+ await control.apply(mode);
522
+ return true;
523
+ }
524
+ //#endregion
459
525
  //#region ../../node_modules/jose/dist/webapi/lib/buffer_utils.js
460
526
  var encoder = new TextEncoder(), decoder = new TextDecoder(), strictDecoder = new TextDecoder("utf-8", { fatal: !0 });
461
527
  function concat(...buffers) {
@@ -87300,6 +87366,9 @@ var TlsTcpTransporter = class extends TcpTransporter {
87300
87366
  meshTls;
87301
87367
  crl;
87302
87368
  tlsSockets = /* @__PURE__ */ new Set();
87369
+ /** Node id (certificate CN) → serial of every peer that completed a mutual-TLS handshake. */
87370
+ tlsPeers = /* @__PURE__ */ new Map();
87371
+ clearDisabled = false;
87303
87372
  constructor(opts = {}) {
87304
87373
  super({ ...opts });
87305
87374
  this.meshTls = opts.meshTls ?? null;
@@ -87392,6 +87461,54 @@ var TlsTcpTransporter = class extends TcpTransporter {
87392
87461
  this.meshTls = material;
87393
87462
  await this.startTlsServerIfReady();
87394
87463
  }
87464
+ /**
87465
+ * How `nodeId` reaches this transporter: `tls` once it completed a mutual-TLS
87466
+ * handshake (certificate CN = node id, issued by the mesh CA — so this cannot
87467
+ * be claimed by a clear-text peer), otherwise `clear`. Sticky until
87468
+ * {@link forgetPeer}: Moleculer closes idle sockets while the node is still
87469
+ * connected, and a closed socket is not a downgrade.
87470
+ */
87471
+ peerTransport(nodeId) {
87472
+ return this.tlsPeers.has(nodeId) ? "tls" : "clear";
87473
+ }
87474
+ /**
87475
+ * The node disconnected: its next connection decides its transport again —
87476
+ * unless it already re-handshook (the disconnect can be noticed after the
87477
+ * restarted node is back), in which case its live TLS socket keeps it `tls`.
87478
+ */
87479
+ forgetPeer(nodeId) {
87480
+ for (const socket of this.tlsSockets) if (socket.meshPeerCn === nodeId && !socket.destroyed) return;
87481
+ this.tlsPeers.delete(nodeId);
87482
+ }
87483
+ /** True once the mutual-TLS listener is bound. */
87484
+ isTlsListening() {
87485
+ return this.tlsServer !== null;
87486
+ }
87487
+ isClearTransportEnabled() {
87488
+ return !this.clearDisabled;
87489
+ }
87490
+ /**
87491
+ * Cutover: stop listening on the clear TCP port, drop the clear-text sockets
87492
+ * already open on it and stop UDP discovery. TLS sockets (own listener, own
87493
+ * port) are untouched. Idempotent.
87494
+ */
87495
+ disableClearTransport() {
87496
+ this.clearDisabled = true;
87497
+ const reader = this.reader;
87498
+ if (reader?.server?.listening) reader.server.close();
87499
+ for (const socket of [...reader?.sockets ?? []]) if (!isTlsSocket(socket)) socket.destroy();
87500
+ this.udpServer?.close();
87501
+ this.udpServer = null;
87502
+ this.logger.info("Mesh clear transport disabled (clear TCP reader and UDP discovery stopped).");
87503
+ }
87504
+ /** The switch back: listen on the clear port again and restart UDP discovery. Idempotent. */
87505
+ async enableClearTransport() {
87506
+ if (!this.clearDisabled) return;
87507
+ this.clearDisabled = false;
87508
+ if (this.reader !== null && this.reader.server?.listening !== true) await this.reader.listen();
87509
+ if (this.udpServer === null) await this.startUdpServer();
87510
+ this.logger.info("Mesh clear transport re-enabled.");
87511
+ }
87395
87512
  /** Close sockets whose peer serial was just revoked. */
87396
87513
  dropRevokedSockets() {
87397
87514
  for (const socket of [...this.tlsSockets]) {
@@ -87414,6 +87531,7 @@ var TlsTcpTransporter = class extends TcpTransporter {
87414
87531
  s.destroy();
87415
87532
  } catch {}
87416
87533
  this.tlsSockets.clear();
87534
+ this.tlsPeers.clear();
87417
87535
  return super.disconnect();
87418
87536
  }
87419
87537
  onIncomingMessage(type, message, socket) {
@@ -87490,6 +87608,7 @@ var TlsTcpTransporter = class extends TcpTransporter {
87490
87608
  socket.meshPeerCn = identity.cn;
87491
87609
  socket.meshPeerSerial = identity.serial;
87492
87610
  this.tlsSockets.add(socket);
87611
+ this.tlsPeers.set(identity.cn, identity.serial);
87493
87612
  const reader = this.reader;
87494
87613
  socket.on("close", () => {
87495
87614
  this.tlsSockets.delete(socket);
@@ -87747,7 +87866,8 @@ var KEY_TO_SECTION = {
87747
87866
  trustedProxies: "auth",
87748
87867
  trustedNetworks: "auth",
87749
87868
  [CLUSTER_SECRET_FIELD]: "cluster",
87750
- [MESH_AUTH_MODE_FIELD]: "cluster"
87869
+ [MESH_AUTH_MODE_FIELD]: "cluster",
87870
+ [MESH_CLEAR_PORT_FIELD]: "cluster"
87751
87871
  };
87752
87872
  /** Bytes of a generated cluster secret (D802) — 256 bits, base64url (43 chars). */
87753
87873
  var GENERATED_SECRET_BYTES = 32;
@@ -87800,10 +87920,11 @@ var SystemConfigAddon = class extends BaseAddon {
87800
87920
  onReady: (ca) => this.onMeshCaReady(ca)
87801
87921
  });
87802
87922
  await this.meshCaKeeper.start();
87923
+ await this.applyStoredClearPort();
87803
87924
  this.ctx.logger.info("Initialized — exposes yml-backed sections via getGlobalSettings");
87804
87925
  return {
87805
87926
  customActions: systemConfigMeshActions,
87806
- actionHandlers: { mintJoinToken: async (input) => this.mintJoinToken(input ?? {}) }
87927
+ actionHandlers: { mintJoinToken: async (input) => this.mintJoinToken(parseMintJoinTokenInput(input)) }
87807
87928
  };
87808
87929
  }
87809
87930
  async onShutdown() {
@@ -87813,6 +87934,20 @@ var SystemConfigAddon = class extends BaseAddon {
87813
87934
  this.clusterSecretKeeper?.stop();
87814
87935
  this.clusterSecretKeeper = null;
87815
87936
  }
87937
+ /**
87938
+ * Boot: a stored `meshClearPort: off` is the operator's standing choice, so it
87939
+ * is applied WITHOUT the agents gate (nobody is connected yet to be checked).
87940
+ * Default `on` changes nothing.
87941
+ */
87942
+ async applyStoredClearPort() {
87943
+ try {
87944
+ const stored = (await this.ctx.settings?.getSection("cluster"))?.[MESH_CLEAR_PORT_FIELD];
87945
+ if (parseMeshClearPortMode(stored) !== "off") return;
87946
+ if (await applyProcessMeshClearPort("off")) this.ctx.logger.info("Mesh clear port is OFF (cluster.meshClearPort)");
87947
+ } catch (err) {
87948
+ this.ctx.logger.warn("could not apply cluster.meshClearPort at boot", { meta: { error: errMsg(err) } });
87949
+ }
87950
+ }
87816
87951
  async onMeshCaReady(ca) {
87817
87952
  const hubLeaf = await issueMeshLeafForNode(ca, "hub");
87818
87953
  await requestProcessMeshTlsEnable({
@@ -87844,7 +87979,7 @@ var SystemConfigAddon = class extends BaseAddon {
87844
87979
  }
87845
87980
  async resolveHubUrl() {
87846
87981
  try {
87847
- const book = await this.ctx.api?.localNetwork?.getAddresses?.({ scheme: "https" });
87982
+ const book = await this.ctx.api?.localNetwork?.getAddresses?.query({ scheme: "https" });
87848
87983
  const url = book?.roles?.publicEndpoint?.resolved ?? book?.roles?.internal?.resolved ?? null;
87849
87984
  if (typeof url === "string" && url.length > 0) return url;
87850
87985
  } catch {}
@@ -87854,7 +87989,7 @@ var SystemConfigAddon = class extends BaseAddon {
87854
87989
  }
87855
87990
  async resolveHubMeshAddress() {
87856
87991
  try {
87857
- const base = (await this.ctx.api?.localNetwork?.getAddresses?.({}))?.roles?.internal?.resolved;
87992
+ const base = (await this.ctx.api?.localNetwork?.getAddresses?.query({}))?.roles?.internal?.resolved;
87858
87993
  if (typeof base === "string" && base.length > 0) return `${new URL(base).hostname}:${DEFAULT_HUB_TCP_PORT}`;
87859
87994
  } catch {}
87860
87995
  const host = (await this.ctx.settings?.getSection("server"))?.["host"];
@@ -87975,6 +88110,20 @@ var SystemConfigAddon = class extends BaseAddon {
87975
88110
  label: "Enforced — refuse"
87976
88111
  }]
87977
88112
  },
88113
+ {
88114
+ type: "select",
88115
+ key: MESH_CLEAR_PORT_FIELD,
88116
+ label: "Clear mesh port (:6000)",
88117
+ description: "On keeps the clear TCP port and UDP discovery next to the TLS port (:6443). Off closes both — refused while any connected agent has not enrolled with a join token (the Cluster page shows TLS / clear per node).",
88118
+ default: "on",
88119
+ options: [{
88120
+ value: "on",
88121
+ label: "On — accept clear agents"
88122
+ }, {
88123
+ value: "off",
88124
+ label: "Off — mutual TLS only"
88125
+ }]
88126
+ },
87978
88127
  {
87979
88128
  type: "info",
87980
88129
  key: "meshAuthStatus",
@@ -88057,7 +88206,8 @@ var SystemConfigAddon = class extends BaseAddon {
88057
88206
  * is REFUSED — never written inline (D787, P5). `meshAuth` is a plain value.
88058
88207
  */
88059
88208
  async writeClusterSection(patch) {
88060
- const { [MESH_AUTH_MODE_FIELD]: mode, ...secretPatch } = patch;
88209
+ const { [MESH_AUTH_MODE_FIELD]: mode, [MESH_CLEAR_PORT_FIELD]: clearPort, ...secretPatch } = patch;
88210
+ if (clearPort !== void 0) await this.writeMeshClearPort(clearPort);
88061
88211
  if (mode !== void 0) {
88062
88212
  if (!isMeshAuthMode(mode)) throw new Error(`system-config: meshAuth must be 'transition' or 'enforced'`);
88063
88213
  await this.writeMeshAuthMode(mode);
@@ -88068,6 +88218,27 @@ var SystemConfigAddon = class extends BaseAddon {
88068
88218
  this.ctx.logger.info("Cluster secret updated");
88069
88219
  await this.clusterSecretKeeper?.reload();
88070
88220
  }
88221
+ /**
88222
+ * `cluster.meshClearPort`: `off` is REFUSED while any online agent still
88223
+ * connects in clear (or the TLS listener is not up); the refusal names them.
88224
+ * Persist, then apply — an `on` save always passes.
88225
+ */
88226
+ async writeMeshClearPort(value) {
88227
+ if (!isMeshClearPortMode(value)) throw new Error(`system-config: meshClearPort must be 'on' or 'off'`);
88228
+ const stored = (await this.ctx.settings.getSection("cluster"))[MESH_CLEAR_PORT_FIELD];
88229
+ if (parseMeshClearPortMode(stored) === value) return;
88230
+ const decision = checkProcessMeshClearPort(value);
88231
+ if (!decision.allowed) {
88232
+ this.ctx.logger.warn("mesh clear port switch-off refused", { meta: {
88233
+ blocking: decision.blocking,
88234
+ error: decision.error
88235
+ } });
88236
+ throw new Error(decision.error);
88237
+ }
88238
+ await this.ctx.settings.setSection("cluster", { [MESH_CLEAR_PORT_FIELD]: value });
88239
+ await applyProcessMeshClearPort(value);
88240
+ this.ctx.logger.info("Mesh clear port switched", { meta: { mode: value } });
88241
+ }
88071
88242
  /** `cluster.meshAuth` (D802), then republish it. */
88072
88243
  async writeMeshAuthMode(mode) {
88073
88244
  await this.ctx.settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: mode });
@@ -88112,13 +88283,28 @@ var SystemConfigAddon = class extends BaseAddon {
88112
88283
  if (orphaned.length > 0) this.ctx.logger.warn("an old cluster secret value could not be deleted from the keychain", { meta: { refs: orphaned } });
88113
88284
  }
88114
88285
  };
88286
+ function parseMintJoinTokenInput(input) {
88287
+ if (input === null || input === void 0 || typeof input !== "object") return {};
88288
+ const out = {};
88289
+ const nodeId = Reflect.get(input, "nodeId");
88290
+ const hubUrl = Reflect.get(input, "hubUrl");
88291
+ const hubMeshAddress = Reflect.get(input, "hubMeshAddress");
88292
+ const ttlSec = Reflect.get(input, "ttlSec");
88293
+ if (typeof nodeId === "string") out.nodeId = nodeId;
88294
+ if (typeof hubUrl === "string") out.hubUrl = hubUrl;
88295
+ if (typeof hubMeshAddress === "string") out.hubMeshAddress = hubMeshAddress;
88296
+ if (typeof ttlSec === "number") out.ttlSec = ttlSec;
88297
+ return out;
88298
+ }
88115
88299
  /** Read `exp` from an unencrypted JWT payload (no verify — already minted here). */
88116
88300
  function readJwtExp(token) {
88117
88301
  const parts = token.split(".");
88118
88302
  if (parts.length < 2 || parts[1] === void 0) return 0;
88119
88303
  const json = Buffer.from(parts[1], "base64url").toString("utf8");
88120
88304
  const payload = JSON.parse(json);
88121
- return typeof payload.exp === "number" ? payload.exp : 0;
88305
+ if (payload === null || typeof payload !== "object") return 0;
88306
+ const exp = Reflect.get(payload, "exp");
88307
+ return typeof exp === "number" ? exp : 0;
88122
88308
  }
88123
88309
  //#endregion
88124
- export { setProcessMeshAuthMode as A, loadMeshCa as C, isMeshAuthMode as D, getProcessMeshAuthStatus as E, parseCidrList as F, readCidrSetting as I, setProcessMeshEnforcer as M, createCidrMatcher as N, parseMeshAuthMode as O, normalizeIp as P, issueMeshLeafFromCsr as S, SYSTEM_CONFIG_CLUSTER_SECTION_ID, SystemConfigAddon, SystemConfigAddon as default, getProcessMeshAuthMode as T, caFingerprintCompact as _, setProcessMeshTlsEnabler as a, generateMeshKeyPair as b, mintMeshJoinToken as c, MeshCrl as d, processMeshCrl as f, caFingerprint as g, requireProcessMeshCa as h, ServiceBroker as i, setProcessMeshAuthStatusSource as j, requestProcessMeshEnforcement as k, processJoinTokenJtiStore as l, getProcessMeshCaState as m, TlsTcpTransporter as n, enrollMeshNode as o, MeshCaKeeper as p, Errors as r, JoinTokenJtiStore as s, systemConfigMeshActions as t, verifyMeshJoinToken as u, createMeshCsrPem as v, MESH_AUTH_MODE_FIELD as w, issueMeshLeafForNode as x, generateMeshCa as y };
88310
+ export { loadMeshCa as A, createCidrMatcher as B, caFingerprintCompact as C, generateMeshKeyPair as D, generateMeshCa as E, parseMeshAuthMode as F, parseCidrList as H, requestProcessMeshEnforcement as I, setProcessMeshAuthMode as L, getProcessMeshAuthMode as M, getProcessMeshAuthStatus as N, issueMeshLeafForNode as O, isMeshAuthMode as P, setProcessMeshAuthStatusSource as R, caFingerprint as S, SYSTEM_CONFIG_CLUSTER_SECTION_ID, SystemConfigAddon, SystemConfigAddon as default, exportMeshPrivateKeyPem as T, readCidrSetting as U, normalizeIp as V, MeshCrl as _, setProcessMeshTlsEnabler as a, getProcessMeshCaState as b, mintMeshJoinToken as c, MESH_CLEAR_PORT_FIELD as d, applyProcessMeshClearPort as f, setProcessMeshClearPortControl as g, getProcessNodeMeshTransport as h, ServiceBroker as i, MESH_AUTH_MODE_FIELD as j, issueMeshLeafFromCsr as k, processJoinTokenJtiStore as l, decideMeshClearPortOff as m, TlsTcpTransporter as n, enrollMeshNode as o, checkProcessMeshClearPort as p, Errors as r, JoinTokenJtiStore as s, systemConfigMeshActions as t, verifyMeshJoinToken as u, processMeshCrl as v, createMeshCsrPem as w, requireProcessMeshCa as x, MeshCaKeeper as y, setProcessMeshEnforcer as z };