@camstack/system 1.2.434 → 1.2.436

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 (103) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  11. package/dist/builtins/calendars/calendars.addon.js +1 -1
  12. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  13. package/dist/builtins/camera-grid/index.js +1 -1
  14. package/dist/builtins/camera-grid/index.mjs +1 -1
  15. package/dist/builtins/composer/composer.addon.js +2 -2
  16. package/dist/builtins/composer/composer.addon.mjs +2 -2
  17. package/dist/builtins/console-logging/index.js +1 -1
  18. package/dist/builtins/console-logging/index.mjs +1 -1
  19. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  20. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  21. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  22. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  23. package/dist/builtins/hub-forwarder/index.js +1 -1
  24. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  25. package/dist/builtins/keychain/index.js +6 -7
  26. package/dist/builtins/keychain/index.mjs +6 -7
  27. package/dist/builtins/keychain/keychain-service.d.ts +1 -2
  28. package/dist/builtins/keychain/system-held-secrets.d.ts +5 -5
  29. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  30. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  31. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  32. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  33. package/dist/builtins/local-network/local-network.addon.d.ts +4 -2
  34. package/dist/builtins/local-network/local-network.addon.js +23 -13
  35. package/dist/builtins/local-network/local-network.addon.mjs +23 -13
  36. package/dist/builtins/loki-logging/index.js +1 -1
  37. package/dist/builtins/loki-logging/index.mjs +1 -1
  38. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  39. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  40. package/dist/builtins/platform-probe/index.js +1 -1
  41. package/dist/builtins/platform-probe/index.mjs +1 -1
  42. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  43. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  44. package/dist/builtins/snapshot/index.js +1 -1
  45. package/dist/builtins/snapshot/index.mjs +1 -1
  46. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  47. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  48. package/dist/builtins/sqlite-storage/index.js +1 -1
  49. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  50. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +3 -3
  51. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +3 -3
  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 +3 -24
  55. package/dist/builtins/system-config/system-config.addon.js +105 -1410
  56. package/dist/builtins/system-config/system-config.addon.mjs +108 -1402
  57. package/dist/builtins/winston-logging/index.js +1 -1
  58. package/dist/builtins/winston-logging/index.mjs +1 -1
  59. package/dist/{composition-sources-B7R1oQnY.js → composition-sources-BAOlCu4j.js} +1 -1
  60. package/dist/{composition-sources-CqrDyHM1.mjs → composition-sources-gN_BWUKp.mjs} +1 -1
  61. package/dist/constants-BFpowodq.mjs +20 -0
  62. package/dist/constants-GENKqk9k.js +66 -0
  63. package/dist/{device-ca-CDjGQQPd.mjs → device-ca-BNM5ai4t.mjs} +1 -1
  64. package/dist/{device-ca-D21ZP00q.js → device-ca-CuWuRW9N.js} +1 -1
  65. package/dist/{dist-CCH3XPqO.js → dist-DVe0Nsp8.js} +48 -16
  66. package/dist/{dist-CYcBqnXk.mjs → dist-DjD2M79P.mjs} +43 -5
  67. package/dist/file-host-worker.js +1 -1
  68. package/dist/file-host-worker.mjs +1 -1
  69. package/dist/index.js +132 -795
  70. package/dist/index.mjs +133 -753
  71. package/dist/kernel/index.d.ts +7 -21
  72. package/dist/kernel/moleculer/agent-ui-unlock.d.ts +7 -32
  73. package/dist/kernel/moleculer/broker-factory.d.ts +3 -9
  74. package/dist/kernel/moleculer/broker-transport-swap.d.ts +17 -0
  75. package/dist/kernel/moleculer/hub-service.d.ts +6 -16
  76. package/dist/kernel/moleculer/mesh-access-middleware.d.ts +7 -17
  77. package/dist/kernel/moleculer/mesh-identity.d.ts +57 -0
  78. package/dist/kernel/moleculer/mesh-mtls/constants.d.ts +1 -4
  79. package/dist/kernel/moleculer/mesh-mtls/index.d.ts +1 -4
  80. package/dist/kernel/moleculer/mesh-mtls/mesh-ca.d.ts +1 -1
  81. package/dist/kernel/moleculer/mesh-mtls/mesh-renewal.d.ts +1 -1
  82. package/dist/kernel/moleculer/node-registry.d.ts +0 -6
  83. package/dist/kernel/moleculer/register-node-client.d.ts +9 -7
  84. package/dist/{restore-archive-5ml7uYMf.mjs → restore-archive-BxmP1ChO.mjs} +1 -1
  85. package/dist/{restore-archive-Do0QwAik.js → restore-archive-D4h904rb.js} +1 -1
  86. package/dist/{retired-settings-keys-CyDdyjLP.mjs → retired-settings-keys-CQaknvx1.mjs} +1 -1
  87. package/dist/{retired-settings-keys-CEiCaI_j.js → retired-settings-keys-CYP5IZJ5.js} +1 -1
  88. package/dist/{wal-maintenance-LZYd8iT-.js → wal-maintenance-Br60onAQ.js} +0 -0
  89. package/dist/{wal-maintenance-DgLLPK14.mjs → wal-maintenance-CaDsrJM_.mjs} +0 -0
  90. package/package.json +1 -1
  91. package/dist/builtins/system-config/cluster-secret-keeper.d.ts +0 -63
  92. package/dist/constants-CrMPWOug.mjs +0 -102
  93. package/dist/constants-CuOfMdYe.js +0 -221
  94. package/dist/kernel/moleculer/agent-mesh-authenticator.d.ts +0 -91
  95. package/dist/kernel/moleculer/cluster-secret.d.ts +0 -46
  96. package/dist/kernel/moleculer/mesh-auth-mode.d.ts +0 -58
  97. package/dist/kernel/moleculer/mesh-authenticator.d.ts +0 -77
  98. package/dist/kernel/moleculer/mesh-auto-enforce.d.ts +0 -46
  99. package/dist/kernel/moleculer/mesh-join-client.d.ts +0 -73
  100. package/dist/kernel/moleculer/mesh-join-hold.d.ts +0 -85
  101. package/dist/kernel/moleculer/mesh-join-proof.d.ts +0 -39
  102. package/dist/kernel/moleculer/mesh-mtls/mesh-enroll.d.ts +0 -34
  103. package/dist/kernel/moleculer/mesh-mtls/mesh-join-token.d.ts +0 -56
@@ -1,12 +1,12 @@
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 { Jr as isSecretMarker, Lr as customAction, Rr as defineCustomActions, Ur as hydrateSchema, Xr as makeSecretMarker, br as BaseAddon, di as transformSecretWrite, qr as isSecretClear, si as secretTreeFromSchema, yr as errMsg } from "../../dist-CYcBqnXk.mjs";
2
+ import { Rr as customAction, Wr as hydrateSchema, br as errMsg, xr as BaseAddon, zr as defineCustomActions } from "../../dist-DjD2M79P.mjs";
3
3
  import { t as require_safe_buffer } from "../../safe-buffer-B1OjL1I9.mjs";
4
4
  import { a as KeyUsagesExtension, c as Pkcs10CertificateRequestGenerator, d as X509Certificate$1, f as X509CertificateGenerator, g as backdatedNotBefore, 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-CDvj67p1.mjs";
5
5
  import { A as AgentHttpsNames, M as setProcessAgentHttpsNames, O as AGENT_HTTPS_NAMES_COLLECTION, d as isPrivateAddress, j as getProcessAgentHttpsNames, k as AGENT_HTTPS_NAMES_COLUMNS, u as isLocalOnlyHostname } from "../../agent-https-B8lKYVtU.mjs";
6
- import { a as MESH_CA_KEY_REF, b as setProcessClusterSecretGate, c as MESH_JOIN_TOKEN_MAX_TTL_SEC, d as CLUSTER_SECRET_REF, i as MESH_CA_FIELD, l as SERVER_AUTH_OID, n as DEFAULT_HUB_TLS_TCP_PORT, o as MESH_CA_SETTINGS_KEY, r as MESH_CA_COMMON_NAME, s as MESH_CA_VALIDITY_DAYS, t as CLIENT_AUTH_OID, v as hashClusterSecret } from "../../constants-CrMPWOug.mjs";
6
+ import { a as MESH_CA_KEY_REF, c as SERVER_AUTH_OID, i as MESH_CA_FIELD, n as DEFAULT_HUB_TLS_TCP_PORT, o as MESH_CA_SETTINGS_KEY, r as MESH_CA_COMMON_NAME, s as MESH_CA_VALIDITY_DAYS, t as CLIENT_AUTH_OID } from "../../constants-BFpowodq.mjs";
7
7
  import { t as require_src } from "../../src-hG4grMU4.mjs";
8
8
  import { z } from "zod";
9
- import { X509Certificate, createHash, createHmac, createPrivateKey, randomBytes, randomInt, sign, timingSafeEqual, verify, webcrypto } from "node:crypto";
9
+ import { X509Certificate, createHash, createPrivateKey, randomBytes, randomInt, sign, timingSafeEqual, verify, webcrypto } from "node:crypto";
10
10
  import { BlockList, isIP } from "node:net";
11
11
  import { lstat, readdir, readlink, realpath } from "node:fs/promises";
12
12
  import { posix, win32 } from "node:path";
@@ -97,73 +97,27 @@ function createCidrMatcher(entries) {
97
97
  };
98
98
  }
99
99
  //#endregion
100
- //#region src/kernel/moleculer/mesh-auth-mode.ts
101
- /** The field key inside the `cluster` section. */
102
- var MESH_AUTH_MODE_FIELD = "meshAuth";
103
- function isMeshAuthMode(value) {
104
- return value === "transition" || value === "enforced";
105
- }
106
- /** A stored value that is not a mode reads as `transition`: never refuse on a typo. */
107
- function parseMeshAuthMode(value) {
108
- return isMeshAuthMode(value) ? value : "transition";
109
- }
110
- var MODE_SLOT = Symbol.for("camstack.cluster.mesh-auth-mode");
111
- var ENFORCER_SLOT = Symbol.for("camstack.cluster.mesh-auth-enforcer");
112
- var STATUS_SLOT = Symbol.for("camstack.cluster.mesh-auth-status");
113
- /** Published by `system-config` whenever the stored mode is (re)read. */
114
- function setProcessMeshAuthMode(mode) {
115
- Reflect.set(globalThis, MODE_SLOT, mode);
116
- }
117
- /**
118
- * The hub's mode. Until `system-config` has read it: `transition` — the safe
119
- * side for an upgrading cluster (nothing that worked before is refused), and
120
- * exactly what an unread setting means.
121
- */
122
- function getProcessMeshAuthMode() {
123
- return parseMeshAuthMode(Reflect.get(globalThis, MODE_SLOT));
124
- }
125
- function setProcessMeshEnforcer(enforcer) {
126
- if (enforcer === null) Reflect.deleteProperty(globalThis, ENFORCER_SLOT);
127
- else Reflect.set(globalThis, ENFORCER_SLOT, enforcer);
128
- }
129
- /** Ask the owner of the setting to switch to `enforced`. `false` when nobody can. */
130
- async function requestProcessMeshEnforcement(reason) {
131
- const enforcer = Reflect.get(globalThis, ENFORCER_SLOT);
132
- if (typeof enforcer !== "function") return false;
133
- await enforcer(reason);
134
- return true;
135
- }
136
- /** Published by the hub's broker; read by System → Cluster. */
137
- function setProcessMeshAuthStatusSource(source) {
138
- if (source === null) Reflect.deleteProperty(globalThis, STATUS_SLOT);
139
- else Reflect.set(globalThis, STATUS_SLOT, source);
140
- }
141
- function getProcessMeshAuthStatus() {
142
- const source = Reflect.get(globalThis, STATUS_SLOT);
143
- return typeof source === "function" ? source() : null;
144
- }
145
- //#endregion
146
100
  //#region src/kernel/moleculer/agent-ui-unlock.ts
147
101
  /**
148
- * Agent UI remote unlock tickets (D821).
102
+ * Agent UI remote unlock tickets (D821, re-keyed by D857).
149
103
  *
150
- * A hub admin mints a short-lived HMAC ticket bound to one agent `nodeId`.
151
- * The agent redeems it once for a tab session Bearer — the cluster secret
152
- * stays a mesh / pairing credential and is not pasted into the browser for
153
- * daily unlock.
104
+ * A hub admin mints a short-lived ticket bound to one agent `nodeId`, signed
105
+ * with the MESH CA key (ES256 JWS). The agent verifies it
106
+ * with the CA certificate it pinned at pairing and redeems it once for a tab
107
+ * session Bearer. There is no shared secret: a ticket is worth exactly as much
108
+ * as the CA key that signed it, and the agent trusts exactly the CA its
109
+ * operator confirmed during pairing.
154
110
  *
155
- * K = SHA-256(cluster secret) — same as mesh join (D802)
156
- * ticket = base64url(JSON payload) + '.' + base64url(HMAC-SHA256(K, DOMAIN|payloadB64))
157
- * payload = { v: 1, nodeId, exp (ms), jti }
158
- */
159
- var AGENT_UI_UNLOCK_DOMAIN = "camstack-agent-ui-unlock/v1";
160
- var AGENT_UI_UNLOCK_VERSION = 1;
161
- /**
162
- * D857 (transition release): a ticket signed by the MESH CA key (ES256 JWS) is accepted by an agent
163
- * IN ADDITION to the HMAC ticket. The removal release mints only this form.
111
+ * ticket = b64url(header) '.' b64url(payload) '.' b64url(ES256 signature)
112
+ * header = { alg: 'ES256', typ: 'camstack-agent-ui-unlock+jwt' }
113
+ * payload = { v: 1, use: 'agent-ui-unlock', nodeId, iat (ms), exp (ms), jti }
114
+ *
115
+ * The `typ` and `use` pins keep a join token or a pairing proof (also signed
116
+ * by the CA key) from ever being accepted here, and the reverse.
164
117
  */
165
118
  var AGENT_UI_UNLOCK_TYP = "camstack-agent-ui-unlock+jwt";
166
119
  var AGENT_UI_UNLOCK_USE = "agent-ui-unlock";
120
+ var AGENT_UI_UNLOCK_VERSION = 1;
167
121
  /** Ticket lifetime — long enough for a redirect hop, short enough to steal poorly. */
168
122
  var AGENT_UI_UNLOCK_TTL_MS = 5 * 6e4;
169
123
  /**
@@ -193,18 +147,28 @@ var UnlockTicketJtiStore = class {
193
147
  for (const [jti, exp] of this.spent) if (exp <= nowMs) this.spent.delete(jti);
194
148
  }
195
149
  };
150
+ function b64urlJson$1(value) {
151
+ return Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
152
+ }
196
153
  function mintAgentUiUnlockTicket(input) {
197
154
  const nowMs = input.nowMs ?? Date.now();
198
155
  const ttl = Math.min(Math.max(1e3, input.ttlMs ?? 3e5), AGENT_UI_UNLOCK_TTL_MS);
199
156
  const payload = {
200
157
  v: 1,
158
+ use: AGENT_UI_UNLOCK_USE,
201
159
  nodeId: input.nodeId,
202
160
  iat: nowMs,
203
161
  exp: nowMs + ttl,
204
162
  jti: input.jti ?? randomBytes(16).toString("base64url")
205
163
  };
206
- const payloadB64 = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
207
- return `${payloadB64}.${sign$1(input.secretHash, payloadB64)}`;
164
+ const signingInput = `${b64urlJson$1({
165
+ alg: "ES256",
166
+ typ: AGENT_UI_UNLOCK_TYP
167
+ })}.${b64urlJson$1(payload)}`;
168
+ return `${signingInput}.${sign("sha256", Buffer.from(signingInput, "utf8"), {
169
+ key: createPrivateKey(input.caKeyPem),
170
+ dsaEncoding: "ieee-p1363"
171
+ }).toString("base64url")}`;
208
172
  }
209
173
  /** A ticket is issued now-ish, lives at most the TTL, and not before `notBeforeMs`. */
210
174
  function issuedSanely(payload, nowMs, notBeforeMs) {
@@ -213,22 +177,36 @@ function issuedSanely(payload, nowMs, notBeforeMs) {
213
177
  return notBeforeMs === void 0 || payload.iat >= notBeforeMs;
214
178
  }
215
179
  /**
216
- * Verify a ticket under `secretHash`. Returns the payload, or `null` when the
217
- * signature, shape, expiry or nodeId fails (no reason string — refuse quietly).
180
+ * Verify a ticket against the mesh CA certificate the agent pinned. Returns the
181
+ * payload, or `null` when the signature, header, shape, expiry or nodeId fails
182
+ * (no reason string — refuse quietly).
218
183
  */
219
- function verifyAgentUiUnlockTicket(secretHash, ticket, options) {
184
+ function verifyAgentUiUnlockTicket(caCertPem, ticket, options) {
220
185
  const nowMs = options.nowMs ?? Date.now();
221
- const dot = ticket.indexOf(".");
222
- if (dot <= 0 || dot === ticket.length - 1) return null;
223
- const payloadB64 = ticket.slice(0, dot);
224
- const sig = ticket.slice(dot + 1);
225
- if (!sigsEqual(sign$1(secretHash, payloadB64), sig)) return null;
186
+ const parts = ticket.split(".");
187
+ if (parts.length !== 3) return null;
188
+ const [headerB64, payloadB64, signatureB64] = parts;
189
+ if (headerB64 === void 0 || payloadB64 === void 0 || signatureB64 === void 0) return null;
190
+ let header;
226
191
  let parsed;
227
192
  try {
193
+ header = JSON.parse(Buffer.from(headerB64, "base64url").toString("utf8"));
228
194
  parsed = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
229
195
  } catch {
230
196
  return null;
231
197
  }
198
+ if (typeof header !== "object" || header === null) return null;
199
+ if (Reflect.get(header, "alg") !== "ES256" || Reflect.get(header, "typ") !== "camstack-agent-ui-unlock+jwt") return null;
200
+ let valid = false;
201
+ try {
202
+ valid = verify("sha256", Buffer.from(`${headerB64}.${payloadB64}`, "utf8"), {
203
+ key: new X509Certificate(caCertPem).publicKey,
204
+ dsaEncoding: "ieee-p1363"
205
+ }, Buffer.from(signatureB64, "base64url"));
206
+ } catch {
207
+ return null;
208
+ }
209
+ if (!valid) return null;
232
210
  const payload = parsePayload(parsed);
233
211
  if (payload === null) return null;
234
212
  if (payload.exp <= nowMs) return null;
@@ -287,96 +265,7 @@ function parseAgentDashboardOrigins(stored) {
287
265
  }
288
266
  return out;
289
267
  }
290
- function sign$1(secretHash, payloadB64) {
291
- return createHmac("sha256", Buffer.from(secretHash, "utf8")).update(`${AGENT_UI_UNLOCK_DOMAIN}|${payloadB64}`, "utf8").digest("base64url");
292
- }
293
- function sigsEqual(expected, presented) {
294
- const a = Buffer.from(expected, "utf8");
295
- const b = Buffer.from(presented, "utf8");
296
- if (a.length !== b.length) return false;
297
- return timingSafeEqual(a, b);
298
- }
299
268
  function parsePayload(value) {
300
- if (typeof value !== "object" || value === null) return null;
301
- const rec = value;
302
- if (rec["v"] !== 1) return null;
303
- const nodeId = rec["nodeId"];
304
- const exp = rec["exp"];
305
- const jti = rec["jti"];
306
- if (typeof nodeId !== "string" || nodeId.length === 0) return null;
307
- if (typeof exp !== "number" || !Number.isFinite(exp)) return null;
308
- if (typeof jti !== "string" || jti.length === 0) return null;
309
- const iat = rec["iat"];
310
- return {
311
- v: 1,
312
- nodeId,
313
- iat: typeof iat === "number" && Number.isFinite(iat) ? iat : exp - AGENT_UI_UNLOCK_TTL_MS,
314
- exp,
315
- jti
316
- };
317
- }
318
- function b64urlJsonEs256(value) {
319
- return Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
320
- }
321
- function mintAgentUiUnlockTicketEs256(input) {
322
- const nowMs = input.nowMs ?? Date.now();
323
- const ttl = Math.min(Math.max(1e3, input.ttlMs ?? 3e5), AGENT_UI_UNLOCK_TTL_MS);
324
- const payload = {
325
- v: 1,
326
- use: AGENT_UI_UNLOCK_USE,
327
- nodeId: input.nodeId,
328
- iat: nowMs,
329
- exp: nowMs + ttl,
330
- jti: input.jti ?? randomBytes(16).toString("base64url")
331
- };
332
- const signingInput = `${b64urlJsonEs256({
333
- alg: "ES256",
334
- typ: AGENT_UI_UNLOCK_TYP
335
- })}.${b64urlJsonEs256(payload)}`;
336
- return `${signingInput}.${sign("sha256", Buffer.from(signingInput, "utf8"), {
337
- key: createPrivateKey(input.caKeyPem),
338
- dsaEncoding: "ieee-p1363"
339
- }).toString("base64url")}`;
340
- }
341
- /**
342
- * Verify a ticket against the mesh CA certificate the agent pinned. Returns the
343
- * payload, or `null` when the signature, header, shape, expiry or nodeId fails
344
- * (no reason string — refuse quietly).
345
- */
346
- function verifyAgentUiUnlockTicketEs256(caCertPem, ticket, options) {
347
- const nowMs = options.nowMs ?? Date.now();
348
- const parts = ticket.split(".");
349
- if (parts.length !== 3) return null;
350
- const [headerB64, payloadB64, signatureB64] = parts;
351
- if (headerB64 === void 0 || payloadB64 === void 0 || signatureB64 === void 0) return null;
352
- let header;
353
- let parsed;
354
- try {
355
- header = JSON.parse(Buffer.from(headerB64, "base64url").toString("utf8"));
356
- parsed = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
357
- } catch {
358
- return null;
359
- }
360
- if (typeof header !== "object" || header === null) return null;
361
- if (Reflect.get(header, "alg") !== "ES256" || Reflect.get(header, "typ") !== "camstack-agent-ui-unlock+jwt") return null;
362
- let valid = false;
363
- try {
364
- valid = verify("sha256", Buffer.from(`${headerB64}.${payloadB64}`, "utf8"), {
365
- key: new X509Certificate(caCertPem).publicKey,
366
- dsaEncoding: "ieee-p1363"
367
- }, Buffer.from(signatureB64, "base64url"));
368
- } catch {
369
- return null;
370
- }
371
- if (!valid) return null;
372
- const payload = parsePayloadEs256(parsed);
373
- if (payload === null) return null;
374
- if (payload.exp <= nowMs) return null;
375
- if (payload.nodeId !== options.expectedNodeId) return null;
376
- if (!issuedSanely(payload, nowMs, options.notBeforeMs)) return null;
377
- return payload;
378
- }
379
- function parsePayloadEs256(value) {
380
269
  if (typeof value !== "object" || value === null) return null;
381
270
  const rec = value;
382
271
  if (rec["v"] !== 1) return null;
@@ -384,10 +273,10 @@ function parsePayloadEs256(value) {
384
273
  const nodeId = rec["nodeId"];
385
274
  const exp = rec["exp"];
386
275
  const jti = rec["jti"];
276
+ const iat = rec["iat"];
387
277
  if (typeof nodeId !== "string" || nodeId.length === 0) return null;
388
278
  if (typeof exp !== "number" || !Number.isFinite(exp)) return null;
389
279
  if (typeof jti !== "string" || jti.length === 0) return null;
390
- const iat = rec["iat"];
391
280
  if (typeof iat !== "number" || !Number.isFinite(iat)) return null;
392
281
  return {
393
282
  v: 1,
@@ -403,7 +292,7 @@ function parsePayloadEs256(value) {
403
292
  * Hub mesh CA — EC P-256, key in the keychain, cert in `cluster.meshCa`.
404
293
  *
405
294
  * Distinct from the HTTPS local CA (`tls/cert-issue.ts`, RSA): the mesh CA
406
- * signs node leaves (CN = nodeID) and join-token JWS (ES256). Issuance uses
295
+ * signs node leaves (CN = nodeID) and unlock-ticket JWS (ES256). Issuance uses
407
296
  * `@peculiar/x509` over WebCrypto — same library the HTTPS path already uses
408
297
  * (installed `1.14.3`; registry latest at adoption time was `2.1.0` — stay on
409
298
  * the pinned range already shipping).
@@ -444,7 +333,7 @@ async function exportKeyPem(key) {
444
333
  function caFingerprint(certPem) {
445
334
  return new X509Certificate(certPem).fingerprint256;
446
335
  }
447
- /** Compact fingerprint (lowercase hex, no colons) for join-token payloads. */
336
+ /** Compact fingerprint (lowercase hex, no colons). */
448
337
  function caFingerprintCompact(certPem) {
449
338
  return createHash("sha256").update(new X509Certificate(certPem).raw).digest("hex");
450
339
  }
@@ -1210,7 +1099,7 @@ function defaultSetTimer(fn, ms) {
1210
1099
  function defaultSleep(ms) {
1211
1100
  return new Promise((resolve) => setTimeout(resolve, ms));
1212
1101
  }
1213
- function message$1(error) {
1102
+ function message(error) {
1214
1103
  return error instanceof Error ? error.message : String(error);
1215
1104
  }
1216
1105
  async function renewMeshCertificate(deps) {
@@ -1276,7 +1165,7 @@ async function runRenewal(deps, inFlight) {
1276
1165
  csrPem = (await deps.client.prepareMeshRenewal()).csrPem;
1277
1166
  log.info("Mesh renewal: the node generated a new key and sent its CSR", { tags });
1278
1167
  } catch (error) {
1279
- return fail("prepare", message$1(error), true);
1168
+ return fail("prepare", message(error), true);
1280
1169
  }
1281
1170
  let issued;
1282
1171
  try {
@@ -1290,7 +1179,7 @@ async function runRenewal(deps, inFlight) {
1290
1179
  }
1291
1180
  });
1292
1181
  } catch (error) {
1293
- return fail("issue", message$1(error), true);
1182
+ return fail("issue", message(error), true);
1294
1183
  }
1295
1184
  const newLeaf = describeMeshLeaf(issued.certPem);
1296
1185
  deps.registry.beginRenewal(newLeaf.fingerprint);
@@ -1330,7 +1219,7 @@ async function runRenewal(deps, inFlight) {
1330
1219
  deadlineTimer.cancel();
1331
1220
  await deps.client.rollbackMeshLeaf().catch(() => void 0);
1332
1221
  await deps.registry.abandon(newLeaf.fingerprint);
1333
- return fail("install", message$1(error), true);
1222
+ return fail("install", message(error), true);
1334
1223
  }
1335
1224
  const deadline = now() + timeoutMs;
1336
1225
  const registered = await registeredPromise;
@@ -1359,7 +1248,7 @@ async function runRenewal(deps, inFlight) {
1359
1248
  }
1360
1249
  confirmError = "the node did not confirm the new leaf";
1361
1250
  } catch (error) {
1362
- confirmError = message$1(error);
1251
+ confirmError = message(error);
1363
1252
  log.warn("Mesh renewal: confirm not accepted yet", {
1364
1253
  tags,
1365
1254
  meta: { error: confirmError }
@@ -1393,7 +1282,7 @@ async function runRenewal(deps, inFlight) {
1393
1282
  }
1394
1283
  /**
1395
1284
  * Revoke every live mesh leaf of `nodeId` and cut its connection. The node cannot
1396
- * reconnect: it must re-join with a new join token. The node is NOT forgotten
1285
+ * reconnect: it must be paired again. The node is NOT forgotten
1397
1286
  * (that stays `clusterNodes.forgetNode`).
1398
1287
  */
1399
1288
  async function revokeMeshCertificate(deps) {
@@ -1420,7 +1309,7 @@ async function revokeMeshCertificate(deps) {
1420
1309
  nodeId,
1421
1310
  revokedSerials: serials,
1422
1311
  disconnected,
1423
- next: "The node can no longer connect. Re-join it with a new join token (System -> Cluster -> Add agent); to remove it from the cluster use Forget node."
1312
+ next: "The node can no longer connect. Pair it again from the hub (System -> Cluster -> Agents); to remove it from the cluster use Forget node."
1424
1313
  };
1425
1314
  }
1426
1315
  //#endregion
@@ -1481,872 +1370,6 @@ function emitProcessNodeRegistered(nodeId, addonIds) {
1481
1370
  } catch {}
1482
1371
  }
1483
1372
  //#endregion
1484
- //#region ../../node_modules/jose/dist/webapi/lib/buffer_utils.js
1485
- var encoder = new TextEncoder(), decoder = new TextDecoder(), strictDecoder = new TextDecoder("utf-8", { fatal: !0 });
1486
- function concat(...buffers) {
1487
- const size = buffers.reduce((acc, { length }) => acc + length, 0), buf = new Uint8Array(size);
1488
- let i = 0;
1489
- for (const buffer of buffers) buf.set(buffer, i), i += buffer.length;
1490
- return buf;
1491
- }
1492
- var NON_ASCII = /[^\x00-\x7f]/;
1493
- function encode$1(string) {
1494
- if (typeof string == "string" && string.length >= 128) {
1495
- if (NON_ASCII.test(string)) throw new TypeError("non-ASCII string encountered in encode()");
1496
- return encoder.encode(string);
1497
- }
1498
- const bytes = new Uint8Array(string.length);
1499
- for (let i = 0; i < string.length; i++) {
1500
- const code = string.charCodeAt(i);
1501
- if (code > 127) throw new TypeError("non-ASCII string encountered in encode()");
1502
- bytes[i] = code;
1503
- }
1504
- return bytes;
1505
- }
1506
- function encodeBase64(input, url = !1) {
1507
- if (Uint8Array.prototype.toBase64) return input.toBase64({
1508
- alphabet: url ? "base64url" : "base64",
1509
- omitPadding: url
1510
- });
1511
- const CHUNK_SIZE = 32768, arr = [];
1512
- for (let i = 0; i < input.length; i += CHUNK_SIZE) arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
1513
- const encoded = btoa(arr.join(""));
1514
- return url ? encoded.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") : encoded;
1515
- }
1516
- function decodeBase64(encoded, url = !1) {
1517
- if (Uint8Array.fromBase64) return Uint8Array.fromBase64(encoded, { alphabet: url ? "base64url" : "base64" });
1518
- if (url) {
1519
- if (encoded.includes("+") || encoded.includes("/")) throw new TypeError("Invalid base64url");
1520
- encoded = encoded.replace(/-/g, "+").replace(/_/g, "/");
1521
- }
1522
- const binary = atob(encoded), bytes = new Uint8Array(binary.length);
1523
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
1524
- return bytes;
1525
- }
1526
- //#endregion
1527
- //#region ../../node_modules/jose/dist/webapi/util/errors.js
1528
- var JOSEError = class extends Error {
1529
- static code = "ERR_JOSE_GENERIC";
1530
- code = "ERR_JOSE_GENERIC";
1531
- constructor(message, options) {
1532
- super(message, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);
1533
- }
1534
- };
1535
- var JWTClaimValidationFailed = class extends JOSEError {
1536
- static code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
1537
- code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
1538
- claim;
1539
- reason;
1540
- payload;
1541
- constructor(message, payload, claim = "unspecified", reason = "unspecified") {
1542
- super(message, { cause: {
1543
- claim,
1544
- reason,
1545
- payload
1546
- } }), this.claim = claim, this.reason = reason, this.payload = payload;
1547
- }
1548
- };
1549
- var JWTExpired = class extends JOSEError {
1550
- static code = "ERR_JWT_EXPIRED";
1551
- code = "ERR_JWT_EXPIRED";
1552
- claim;
1553
- reason;
1554
- payload;
1555
- constructor(message, payload, claim = "unspecified", reason = "unspecified") {
1556
- super(message, { cause: {
1557
- claim,
1558
- reason,
1559
- payload
1560
- } }), this.claim = claim, this.reason = reason, this.payload = payload;
1561
- }
1562
- };
1563
- var JOSEAlgNotAllowed = class extends JOSEError {
1564
- static code = "ERR_JOSE_ALG_NOT_ALLOWED";
1565
- code = "ERR_JOSE_ALG_NOT_ALLOWED";
1566
- };
1567
- var JOSENotSupported = class extends JOSEError {
1568
- static code = "ERR_JOSE_NOT_SUPPORTED";
1569
- code = "ERR_JOSE_NOT_SUPPORTED";
1570
- };
1571
- var JWSInvalid = class extends JOSEError {
1572
- static code = "ERR_JWS_INVALID";
1573
- code = "ERR_JWS_INVALID";
1574
- };
1575
- var JWTInvalid = class extends JOSEError {
1576
- static code = "ERR_JWT_INVALID";
1577
- code = "ERR_JWT_INVALID";
1578
- };
1579
- var JWSSignatureVerificationFailed = class extends JOSEError {
1580
- static code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
1581
- code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
1582
- constructor(message = "signature verification failed", options) {
1583
- super(message, options);
1584
- }
1585
- };
1586
- //#endregion
1587
- //#region ../../node_modules/jose/dist/webapi/util/base64url.js
1588
- var invalid = "The input to be decoded is not correctly encoded.";
1589
- function decode(input) {
1590
- try {
1591
- return decodeBase64(typeof input == "string" ? input : decoder.decode(input), !0);
1592
- } catch (cause) {
1593
- throw new TypeError(invalid, { cause });
1594
- }
1595
- }
1596
- function encode(input) {
1597
- return encodeBase64(typeof input == "string" ? encoder.encode(input) : input, !0);
1598
- }
1599
- //#endregion
1600
- //#region ../../node_modules/jose/dist/webapi/lib/validate.js
1601
- function isObject$1(input) {
1602
- if (typeof input != "object" || input === null || Object.prototype.toString.call(input) !== "[object Object]") return !1;
1603
- const prototype = Object.getPrototypeOf(input);
1604
- return prototype === null || Object.getPrototypeOf(prototype) === null;
1605
- }
1606
- function isDisjoint(...headers) {
1607
- const parameters = /* @__PURE__ */ new Set();
1608
- for (const header of headers) if (header) for (const parameter of Object.keys(header)) {
1609
- if (parameters.has(parameter)) return !1;
1610
- parameters.add(parameter);
1611
- }
1612
- return !0;
1613
- }
1614
- function assertNotSet(value, name) {
1615
- if (value !== void 0) throw new TypeError(`${name} can only be called once`);
1616
- }
1617
- function decodeBase64url(value, label, ErrorClass) {
1618
- try {
1619
- return decode(value);
1620
- } catch {
1621
- throw new ErrorClass(`Failed to base64url decode the ${label}`);
1622
- }
1623
- }
1624
- function encodeBase64url(value, label, ErrorClass) {
1625
- try {
1626
- return encode$1(value);
1627
- } catch {
1628
- throw new ErrorClass(`The ${label} is not a valid base64url string`);
1629
- }
1630
- }
1631
- function parseJoseHeader(b64, ErrorClass, message) {
1632
- let parsed;
1633
- try {
1634
- parsed = JSON.parse(strictDecoder.decode(decode(b64)));
1635
- } catch {
1636
- throw new ErrorClass(message);
1637
- }
1638
- if (!isObject$1(parsed)) throw new ErrorClass(message);
1639
- return parsed;
1640
- }
1641
- var JWS_RECOGNIZED = {
1642
- __proto__: null,
1643
- b64: !0
1644
- };
1645
- function validateAlgorithms(option, algorithms) {
1646
- if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s != "string"))) throw new TypeError(`"${option}" option must be an array of strings`);
1647
- return algorithms === void 0 ? void 0 : new Set(algorithms);
1648
- }
1649
- function validateCritDuplicates(Err, protectedHeader) {
1650
- const { crit } = protectedHeader ?? {};
1651
- if (Array.isArray(crit) && new Set(crit).size !== crit.length) throw new Err("\"crit\" (Critical) Header Parameter MUST NOT contain duplicate values");
1652
- }
1653
- function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
1654
- if (joseHeader.crit !== void 0 && protectedHeader?.crit === void 0) throw new Err("\"crit\" (Critical) Header Parameter MUST be integrity protected");
1655
- if (!protectedHeader || protectedHeader.crit === void 0) return [];
1656
- if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input != "string" || input.length === 0)) throw new Err("\"crit\" (Critical) Header Parameter MUST be an array of non-empty strings when present");
1657
- const recognized = recognizedOption === void 0 ? recognizedDefault : {
1658
- __proto__: null,
1659
- ...recognizedOption,
1660
- ...recognizedDefault
1661
- };
1662
- for (const parameter of protectedHeader.crit) {
1663
- if (!(parameter in recognized)) throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
1664
- if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === void 0) throw new Err(`Extension Header Parameter "${parameter}" is missing`);
1665
- if (recognized[parameter] && (!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === void 0)) throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
1666
- }
1667
- return protectedHeader.crit;
1668
- }
1669
- function validateB64(protectedHeader, extensions) {
1670
- if (extensions.includes("b64")) {
1671
- const b64 = protectedHeader.b64;
1672
- if (typeof b64 != "boolean") throw new JWSInvalid("The \"b64\" (base64url-encode payload) Header Parameter must be a boolean");
1673
- return b64;
1674
- }
1675
- return !0;
1676
- }
1677
- function serializeJoseHeader(Err, header) {
1678
- let serialized, parsed;
1679
- try {
1680
- serialized = JSON.stringify(header), parsed = JSON.parse(serialized);
1681
- } catch (cause) {
1682
- throw new Err("JOSE Header is not valid JSON", { cause });
1683
- }
1684
- if (!isObject$1(parsed)) throw new Err("JOSE Header is not a JSON object");
1685
- return [parsed, serialized];
1686
- }
1687
- //#endregion
1688
- //#region ../../node_modules/jose/dist/webapi/lib/key.js
1689
- var tag = (key) => key[Symbol.toStringTag], jwkMatchesOp = (entry, key, usage) => {
1690
- const { alg } = entry;
1691
- if (key.use !== void 0) {
1692
- const expected = usage === "sign" || usage === "verify" ? "sig" : "enc";
1693
- if (key.use !== expected) throw new TypeError(`Invalid key for this operation, its "use" must be "${expected}" when present`);
1694
- }
1695
- if (key.alg !== void 0 && key.alg !== alg) throw new TypeError(`Invalid key for this operation, its "alg" must be "${alg}" when present`);
1696
- if (Array.isArray(key.key_ops)) {
1697
- const expectedKeyOp = usage === "encrypt" || usage === "decrypt" ? entry.ops?.[usage === "encrypt" ? 0 : 1] : usage;
1698
- if (expectedKeyOp && !key.key_ops.includes(expectedKeyOp)) throw new TypeError(`Invalid key for this operation, its "key_ops" must include "${expectedKeyOp}" when present`);
1699
- }
1700
- };
1701
- async function prepareKey(entry, key, usage) {
1702
- const { alg, secret } = entry, privateKey = usage === "decrypt" || usage === "sign";
1703
- if (secret && key instanceof Uint8Array) return key;
1704
- let normalized, keyObject;
1705
- if (isObject$1(key)) {
1706
- if (normalized = normalizeJwk(key), typeof normalized.kty != "string") throw invalidKeyType(alg, key, secret);
1707
- if (!(secret ? normalized.kty === "oct" && typeof normalized.k == "string" : normalized.kty !== "oct" && (privateKey ? normalized.kty === "AKP" && typeof normalized.priv == "string" || typeof normalized.d == "string" : normalized.d === void 0 && normalized.priv === void 0))) throw new TypeError(secret ? "JSON Web Key for symmetric algorithms must have JWK \"kty\" (Key Type) equal to \"oct\" and the JWK \"k\" (Key Value) present" : `JSON Web Key for this operation must be a ${privateKey ? "private" : "public"} JWK`);
1708
- if (jwkMatchesOp(entry, normalized, usage), normalized.kty === "oct") return decode(normalized.k);
1709
- if (!Object.isFrozen(key)) {
1710
- const { key_ops } = key;
1711
- Array.isArray(key_ops) && Object.freeze(key_ops), Object.freeze(key);
1712
- }
1713
- } else {
1714
- if (!isKeyLike(key)) throw invalidKeyType(alg, key, secret);
1715
- const expectedType = secret ? "secret" : privateKey ? "private" : "public";
1716
- if (key.type !== expectedType && (secret || [
1717
- "secret",
1718
- "public",
1719
- "private"
1720
- ].includes(key.type))) throw new TypeError(`${tag(key)} instances must be of type "${expectedType}" for the ${alg} algorithm`);
1721
- if (isCryptoKey(key)) return key;
1722
- if (keyObject = key, keyObject.type === "secret") return keyObject.export();
1723
- }
1724
- cache ||= /* @__PURE__ */ new WeakMap();
1725
- const cacheKey = key;
1726
- let cached = cache.get(cacheKey);
1727
- if (cached?.[alg]) return cached[alg];
1728
- if (cached || cache.set(cacheKey, cached = {}), keyObject && typeof keyObject.toCryptoKey == "function") {
1729
- const isPublic = keyObject.type === "public", crv = nist[keyObject.asymmetricKeyDetails?.namedCurve], params = entry.resolve?.({
1730
- crv,
1731
- asymmetricKeyType: keyObject.asymmetricKeyType
1732
- }) ?? entry.subtle;
1733
- return cached[alg] = keyObject.toCryptoKey(params, isPublic, entry.usages[isPublic ? 0 : 1]);
1734
- }
1735
- return normalized ??= keyObject.export({ format: "jwk" }), normalized.alg = alg, cached[alg] = await jwkToKey(entry, normalized);
1736
- }
1737
- var cache;
1738
- var nist = {
1739
- __proto__: null,
1740
- prime256v1: "P-256",
1741
- secp384r1: "P-384",
1742
- secp521r1: "P-521"
1743
- };
1744
- var isCryptoKey = (key) => {
1745
- if (key?.[Symbol.toStringTag] === "CryptoKey") return !0;
1746
- try {
1747
- return key instanceof CryptoKey;
1748
- } catch {
1749
- return !1;
1750
- }
1751
- }, isKeyObject = (key) => key?.[Symbol.toStringTag] === "KeyObject", isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);
1752
- function message(msg, actual, ...types) {
1753
- if (types.length > 2) {
1754
- const last = types.pop();
1755
- msg += `one of type ${types.join(", ")}, or ${last}.`;
1756
- } else types.length === 2 ? msg += `one of type ${types[0]} or ${types[1]}.` : msg += `of type ${types[0]}.`;
1757
- return actual == null ? msg += ` Received ${actual}` : typeof actual == "function" && actual.name ? msg += ` Received function ${actual.name}` : typeof actual == "object" && actual != null && actual.constructor?.name && (msg += ` Received an instance of ${actual.constructor.name}`), msg;
1758
- }
1759
- function invalidKeyType(alg, actual, secret) {
1760
- const types = [
1761
- "CryptoKey",
1762
- "KeyObject",
1763
- "JSON Web Key"
1764
- ];
1765
- return secret && types.push("Uint8Array"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));
1766
- }
1767
- var unusable = (name, prop = "algorithm.name") => /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
1768
- function checkUsage(key, usage) {
1769
- if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
1770
- }
1771
- function checkModulusLength(alg, key) {
1772
- const { modulusLength } = key.algorithm;
1773
- if (typeof modulusLength != "number" || modulusLength < 2048) throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
1774
- }
1775
- function checkCryptoKey(key, expected, usage) {
1776
- const algorithm = key.algorithm;
1777
- if (algorithm.name !== expected.name) throw unusable(expected.name);
1778
- if (expected.hash && algorithm.hash?.name !== expected.hash) throw unusable(expected.hash, "algorithm.hash");
1779
- if (expected.namedCurve && algorithm.namedCurve !== expected.namedCurve) throw unusable(expected.namedCurve, "algorithm.namedCurve");
1780
- if (expected.length !== void 0 && algorithm.length !== expected.length) throw unusable(expected.length, "algorithm.length");
1781
- checkUsage(key, usage);
1782
- }
1783
- function snapshotJwk(jwk) {
1784
- return {
1785
- __proto__: null,
1786
- ...jwk
1787
- };
1788
- }
1789
- function normalizeJwk(jwk) {
1790
- const normalized = snapshotJwk(jwk);
1791
- if (normalized.ext !== void 0 && typeof normalized.ext != "boolean") throw new TypeError("\"ext\" (Extractable) Parameter must be a boolean");
1792
- if (normalized.key_ops !== void 0) {
1793
- const value = normalized.key_ops, keyOps = Array.isArray(value) ? [...value] : void 0;
1794
- if (!keyOps || keyOps.some((operation) => typeof operation != "string") || new Set(keyOps).size !== keyOps.length) throw new TypeError("\"key_ops\" (Key Operations) Parameter must be an array of unique strings");
1795
- normalized.key_ops = keyOps;
1796
- }
1797
- return normalized;
1798
- }
1799
- async function jwkToKey(entry, jwk, extractable) {
1800
- if (!entry.kty.includes(jwk.kty)) throw new JOSENotSupported("Invalid or unsupported JWK \"alg\" (Algorithm) Parameter value");
1801
- const algorithm = entry.resolve?.({
1802
- kty: jwk.kty,
1803
- crv: jwk.crv
1804
- }) ?? entry.subtle, isPrivate = !!(jwk.d || jwk.priv), keyData = {
1805
- ...jwk,
1806
- ext: extractable ?? jwk.ext
1807
- };
1808
- return keyData.kty !== "AKP" && delete keyData.alg, delete keyData.use, crypto.subtle.importKey("jwk", keyData, algorithm, keyData.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);
1809
- }
1810
- async function rawKey(key, expected, usage, extractable = !1) {
1811
- return key instanceof Uint8Array && (key = await crypto.subtle.importKey("raw", key, expected, extractable, [usage])), checkCryptoKey(key, expected, usage), key;
1812
- }
1813
- //#endregion
1814
- //#region ../../node_modules/jose/dist/webapi/lib/key_descriptor.js
1815
- function table(entries) {
1816
- const out = { __proto__: null };
1817
- for (const alg in entries) out[alg] = {
1818
- ...entries[alg],
1819
- alg
1820
- };
1821
- return out;
1822
- }
1823
- //#endregion
1824
- //#region ../../node_modules/jose/dist/webapi/lib/jws_algorithms.js
1825
- var sig = [["verify"], ["sign"]];
1826
- function hmac(bits) {
1827
- const subtle = {
1828
- name: "HMAC",
1829
- hash: `SHA-${bits}`
1830
- };
1831
- return {
1832
- kty: ["oct"],
1833
- secret: !0,
1834
- subtle,
1835
- signing: subtle,
1836
- usages: sig
1837
- };
1838
- }
1839
- function rsa(bits, saltLength) {
1840
- const subtle = {
1841
- name: saltLength ? "RSA-PSS" : "RSASSA-PKCS1-v1_5",
1842
- hash: `SHA-${bits}`
1843
- };
1844
- return {
1845
- kty: ["RSA"],
1846
- subtle,
1847
- signing: saltLength ? {
1848
- ...subtle,
1849
- saltLength
1850
- } : subtle,
1851
- usages: sig,
1852
- minRsaBits: 2048
1853
- };
1854
- }
1855
- function ecdsa(crv, bits) {
1856
- return {
1857
- kty: ["EC"],
1858
- crv,
1859
- subtle: {
1860
- name: "ECDSA",
1861
- namedCurve: crv
1862
- },
1863
- signing: {
1864
- name: "ECDSA",
1865
- hash: `SHA-${bits}`
1866
- },
1867
- usages: sig
1868
- };
1869
- }
1870
- function eddsa() {
1871
- const subtle = { name: "Ed25519" };
1872
- return {
1873
- kty: ["OKP"],
1874
- crv: "Ed25519",
1875
- subtle,
1876
- signing: subtle,
1877
- usages: sig
1878
- };
1879
- }
1880
- function mldsa(bits) {
1881
- const subtle = { name: `ML-DSA-${bits}` };
1882
- return {
1883
- kty: ["AKP"],
1884
- subtle,
1885
- signing: subtle,
1886
- usages: sig
1887
- };
1888
- }
1889
- var JWS = table({
1890
- HS256: hmac(256),
1891
- HS384: hmac(384),
1892
- HS512: hmac(512),
1893
- RS256: rsa(256),
1894
- RS384: rsa(384),
1895
- RS512: rsa(512),
1896
- PS256: rsa(256, 32),
1897
- PS384: rsa(384, 48),
1898
- PS512: rsa(512, 64),
1899
- ES256: ecdsa("P-256", 256),
1900
- ES384: ecdsa("P-384", 384),
1901
- ES512: ecdsa("P-521", 512),
1902
- EdDSA: eddsa(),
1903
- Ed25519: eddsa(),
1904
- "ML-DSA-44": mldsa(44),
1905
- "ML-DSA-65": mldsa(65),
1906
- "ML-DSA-87": mldsa(87)
1907
- });
1908
- function jwsAlgorithm(alg) {
1909
- const entry = typeof alg == "string" ? JWS[alg] : void 0;
1910
- if (!entry) throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
1911
- return entry;
1912
- }
1913
- //#endregion
1914
- //#region ../../node_modules/jose/dist/webapi/lib/jws_verify.js
1915
- function prepareVerify(options) {
1916
- return [options && validateAlgorithms("algorithms", options.algorithms), options?.crit];
1917
- }
1918
- function parseProtectedHeader(encodedProtected) {
1919
- return encodedProtected === void 0 ? {} : parseJoseHeader(encodedProtected, JWSInvalid, "JWS Protected Header is invalid");
1920
- }
1921
- function encodeCompactUnencodedPayload(payload) {
1922
- try {
1923
- return encode$1(payload);
1924
- } catch {
1925
- throw new JWSInvalid("JWS Compact Serialization payload must use only ASCII characters");
1926
- }
1927
- }
1928
- async function verifySignature(jws, shared, key, encodeUnencodedPayload, parsedProtected) {
1929
- const { protected: encodedProtected, header, payload: inputPayload } = jws, parsedProt = parsedProtected ?? parseProtectedHeader(encodedProtected);
1930
- if (!isDisjoint(parsedProt, header)) throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
1931
- const joseHeader = {
1932
- ...parsedProt,
1933
- ...header
1934
- }, b64 = validateB64(parsedProt, validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader)), { alg } = joseHeader;
1935
- if (typeof alg != "string" || !alg) throw new JWSInvalid("JWS \"alg\" (Algorithm) Header Parameter missing or invalid");
1936
- if (shared[0] && !shared[0].has(alg)) throw new JOSEAlgNotAllowed("\"alg\" (Algorithm) Header Parameter value not allowed");
1937
- if (b64) {
1938
- if (typeof inputPayload != "string") throw new JWSInvalid("JWS Payload must be a string");
1939
- } else if (typeof inputPayload != "string" && !(inputPayload instanceof Uint8Array)) throw new JWSInvalid("JWS Payload must be a string or an Uint8Array instance");
1940
- const signingPayload = b64 || typeof inputPayload != "string" ? inputPayload : encodeUnencodedPayload(inputPayload);
1941
- let resolvedKey = !1;
1942
- typeof key == "function" && (key = await key(parsedProt, jws), resolvedKey = !0);
1943
- const entry = jwsAlgorithm(alg), data = concat(encodedProtected !== void 0 ? encode$1(encodedProtected) : new Uint8Array(), encode$1("."), typeof signingPayload == "string" ? shared[2] ??= encodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload), signature = decodeBase64url(jws.signature, "signature", JWSInvalid), k = await prepareKey(entry, key, "verify"), cryptoKey = await rawKey(k, entry.subtle, "verify");
1944
- entry.minRsaBits && checkModulusLength(entry.alg, cryptoKey);
1945
- let verified = !1;
1946
- try {
1947
- verified = await crypto.subtle.verify(entry.signing, cryptoKey, signature, data);
1948
- } catch {}
1949
- if (!verified) throw new JWSSignatureVerificationFailed();
1950
- const result = { payload: typeof signingPayload == "string" ? decodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload };
1951
- return encodedProtected !== void 0 && (result.protectedHeader = parsedProt), header !== void 0 && (result.unprotectedHeader = header), resolvedKey ? [{
1952
- ...result,
1953
- key: k
1954
- }, b64] : [result, b64];
1955
- }
1956
- async function verifyCompact(jws, shared, key) {
1957
- if (jws instanceof Uint8Array && (jws = decoder.decode(jws)), typeof jws != "string") throw new JWSInvalid("Compact JWS must be a string or Uint8Array");
1958
- const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split(".");
1959
- if (length !== 3) throw new JWSInvalid("Invalid Compact JWS");
1960
- return verifySignature({
1961
- payload,
1962
- protected: protectedHeader,
1963
- signature
1964
- }, shared, key, encodeCompactUnencodedPayload);
1965
- }
1966
- //#endregion
1967
- //#region ../../node_modules/jose/dist/webapi/lib/jwt_claims_set.js
1968
- var epoch = (date) => Math.floor(date.getTime() / 1e3), multipliers = {
1969
- s: 1,
1970
- m: 60,
1971
- h: 3600,
1972
- d: 86400,
1973
- w: 604800,
1974
- y: 31557600
1975
- }, REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i, checkFailed = "check_failed";
1976
- function invalidDuration() {
1977
- throw new TypeError("Invalid time period format");
1978
- }
1979
- function secs(str) {
1980
- typeof str != "string" && invalidDuration();
1981
- const matched = REGEX.exec(str);
1982
- (!matched || matched[4] && matched[1]) && invalidDuration();
1983
- const value = parseFloat(matched[2]), numericDate2 = Math.round(value * multipliers[matched[3][0].toLowerCase()]);
1984
- return Number.isFinite(numericDate2) || invalidDuration(), matched[1] === "-" || matched[4] === "ago" ? -numericDate2 : numericDate2;
1985
- }
1986
- function validateInput(label, input) {
1987
- if (!Number.isFinite(input)) throw new TypeError(`Invalid ${label} input`);
1988
- return input;
1989
- }
1990
- function validateStringClaim(claim, value) {
1991
- if (typeof value != "string") throw new TypeError(`"${claim}" claim must be a string`);
1992
- }
1993
- function validateAudienceClaim(value) {
1994
- if (typeof value != "string" && (!Array.isArray(value) || Array.from(value).some((member) => typeof member != "string"))) throw new TypeError("\"aud\" claim must be a string or an array of strings");
1995
- }
1996
- function numericDate(value, label) {
1997
- return typeof value == "number" ? validateInput(label, value) : value instanceof Date ? validateInput(label, epoch(value)) : epoch(/* @__PURE__ */ new Date()) + secs(value);
1998
- }
1999
- var normalizeTyp = (value) => {
2000
- const normalized = value.toLowerCase();
2001
- return value.includes("/") ? normalized : `application/${normalized}`;
2002
- }, checkAudiencePresence = (audPayload, audOption) => typeof audPayload == "string" ? audOption.includes(audPayload) : Array.isArray(audPayload) ? audOption.some((aud) => audPayload.includes(aud)) : !1;
2003
- function validateNumericDate(payload, claim, required = !1) {
2004
- const value = payload[claim];
2005
- if (!(value === void 0 && !required)) {
2006
- if (typeof value != "number") throw new JWTClaimValidationFailed(`"${claim}" claim must be a number`, payload, claim, "invalid");
2007
- return value;
2008
- }
2009
- }
2010
- function unexpectedClaim(payload, claim) {
2011
- throw new JWTClaimValidationFailed(`unexpected "${claim}" claim value`, payload, claim, checkFailed);
2012
- }
2013
- function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
2014
- let payload;
2015
- try {
2016
- payload = JSON.parse(strictDecoder.decode(encodedPayload));
2017
- } catch {}
2018
- if (!isObject$1(payload)) throw new JWTInvalid("JWT Claims Set must be a top-level JSON object");
2019
- const { typ } = options;
2020
- if (typ !== void 0 && (typeof protectedHeader.typ != "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) throw new JWTClaimValidationFailed("unexpected \"typ\" JWT header value", payload, "typ", checkFailed);
2021
- const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options, presenceCheck = [...requiredClaims];
2022
- maxTokenAge !== void 0 && presenceCheck.push("iat"), audience !== void 0 && presenceCheck.push("aud"), subject !== void 0 && presenceCheck.push("sub"), issuer !== void 0 && presenceCheck.push("iss");
2023
- for (const claim of new Set(presenceCheck.reverse())) if (!Object.hasOwn(payload, claim)) throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, "missing");
2024
- issuer !== void 0 && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss) && unexpectedClaim(payload, "iss"), subject !== void 0 && payload.sub !== subject && unexpectedClaim(payload, "sub"), audience !== void 0 && !checkAudiencePresence(payload.aud, typeof audience == "string" ? [audience] : audience) && unexpectedClaim(payload, "aud");
2025
- const { clockTolerance } = options;
2026
- let tolerance = 0;
2027
- if (typeof clockTolerance == "string") tolerance = secs(clockTolerance);
2028
- else if (clockTolerance !== void 0) {
2029
- if (typeof clockTolerance != "number") throw new TypeError("Invalid clockTolerance option type");
2030
- tolerance = clockTolerance;
2031
- }
2032
- validateInput("clockTolerance option", tolerance);
2033
- const { currentDate } = options, now = validateInput("currentDate option", epoch(currentDate === void 0 ? /* @__PURE__ */ new Date() : currentDate)), iat = validateNumericDate(payload, "iat", maxTokenAge !== void 0), nbf = validateNumericDate(payload, "nbf");
2034
- if (nbf !== void 0 && nbf > now + tolerance) throw new JWTClaimValidationFailed("\"nbf\" claim timestamp check failed", payload, "nbf", checkFailed);
2035
- const exp = validateNumericDate(payload, "exp");
2036
- if (exp !== void 0 && exp <= now - tolerance) throw new JWTExpired("\"exp\" claim timestamp check failed", payload, "exp", checkFailed);
2037
- if (maxTokenAge !== void 0) {
2038
- const age = now - iat, max = validateInput("maxTokenAge option", typeof maxTokenAge == "number" ? maxTokenAge : secs(maxTokenAge));
2039
- if (age - tolerance > max) throw new JWTExpired("\"iat\" claim timestamp check failed (too far in the past)", payload, "iat", checkFailed);
2040
- if (age < -tolerance) throw new JWTClaimValidationFailed("\"iat\" claim timestamp check failed (it should be in the past)", payload, "iat", checkFailed);
2041
- }
2042
- return payload;
2043
- }
2044
- var producerPayloads;
2045
- function producerPayload(producer) {
2046
- return producerPayloads.get(producer);
2047
- }
2048
- function jwtData(producer) {
2049
- const payload = producerPayload(producer);
2050
- for (const claim of [
2051
- "iat",
2052
- "nbf",
2053
- "exp"
2054
- ]) {
2055
- const value = payload[claim];
2056
- if (typeof value == "number" && !Number.isFinite(value)) throw new TypeError(`"${claim}" claim must be a finite number`);
2057
- }
2058
- return encoder.encode(JSON.stringify(payload));
2059
- }
2060
- var JWTClaimsBuilder = class {
2061
- constructor(payload = {}) {
2062
- if (!isObject$1(payload)) throw new TypeError("JWT Claims Set MUST be an object");
2063
- (producerPayloads ||= /* @__PURE__ */ new WeakMap()).set(this, structuredClone(payload));
2064
- }
2065
- setIssuer(value) {
2066
- return validateStringClaim("iss", value), producerPayload(this).iss = value, this;
2067
- }
2068
- setSubject(value) {
2069
- return validateStringClaim("sub", value), producerPayload(this).sub = value, this;
2070
- }
2071
- setAudience(value) {
2072
- return validateAudienceClaim(value), producerPayload(this).aud = value, this;
2073
- }
2074
- setJti(value) {
2075
- return validateStringClaim("jti", value), producerPayload(this).jti = value, this;
2076
- }
2077
- setNotBefore(value) {
2078
- return producerPayload(this).nbf = numericDate(value, "setNotBefore"), this;
2079
- }
2080
- setExpirationTime(value) {
2081
- return producerPayload(this).exp = numericDate(value, "setExpirationTime"), this;
2082
- }
2083
- setIssuedAt(value) {
2084
- const payload = producerPayload(this);
2085
- return value === void 0 ? payload.iat = epoch(/* @__PURE__ */ new Date()) : typeof value == "string" ? payload.iat = validateInput("setIssuedAt", epoch(/* @__PURE__ */ new Date()) + secs(value)) : payload.iat = numericDate(value, "setIssuedAt"), this;
2086
- }
2087
- };
2088
- //#endregion
2089
- //#region ../../node_modules/jose/dist/webapi/jwt/verify.js
2090
- async function jwtVerify(jwt, key, options) {
2091
- const [verified, b64] = await verifyCompact(jwt, prepareVerify(options), key);
2092
- if (!b64) throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
2093
- const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);
2094
- return {
2095
- ...verified,
2096
- payload
2097
- };
2098
- }
2099
- //#endregion
2100
- //#region ../../node_modules/jose/dist/webapi/lib/jws_sign.js
2101
- async function createSignature(input, key, rejectUnencoded) {
2102
- let [payload, protectedHeader, unprotectedHeader, crit] = input, protectedHeaderString = "";
2103
- if (protectedHeader !== void 0) {
2104
- const normalized = serializeJoseHeader(JWSInvalid, protectedHeader);
2105
- protectedHeader = normalized[0], protectedHeaderString = encode(normalized[1]);
2106
- }
2107
- if (unprotectedHeader !== void 0 && (unprotectedHeader = serializeJoseHeader(JWSInvalid, unprotectedHeader)[0]), !protectedHeader && !unprotectedHeader) throw new JWSInvalid("either setProtectedHeader or setUnprotectedHeader must be called before #sign()");
2108
- if (!isDisjoint(protectedHeader, unprotectedHeader)) throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
2109
- const joseHeader = {
2110
- ...protectedHeader,
2111
- ...unprotectedHeader
2112
- };
2113
- validateCritDuplicates(JWSInvalid, protectedHeader);
2114
- const b64 = validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, crit, protectedHeader, joseHeader));
2115
- b64 || rejectUnencoded?.();
2116
- const { alg } = joseHeader;
2117
- if (typeof alg != "string" || !alg) throw new JWSInvalid("JWS \"alg\" (Algorithm) Header Parameter missing or invalid");
2118
- const entry = jwsAlgorithm(alg);
2119
- let payloadS = "", payloadB = payload, data;
2120
- if (b64) {
2121
- const encoded = input[4];
2122
- encoded ? (payloadS = encoded[0] ??= encode(payload), payloadB = encoded[1] ??= encode$1(payloadS)) : (payloadS = encode(payload), data = encoder.encode(`${protectedHeaderString}.${payloadS}`));
2123
- }
2124
- data ??= concat(encode$1(protectedHeaderString), encode$1("."), payloadB);
2125
- const k = await rawKey(await prepareKey(entry, key, "sign"), entry.subtle, "sign");
2126
- entry.minRsaBits && checkModulusLength(entry.alg, k);
2127
- const jws = {
2128
- signature: encode(new Uint8Array(await crypto.subtle.sign(entry.signing, k, data))),
2129
- payload: payloadS
2130
- };
2131
- return protectedHeader && (jws.protected = protectedHeaderString), unprotectedHeader && (jws.header = unprotectedHeader), [jws, b64];
2132
- }
2133
- async function createCompactSignature(payload, protectedHeader, crit, key, rejectUnencoded) {
2134
- const [jws] = await createSignature([
2135
- payload,
2136
- protectedHeader,
2137
- void 0,
2138
- crit
2139
- ], key, rejectUnencoded);
2140
- return `${jws.protected}.${jws.payload}.${jws.signature}`;
2141
- }
2142
- //#endregion
2143
- //#region ../../node_modules/jose/dist/webapi/jwt/sign.js
2144
- var SignJWT_base = JWTClaimsBuilder;
2145
- var SignJWT = class extends SignJWT_base {
2146
- #protectedHeader;
2147
- setProtectedHeader(protectedHeader) {
2148
- return assertNotSet(this.#protectedHeader, "setProtectedHeader"), this.#protectedHeader = protectedHeader, this;
2149
- }
2150
- async sign(key, options) {
2151
- return createCompactSignature(jwtData(this), this.#protectedHeader, options?.crit, key, () => {
2152
- throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
2153
- });
2154
- }
2155
- };
2156
- //#endregion
2157
- //#region src/kernel/moleculer/mesh-mtls/mesh-join-token.ts
2158
- /**
2159
- * Mesh join tokens — single-use JWS ES256 signed with the hub mesh CA key.
2160
- *
2161
- * Payload: `{ v, hubUrl, hubMeshAddress, caFingerprint, nodeId, jti, exp }`. The node id is MANDATORY
2162
- * (D857): a token that does not name the node it is for would let its holder pick any id, `hub` included.
2163
- * `exp` ≤ now + 1 h. `jti` is recorded until expiry so a token cannot be reused.
2164
- */
2165
- /** In-memory + optional persist of spent `jti` until `exp`. */
2166
- var JoinTokenJtiStore = class {
2167
- spent = /* @__PURE__ */ new Map();
2168
- /** True when `jti` was already consumed (and not yet expired). */
2169
- isSpent(jti, nowSec = Math.floor(Date.now() / 1e3)) {
2170
- this.gc(nowSec);
2171
- return this.spent.has(jti);
2172
- }
2173
- /** Record a jti as used until `exp`. Returns false if already spent. */
2174
- consume(jti, exp, nowSec = Math.floor(Date.now() / 1e3)) {
2175
- this.gc(nowSec);
2176
- if (this.spent.has(jti)) return false;
2177
- this.spent.set(jti, exp);
2178
- return true;
2179
- }
2180
- /** Test helper. */
2181
- size() {
2182
- return this.spent.size;
2183
- }
2184
- gc(nowSec) {
2185
- for (const [jti, exp] of this.spent) if (exp <= nowSec) this.spent.delete(jti);
2186
- }
2187
- };
2188
- var processJtiStore = new JoinTokenJtiStore();
2189
- function processJoinTokenJtiStore() {
2190
- return processJtiStore;
2191
- }
2192
- async function mintMeshJoinToken(input) {
2193
- if (!isValidAgentNodeId(input.nodeId)) throw new Error(`a join token needs the node id it is for; "${input.nodeId}" is not one an agent may carry`);
2194
- const now = input.now ?? /* @__PURE__ */ new Date();
2195
- const nowSec = Math.floor(now.getTime() / 1e3);
2196
- const exp = nowSec + Math.min(Math.max(1, input.ttlSec ?? 3600), MESH_JOIN_TOKEN_MAX_TTL_SEC);
2197
- const jti = input.jti ?? randomBytes(16).toString("base64url");
2198
- return new SignJWT({
2199
- v: 1,
2200
- hubUrl: input.hubUrl,
2201
- hubMeshAddress: input.hubMeshAddress,
2202
- caFingerprint: caFingerprintCompact(input.ca.certPem),
2203
- nodeId: input.nodeId,
2204
- jti,
2205
- exp
2206
- }).setProtectedHeader({
2207
- alg: "ES256",
2208
- typ: "JWT"
2209
- }).setIssuedAt(nowSec).setExpirationTime(exp).setJti(jti).sign(input.ca.privateKey);
2210
- }
2211
- async function verifyMeshJoinToken(input) {
2212
- const now = input.now ?? /* @__PURE__ */ new Date();
2213
- const nowSec = Math.floor(now.getTime() / 1e3);
2214
- let payload;
2215
- try {
2216
- payload = (await jwtVerify(input.token, input.ca.publicKey, {
2217
- algorithms: ["ES256"],
2218
- currentDate: now
2219
- })).payload;
2220
- } catch (err) {
2221
- const msg = err instanceof Error ? err.message : String(err);
2222
- if (/exp/i.test(msg) || /timestamp/i.test(msg)) return {
2223
- ok: false,
2224
- reason: "token expired"
2225
- };
2226
- return {
2227
- ok: false,
2228
- reason: `token signature invalid: ${msg}`
2229
- };
2230
- }
2231
- const claims = parseClaims$1(payload);
2232
- if (claims === null) return {
2233
- ok: false,
2234
- reason: "token claims malformed"
2235
- };
2236
- if (claims.exp <= nowSec) return {
2237
- ok: false,
2238
- reason: "token expired"
2239
- };
2240
- const expectedFp = caFingerprintCompact(input.ca.certPem);
2241
- if (claims.caFingerprint !== expectedFp) return {
2242
- ok: false,
2243
- reason: "token caFingerprint does not match this hub CA"
2244
- };
2245
- if (input.expectedCaFingerprint !== void 0 && claims.caFingerprint !== normalizeFp(input.expectedCaFingerprint)) return {
2246
- ok: false,
2247
- reason: "token caFingerprint pin mismatch"
2248
- };
2249
- const store = input.jtiStore ?? processJtiStore;
2250
- if (input.consume !== false) {
2251
- if (!store.consume(claims.jti, claims.exp, nowSec)) return {
2252
- ok: false,
2253
- reason: "token already used"
2254
- };
2255
- } else if (store.isSpent(claims.jti, nowSec)) return {
2256
- ok: false,
2257
- reason: "token already used"
2258
- };
2259
- return {
2260
- ok: true,
2261
- claims
2262
- };
2263
- }
2264
- function normalizeFp(fp) {
2265
- return fp.replace(/:/g, "").toLowerCase();
2266
- }
2267
- function parseClaims$1(payload) {
2268
- if (payload["v"] !== 1) return null;
2269
- const hubUrl = payload["hubUrl"];
2270
- const hubMeshAddress = payload["hubMeshAddress"];
2271
- const caFingerprint = payload["caFingerprint"];
2272
- const jti = typeof payload.jti === "string" ? payload.jti : payload["jti"];
2273
- const exp = typeof payload.exp === "number" ? payload.exp : null;
2274
- if (typeof hubUrl !== "string" || hubUrl.length === 0) return null;
2275
- if (typeof hubMeshAddress !== "string" || hubMeshAddress.length === 0) return null;
2276
- if (typeof caFingerprint !== "string" || caFingerprint.length === 0) return null;
2277
- if (typeof jti !== "string" || jti.length === 0) return null;
2278
- if (exp === null) return null;
2279
- const nodeId = payload["nodeId"];
2280
- if (typeof nodeId !== "string" || !isValidAgentNodeId(nodeId)) return null;
2281
- return {
2282
- v: 1,
2283
- hubUrl,
2284
- hubMeshAddress,
2285
- caFingerprint: normalizeFp(caFingerprint),
2286
- nodeId,
2287
- jti,
2288
- exp
2289
- };
2290
- }
2291
- //#endregion
2292
- //#region src/kernel/moleculer/mesh-mtls/mesh-enroll.ts
2293
- /**
2294
- * `POST /api/mesh/enroll` — verify join token, issue a 30-day leaf from CSR.
2295
- */
2296
- async function enrollMeshNode(body, deps) {
2297
- if (typeof body.token !== "string" || body.token.length === 0) return {
2298
- ok: false,
2299
- error: "token is required",
2300
- status: 400
2301
- };
2302
- if (typeof body.csr !== "string" || !body.csr.includes("BEGIN CERTIFICATE REQUEST")) return {
2303
- ok: false,
2304
- error: "csr PEM is required",
2305
- status: 400
2306
- };
2307
- const verified = await verifyMeshJoinToken({
2308
- token: body.token,
2309
- ca: deps.ca,
2310
- jtiStore: deps.jtiStore ?? processJoinTokenJtiStore(),
2311
- now: deps.now,
2312
- consume: true
2313
- });
2314
- if (!verified.ok) {
2315
- const status = verified.reason.includes("expired") ? 401 : verified.reason.includes("already used") ? 409 : 401;
2316
- return {
2317
- ok: false,
2318
- error: verified.reason,
2319
- status
2320
- };
2321
- }
2322
- let leaf;
2323
- try {
2324
- leaf = await issueMeshLeafFromCsr(deps.ca, body.csr, {
2325
- expectedNodeId: verified.claims.nodeId,
2326
- now: deps.now
2327
- });
2328
- } catch (err) {
2329
- return {
2330
- ok: false,
2331
- error: err instanceof Error ? err.message : String(err),
2332
- status: 400
2333
- };
2334
- }
2335
- try {
2336
- await deps.onIssued?.(leaf.commonName, leaf.certPem);
2337
- } catch (err) {
2338
- deps.onRecordFailed?.(leaf.commonName, err);
2339
- }
2340
- return {
2341
- ok: true,
2342
- cert: leaf.certPem,
2343
- caCert: deps.ca.certPem,
2344
- nodeId: leaf.commonName,
2345
- notAfter: leaf.notAfter.toISOString(),
2346
- serialNumber: leaf.serialNumber
2347
- };
2348
- }
2349
- //#endregion
2350
1373
  //#region src/kernel/moleculer/mesh-mtls/mesh-tls-enabler.ts
2351
1374
  var SLOT = Symbol.for("camstack.mesh.tls-enabler");
2352
1375
  function setProcessMeshTlsEnabler(enabler) {
@@ -27742,7 +26765,7 @@ var require_index_min = /* @__PURE__ */ __commonJSMin(((exports) => {
27742
26765
  var require_shard = /* @__PURE__ */ __commonJSMin(((exports, module) => {
27743
26766
  var _ = require_lodash();
27744
26767
  var BaseStrategy = require_base$7();
27745
- var crypto$4 = __require("crypto");
26768
+ var crypto$3 = __require("crypto");
27746
26769
  var { LRUCache } = require_index_min();
27747
26770
  var { isFunction, randomInt } = require_utils$3();
27748
26771
  /**
@@ -27848,7 +26871,7 @@ var require_shard = /* @__PURE__ */ __commonJSMin(((exports, module) => {
27848
26871
  * @memberof ShardStrategy
27849
26872
  */
27850
26873
  getHash(key) {
27851
- const hash = crypto$4.createHash("md5").update(key).digest("hex");
26874
+ const hash = crypto$3.createHash("md5").update(key).digest("hex");
27852
26875
  const hashNum = parseInt(hash.substring(0, 8), 16);
27853
26876
  return this.opts.ringSize ? hashNum % this.opts.ringSize : hashNum;
27854
26877
  }
@@ -49106,7 +48129,7 @@ var require_validators$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
49106
48129
  //#region ../../node_modules/moleculer/src/cachers/base.js
49107
48130
  var require_base$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
49108
48131
  var _ = require_lodash();
49109
- var crypto$3 = __require("crypto");
48132
+ var crypto$2 = __require("crypto");
49110
48133
  var { METRIC } = require_metrics$1();
49111
48134
  var { isObject, isFunction, isDate } = require_utils$3();
49112
48135
  /**
@@ -49370,7 +48393,7 @@ var require_base$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
49370
48393
  const maxParamsLength = this.opts.maxParamsLength;
49371
48394
  if (!maxParamsLength || maxParamsLength < 44 || key.length <= maxParamsLength) return key;
49372
48395
  const prefixLength = maxParamsLength - 44;
49373
- const base64Hash = crypto$3.createHash("sha256").update(key).digest("base64");
48396
+ const base64Hash = crypto$2.createHash("sha256").update(key).digest("base64");
49374
48397
  if (prefixLength < 1) return base64Hash;
49375
48398
  return key.substring(0, prefixLength) + base64Hash;
49376
48399
  }
@@ -76118,7 +75141,7 @@ var require_hot_reload = /* @__PURE__ */ __commonJSMin(((exports, module) => {
76118
75141
  //#endregion
76119
75142
  //#region ../../node_modules/moleculer/src/middlewares/transmit/encryption.js
76120
75143
  var require_encryption = /* @__PURE__ */ __commonJSMin(((exports, module) => {
76121
- var crypto$2 = __require("crypto");
75144
+ var crypto$1 = __require("crypto");
76122
75145
  /**
76123
75146
  * This is a AES encryption middleware to protect the whole
76124
75147
  * Moleculer transporter communication with AES.
@@ -76141,14 +75164,14 @@ var require_encryption = /* @__PURE__ */ __commonJSMin(((exports, module) => {
76141
75164
  },
76142
75165
  transporterSend(next) {
76143
75166
  return (topic, data, meta) => {
76144
- const encrypter = iv ? crypto$2.createCipheriv(algorithm, password, iv) : crypto$2.createCipher(algorithm, password);
75167
+ const encrypter = iv ? crypto$1.createCipheriv(algorithm, password, iv) : crypto$1.createCipher(algorithm, password);
76145
75168
  return next(topic, Buffer.concat([encrypter.update(data), encrypter.final()]), meta);
76146
75169
  };
76147
75170
  },
76148
75171
  transporterReceive(next) {
76149
75172
  return (cmd, data, s) => {
76150
75173
  try {
76151
- const decrypter = iv ? crypto$2.createDecipheriv(algorithm, password, iv) : crypto$2.createDecipher(algorithm, password);
75174
+ const decrypter = iv ? crypto$1.createDecipheriv(algorithm, password, iv) : crypto$1.createDecipher(algorithm, password);
76152
75175
  return next(cmd, Buffer.concat([decrypter.update(data), decrypter.final()]), s);
76153
75176
  } catch (err) {
76154
75177
  logger.error("Received packet decryption error.", err);
@@ -83643,7 +82666,7 @@ var require_main = /* @__PURE__ */ __commonJSMin(((exports, module) => {
83643
82666
  var fs$2 = __require("fs");
83644
82667
  var path$2 = __require("path");
83645
82668
  var os$2 = __require("os");
83646
- var crypto$1 = __require("crypto");
82669
+ var crypto = __require("crypto");
83647
82670
  var TIPS = [
83648
82671
  "◈ encrypted .env [www.dotenvx.com]",
83649
82672
  "◈ secrets for agents [www.dotenvx.com]",
@@ -83848,7 +82871,7 @@ var require_main = /* @__PURE__ */ __commonJSMin(((exports, module) => {
83848
82871
  const authTag = ciphertext.subarray(-16);
83849
82872
  ciphertext = ciphertext.subarray(12, -16);
83850
82873
  try {
83851
- const aesgcm = crypto$1.createDecipheriv("aes-256-gcm", key, nonce);
82874
+ const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce);
83852
82875
  aesgcm.setAuthTag(authTag);
83853
82876
  return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
83854
82877
  } catch (error) {
@@ -88009,6 +87032,11 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
88009
87032
  this.logger.warn("Dropping mesh packet — an enrolled agent accepts TLS from the hub only");
88010
87033
  return;
88011
87034
  }
87035
+ const wireSender = peekSender(this, type, message);
87036
+ if (wireSender === null || wireSender === this.nodeID) {
87037
+ this.logger.warn(wireSender === null ? "Dropping mesh packet - it names no sender" : "Dropping mesh packet - it claims this node as its sender");
87038
+ return;
87039
+ }
88012
87040
  if (socket !== void 0 && isTlsSocket(socket)) {
88013
87041
  const tlsSock = socket;
88014
87042
  const identity = tlsSock.meshPeerCn ? {
@@ -88024,9 +87052,8 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
88024
87052
  tlsSock.destroy();
88025
87053
  return;
88026
87054
  }
88027
- const sender = peekSender(this, type, message);
88028
- if (sender !== null && sender !== identity.cn) {
88029
- this.logger.warn(`Dropping forged mesh packet — sender=${sender} peerCN=${identity.cn}`);
87055
+ if (wireSender !== identity.cn) {
87056
+ this.logger.warn(`Dropping forged mesh packet — sender=${wireSender} peerCN=${identity.cn}`);
88030
87057
  return;
88031
87058
  }
88032
87059
  tlsSock.meshPeerCn = identity.cn;
@@ -89286,196 +88313,24 @@ var HubPairing = class {
89286
88313
  };
89287
88314
  }
89288
88315
  };
89289
- //#endregion
89290
- //#region src/builtins/system-config/cluster-secret-keeper.ts
89291
- /**
89292
- * Keeps the hub's cluster-secret gate in step with System → Cluster (D797,
89293
- * D802).
89294
- *
89295
- * The field is a `password` of `system-config`: its `system-settings` row
89296
- * (`cluster.clusterSecret`) holds a keychain marker, the value lives in the
89297
- * keychain (owner `system`). The gate the mesh join reads:
89298
- *
89299
- * - no marker → the secret is MANDATORY (D802): the keeper asks
89300
- * its owner to generate one (`generateSecret`), and
89301
- * until that succeeds the gate is `off` — no node can
89302
- * authenticate (mesh auth is always enforced, D821);
89303
- * - marker + value → `on` — nodes prove the secret;
89304
- * - marker, no answer → `unknown` — refused, retryably, and retried here.
89305
- * A locked keychain or a value lost to a reset NEVER opens the gate, and
89306
- * never triggers a generation (the operator decides what replaces a value
89307
- * the keychain lost).
89308
- *
89309
- * It also publishes the mesh mode (`cluster.meshAuth`, `mesh-auth-mode.ts`).
89310
- */
89311
- /** The field key inside the `cluster` section. */
89312
- var CLUSTER_SECRET_FIELD = "clusterSecret";
89313
- var ClusterSecretKeeper = class {
89314
- deps;
89315
- retryTimer = null;
89316
- unsubscribe = null;
89317
- stopped = false;
89318
- last = null;
89319
- constructor(deps) {
89320
- this.deps = deps;
89321
- }
89322
- async start() {
89323
- this.unsubscribe = this.deps.secrets?.onChange((ref) => {
89324
- if (ref === CLUSTER_SECRET_REF) this.reload();
89325
- }) ?? null;
89326
- await this.reload();
89327
- }
89328
- stop() {
89329
- this.stopped = true;
89330
- if (this.retryTimer !== null) clearTimeout(this.retryTimer);
89331
- this.retryTimer = null;
89332
- this.unsubscribe?.();
89333
- this.unsubscribe = null;
89334
- }
89335
- /** Re-read the row and the keychain; publish the gate. */
89336
- async reload() {
89337
- if (this.stopped) return;
89338
- let gate = await this.compute();
89339
- if (gate.kind === "off" && this.deps.generateSecret !== void 0 && !this.stopped) gate = await this.generate();
89340
- if (this.stopped) return;
89341
- setProcessClusterSecretGate(gate);
89342
- this.report(gate);
89343
- if (gate.kind === "unknown" || gate.kind === "off" && this.deps.generateSecret !== void 0) this.scheduleRetry();
89344
- }
89345
- async generate() {
89346
- try {
89347
- await this.deps.generateSecret?.();
89348
- } catch (err) {
89349
- this.deps.logger.error("The cluster secret is mandatory but could not be generated yet — retrying. Agents cannot authenticate until it exists.", { meta: { error: errText(err) } });
89350
- return { kind: "off" };
89351
- }
89352
- this.deps.logger.info("Generated the cluster secret (none was stored). Reveal it on System → Cluster and enter it in each agent.");
89353
- return this.compute();
89354
- }
89355
- async compute() {
89356
- let section;
89357
- try {
89358
- section = await this.deps.readSection();
89359
- } catch (err) {
89360
- return {
89361
- kind: "unknown",
89362
- reason: `settings unreadable: ${errText(err)}`
89363
- };
89364
- }
89365
- setProcessMeshAuthMode("enforced");
89366
- const stored = section[CLUSTER_SECRET_FIELD];
89367
- if (stored === void 0 || stored === null || stored === "") return { kind: "off" };
89368
- if (!isSecretMarker(stored)) return {
89369
- kind: "unknown",
89370
- reason: "stored inline, not in the keychain — set it again in System → Cluster"
89371
- };
89372
- const secrets = this.deps.secrets;
89373
- if (secrets === null) return {
89374
- kind: "unknown",
89375
- reason: "no keychain door in this process"
89376
- };
89377
- let entry;
89378
- try {
89379
- entry = (await secrets.resolve([CLUSTER_SECRET_REF]))[CLUSTER_SECRET_REF];
89380
- } catch (err) {
89381
- return {
89382
- kind: "unknown",
89383
- reason: errText(err)
89384
- };
89385
- }
89386
- if (entry === void 0) return {
89387
- kind: "unknown",
89388
- reason: "no-answer"
89389
- };
89390
- switch (entry.kind) {
89391
- case "value": return {
89392
- kind: "on",
89393
- hash: hashClusterSecret(entry.value)
89394
- };
89395
- case "unavailable": return {
89396
- kind: "unknown",
89397
- reason: entry.reason
89398
- };
89399
- case "missing": return {
89400
- kind: "unknown",
89401
- reason: "the stored value is gone — set it again in System → Cluster"
89402
- };
89403
- case "forbidden": return {
89404
- kind: "unknown",
89405
- reason: "forbidden"
89406
- };
89407
- }
89408
- }
89409
- report(gate) {
89410
- const changed = this.last !== gate.kind;
89411
- this.last = gate.kind;
89412
- if (gate.kind === "unknown") {
89413
- this.deps.logger.error("Agents cannot register: a cluster secret is set but cannot be read from the keychain. Registrations are refused until it can (no fallback).", { meta: { reason: gate.reason } });
89414
- return;
89415
- }
89416
- if (changed) this.deps.logger.info(gate.kind === "on" ? "Cluster secret set — agents prove it to join the mesh" : "No cluster secret yet — no node can authenticate to the mesh");
89417
- }
89418
- scheduleRetry() {
89419
- if (this.stopped || this.retryTimer !== null) return;
89420
- this.retryTimer = setTimeout(() => {
89421
- this.retryTimer = null;
89422
- this.reload();
89423
- }, this.deps.retryMs ?? 3e4);
89424
- this.retryTimer.unref?.();
89425
- }
89426
- };
89427
- function errText(err) {
89428
- return err instanceof Error ? err.message : String(err);
89429
- }
89430
- //#endregion
89431
- //#region src/builtins/system-config/mesh-actions.ts
89432
- /**
89433
- * System → Cluster — mint a single-use mesh join token (Release A) and
89434
- * agent-ui unlock tickets (D821).
89435
- */
89436
- var mintJoinTokenInputSchema = z.object({
89437
- /** The node id the token is for - mandatory: the CSR CN must be exactly this. */
89438
- nodeId: z.string().min(1),
89439
- /** Override hub HTTPS base (default: local-network publicEndpoint). */
89440
- hubUrl: z.string().url().optional(),
89441
- /** Override `host:6443` TLS mesh address carried in the token. */
89442
- hubMeshAddress: z.string().min(3).optional(),
89443
- /** Seconds until expiry; capped at 1 h. */
89444
- ttlSec: z.number().int().positive().max(3600).optional()
89445
- });
89446
- var mintJoinTokenOutputSchema = z.object({
89447
- token: z.string().min(1),
89448
- exp: z.number().int(),
89449
- hubUrl: z.string(),
89450
- hubMeshAddress: z.string(),
89451
- caFingerprint: z.string(),
89452
- nodeId: z.string()
89453
- });
89454
- var mintAgentUiUnlockInputSchema = z.object({
88316
+ var systemConfigMeshActions = defineCustomActions({
88317
+ /** Hub admin → short-lived ticket the agent redeems for a tab session (D821). */
88318
+ mintAgentUiUnlockTicket: customAction(z.object({
89455
88319
  nodeId: z.string().min(1),
89456
88320
  /**
89457
88321
  * Preferred agent dashboard base (`http://host:4444`). When set, the
89458
88322
  * answer includes `dashboardUrl` with `?ticket=` already attached.
89459
88323
  */
89460
88324
  dashboardBaseUrl: z.string().url().optional()
89461
- });
89462
- var mintAgentUiUnlockOutputSchema = z.object({
88325
+ }), z.object({
89463
88326
  ticket: z.string().min(1),
89464
88327
  exp: z.number().int(),
89465
88328
  nodeId: z.string().min(1),
89466
88329
  dashboardUrl: z.string().url().optional()
89467
- });
89468
- var systemConfigMeshActions = defineCustomActions({
89469
- mintJoinToken: customAction(mintJoinTokenInputSchema, mintJoinTokenOutputSchema, {
89470
- kind: "mutation",
89471
- auth: "admin"
89472
- }),
89473
- /** Hub admin → short-lived ticket the agent redeems for a tab session (D821). */
89474
- mintAgentUiUnlockTicket: customAction(mintAgentUiUnlockInputSchema, mintAgentUiUnlockOutputSchema, {
89475
- kind: "mutation",
89476
- auth: "admin"
89477
- })
89478
- });
88330
+ }), {
88331
+ kind: "mutation",
88332
+ auth: "admin"
88333
+ }) });
89479
88334
  //#endregion
89480
88335
  //#region src/builtins/system-config/system-config.addon.ts
89481
88336
  /** How often superseded and expired mesh certificate rows are reaped. */
@@ -89493,10 +88348,9 @@ var MESH_REAP_MS = 24 * 36e5;
89493
88348
  * - server (port, host, dataPath — READ-ONLY, loaded from config.yaml at bootstrap)
89494
88349
  * - auth (tokenExpiry = session lifetime, accessTokenExpiry, trustedProxies,
89495
88350
  * trustedNetworks)
89496
- * - cluster (clusterSecret — a `password` field: the value goes to the
89497
- * keychain, owner `system`; the row keeps a marker. Also drawn
89498
- * on System → Cluster. D797. MANDATORY and generated at first
89499
- * boot; `meshAuth` — transition / enforced. D802)
88351
+ * - cluster (no operator field remains: the cluster secret and the mesh auth
88352
+ * mode were removed by D857 — a node's identity is its mesh
88353
+ * certificate, and agents are paired, not given a secret)
89500
88354
  *
89501
88355
  * Sections NOT exposed (each owned by the addon that implements them):
89502
88356
  * - logging → `winston-logging` addon (level + retention)
@@ -89539,12 +88393,13 @@ var KEY_TO_SECTION = {
89539
88393
  tokenExpiry: "auth",
89540
88394
  accessTokenExpiry: "auth",
89541
88395
  trustedProxies: "auth",
89542
- trustedNetworks: "auth",
89543
- [CLUSTER_SECRET_FIELD]: "cluster",
89544
- [MESH_AUTH_MODE_FIELD]: "cluster"
88396
+ trustedNetworks: "auth"
89545
88397
  };
89546
- /** Bytes of a generated cluster secret (D802) — 256 bits, base64url (43 chars). */
89547
- var GENERATED_SECRET_BYTES = 32;
88398
+ /**
88399
+ * Keys a pre-D857 form may still send. They are dropped with a line, never
88400
+ * written: the cluster secret and the mesh auth mode no longer exist.
88401
+ */
88402
+ var RETIRED_CLUSTER_KEYS = new Set(["clusterSecret", "meshAuth"]);
89548
88403
  /**
89549
88404
  * Auth keys holding a list of networks (CIDR or single address). A save with
89550
88405
  * an entry that does not parse is REFUSED whole: dropping it would trust less
@@ -89555,7 +88410,6 @@ var CIDR_LIST_KEYS = new Set(["trustedProxies", "trustedNetworks"]);
89555
88410
  var SYSTEM_CONFIG_CLUSTER_SECTION_ID = "system-config-cluster";
89556
88411
  var SystemConfigAddon = class extends BaseAddon {
89557
88412
  id = "system-config";
89558
- clusterSecretKeeper = null;
89559
88413
  meshCaKeeper = null;
89560
88414
  meshReapTimer = null;
89561
88415
  meshGateTimers = /* @__PURE__ */ new Set();
@@ -89565,20 +88419,6 @@ var SystemConfigAddon = class extends BaseAddon {
89565
88419
  super({});
89566
88420
  }
89567
88421
  async onInitialize() {
89568
- this.clusterSecretKeeper = new ClusterSecretKeeper({
89569
- secrets: this.systemSecrets(),
89570
- readSection: async () => await this.ctx.settings?.getSection("cluster") ?? {},
89571
- logger: this.ctx.logger,
89572
- generateSecret: () => this.generateClusterSecret()
89573
- });
89574
- setProcessMeshEnforcer(async (reason) => {
89575
- await this.writeMeshAuthMode("enforced");
89576
- this.ctx.logger.info("Mesh authentication is now ENFORCED", { meta: { reason } });
89577
- });
89578
- await this.clusterSecretKeeper.start();
89579
- try {
89580
- await this.writeMeshAuthMode("enforced");
89581
- } catch {}
89582
88422
  this.meshCaKeeper = new MeshCaKeeper({
89583
88423
  secrets: this.systemSecrets(),
89584
88424
  readClusterSection: async () => await this.ctx.settings?.getSection("cluster") ?? {},
@@ -89592,14 +88432,10 @@ var SystemConfigAddon = class extends BaseAddon {
89592
88432
  this.ctx.logger.info("Initialized — exposes yml-backed sections via getGlobalSettings");
89593
88433
  return {
89594
88434
  customActions: systemConfigMeshActions,
89595
- actionHandlers: {
89596
- mintJoinToken: async (input) => this.mintJoinToken(parseMintJoinTokenInput(input)),
89597
- mintAgentUiUnlockTicket: async (input) => this.mintAgentUiUnlock(parseMintUnlockInput(input))
89598
- }
88435
+ actionHandlers: { mintAgentUiUnlockTicket: async (input) => this.mintAgentUiUnlock(parseMintUnlockInput(input)) }
89599
88436
  };
89600
88437
  }
89601
88438
  async onShutdown() {
89602
- setProcessMeshEnforcer(null);
89603
88439
  setProcessMeshCertificateRegistry(null);
89604
88440
  setProcessMeshTlsListenerState(null);
89605
88441
  setProcessAgentHttpsNames(null);
@@ -89610,8 +88446,6 @@ var SystemConfigAddon = class extends BaseAddon {
89610
88446
  this.meshGateTimers.clear();
89611
88447
  this.meshCaKeeper?.stop();
89612
88448
  this.meshCaKeeper = null;
89613
- this.clusterSecretKeeper?.stop();
89614
- this.clusterSecretKeeper = null;
89615
88449
  }
89616
88450
  /**
89617
88451
  * The hub's record of mesh leaves (D856): declared and loaded BEFORE the TLS
@@ -89702,32 +88536,9 @@ var SystemConfigAddon = class extends BaseAddon {
89702
88536
  onError: (err) => this.ctx.logger.error("Mesh certificate registry failed to load", { meta: { error: err instanceof Error ? err.message : String(err) } })
89703
88537
  });
89704
88538
  }
89705
- async mintJoinToken(input) {
89706
- const ca = requireProcessMeshCa();
89707
- const hubUrl = input.hubUrl ?? await this.resolveHubUrl();
89708
- const hubMeshAddress = input.hubMeshAddress ?? await this.resolveHubMeshAddress();
89709
- const token = await mintMeshJoinToken({
89710
- ca,
89711
- hubUrl,
89712
- hubMeshAddress,
89713
- nodeId: input.nodeId,
89714
- ...input.ttlSec !== void 0 ? { ttlSec: input.ttlSec } : {}
89715
- });
89716
- return {
89717
- token,
89718
- exp: readJwtExp(token),
89719
- hubUrl,
89720
- hubMeshAddress,
89721
- caFingerprint: caFingerprintCompact(ca.certPem),
89722
- nodeId: input.nodeId
89723
- };
89724
- }
89725
88539
  /** D821 — short-lived ticket for the agent's `:4444` unlock session. */
89726
88540
  async mintAgentUiUnlock(input) {
89727
- const secrets = this.systemSecrets();
89728
- if (secrets === null) throw new Error("system-config: no keychain door — cannot mint an unlock ticket");
89729
- const entry = (await secrets.resolve([CLUSTER_SECRET_REF]))[CLUSTER_SECRET_REF];
89730
- if (entry?.kind !== "value") throw new Error("system-config: cluster secret is not available — cannot mint an unlock ticket");
88541
+ const ca = requireProcessMeshCa();
89731
88542
  if (input.dashboardBaseUrl !== void 0) {
89732
88543
  const origins = await this.readAgentDashboardOrigins();
89733
88544
  if (!isAllowedAgentDashboardReturnTo(input.dashboardBaseUrl, origins)) throw new Error("dashboardBaseUrl is not an allowed agent dashboard origin (:4444 or agentDashboardOrigins)");
@@ -89735,10 +88546,10 @@ var SystemConfigAddon = class extends BaseAddon {
89735
88546
  if (!isKnownAgentAddress(input.dashboardBaseUrl, known, origins)) throw new Error(`dashboardBaseUrl is not an address this hub knows for "${input.nodeId}" - open the agent at its own address`);
89736
88547
  }
89737
88548
  const ticket = mintAgentUiUnlockTicket({
89738
- secretHash: hashClusterSecret(entry.value),
88549
+ caKeyPem: ca.keyPem,
89739
88550
  nodeId: input.nodeId
89740
88551
  });
89741
- const payloadJson = Buffer.from(ticket.split(".")[0] ?? "", "base64url").toString("utf8");
88552
+ const payloadJson = Buffer.from(ticket.split(".")[1] ?? "", "base64url").toString("utf8");
89742
88553
  const exp = JSON.parse(payloadJson).exp ?? 0;
89743
88554
  const dashboardUrl = input.dashboardBaseUrl === void 0 ? void 0 : (() => {
89744
88555
  const u = new URL(input.dashboardBaseUrl);
@@ -89759,24 +88570,6 @@ var SystemConfigAddon = class extends BaseAddon {
89759
88570
  return [];
89760
88571
  }
89761
88572
  }
89762
- async resolveHubUrl() {
89763
- try {
89764
- const book = await this.ctx.api?.localNetwork?.getAddresses?.query({ scheme: "https" });
89765
- const url = book?.roles?.publicEndpoint?.resolved ?? book?.roles?.internal?.resolved ?? null;
89766
- if (typeof url === "string" && url.length > 0) return url;
89767
- } catch {}
89768
- const host = (await this.ctx.settings?.getSection("server"))?.["host"];
89769
- const port = (await this.ctx.settings?.getSection("server"))?.["port"];
89770
- return `https://${typeof host === "string" && host.length > 0 && host !== "0.0.0.0" ? host : "127.0.0.1"}:${typeof port === "number" ? port : Number(port) || 4443}`;
89771
- }
89772
- async resolveHubMeshAddress() {
89773
- try {
89774
- const base = (await this.ctx.api?.localNetwork?.getAddresses?.query({}))?.roles?.internal?.resolved;
89775
- if (typeof base === "string" && base.length > 0) return `${new URL(base).hostname}:${DEFAULT_HUB_TLS_TCP_PORT}`;
89776
- } catch {}
89777
- const host = (await this.ctx.settings?.getSection("server"))?.["host"];
89778
- return `${typeof host === "string" && host.length > 0 && host !== "0.0.0.0" ? host : "127.0.0.1"}:${DEFAULT_HUB_TLS_TCP_PORT}`;
89779
- }
89780
88573
  /** The keychain handle for owner `system`; `null` where there is no door. */
89781
88574
  systemSecrets() {
89782
88575
  const forOwner = this.ctx.secrets?.forOwner ?? null;
@@ -89869,14 +88662,9 @@ var SystemConfigAddon = class extends BaseAddon {
89869
88662
  {
89870
88663
  id: SYSTEM_CONFIG_CLUSTER_SECTION_ID,
89871
88664
  title: SECTION_TITLES["cluster"],
89872
- description: "Agents joining this hub. Every agent proves the cluster secret when it connects (challenge–response; the secret never crosses the network).",
88665
+ description: "Agents join this hub by pairing (System → Cluster → Agents): the hub signs a certificate for the agent after the operator confirms the same 6-digit code on the hub and in the agent's own page. There is no shared cluster secret.",
89873
88666
  columns: 1,
89874
- fields: [{
89875
- type: "password",
89876
- key: CLUSTER_SECRET_FIELD,
89877
- label: "Cluster secret",
89878
- description: "Reveal and copy into each agent to pair. Saving a new value rotates it for every agent."
89879
- }]
88667
+ fields: []
89880
88668
  }
89881
88669
  ] };
89882
88670
  }
@@ -89899,9 +88687,9 @@ var SystemConfigAddon = class extends BaseAddon {
89899
88687
  }
89900
88688
  /**
89901
88689
  * The admin UI / `addonSettings` cap prefer {@link BaseAddon.updateGlobalSettingsAsOperator}.
89902
- * That BaseAddon path writes the generic addon store and never reaches
89903
- * {@link writeClusterSection}. Route the operator write through the same
89904
- * section-aware path as {@link updateGlobalSettings}.
88690
+ * That BaseAddon path writes the generic addon store and never reaches the
88691
+ * yml sections. Route the operator write through the same section-aware path
88692
+ * as {@link updateGlobalSettings}.
89905
88693
  */
89906
88694
  async updateGlobalSettingsAsOperator(patch, _nodeId) {
89907
88695
  await this.updateGlobalSettings(patch);
@@ -89918,6 +88706,10 @@ var SystemConfigAddon = class extends BaseAddon {
89918
88706
  this.ctx.logger.warn("cluster.agentDashboardOrigins is no longer writable from settings — ignoring this patch");
89919
88707
  continue;
89920
88708
  }
88709
+ if (RETIRED_CLUSTER_KEYS.has(key)) {
88710
+ this.ctx.logger.warn(`cluster.${key} is retired (D857) — agents are paired by certificate; ignoring this patch`);
88711
+ continue;
88712
+ }
89921
88713
  const section = KEY_TO_SECTION[key];
89922
88714
  if (section === void 0) {
89923
88715
  this.ctx.logger.warn("update ignored unknown key (not mapped to any section)", { meta: { key } });
@@ -89939,10 +88731,6 @@ var SystemConfigAddon = class extends BaseAddon {
89939
88731
  bucket[key] = value;
89940
88732
  }
89941
88733
  for (const [section, sectionPatch] of perSection) {
89942
- if (section === "cluster") {
89943
- await this.writeClusterSection(sectionPatch);
89944
- continue;
89945
- }
89946
88734
  await this.ctx.settings.setSection(section, sectionPatch);
89947
88735
  this.ctx.logger.info("Wrote keys to section", { meta: {
89948
88736
  section,
@@ -89950,79 +88738,7 @@ var SystemConfigAddon = class extends BaseAddon {
89950
88738
  } });
89951
88739
  }
89952
88740
  }
89953
- /**
89954
- * The cluster secret's save path: the value goes to the keychain (owner
89955
- * `system`), the row gets the marker; `''` / a view keeps. `$secretClear` is
89956
- * REFUSED: the secret is mandatory (D802). A value the keychain cannot store
89957
- * is REFUSED — never written inline (D787, P5). Mesh auth mode is always
89958
- * enforced (D821) — a legacy `meshAuth` in the patch is ignored.
89959
- */
89960
- async writeClusterSection(patch) {
89961
- const { [MESH_AUTH_MODE_FIELD]: _legacyMode, ...secretPatch } = patch;
89962
- await this.writeMeshAuthMode("enforced");
89963
- if (Object.keys(secretPatch).length === 0) return;
89964
- if (isSecretClear(secretPatch["clusterSecret"])) throw new Error("The cluster secret is mandatory and cannot be cleared — save a new value to rotate it");
89965
- await this.storeClusterSecret(secretPatch);
89966
- this.ctx.logger.info("Cluster secret updated");
89967
- await this.clusterSecretKeeper?.reload();
89968
- }
89969
- /** Persist + publish mesh auth mode (always `enforced` after D821). */
89970
- async writeMeshAuthMode(mode) {
89971
- await this.ctx.settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: mode });
89972
- setProcessMeshAuthMode(mode);
89973
- }
89974
- /**
89975
- * First boot, or a keychain reset that cleared the marker (D802): a fresh
89976
- * random secret, stored like an operator save. Mesh auth stays enforced
89977
- * (D821) — every agent must present the secret; there is no transition window.
89978
- */
89979
- async generateClusterSecret() {
89980
- const settings = this.ctx.settings;
89981
- const secrets = this.systemSecrets();
89982
- if (!settings || secrets === null) throw new Error("system-config: no keychain door — the cluster secret cannot be generated");
89983
- const existing = (await secrets.resolve([CLUSTER_SECRET_REF]))[CLUSTER_SECRET_REF];
89984
- if (existing?.kind === "value") {
89985
- await settings.setSection("cluster", { [CLUSTER_SECRET_FIELD]: makeSecretMarker(CLUSTER_SECRET_REF) });
89986
- this.ctx.logger.warn("The cluster secret row was empty but the keychain holds a value — re-adopted it");
89987
- return;
89988
- }
89989
- if (existing?.kind !== "missing") throw new Error(`the keychain cannot answer for the cluster secret (${existing?.kind ?? "no answer"})`);
89990
- const value = randomBytes(GENERATED_SECRET_BYTES).toString("base64url");
89991
- await this.storeClusterSecret({ [CLUSTER_SECRET_FIELD]: value });
89992
- await settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: "enforced" });
89993
- setProcessMeshAuthMode("enforced");
89994
- }
89995
- async storeClusterSecret(patch) {
89996
- const settings = this.ctx.settings;
89997
- if (!settings) throw new Error("system-config: ctx.settings not available");
89998
- const secrets = this.systemSecrets();
89999
- if (secrets === null) throw new Error("system-config: no keychain door — the cluster secret cannot be stored");
90000
- const stored = await settings.getSection("cluster");
90001
- const result = await transformSecretWrite({
90002
- tree: secretTreeFromSchema(this.buildGlobalSchema()),
90003
- secrets,
90004
- patch,
90005
- stored,
90006
- operatorWrite: true
90007
- });
90008
- if (Object.keys(result.patch).length > 0) await settings.setSection("cluster", result.patch);
90009
- const { orphaned } = await result.afterWrite();
90010
- if (orphaned.length > 0) this.ctx.logger.warn("an old cluster secret value could not be deleted from the keychain", { meta: { refs: orphaned } });
90011
- }
90012
88741
  };
90013
- function parseMintJoinTokenInput(input) {
90014
- if (input === null || input === void 0 || typeof input !== "object") throw new Error("system-config: mintJoinToken requires { nodeId }");
90015
- const nodeId = Reflect.get(input, "nodeId");
90016
- if (typeof nodeId !== "string" || nodeId.length === 0) throw new Error("system-config: mintJoinToken requires { nodeId } - the node the token is for");
90017
- const out = { nodeId };
90018
- const hubUrl = Reflect.get(input, "hubUrl");
90019
- const hubMeshAddress = Reflect.get(input, "hubMeshAddress");
90020
- const ttlSec = Reflect.get(input, "ttlSec");
90021
- if (typeof hubUrl === "string") out.hubUrl = hubUrl;
90022
- if (typeof hubMeshAddress === "string") out.hubMeshAddress = hubMeshAddress;
90023
- if (typeof ttlSec === "number") out.ttlSec = ttlSec;
90024
- return out;
90025
- }
90026
88742
  function parseMintUnlockInput(input) {
90027
88743
  if (input === null || typeof input !== "object") throw new Error("system-config: mintAgentUiUnlockTicket requires { nodeId }");
90028
88744
  const nodeId = Reflect.get(input, "nodeId");
@@ -90033,15 +88749,5 @@ function parseMintUnlockInput(input) {
90033
88749
  ...typeof dashboardBaseUrl === "string" ? { dashboardBaseUrl } : {}
90034
88750
  };
90035
88751
  }
90036
- /** Read `exp` from an unencrypted JWT payload (no verify — already minted here). */
90037
- function readJwtExp(token) {
90038
- const parts = token.split(".");
90039
- if (parts.length < 2 || parts[1] === void 0) return 0;
90040
- const json = Buffer.from(parts[1], "base64url").toString("utf8");
90041
- const payload = JSON.parse(json);
90042
- if (payload === null || typeof payload !== "object") return 0;
90043
- const exp = Reflect.get(payload, "exp");
90044
- return typeof exp === "number" ? exp : 0;
90045
- }
90046
88752
  //#endregion
90047
- export { requireProcessMeshCa as $, dropProcessPeer as A, setProcessMeshAuthStatusSource as At, openMeshTlsWhenRevocationsLoaded as B, Transporters as C, MESH_AUTH_MODE_FIELD as Ct, mintMeshJoinToken as D, parseMeshAuthMode as Dt, JoinTokenJtiStore as E, isMeshAuthMode as Et, onProcessNodeRegistered as F, readCidrSetting as Ft, RENEWAL_ABANDONED as G, MESH_NODE_CERTIFICATES_COLLECTION as H, setProcessMeshPeerTransportLookup as I, setProcessMeshCertificateRegistry as J, describeMeshLeaf as K, renewMeshCertificate as L, getProcessNodeMeshTransport as M, createCidrMatcher as Mt, getProcessPeerLeaf as N, normalizeIp as Nt, processJoinTokenJtiStore as O, requestProcessMeshEnforcement as Ot, isProcessVerifiedMeshPeer as P, parseCidrList as Pt, getProcessMeshCaState as Q, revokeMeshCertificate as R, ServiceBroker as S, SYSTEM_CONFIG_CLUSTER_SECTION_ID, verifyAgentUiUnlockTicketEs256 as St, SystemConfigAddon, SystemConfigAddon as default, enrollMeshNode as T, getProcessMeshAuthStatus as Tt, MESH_NODE_CERTIFICATES_COLUMNS as U, setProcessMeshTlsListenerState as V, MeshCertificateRegistry as W, processMeshCrl as X, MeshCrl as Y, MeshCaKeeper as Z, signPairingProof as _, isAllowedAgentDashboardReturnTo as _t, hubHostFromMeshAddress as a, generateMeshKeyPair as at, TlsTcpTransporter as b, parseAgentDashboardOrigins as bt, PAIRING_MAX_SESSIONS as c, issueMeshLeafFromCsr as ct, PAIRING_PROTOCOL as d, AGENT_UI_UNLOCK_DOMAIN as dt, caFingerprint as et, PAIRING_TTL_MS as f, AGENT_UI_UNLOCK_TTL_MS as ft, keyFingerprintShort as g, UnlockTicketJtiStore as gt, describeCsr as h, AGENT_UI_UNLOCK_VERSION as ht, AgentPairing as i, generateMeshCa as it, emitProcessNodeRegistered as j, setProcessMeshEnforcer as jt, verifyMeshJoinToken as k, setProcessMeshAuthMode as kt, PAIRING_PATHS as l, loadMeshCa as lt, computePairingSas as m, AGENT_UI_UNLOCK_USE as mt, HubPairing as n, createMeshCsrPem as nt, PAIRING_CODE_DIGITS as o, isValidAgentNodeId as ot, computePairingCommitment as p, AGENT_UI_UNLOCK_TYP as pt, getProcessMeshCertificateRegistry as q, PairingHttpError as r, exportMeshPrivateKeyPem as rt, PAIRING_MAX_CODE_ATTEMPTS as s, issueMeshLeafForNode as st, systemConfigMeshActions as t, caFingerprintCompact as tt, PAIRING_PIN_DIGITS as u, AGENT_DASHBOARD_ORIGINS_FIELD as ut, verifyPairingProof as v, mintAgentUiUnlockTicket as vt, setProcessMeshTlsEnabler as w, getProcessMeshAuthMode as wt, Errors as x, verifyAgentUiUnlockTicket as xt, MESH_TLS_PORT_HELLO_FIELD as y, mintAgentUiUnlockTicketEs256 as yt, getProcessMeshTlsListenerState as z };
88753
+ export { generateMeshKeyPair as $, setProcessMeshPeerTransportLookup as A, describeMeshLeaf as B, setProcessMeshTlsEnabler as C, getProcessPeerLeaf as D, getProcessNodeMeshTransport as E, setProcessMeshTlsListenerState as F, MeshCaKeeper as G, setProcessMeshCertificateRegistry as H, MESH_NODE_CERTIFICATES_COLLECTION as I, caFingerprint as J, getProcessMeshCaState as K, MESH_NODE_CERTIFICATES_COLUMNS as L, revokeMeshCertificate as M, getProcessMeshTlsListenerState as N, isProcessVerifiedMeshPeer as O, openMeshTlsWhenRevocationsLoaded as P, generateMeshCa as Q, MeshCertificateRegistry as R, ServiceBroker as S, SYSTEM_CONFIG_CLUSTER_SECTION_ID, SystemConfigAddon, SystemConfigAddon as default, emitProcessNodeRegistered as T, MeshCrl as U, getProcessMeshCertificateRegistry as V, processMeshCrl as W, createMeshCsrPem as X, caFingerprintCompact as Y, exportMeshPrivateKeyPem as Z, signPairingProof as _, readCidrSetting as _t, hubHostFromMeshAddress as a, AGENT_UI_UNLOCK_TTL_MS as at, TlsTcpTransporter as b, PAIRING_MAX_SESSIONS as c, AGENT_UI_UNLOCK_VERSION as ct, PAIRING_PROTOCOL as d, mintAgentUiUnlockTicket as dt, isValidAgentNodeId as et, PAIRING_TTL_MS as f, parseAgentDashboardOrigins as ft, keyFingerprintShort as g, parseCidrList as gt, describeCsr as h, normalizeIp as ht, AgentPairing as i, AGENT_DASHBOARD_ORIGINS_FIELD as it, renewMeshCertificate as j, onProcessNodeRegistered as k, PAIRING_PATHS as l, UnlockTicketJtiStore as lt, computePairingSas as m, createCidrMatcher as mt, HubPairing as n, issueMeshLeafFromCsr as nt, PAIRING_CODE_DIGITS as o, AGENT_UI_UNLOCK_TYP as ot, computePairingCommitment as p, verifyAgentUiUnlockTicket as pt, requireProcessMeshCa as q, PairingHttpError as r, loadMeshCa as rt, PAIRING_MAX_CODE_ATTEMPTS as s, AGENT_UI_UNLOCK_USE as st, systemConfigMeshActions as t, issueMeshLeafForNode as tt, PAIRING_PIN_DIGITS as u, isAllowedAgentDashboardReturnTo as ut, verifyPairingProof as v, dropProcessPeer as w, Errors as x, MESH_TLS_PORT_HELLO_FIELD as y, RENEWAL_ABANDONED as z };