@camstack/system 1.2.385 → 1.2.387

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.
@@ -15,5 +15,17 @@ export declare const systemConfigMeshActions: {
15
15
  }, z.core.$strip>, "mutation", "admin", {
16
16
  readonly kind: "system";
17
17
  }>;
18
+ /** Hub admin → short-lived ticket the agent redeems for a tab session (D821). */
19
+ mintAgentUiUnlockTicket: import('@camstack/types').CustomActionSpec<z.ZodObject<{
20
+ nodeId: z.ZodString;
21
+ dashboardBaseUrl: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strip>, z.ZodObject<{
23
+ ticket: z.ZodString;
24
+ exp: z.ZodNumber;
25
+ nodeId: z.ZodString;
26
+ dashboardUrl: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$strip>, "mutation", "admin", {
28
+ readonly kind: "system";
29
+ }>;
18
30
  };
19
31
  export type SystemConfigMeshActions = typeof systemConfigMeshActions;
@@ -23,6 +23,17 @@ export declare class SystemConfigAddon extends BaseAddon {
23
23
  }, import('zod/v4/core').$strip>, "mutation", "admin", {
24
24
  readonly kind: "system";
25
25
  }>;
26
+ mintAgentUiUnlockTicket: import('@camstack/types').CustomActionSpec<import('zod').ZodObject<{
27
+ nodeId: import('zod').ZodString;
28
+ dashboardBaseUrl: import('zod').ZodOptional<import('zod').ZodString>;
29
+ }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
30
+ ticket: import('zod').ZodString;
31
+ exp: import('zod').ZodNumber;
32
+ nodeId: import('zod').ZodString;
33
+ dashboardUrl: import('zod').ZodOptional<import('zod').ZodString>;
34
+ }, import('zod/v4/core').$strip>, "mutation", "admin", {
35
+ readonly kind: "system";
36
+ }>;
26
37
  };
27
38
  actionHandlers: {
28
39
  mintJoinToken: (input: unknown) => Promise<{
@@ -33,6 +44,12 @@ export declare class SystemConfigAddon extends BaseAddon {
33
44
  caFingerprint: string;
34
45
  nodeId?: string;
35
46
  }>;
47
+ mintAgentUiUnlockTicket: (input: unknown) => Promise<{
48
+ ticket: string;
49
+ exp: number;
50
+ nodeId: string;
51
+ dashboardUrl?: string;
52
+ }>;
36
53
  };
37
54
  }>;
38
55
  protected onShutdown(): Promise<void>;
@@ -44,6 +61,9 @@ export declare class SystemConfigAddon extends BaseAddon {
44
61
  private applyStoredClearPort;
45
62
  private onMeshCaReady;
46
63
  private mintJoinToken;
64
+ /** D821 — short-lived ticket for the agent's `:4444` unlock session. */
65
+ private mintAgentUiUnlock;
66
+ private readAgentDashboardOrigins;
47
67
  private resolveHubUrl;
48
68
  private resolveHubMeshAddress;
49
69
  /** The keychain handle for owner `system`; `null` where there is no door. */
@@ -55,7 +75,8 @@ export declare class SystemConfigAddon extends BaseAddon {
55
75
  * The cluster secret's save path: the value goes to the keychain (owner
56
76
  * `system`), the row gets the marker; `''` / a view keeps. `$secretClear` is
57
77
  * REFUSED: the secret is mandatory (D802). A value the keychain cannot store
58
- * is REFUSED — never written inline (D787, P5). `meshAuth` is a plain value.
78
+ * is REFUSED — never written inline (D787, P5). Mesh auth mode is always
79
+ * enforced (D821) — a legacy `meshAuth` in the patch is ignored.
59
80
  */
60
81
  private writeClusterSection;
61
82
  /**
@@ -64,13 +85,12 @@ export declare class SystemConfigAddon extends BaseAddon {
64
85
  * Persist, then apply — an `on` save always passes.
65
86
  */
66
87
  private writeMeshClearPort;
67
- /** `cluster.meshAuth` (D802), then republish it. */
88
+ /** Persist + publish mesh auth mode (always `enforced` after D821). */
68
89
  private writeMeshAuthMode;
69
90
  /**
70
91
  * First boot, or a keychain reset that cleared the marker (D802): a fresh
71
- * random secret, stored like an operator save. A NEW secret means every
72
- * agent must be given it, so the mesh goes (back) to `transition`; on a hub
73
- * with no agent yet, the hub's auto-enforce ends it at once.
92
+ * random secret, stored like an operator save. Mesh auth stays enforced
93
+ * (D821) — every agent must present the secret; there is no transition window.
74
94
  */
75
95
  private generateClusterSecret;
76
96
  private storeClusterSecret;
@@ -150,6 +150,145 @@ function getProcessMeshAuthStatus() {
150
150
  return typeof source === "function" ? source() : null;
151
151
  }
152
152
  //#endregion
153
+ //#region src/kernel/moleculer/agent-ui-unlock.ts
154
+ /**
155
+ * Agent UI remote unlock tickets (D821).
156
+ *
157
+ * A hub admin mints a short-lived HMAC ticket bound to one agent `nodeId`.
158
+ * The agent redeems it once for a tab session Bearer — the cluster secret
159
+ * stays a mesh / pairing credential and is not pasted into the browser for
160
+ * daily unlock.
161
+ *
162
+ * K = SHA-256(cluster secret) — same as mesh join (D802)
163
+ * ticket = base64url(JSON payload) + '.' + base64url(HMAC-SHA256(K, DOMAIN|payloadB64))
164
+ * payload = { v: 1, nodeId, exp (ms), jti }
165
+ */
166
+ var AGENT_UI_UNLOCK_DOMAIN = "camstack-agent-ui-unlock/v1";
167
+ var AGENT_UI_UNLOCK_VERSION = 1;
168
+ /** Ticket lifetime — long enough for a redirect hop, short enough to steal poorly. */
169
+ var AGENT_UI_UNLOCK_TTL_MS = 5 * 6e4;
170
+ /**
171
+ * Cluster settings key: extra origins allowed as `returnTo` for Continue-with-hub
172
+ * (NPM hostnames that are not `:4444` on a LAN IP). Comma-separated origins.
173
+ */
174
+ var AGENT_DASHBOARD_ORIGINS_FIELD = "agentDashboardOrigins";
175
+ /** In-memory spent `jti` until `exp` (ms). */
176
+ var UnlockTicketJtiStore = class {
177
+ spent = /* @__PURE__ */ new Map();
178
+ isSpent(jti, nowMs = Date.now()) {
179
+ this.gc(nowMs);
180
+ return this.spent.has(jti);
181
+ }
182
+ /** Record a jti as used until `exp`. Returns false if already spent. */
183
+ consume(jti, exp, nowMs = Date.now()) {
184
+ this.gc(nowMs);
185
+ if (this.spent.has(jti)) return false;
186
+ this.spent.set(jti, exp);
187
+ return true;
188
+ }
189
+ /** Test helper. */
190
+ size() {
191
+ return this.spent.size;
192
+ }
193
+ gc(nowMs) {
194
+ for (const [jti, exp] of this.spent) if (exp <= nowMs) this.spent.delete(jti);
195
+ }
196
+ };
197
+ function mintAgentUiUnlockTicket(input) {
198
+ const nowMs = input.nowMs ?? Date.now();
199
+ const ttl = Math.min(Math.max(1e3, input.ttlMs ?? 3e5), AGENT_UI_UNLOCK_TTL_MS);
200
+ const payload = {
201
+ v: 1,
202
+ nodeId: input.nodeId,
203
+ exp: nowMs + ttl,
204
+ jti: input.jti ?? (0, node_crypto.randomBytes)(16).toString("base64url")
205
+ };
206
+ const payloadB64 = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
207
+ return `${payloadB64}.${sign(input.secretHash, payloadB64)}`;
208
+ }
209
+ /**
210
+ * Verify a ticket under `secretHash`. Returns the payload, or `null` when the
211
+ * signature, shape, expiry or nodeId fails (no reason string — refuse quietly).
212
+ */
213
+ function verifyAgentUiUnlockTicket(secretHash, ticket, options) {
214
+ const nowMs = options.nowMs ?? Date.now();
215
+ const dot = ticket.indexOf(".");
216
+ if (dot <= 0 || dot === ticket.length - 1) return null;
217
+ const payloadB64 = ticket.slice(0, dot);
218
+ const sig = ticket.slice(dot + 1);
219
+ if (!sigsEqual(sign(secretHash, payloadB64), sig)) return null;
220
+ let parsed;
221
+ try {
222
+ parsed = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
223
+ } catch {
224
+ return null;
225
+ }
226
+ const payload = parsePayload(parsed);
227
+ if (payload === null) return null;
228
+ if (payload.exp <= nowMs) return null;
229
+ if (payload.nodeId !== options.expectedNodeId) return null;
230
+ return payload;
231
+ }
232
+ /**
233
+ * True when `returnTo` may receive a ticket redirect. Default: any http(s)
234
+ * origin whose port is 4444. Extra origins come from
235
+ * {@link AGENT_DASHBOARD_ORIGINS_FIELD} (exact origin match).
236
+ */
237
+ function isAllowedAgentDashboardReturnTo(returnTo, extraOrigins = []) {
238
+ let url;
239
+ try {
240
+ url = new URL(returnTo);
241
+ } catch {
242
+ return false;
243
+ }
244
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
245
+ if (url.username !== "" || url.password !== "") return false;
246
+ if (url.pathname !== "/" && url.pathname !== "") return false;
247
+ if (url.port === "4444") return true;
248
+ const origin = url.origin;
249
+ return extraOrigins.some((o) => o === origin);
250
+ }
251
+ /** Parse a stored comma/whitespace-separated origins list. */
252
+ function parseAgentDashboardOrigins(stored) {
253
+ if (typeof stored !== "string" || stored.trim().length === 0) return [];
254
+ const out = [];
255
+ for (const part of stored.split(/[,\s]+/)) {
256
+ const t = part.trim();
257
+ if (t.length === 0) continue;
258
+ try {
259
+ const u = new URL(t);
260
+ if (u.protocol === "http:" || u.protocol === "https:") out.push(u.origin);
261
+ } catch {}
262
+ }
263
+ return out;
264
+ }
265
+ function sign(secretHash, payloadB64) {
266
+ return (0, node_crypto.createHmac)("sha256", Buffer.from(secretHash, "utf8")).update(`${AGENT_UI_UNLOCK_DOMAIN}|${payloadB64}`, "utf8").digest("base64url");
267
+ }
268
+ function sigsEqual(expected, presented) {
269
+ const a = Buffer.from(expected, "utf8");
270
+ const b = Buffer.from(presented, "utf8");
271
+ if (a.length !== b.length) return false;
272
+ return (0, node_crypto.timingSafeEqual)(a, b);
273
+ }
274
+ function parsePayload(value) {
275
+ if (typeof value !== "object" || value === null) return null;
276
+ const rec = value;
277
+ if (rec["v"] !== 1) return null;
278
+ const nodeId = rec["nodeId"];
279
+ const exp = rec["exp"];
280
+ const jti = rec["jti"];
281
+ if (typeof nodeId !== "string" || nodeId.length === 0) return null;
282
+ if (typeof exp !== "number" || !Number.isFinite(exp)) return null;
283
+ if (typeof jti !== "string" || jti.length === 0) return null;
284
+ return {
285
+ v: 1,
286
+ nodeId,
287
+ exp,
288
+ jti
289
+ };
290
+ }
291
+ //#endregion
153
292
  //#region src/kernel/moleculer/mesh-mtls/mesh-ca.ts
154
293
  require_x509_es.cryptoProvider.set(node_crypto.webcrypto);
155
294
  var MS_PER_DAY = 864e5;
@@ -87660,7 +87799,7 @@ function isTlsSocket(socket) {
87660
87799
  * - no marker → the secret is MANDATORY (D802): the keeper asks
87661
87800
  * its owner to generate one (`generateSecret`), and
87662
87801
  * until that succeeds the gate is `off` — no node can
87663
- * authenticate, and only `transition` lets them in;
87802
+ * authenticate (mesh auth is always enforced, D821);
87664
87803
  * - marker + value → `on` — nodes prove the secret;
87665
87804
  * - marker, no answer → `unknown` — refused, retryably, and retried here.
87666
87805
  * A locked keychain or a value lost to a reset NEVER opens the gate, and
@@ -87723,7 +87862,7 @@ var ClusterSecretKeeper = class {
87723
87862
  reason: `settings unreadable: ${errText(err)}`
87724
87863
  };
87725
87864
  }
87726
- setProcessMeshAuthMode(parseMeshAuthMode(section[MESH_AUTH_MODE_FIELD]));
87865
+ setProcessMeshAuthMode("enforced");
87727
87866
  const stored = section[CLUSTER_SECRET_FIELD];
87728
87867
  if (stored === void 0 || stored === null || stored === "") return { kind: "off" };
87729
87868
  if (!require_dist.isSecretMarker(stored)) return {
@@ -87791,28 +87930,48 @@ function errText(err) {
87791
87930
  //#endregion
87792
87931
  //#region src/builtins/system-config/mesh-actions.ts
87793
87932
  /**
87794
- * System → Cluster — mint a single-use mesh join token (Release A).
87933
+ * System → Cluster — mint a single-use mesh join token (Release A) and
87934
+ * agent-ui unlock tickets (D821).
87795
87935
  */
87796
- var systemConfigMeshActions = require_dist.defineCustomActions({ mintJoinToken: require_dist.customAction(zod.z.object({
87797
- /** Optional pinned node id for the CSR CN. */
87798
- nodeId: zod.z.string().min(1).optional(),
87799
- /** Override hub HTTPS base (default: local-network publicEndpoint). */
87800
- hubUrl: zod.z.string().url().optional(),
87801
- /** Override `host:6000` clear mesh address carried in the token. */
87802
- hubMeshAddress: zod.z.string().min(3).optional(),
87803
- /** Seconds until expiry; capped at 1 h. */
87804
- ttlSec: zod.z.number().int().positive().max(3600).optional()
87805
- }).optional(), zod.z.object({
87806
- token: zod.z.string().min(1),
87807
- exp: zod.z.number().int(),
87808
- hubUrl: zod.z.string(),
87809
- hubMeshAddress: zod.z.string(),
87810
- caFingerprint: zod.z.string(),
87811
- nodeId: zod.z.string().optional()
87812
- }), {
87813
- kind: "mutation",
87814
- auth: "admin"
87815
- }) });
87936
+ var systemConfigMeshActions = require_dist.defineCustomActions({
87937
+ mintJoinToken: require_dist.customAction(zod.z.object({
87938
+ /** Optional pinned node id for the CSR CN. */
87939
+ nodeId: zod.z.string().min(1).optional(),
87940
+ /** Override hub HTTPS base (default: local-network publicEndpoint). */
87941
+ hubUrl: zod.z.string().url().optional(),
87942
+ /** Override `host:6000` clear mesh address carried in the token. */
87943
+ hubMeshAddress: zod.z.string().min(3).optional(),
87944
+ /** Seconds until expiry; capped at 1 h. */
87945
+ ttlSec: zod.z.number().int().positive().max(3600).optional()
87946
+ }).optional(), zod.z.object({
87947
+ token: zod.z.string().min(1),
87948
+ exp: zod.z.number().int(),
87949
+ hubUrl: zod.z.string(),
87950
+ hubMeshAddress: zod.z.string(),
87951
+ caFingerprint: zod.z.string(),
87952
+ nodeId: zod.z.string().optional()
87953
+ }), {
87954
+ kind: "mutation",
87955
+ auth: "admin"
87956
+ }),
87957
+ /** Hub admin → short-lived ticket the agent redeems for a tab session (D821). */
87958
+ mintAgentUiUnlockTicket: require_dist.customAction(zod.z.object({
87959
+ nodeId: zod.z.string().min(1),
87960
+ /**
87961
+ * Preferred agent dashboard base (`http://host:4444`). When set, the
87962
+ * answer includes `dashboardUrl` with `?ticket=` already attached.
87963
+ */
87964
+ dashboardBaseUrl: zod.z.string().url().optional()
87965
+ }), zod.z.object({
87966
+ ticket: zod.z.string().min(1),
87967
+ exp: zod.z.number().int(),
87968
+ nodeId: zod.z.string().min(1),
87969
+ dashboardUrl: zod.z.string().url().optional()
87970
+ }), {
87971
+ kind: "mutation",
87972
+ auth: "admin"
87973
+ })
87974
+ });
87816
87975
  //#endregion
87817
87976
  //#region src/builtins/system-config/system-config.addon.ts
87818
87977
  /**
@@ -87877,7 +88036,8 @@ var KEY_TO_SECTION = {
87877
88036
  trustedNetworks: "auth",
87878
88037
  [CLUSTER_SECRET_FIELD]: "cluster",
87879
88038
  [MESH_AUTH_MODE_FIELD]: "cluster",
87880
- [MESH_CLEAR_PORT_FIELD]: "cluster"
88039
+ [MESH_CLEAR_PORT_FIELD]: "cluster",
88040
+ [AGENT_DASHBOARD_ORIGINS_FIELD]: "cluster"
87881
88041
  };
87882
88042
  /** Bytes of a generated cluster secret (D802) — 256 bits, base64url (43 chars). */
87883
88043
  var GENERATED_SECRET_BYTES = 32;
@@ -87886,10 +88046,10 @@ var STATE_LABELS = {
87886
88046
  legacy: "legacy hash only — update the agent",
87887
88047
  unauthenticated: "not authenticated — enter the cluster secret in this agent"
87888
88048
  };
87889
- /** The Cluster page's live line per node (D802). */
88049
+ /** The Cluster page's live line per node (D802). Mode is always enforced (D821). */
87890
88050
  function describeMeshStatus(status) {
87891
88051
  if (status === null) return "The mesh status is available on the hub only.";
87892
- const head = status.mode === "enforced" ? "Enforced: a node that has not authenticated is refused." : "Transition: nodes without the secret are still let in (with a warning). The hub switches to Enforced by itself once every known agent has authenticated.";
88052
+ const head = "Enforced: a node that has not authenticated is refused.";
87893
88053
  if (status.nodes.length === 0) return `${head}\nNo other node is connected.`;
87894
88054
  return `${head}\n${status.nodes.map((n) => `• ${n.nodeId}: ${STATE_LABELS[n.state] ?? n.state}`).join("\n")}`;
87895
88055
  }
@@ -87920,6 +88080,9 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
87920
88080
  this.ctx.logger.info("Mesh authentication is now ENFORCED", { meta: { reason } });
87921
88081
  });
87922
88082
  await this.clusterSecretKeeper.start();
88083
+ try {
88084
+ await this.writeMeshAuthMode("enforced");
88085
+ } catch {}
87923
88086
  this.meshCaKeeper = new MeshCaKeeper({
87924
88087
  secrets: this.systemSecrets(),
87925
88088
  readClusterSection: async () => await this.ctx.settings?.getSection("cluster") ?? {},
@@ -87934,7 +88097,10 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
87934
88097
  this.ctx.logger.info("Initialized — exposes yml-backed sections via getGlobalSettings");
87935
88098
  return {
87936
88099
  customActions: systemConfigMeshActions,
87937
- actionHandlers: { mintJoinToken: async (input) => this.mintJoinToken(parseMintJoinTokenInput(input)) }
88100
+ actionHandlers: {
88101
+ mintJoinToken: async (input) => this.mintJoinToken(parseMintJoinTokenInput(input)),
88102
+ mintAgentUiUnlockTicket: async (input) => this.mintAgentUiUnlock(parseMintUnlockInput(input))
88103
+ }
87938
88104
  };
87939
88105
  }
87940
88106
  async onShutdown() {
@@ -87987,6 +88153,41 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
87987
88153
  ...input.nodeId !== void 0 ? { nodeId: input.nodeId } : {}
87988
88154
  };
87989
88155
  }
88156
+ /** D821 — short-lived ticket for the agent's `:4444` unlock session. */
88157
+ async mintAgentUiUnlock(input) {
88158
+ const secrets = this.systemSecrets();
88159
+ if (secrets === null) throw new Error("system-config: no keychain door — cannot mint an unlock ticket");
88160
+ const entry = (await secrets.resolve([require_constants$8.CLUSTER_SECRET_REF]))[require_constants$8.CLUSTER_SECRET_REF];
88161
+ if (entry?.kind !== "value") throw new Error("system-config: cluster secret is not available — cannot mint an unlock ticket");
88162
+ if (input.dashboardBaseUrl !== void 0) {
88163
+ const origins = await this.readAgentDashboardOrigins();
88164
+ if (!isAllowedAgentDashboardReturnTo(input.dashboardBaseUrl, origins)) throw new Error("dashboardBaseUrl is not an allowed agent dashboard origin (:4444 or agentDashboardOrigins)");
88165
+ }
88166
+ const ticket = mintAgentUiUnlockTicket({
88167
+ secretHash: require_constants$8.hashClusterSecret(entry.value),
88168
+ nodeId: input.nodeId
88169
+ });
88170
+ const payloadJson = Buffer.from(ticket.split(".")[0] ?? "", "base64url").toString("utf8");
88171
+ const exp = JSON.parse(payloadJson).exp ?? 0;
88172
+ const dashboardUrl = input.dashboardBaseUrl === void 0 ? void 0 : (() => {
88173
+ const u = new URL(input.dashboardBaseUrl);
88174
+ u.searchParams.set("ticket", ticket);
88175
+ return u.toString();
88176
+ })();
88177
+ return {
88178
+ ticket,
88179
+ exp,
88180
+ nodeId: input.nodeId,
88181
+ ...dashboardUrl !== void 0 ? { dashboardUrl } : {}
88182
+ };
88183
+ }
88184
+ async readAgentDashboardOrigins() {
88185
+ try {
88186
+ return parseAgentDashboardOrigins((await this.ctx.settings?.getSection("cluster"))?.[AGENT_DASHBOARD_ORIGINS_FIELD]);
88187
+ } catch {
88188
+ return [];
88189
+ }
88190
+ }
87990
88191
  async resolveHubUrl() {
87991
88192
  try {
87992
88193
  const book = await this.ctx.api?.localNetwork?.getAddresses?.query({ scheme: "https" });
@@ -88106,20 +88307,6 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
88106
88307
  label: "Cluster secret",
88107
88308
  description: "Mandatory — generated by the hub at first boot and stored in the keychain. To pair an agent: Reveal and copy it here, then enter it in the agent (its status page, or the desktop setup window). Saving a new value rotates it: every agent must then be given the new one."
88108
88309
  },
88109
- {
88110
- type: "select",
88111
- key: MESH_AUTH_MODE_FIELD,
88112
- label: "Agents without the secret",
88113
- description: "Transition lets an agent that has not authenticated keep working (with a warning) while you give each agent the secret; it ends by itself once every known agent has authenticated. Enforced refuses it.",
88114
- default: "transition",
88115
- options: [{
88116
- value: "transition",
88117
- label: "Transition — allow, warn"
88118
- }, {
88119
- value: "enforced",
88120
- label: "Enforced — refuse"
88121
- }]
88122
- },
88123
88310
  {
88124
88311
  type: "select",
88125
88312
  key: MESH_CLEAR_PORT_FIELD,
@@ -88141,6 +88328,12 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
88141
88328
  content: describeMeshStatus(getProcessMeshAuthStatus()),
88142
88329
  variant: "info"
88143
88330
  },
88331
+ {
88332
+ type: "string",
88333
+ key: AGENT_DASHBOARD_ORIGINS_FIELD,
88334
+ label: "Agent dashboard origins (NPM)",
88335
+ description: "Extra origins allowed for Continue-with-hub unlock redirects (comma-separated), e.g. https://agent.example.com. LAN agents on :4444 are always allowed."
88336
+ },
88144
88337
  {
88145
88338
  type: "addon-action-button",
88146
88339
  key: "mintJoinTokenAction",
@@ -88213,15 +88406,14 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
88213
88406
  * The cluster secret's save path: the value goes to the keychain (owner
88214
88407
  * `system`), the row gets the marker; `''` / a view keeps. `$secretClear` is
88215
88408
  * REFUSED: the secret is mandatory (D802). A value the keychain cannot store
88216
- * is REFUSED — never written inline (D787, P5). `meshAuth` is a plain value.
88409
+ * is REFUSED — never written inline (D787, P5). Mesh auth mode is always
88410
+ * enforced (D821) — a legacy `meshAuth` in the patch is ignored.
88217
88411
  */
88218
88412
  async writeClusterSection(patch) {
88219
- const { [MESH_AUTH_MODE_FIELD]: mode, [MESH_CLEAR_PORT_FIELD]: clearPort, ...secretPatch } = patch;
88413
+ const { [MESH_AUTH_MODE_FIELD]: _legacyMode, [MESH_CLEAR_PORT_FIELD]: clearPort, [AGENT_DASHBOARD_ORIGINS_FIELD]: dashboardOrigins, ...secretPatch } = patch;
88220
88414
  if (clearPort !== void 0) await this.writeMeshClearPort(clearPort);
88221
- if (mode !== void 0) {
88222
- if (!isMeshAuthMode(mode)) throw new Error(`system-config: meshAuth must be 'transition' or 'enforced'`);
88223
- await this.writeMeshAuthMode(mode);
88224
- }
88415
+ if (dashboardOrigins !== void 0) await this.ctx.settings.setSection("cluster", { [AGENT_DASHBOARD_ORIGINS_FIELD]: typeof dashboardOrigins === "string" ? dashboardOrigins : "" });
88416
+ await this.writeMeshAuthMode("enforced");
88225
88417
  if (Object.keys(secretPatch).length === 0) return;
88226
88418
  if (require_dist.isSecretClear(secretPatch["clusterSecret"])) throw new Error("The cluster secret is mandatory and cannot be cleared — save a new value to rotate it");
88227
88419
  await this.storeClusterSecret(secretPatch);
@@ -88249,16 +88441,15 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
88249
88441
  await applyProcessMeshClearPort(value);
88250
88442
  this.ctx.logger.info("Mesh clear port switched", { meta: { mode: value } });
88251
88443
  }
88252
- /** `cluster.meshAuth` (D802), then republish it. */
88444
+ /** Persist + publish mesh auth mode (always `enforced` after D821). */
88253
88445
  async writeMeshAuthMode(mode) {
88254
88446
  await this.ctx.settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: mode });
88255
- await this.clusterSecretKeeper?.reload();
88447
+ setProcessMeshAuthMode(mode);
88256
88448
  }
88257
88449
  /**
88258
88450
  * First boot, or a keychain reset that cleared the marker (D802): a fresh
88259
- * random secret, stored like an operator save. A NEW secret means every
88260
- * agent must be given it, so the mesh goes (back) to `transition`; on a hub
88261
- * with no agent yet, the hub's auto-enforce ends it at once.
88451
+ * random secret, stored like an operator save. Mesh auth stays enforced
88452
+ * (D821) — every agent must present the secret; there is no transition window.
88262
88453
  */
88263
88454
  async generateClusterSecret() {
88264
88455
  const settings = this.ctx.settings;
@@ -88273,7 +88464,8 @@ var SystemConfigAddon = class extends require_dist.BaseAddon {
88273
88464
  if (existing?.kind !== "missing") throw new Error(`the keychain cannot answer for the cluster secret (${existing?.kind ?? "no answer"})`);
88274
88465
  const value = (0, node_crypto.randomBytes)(GENERATED_SECRET_BYTES).toString("base64url");
88275
88466
  await this.storeClusterSecret({ [CLUSTER_SECRET_FIELD]: value });
88276
- await settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: "transition" });
88467
+ await settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: "enforced" });
88468
+ setProcessMeshAuthMode("enforced");
88277
88469
  }
88278
88470
  async storeClusterSecret(patch) {
88279
88471
  const settings = this.ctx.settings;
@@ -88306,6 +88498,16 @@ function parseMintJoinTokenInput(input) {
88306
88498
  if (typeof ttlSec === "number") out.ttlSec = ttlSec;
88307
88499
  return out;
88308
88500
  }
88501
+ function parseMintUnlockInput(input) {
88502
+ if (input === null || typeof input !== "object") throw new Error("system-config: mintAgentUiUnlockTicket requires { nodeId }");
88503
+ const nodeId = Reflect.get(input, "nodeId");
88504
+ if (typeof nodeId !== "string" || nodeId.length === 0) throw new Error("system-config: mintAgentUiUnlockTicket requires { nodeId }");
88505
+ const dashboardBaseUrl = Reflect.get(input, "dashboardBaseUrl");
88506
+ return {
88507
+ nodeId,
88508
+ ...typeof dashboardBaseUrl === "string" ? { dashboardBaseUrl } : {}
88509
+ };
88510
+ }
88309
88511
  /** Read `exp` from an unencrypted JWT payload (no verify — already minted here). */
88310
88512
  function readJwtExp(token) {
88311
88513
  const parts = token.split(".");
@@ -88317,6 +88519,10 @@ function readJwtExp(token) {
88317
88519
  return typeof exp === "number" ? exp : 0;
88318
88520
  }
88319
88521
  //#endregion
88522
+ exports.AGENT_DASHBOARD_ORIGINS_FIELD = AGENT_DASHBOARD_ORIGINS_FIELD;
88523
+ exports.AGENT_UI_UNLOCK_DOMAIN = AGENT_UI_UNLOCK_DOMAIN;
88524
+ exports.AGENT_UI_UNLOCK_TTL_MS = AGENT_UI_UNLOCK_TTL_MS;
88525
+ exports.AGENT_UI_UNLOCK_VERSION = AGENT_UI_UNLOCK_VERSION;
88320
88526
  exports.Errors = Errors;
88321
88527
  exports.JoinTokenJtiStore = JoinTokenJtiStore;
88322
88528
  exports.MESH_AUTH_MODE_FIELD = MESH_AUTH_MODE_FIELD;
@@ -88328,6 +88534,7 @@ exports.ServiceBroker = ServiceBroker;
88328
88534
  exports.SystemConfigAddon = SystemConfigAddon;
88329
88535
  exports.default = SystemConfigAddon;
88330
88536
  exports.TlsTcpTransporter = TlsTcpTransporter;
88537
+ exports.UnlockTicketJtiStore = UnlockTicketJtiStore;
88331
88538
  exports.applyProcessMeshClearPort = applyProcessMeshClearPort;
88332
88539
  exports.caFingerprint = caFingerprint;
88333
88540
  exports.caFingerprintCompact = caFingerprintCompact;
@@ -88343,12 +88550,15 @@ exports.getProcessMeshAuthMode = getProcessMeshAuthMode;
88343
88550
  exports.getProcessMeshAuthStatus = getProcessMeshAuthStatus;
88344
88551
  exports.getProcessMeshCaState = getProcessMeshCaState;
88345
88552
  exports.getProcessNodeMeshTransport = getProcessNodeMeshTransport;
88553
+ exports.isAllowedAgentDashboardReturnTo = isAllowedAgentDashboardReturnTo;
88346
88554
  exports.isMeshAuthMode = isMeshAuthMode;
88347
88555
  exports.issueMeshLeafForNode = issueMeshLeafForNode;
88348
88556
  exports.issueMeshLeafFromCsr = issueMeshLeafFromCsr;
88349
88557
  exports.loadMeshCa = loadMeshCa;
88558
+ exports.mintAgentUiUnlockTicket = mintAgentUiUnlockTicket;
88350
88559
  exports.mintMeshJoinToken = mintMeshJoinToken;
88351
88560
  exports.normalizeIp = normalizeIp;
88561
+ exports.parseAgentDashboardOrigins = parseAgentDashboardOrigins;
88352
88562
  exports.parseCidrList = parseCidrList;
88353
88563
  exports.parseMeshAuthMode = parseMeshAuthMode;
88354
88564
  exports.processJoinTokenJtiStore = processJoinTokenJtiStore;
@@ -88362,4 +88572,5 @@ exports.setProcessMeshClearPortControl = setProcessMeshClearPortControl;
88362
88572
  exports.setProcessMeshEnforcer = setProcessMeshEnforcer;
88363
88573
  exports.setProcessMeshTlsEnabler = setProcessMeshTlsEnabler;
88364
88574
  exports.systemConfigMeshActions = systemConfigMeshActions;
88575
+ exports.verifyAgentUiUnlockTicket = verifyAgentUiUnlockTicket;
88365
88576
  exports.verifyMeshJoinToken = verifyMeshJoinToken;
@@ -4,7 +4,7 @@ import { n as require_safe_buffer, t as require_ms } from "../../ms-DlIWNkkk.mjs
4
4
  import { a as MESH_CA_FIELD, c as MESH_CA_VALIDITY_DAYS, f as CLUSTER_SECRET_REF, i as MESH_CA_COMMON_NAME, l as MESH_JOIN_TOKEN_MAX_TTL_SEC, n as DEFAULT_HUB_TCP_PORT, o as MESH_CA_KEY_REF, r as DEFAULT_HUB_TLS_TCP_PORT, s as MESH_CA_SETTINGS_KEY, t as CLIENT_AUTH_OID, u as SERVER_AUTH_OID, x as setProcessClusterSecretGate, y as hashClusterSecret } from "../../constants-D5z4EwY8.mjs";
5
5
  import { a as KeyUsagesExtension, c as Pkcs10CertificateRequestGenerator, d as X509Certificate$1, f as X509CertificateGenerator, i as KeyUsageFlags, n as BasicConstraintsExtension, o as PemConverter, p as cryptoProvider, r as ExtendedKeyUsageExtension, s as Pkcs10CertificateRequest, t as AuthorityKeyIdentifierExtension, u as SubjectKeyIdentifierExtension } from "../../x509.es-Xf3K_brW.mjs";
6
6
  import { z } from "zod";
7
- import { X509Certificate, createHash, randomBytes, webcrypto } from "node:crypto";
7
+ import { X509Certificate, createHash, createHmac, randomBytes, timingSafeEqual, webcrypto } from "node:crypto";
8
8
  import { BlockList, isIP } from "node:net";
9
9
  import { lstat, readdir, readlink, realpath } from "node:fs/promises";
10
10
  import { posix, win32 } from "node:path";
@@ -141,6 +141,145 @@ function getProcessMeshAuthStatus() {
141
141
  return typeof source === "function" ? source() : null;
142
142
  }
143
143
  //#endregion
144
+ //#region src/kernel/moleculer/agent-ui-unlock.ts
145
+ /**
146
+ * Agent UI remote unlock tickets (D821).
147
+ *
148
+ * A hub admin mints a short-lived HMAC ticket bound to one agent `nodeId`.
149
+ * The agent redeems it once for a tab session Bearer — the cluster secret
150
+ * stays a mesh / pairing credential and is not pasted into the browser for
151
+ * daily unlock.
152
+ *
153
+ * K = SHA-256(cluster secret) — same as mesh join (D802)
154
+ * ticket = base64url(JSON payload) + '.' + base64url(HMAC-SHA256(K, DOMAIN|payloadB64))
155
+ * payload = { v: 1, nodeId, exp (ms), jti }
156
+ */
157
+ var AGENT_UI_UNLOCK_DOMAIN = "camstack-agent-ui-unlock/v1";
158
+ var AGENT_UI_UNLOCK_VERSION = 1;
159
+ /** Ticket lifetime — long enough for a redirect hop, short enough to steal poorly. */
160
+ var AGENT_UI_UNLOCK_TTL_MS = 5 * 6e4;
161
+ /**
162
+ * Cluster settings key: extra origins allowed as `returnTo` for Continue-with-hub
163
+ * (NPM hostnames that are not `:4444` on a LAN IP). Comma-separated origins.
164
+ */
165
+ var AGENT_DASHBOARD_ORIGINS_FIELD = "agentDashboardOrigins";
166
+ /** In-memory spent `jti` until `exp` (ms). */
167
+ var UnlockTicketJtiStore = class {
168
+ spent = /* @__PURE__ */ new Map();
169
+ isSpent(jti, nowMs = Date.now()) {
170
+ this.gc(nowMs);
171
+ return this.spent.has(jti);
172
+ }
173
+ /** Record a jti as used until `exp`. Returns false if already spent. */
174
+ consume(jti, exp, nowMs = Date.now()) {
175
+ this.gc(nowMs);
176
+ if (this.spent.has(jti)) return false;
177
+ this.spent.set(jti, exp);
178
+ return true;
179
+ }
180
+ /** Test helper. */
181
+ size() {
182
+ return this.spent.size;
183
+ }
184
+ gc(nowMs) {
185
+ for (const [jti, exp] of this.spent) if (exp <= nowMs) this.spent.delete(jti);
186
+ }
187
+ };
188
+ function mintAgentUiUnlockTicket(input) {
189
+ const nowMs = input.nowMs ?? Date.now();
190
+ const ttl = Math.min(Math.max(1e3, input.ttlMs ?? 3e5), AGENT_UI_UNLOCK_TTL_MS);
191
+ const payload = {
192
+ v: 1,
193
+ nodeId: input.nodeId,
194
+ exp: nowMs + ttl,
195
+ jti: input.jti ?? randomBytes(16).toString("base64url")
196
+ };
197
+ const payloadB64 = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
198
+ return `${payloadB64}.${sign(input.secretHash, payloadB64)}`;
199
+ }
200
+ /**
201
+ * Verify a ticket under `secretHash`. Returns the payload, or `null` when the
202
+ * signature, shape, expiry or nodeId fails (no reason string — refuse quietly).
203
+ */
204
+ function verifyAgentUiUnlockTicket(secretHash, ticket, options) {
205
+ const nowMs = options.nowMs ?? Date.now();
206
+ const dot = ticket.indexOf(".");
207
+ if (dot <= 0 || dot === ticket.length - 1) return null;
208
+ const payloadB64 = ticket.slice(0, dot);
209
+ const sig = ticket.slice(dot + 1);
210
+ if (!sigsEqual(sign(secretHash, payloadB64), sig)) return null;
211
+ let parsed;
212
+ try {
213
+ parsed = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
214
+ } catch {
215
+ return null;
216
+ }
217
+ const payload = parsePayload(parsed);
218
+ if (payload === null) return null;
219
+ if (payload.exp <= nowMs) return null;
220
+ if (payload.nodeId !== options.expectedNodeId) return null;
221
+ return payload;
222
+ }
223
+ /**
224
+ * True when `returnTo` may receive a ticket redirect. Default: any http(s)
225
+ * origin whose port is 4444. Extra origins come from
226
+ * {@link AGENT_DASHBOARD_ORIGINS_FIELD} (exact origin match).
227
+ */
228
+ function isAllowedAgentDashboardReturnTo(returnTo, extraOrigins = []) {
229
+ let url;
230
+ try {
231
+ url = new URL(returnTo);
232
+ } catch {
233
+ return false;
234
+ }
235
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
236
+ if (url.username !== "" || url.password !== "") return false;
237
+ if (url.pathname !== "/" && url.pathname !== "") return false;
238
+ if (url.port === "4444") return true;
239
+ const origin = url.origin;
240
+ return extraOrigins.some((o) => o === origin);
241
+ }
242
+ /** Parse a stored comma/whitespace-separated origins list. */
243
+ function parseAgentDashboardOrigins(stored) {
244
+ if (typeof stored !== "string" || stored.trim().length === 0) return [];
245
+ const out = [];
246
+ for (const part of stored.split(/[,\s]+/)) {
247
+ const t = part.trim();
248
+ if (t.length === 0) continue;
249
+ try {
250
+ const u = new URL(t);
251
+ if (u.protocol === "http:" || u.protocol === "https:") out.push(u.origin);
252
+ } catch {}
253
+ }
254
+ return out;
255
+ }
256
+ function sign(secretHash, payloadB64) {
257
+ return createHmac("sha256", Buffer.from(secretHash, "utf8")).update(`${AGENT_UI_UNLOCK_DOMAIN}|${payloadB64}`, "utf8").digest("base64url");
258
+ }
259
+ function sigsEqual(expected, presented) {
260
+ const a = Buffer.from(expected, "utf8");
261
+ const b = Buffer.from(presented, "utf8");
262
+ if (a.length !== b.length) return false;
263
+ return timingSafeEqual(a, b);
264
+ }
265
+ function parsePayload(value) {
266
+ if (typeof value !== "object" || value === null) return null;
267
+ const rec = value;
268
+ if (rec["v"] !== 1) return null;
269
+ const nodeId = rec["nodeId"];
270
+ const exp = rec["exp"];
271
+ const jti = rec["jti"];
272
+ if (typeof nodeId !== "string" || nodeId.length === 0) return null;
273
+ if (typeof exp !== "number" || !Number.isFinite(exp)) return null;
274
+ if (typeof jti !== "string" || jti.length === 0) return null;
275
+ return {
276
+ v: 1,
277
+ nodeId,
278
+ exp,
279
+ jti
280
+ };
281
+ }
282
+ //#endregion
144
283
  //#region src/kernel/moleculer/mesh-mtls/mesh-ca.ts
145
284
  cryptoProvider.set(webcrypto);
146
285
  var MS_PER_DAY = 864e5;
@@ -87650,7 +87789,7 @@ function isTlsSocket(socket) {
87650
87789
  * - no marker → the secret is MANDATORY (D802): the keeper asks
87651
87790
  * its owner to generate one (`generateSecret`), and
87652
87791
  * until that succeeds the gate is `off` — no node can
87653
- * authenticate, and only `transition` lets them in;
87792
+ * authenticate (mesh auth is always enforced, D821);
87654
87793
  * - marker + value → `on` — nodes prove the secret;
87655
87794
  * - marker, no answer → `unknown` — refused, retryably, and retried here.
87656
87795
  * A locked keychain or a value lost to a reset NEVER opens the gate, and
@@ -87713,7 +87852,7 @@ var ClusterSecretKeeper = class {
87713
87852
  reason: `settings unreadable: ${errText(err)}`
87714
87853
  };
87715
87854
  }
87716
- setProcessMeshAuthMode(parseMeshAuthMode(section[MESH_AUTH_MODE_FIELD]));
87855
+ setProcessMeshAuthMode("enforced");
87717
87856
  const stored = section[CLUSTER_SECRET_FIELD];
87718
87857
  if (stored === void 0 || stored === null || stored === "") return { kind: "off" };
87719
87858
  if (!isSecretMarker(stored)) return {
@@ -87781,28 +87920,48 @@ function errText(err) {
87781
87920
  //#endregion
87782
87921
  //#region src/builtins/system-config/mesh-actions.ts
87783
87922
  /**
87784
- * System → Cluster — mint a single-use mesh join token (Release A).
87923
+ * System → Cluster — mint a single-use mesh join token (Release A) and
87924
+ * agent-ui unlock tickets (D821).
87785
87925
  */
87786
- var systemConfigMeshActions = defineCustomActions({ mintJoinToken: customAction(z.object({
87787
- /** Optional pinned node id for the CSR CN. */
87788
- nodeId: z.string().min(1).optional(),
87789
- /** Override hub HTTPS base (default: local-network publicEndpoint). */
87790
- hubUrl: z.string().url().optional(),
87791
- /** Override `host:6000` clear mesh address carried in the token. */
87792
- hubMeshAddress: z.string().min(3).optional(),
87793
- /** Seconds until expiry; capped at 1 h. */
87794
- ttlSec: z.number().int().positive().max(3600).optional()
87795
- }).optional(), z.object({
87796
- token: z.string().min(1),
87797
- exp: z.number().int(),
87798
- hubUrl: z.string(),
87799
- hubMeshAddress: z.string(),
87800
- caFingerprint: z.string(),
87801
- nodeId: z.string().optional()
87802
- }), {
87803
- kind: "mutation",
87804
- auth: "admin"
87805
- }) });
87926
+ var systemConfigMeshActions = defineCustomActions({
87927
+ mintJoinToken: customAction(z.object({
87928
+ /** Optional pinned node id for the CSR CN. */
87929
+ nodeId: z.string().min(1).optional(),
87930
+ /** Override hub HTTPS base (default: local-network publicEndpoint). */
87931
+ hubUrl: z.string().url().optional(),
87932
+ /** Override `host:6000` clear mesh address carried in the token. */
87933
+ hubMeshAddress: z.string().min(3).optional(),
87934
+ /** Seconds until expiry; capped at 1 h. */
87935
+ ttlSec: z.number().int().positive().max(3600).optional()
87936
+ }).optional(), z.object({
87937
+ token: z.string().min(1),
87938
+ exp: z.number().int(),
87939
+ hubUrl: z.string(),
87940
+ hubMeshAddress: z.string(),
87941
+ caFingerprint: z.string(),
87942
+ nodeId: z.string().optional()
87943
+ }), {
87944
+ kind: "mutation",
87945
+ auth: "admin"
87946
+ }),
87947
+ /** Hub admin → short-lived ticket the agent redeems for a tab session (D821). */
87948
+ mintAgentUiUnlockTicket: customAction(z.object({
87949
+ nodeId: z.string().min(1),
87950
+ /**
87951
+ * Preferred agent dashboard base (`http://host:4444`). When set, the
87952
+ * answer includes `dashboardUrl` with `?ticket=` already attached.
87953
+ */
87954
+ dashboardBaseUrl: z.string().url().optional()
87955
+ }), z.object({
87956
+ ticket: z.string().min(1),
87957
+ exp: z.number().int(),
87958
+ nodeId: z.string().min(1),
87959
+ dashboardUrl: z.string().url().optional()
87960
+ }), {
87961
+ kind: "mutation",
87962
+ auth: "admin"
87963
+ })
87964
+ });
87806
87965
  //#endregion
87807
87966
  //#region src/builtins/system-config/system-config.addon.ts
87808
87967
  /**
@@ -87867,7 +88026,8 @@ var KEY_TO_SECTION = {
87867
88026
  trustedNetworks: "auth",
87868
88027
  [CLUSTER_SECRET_FIELD]: "cluster",
87869
88028
  [MESH_AUTH_MODE_FIELD]: "cluster",
87870
- [MESH_CLEAR_PORT_FIELD]: "cluster"
88029
+ [MESH_CLEAR_PORT_FIELD]: "cluster",
88030
+ [AGENT_DASHBOARD_ORIGINS_FIELD]: "cluster"
87871
88031
  };
87872
88032
  /** Bytes of a generated cluster secret (D802) — 256 bits, base64url (43 chars). */
87873
88033
  var GENERATED_SECRET_BYTES = 32;
@@ -87876,10 +88036,10 @@ var STATE_LABELS = {
87876
88036
  legacy: "legacy hash only — update the agent",
87877
88037
  unauthenticated: "not authenticated — enter the cluster secret in this agent"
87878
88038
  };
87879
- /** The Cluster page's live line per node (D802). */
88039
+ /** The Cluster page's live line per node (D802). Mode is always enforced (D821). */
87880
88040
  function describeMeshStatus(status) {
87881
88041
  if (status === null) return "The mesh status is available on the hub only.";
87882
- const head = status.mode === "enforced" ? "Enforced: a node that has not authenticated is refused." : "Transition: nodes without the secret are still let in (with a warning). The hub switches to Enforced by itself once every known agent has authenticated.";
88042
+ const head = "Enforced: a node that has not authenticated is refused.";
87883
88043
  if (status.nodes.length === 0) return `${head}\nNo other node is connected.`;
87884
88044
  return `${head}\n${status.nodes.map((n) => `• ${n.nodeId}: ${STATE_LABELS[n.state] ?? n.state}`).join("\n")}`;
87885
88045
  }
@@ -87910,6 +88070,9 @@ var SystemConfigAddon = class extends BaseAddon {
87910
88070
  this.ctx.logger.info("Mesh authentication is now ENFORCED", { meta: { reason } });
87911
88071
  });
87912
88072
  await this.clusterSecretKeeper.start();
88073
+ try {
88074
+ await this.writeMeshAuthMode("enforced");
88075
+ } catch {}
87913
88076
  this.meshCaKeeper = new MeshCaKeeper({
87914
88077
  secrets: this.systemSecrets(),
87915
88078
  readClusterSection: async () => await this.ctx.settings?.getSection("cluster") ?? {},
@@ -87924,7 +88087,10 @@ var SystemConfigAddon = class extends BaseAddon {
87924
88087
  this.ctx.logger.info("Initialized — exposes yml-backed sections via getGlobalSettings");
87925
88088
  return {
87926
88089
  customActions: systemConfigMeshActions,
87927
- actionHandlers: { mintJoinToken: async (input) => this.mintJoinToken(parseMintJoinTokenInput(input)) }
88090
+ actionHandlers: {
88091
+ mintJoinToken: async (input) => this.mintJoinToken(parseMintJoinTokenInput(input)),
88092
+ mintAgentUiUnlockTicket: async (input) => this.mintAgentUiUnlock(parseMintUnlockInput(input))
88093
+ }
87928
88094
  };
87929
88095
  }
87930
88096
  async onShutdown() {
@@ -87977,6 +88143,41 @@ var SystemConfigAddon = class extends BaseAddon {
87977
88143
  ...input.nodeId !== void 0 ? { nodeId: input.nodeId } : {}
87978
88144
  };
87979
88145
  }
88146
+ /** D821 — short-lived ticket for the agent's `:4444` unlock session. */
88147
+ async mintAgentUiUnlock(input) {
88148
+ const secrets = this.systemSecrets();
88149
+ if (secrets === null) throw new Error("system-config: no keychain door — cannot mint an unlock ticket");
88150
+ const entry = (await secrets.resolve([CLUSTER_SECRET_REF]))[CLUSTER_SECRET_REF];
88151
+ if (entry?.kind !== "value") throw new Error("system-config: cluster secret is not available — cannot mint an unlock ticket");
88152
+ if (input.dashboardBaseUrl !== void 0) {
88153
+ const origins = await this.readAgentDashboardOrigins();
88154
+ if (!isAllowedAgentDashboardReturnTo(input.dashboardBaseUrl, origins)) throw new Error("dashboardBaseUrl is not an allowed agent dashboard origin (:4444 or agentDashboardOrigins)");
88155
+ }
88156
+ const ticket = mintAgentUiUnlockTicket({
88157
+ secretHash: hashClusterSecret(entry.value),
88158
+ nodeId: input.nodeId
88159
+ });
88160
+ const payloadJson = Buffer.from(ticket.split(".")[0] ?? "", "base64url").toString("utf8");
88161
+ const exp = JSON.parse(payloadJson).exp ?? 0;
88162
+ const dashboardUrl = input.dashboardBaseUrl === void 0 ? void 0 : (() => {
88163
+ const u = new URL(input.dashboardBaseUrl);
88164
+ u.searchParams.set("ticket", ticket);
88165
+ return u.toString();
88166
+ })();
88167
+ return {
88168
+ ticket,
88169
+ exp,
88170
+ nodeId: input.nodeId,
88171
+ ...dashboardUrl !== void 0 ? { dashboardUrl } : {}
88172
+ };
88173
+ }
88174
+ async readAgentDashboardOrigins() {
88175
+ try {
88176
+ return parseAgentDashboardOrigins((await this.ctx.settings?.getSection("cluster"))?.[AGENT_DASHBOARD_ORIGINS_FIELD]);
88177
+ } catch {
88178
+ return [];
88179
+ }
88180
+ }
87980
88181
  async resolveHubUrl() {
87981
88182
  try {
87982
88183
  const book = await this.ctx.api?.localNetwork?.getAddresses?.query({ scheme: "https" });
@@ -88096,20 +88297,6 @@ var SystemConfigAddon = class extends BaseAddon {
88096
88297
  label: "Cluster secret",
88097
88298
  description: "Mandatory — generated by the hub at first boot and stored in the keychain. To pair an agent: Reveal and copy it here, then enter it in the agent (its status page, or the desktop setup window). Saving a new value rotates it: every agent must then be given the new one."
88098
88299
  },
88099
- {
88100
- type: "select",
88101
- key: MESH_AUTH_MODE_FIELD,
88102
- label: "Agents without the secret",
88103
- description: "Transition lets an agent that has not authenticated keep working (with a warning) while you give each agent the secret; it ends by itself once every known agent has authenticated. Enforced refuses it.",
88104
- default: "transition",
88105
- options: [{
88106
- value: "transition",
88107
- label: "Transition — allow, warn"
88108
- }, {
88109
- value: "enforced",
88110
- label: "Enforced — refuse"
88111
- }]
88112
- },
88113
88300
  {
88114
88301
  type: "select",
88115
88302
  key: MESH_CLEAR_PORT_FIELD,
@@ -88131,6 +88318,12 @@ var SystemConfigAddon = class extends BaseAddon {
88131
88318
  content: describeMeshStatus(getProcessMeshAuthStatus()),
88132
88319
  variant: "info"
88133
88320
  },
88321
+ {
88322
+ type: "string",
88323
+ key: AGENT_DASHBOARD_ORIGINS_FIELD,
88324
+ label: "Agent dashboard origins (NPM)",
88325
+ description: "Extra origins allowed for Continue-with-hub unlock redirects (comma-separated), e.g. https://agent.example.com. LAN agents on :4444 are always allowed."
88326
+ },
88134
88327
  {
88135
88328
  type: "addon-action-button",
88136
88329
  key: "mintJoinTokenAction",
@@ -88203,15 +88396,14 @@ var SystemConfigAddon = class extends BaseAddon {
88203
88396
  * The cluster secret's save path: the value goes to the keychain (owner
88204
88397
  * `system`), the row gets the marker; `''` / a view keeps. `$secretClear` is
88205
88398
  * REFUSED: the secret is mandatory (D802). A value the keychain cannot store
88206
- * is REFUSED — never written inline (D787, P5). `meshAuth` is a plain value.
88399
+ * is REFUSED — never written inline (D787, P5). Mesh auth mode is always
88400
+ * enforced (D821) — a legacy `meshAuth` in the patch is ignored.
88207
88401
  */
88208
88402
  async writeClusterSection(patch) {
88209
- const { [MESH_AUTH_MODE_FIELD]: mode, [MESH_CLEAR_PORT_FIELD]: clearPort, ...secretPatch } = patch;
88403
+ const { [MESH_AUTH_MODE_FIELD]: _legacyMode, [MESH_CLEAR_PORT_FIELD]: clearPort, [AGENT_DASHBOARD_ORIGINS_FIELD]: dashboardOrigins, ...secretPatch } = patch;
88210
88404
  if (clearPort !== void 0) await this.writeMeshClearPort(clearPort);
88211
- if (mode !== void 0) {
88212
- if (!isMeshAuthMode(mode)) throw new Error(`system-config: meshAuth must be 'transition' or 'enforced'`);
88213
- await this.writeMeshAuthMode(mode);
88214
- }
88405
+ if (dashboardOrigins !== void 0) await this.ctx.settings.setSection("cluster", { [AGENT_DASHBOARD_ORIGINS_FIELD]: typeof dashboardOrigins === "string" ? dashboardOrigins : "" });
88406
+ await this.writeMeshAuthMode("enforced");
88215
88407
  if (Object.keys(secretPatch).length === 0) return;
88216
88408
  if (isSecretClear(secretPatch["clusterSecret"])) throw new Error("The cluster secret is mandatory and cannot be cleared — save a new value to rotate it");
88217
88409
  await this.storeClusterSecret(secretPatch);
@@ -88239,16 +88431,15 @@ var SystemConfigAddon = class extends BaseAddon {
88239
88431
  await applyProcessMeshClearPort(value);
88240
88432
  this.ctx.logger.info("Mesh clear port switched", { meta: { mode: value } });
88241
88433
  }
88242
- /** `cluster.meshAuth` (D802), then republish it. */
88434
+ /** Persist + publish mesh auth mode (always `enforced` after D821). */
88243
88435
  async writeMeshAuthMode(mode) {
88244
88436
  await this.ctx.settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: mode });
88245
- await this.clusterSecretKeeper?.reload();
88437
+ setProcessMeshAuthMode(mode);
88246
88438
  }
88247
88439
  /**
88248
88440
  * First boot, or a keychain reset that cleared the marker (D802): a fresh
88249
- * random secret, stored like an operator save. A NEW secret means every
88250
- * agent must be given it, so the mesh goes (back) to `transition`; on a hub
88251
- * with no agent yet, the hub's auto-enforce ends it at once.
88441
+ * random secret, stored like an operator save. Mesh auth stays enforced
88442
+ * (D821) — every agent must present the secret; there is no transition window.
88252
88443
  */
88253
88444
  async generateClusterSecret() {
88254
88445
  const settings = this.ctx.settings;
@@ -88263,7 +88454,8 @@ var SystemConfigAddon = class extends BaseAddon {
88263
88454
  if (existing?.kind !== "missing") throw new Error(`the keychain cannot answer for the cluster secret (${existing?.kind ?? "no answer"})`);
88264
88455
  const value = randomBytes(GENERATED_SECRET_BYTES).toString("base64url");
88265
88456
  await this.storeClusterSecret({ [CLUSTER_SECRET_FIELD]: value });
88266
- await settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: "transition" });
88457
+ await settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: "enforced" });
88458
+ setProcessMeshAuthMode("enforced");
88267
88459
  }
88268
88460
  async storeClusterSecret(patch) {
88269
88461
  const settings = this.ctx.settings;
@@ -88296,6 +88488,16 @@ function parseMintJoinTokenInput(input) {
88296
88488
  if (typeof ttlSec === "number") out.ttlSec = ttlSec;
88297
88489
  return out;
88298
88490
  }
88491
+ function parseMintUnlockInput(input) {
88492
+ if (input === null || typeof input !== "object") throw new Error("system-config: mintAgentUiUnlockTicket requires { nodeId }");
88493
+ const nodeId = Reflect.get(input, "nodeId");
88494
+ if (typeof nodeId !== "string" || nodeId.length === 0) throw new Error("system-config: mintAgentUiUnlockTicket requires { nodeId }");
88495
+ const dashboardBaseUrl = Reflect.get(input, "dashboardBaseUrl");
88496
+ return {
88497
+ nodeId,
88498
+ ...typeof dashboardBaseUrl === "string" ? { dashboardBaseUrl } : {}
88499
+ };
88500
+ }
88299
88501
  /** Read `exp` from an unencrypted JWT payload (no verify — already minted here). */
88300
88502
  function readJwtExp(token) {
88301
88503
  const parts = token.split(".");
@@ -88307,4 +88509,4 @@ function readJwtExp(token) {
88307
88509
  return typeof exp === "number" ? exp : 0;
88308
88510
  }
88309
88511
  //#endregion
88310
- export { loadMeshCa as A, createCidrMatcher as B, caFingerprintCompact as C, generateMeshKeyPair as D, generateMeshCa as E, parseMeshAuthMode as F, parseCidrList as H, requestProcessMeshEnforcement as I, setProcessMeshAuthMode as L, getProcessMeshAuthMode as M, getProcessMeshAuthStatus as N, issueMeshLeafForNode as O, isMeshAuthMode as P, setProcessMeshAuthStatusSource as R, caFingerprint as S, SYSTEM_CONFIG_CLUSTER_SECTION_ID, SystemConfigAddon, SystemConfigAddon as default, exportMeshPrivateKeyPem as T, readCidrSetting as U, normalizeIp as V, MeshCrl as _, setProcessMeshTlsEnabler as a, getProcessMeshCaState as b, mintMeshJoinToken as c, MESH_CLEAR_PORT_FIELD as d, applyProcessMeshClearPort as f, setProcessMeshClearPortControl as g, getProcessNodeMeshTransport as h, ServiceBroker as i, MESH_AUTH_MODE_FIELD as j, issueMeshLeafFromCsr as k, processJoinTokenJtiStore as l, decideMeshClearPortOff as m, TlsTcpTransporter as n, enrollMeshNode as o, checkProcessMeshClearPort as p, Errors as r, JoinTokenJtiStore as s, systemConfigMeshActions as t, verifyMeshJoinToken as u, processMeshCrl as v, createMeshCsrPem as w, requireProcessMeshCa as x, MeshCaKeeper as y, setProcessMeshEnforcer as z };
88512
+ export { loadMeshCa as A, MESH_AUTH_MODE_FIELD as B, caFingerprintCompact as C, generateMeshKeyPair as D, generateMeshCa as E, UnlockTicketJtiStore as F, requestProcessMeshEnforcement as G, getProcessMeshAuthStatus as H, isAllowedAgentDashboardReturnTo as I, setProcessMeshEnforcer as J, setProcessMeshAuthMode as K, mintAgentUiUnlockTicket as L, AGENT_UI_UNLOCK_DOMAIN as M, AGENT_UI_UNLOCK_TTL_MS as N, issueMeshLeafForNode as O, AGENT_UI_UNLOCK_VERSION as P, readCidrSetting as Q, parseAgentDashboardOrigins as R, caFingerprint as S, SYSTEM_CONFIG_CLUSTER_SECTION_ID, SystemConfigAddon, SystemConfigAddon as default, exportMeshPrivateKeyPem as T, isMeshAuthMode as U, getProcessMeshAuthMode as V, parseMeshAuthMode as W, normalizeIp as X, createCidrMatcher as Y, parseCidrList as Z, MeshCrl as _, setProcessMeshTlsEnabler as a, getProcessMeshCaState as b, mintMeshJoinToken as c, MESH_CLEAR_PORT_FIELD as d, applyProcessMeshClearPort as f, setProcessMeshClearPortControl as g, getProcessNodeMeshTransport as h, ServiceBroker as i, AGENT_DASHBOARD_ORIGINS_FIELD as j, issueMeshLeafFromCsr as k, processJoinTokenJtiStore as l, decideMeshClearPortOff as m, TlsTcpTransporter as n, enrollMeshNode as o, checkProcessMeshClearPort as p, setProcessMeshAuthStatusSource as q, Errors as r, JoinTokenJtiStore as s, systemConfigMeshActions as t, verifyMeshJoinToken as u, processMeshCrl as v, createMeshCsrPem as w, requireProcessMeshCa as x, MeshCaKeeper as y, verifyAgentUiUnlockTicket as z };
package/dist/index.js CHANGED
@@ -13863,8 +13863,12 @@ exports.ADMIN_TOKEN_MAX_LIFETIME_MS = require_builtins_local_auth_local_auth_add
13863
13863
  exports.ADMIN_TOKEN_PREFIX = require_builtins_local_auth_local_auth_addon.ADMIN_TOKEN_PREFIX;
13864
13864
  exports.AGENT_CAP_FWD_ACTION = require_child_cap_dispatch.AGENT_CAP_FWD_ACTION;
13865
13865
  exports.AGENT_CAP_FWD_SERVICE = require_child_cap_dispatch.AGENT_CAP_FWD_SERVICE;
13866
+ exports.AGENT_DASHBOARD_ORIGINS_FIELD = require_builtins_system_config_system_config_addon.AGENT_DASHBOARD_ORIGINS_FIELD;
13866
13867
  exports.AGENT_READINESS_SERVICE_NAME = AGENT_READINESS_SERVICE_NAME;
13867
13868
  exports.AGENT_REGISTER_NODE_ACTION = AGENT_REGISTER_NODE_ACTION;
13869
+ exports.AGENT_UI_UNLOCK_DOMAIN = require_builtins_system_config_system_config_addon.AGENT_UI_UNLOCK_DOMAIN;
13870
+ exports.AGENT_UI_UNLOCK_TTL_MS = require_builtins_system_config_system_config_addon.AGENT_UI_UNLOCK_TTL_MS;
13871
+ exports.AGENT_UI_UNLOCK_VERSION = require_builtins_system_config_system_config_addon.AGENT_UI_UNLOCK_VERSION;
13868
13872
  exports.ALL_CAPABILITY_DEFINITIONS = require_dist.ALL_CAPABILITY_DEFINITIONS;
13869
13873
  exports.ARRAY_BUFFER_CENSUS_COOLDOWN_MS = require_child_cap_dispatch.ARRAY_BUFFER_CENSUS_COOLDOWN_MS;
13870
13874
  exports.ARRAY_BUFFER_CENSUS_SUSTAIN_MS = require_child_cap_dispatch.ARRAY_BUFFER_CENSUS_SUSTAIN_MS;
@@ -14060,6 +14064,7 @@ exports.UPLOADED_CA_FILE = require_tls.UPLOADED_CA_FILE;
14060
14064
  exports.USER_MANAGEMENT_INTERNAL = require_builtins_local_auth_local_auth_addon.USER_MANAGEMENT_INTERNAL;
14061
14065
  exports.UdsLocalTransportClient = require_child_cap_dispatch.UdsLocalTransportClient;
14062
14066
  exports.UdsLocalTransportServer = require_child_cap_dispatch.UdsLocalTransportServer;
14067
+ exports.UnlockTicketJtiStore = require_builtins_system_config_system_config_addon.UnlockTicketJtiStore;
14063
14068
  exports.UserManager = require_builtins_local_auth_local_auth_addon.UserManager;
14064
14069
  exports.WinstonDestination = require_builtins_winston_logging_index.WinstonDestination$1;
14065
14070
  exports.WinstonLoggingAddon = require_builtins_winston_logging_index.WinstonLoggingAddon$1;
@@ -14292,6 +14297,7 @@ Object.defineProperty(exports, "installPythonRequirements", {
14292
14297
  });
14293
14298
  exports.ipcParentLink = require_child_cap_dispatch.ipcParentLink;
14294
14299
  exports.isAddonDeploySource = isAddonDeploySource;
14300
+ exports.isAllowedAgentDashboardReturnTo = require_builtins_system_config_system_config_addon.isAllowedAgentDashboardReturnTo;
14295
14301
  exports.isArrayBufferCensusReport = require_child_cap_dispatch.isArrayBufferCensusReport;
14296
14302
  exports.isArrayOutputSchema = require_dist.isArrayOutputSchema;
14297
14303
  exports.isClusterSecretMismatchError = require_constants.isClusterSecretMismatchError;
@@ -14314,11 +14320,13 @@ exports.loadTlsCert = require_tls.loadTlsCert;
14314
14320
  exports.localEndpointPath = require_child_cap_dispatch.localEndpointPath;
14315
14321
  exports.localProviderLink = require_child_cap_dispatch.localProviderLink;
14316
14322
  exports.meshErrorType = meshErrorType;
14323
+ exports.mintAgentUiUnlockTicket = require_builtins_system_config_system_config_addon.mintAgentUiUnlockTicket;
14317
14324
  exports.mintMeshJoinToken = require_builtins_system_config_system_config_addon.mintMeshJoinToken;
14318
14325
  exports.mountNativeCapService = require_child_cap_dispatch.mountNativeCapService;
14319
14326
  exports.nextRssBudgetState = require_child_cap_dispatch.nextRssBudgetState;
14320
14327
  exports.normalizeIp = require_builtins_system_config_system_config_addon.normalizeIp;
14321
14328
  exports.overlayGetStatus = overlayGetStatus;
14329
+ exports.parseAgentDashboardOrigins = require_builtins_system_config_system_config_addon.parseAgentDashboardOrigins;
14322
14330
  exports.parseCapAction = require_child_cap_dispatch.parseCapAction;
14323
14331
  exports.parseCidrList = require_builtins_system_config_system_config_addon.parseCidrList;
14324
14332
  exports.parseMeshAuthMode = require_builtins_system_config_system_config_addon.parseMeshAuthMode;
@@ -14404,6 +14412,7 @@ exports.subscribePassthrough = require_child_cap_dispatch.subscribePassthrough;
14404
14412
  exports.udsChildLogToWorkerEntry = require_child_cap_dispatch.udsChildLogToWorkerEntry;
14405
14413
  exports.validateProviderRegistrations = require_child_cap_dispatch.validateProviderRegistrations;
14406
14414
  exports.validateUploadedTls = require_tls.validateUploadedTls;
14415
+ exports.verifyAgentUiUnlockTicket = require_builtins_system_config_system_config_addon.verifyAgentUiUnlockTicket;
14407
14416
  exports.verifyMeshJoinToken = require_builtins_system_config_system_config_addon.verifyMeshJoinToken;
14408
14417
  exports.waitUntilReady = waitUntilReady;
14409
14418
  exports.writeExtraSans = require_tls.writeExtraSans;
package/dist/index.mjs CHANGED
@@ -27,7 +27,7 @@ import { i as stripForwardedCaller, n as HUB_CAP_FWD_SERVICE, r as createHubCapF
27
27
  import { StorageOrchestratorAddon, n as buildStorageLocationRegistry, t as StorageOrchestratorService } from "./builtins/storage-orchestrator/storage-orchestrator.addon.mjs";
28
28
  import "./builtins/storage-orchestrator/index.mjs";
29
29
  import { _ as clusterSecretMatches, a as MESH_CA_FIELD, b as isClusterSecretMismatchError, d as CLUSTER_SECRET_MISMATCH_TYPE, f as CLUSTER_SECRET_REF, g as checkClusterSecret, h as CLUSTER_SECRET_UNAVAILABLE_TYPE, m as CLUSTER_SECRET_SETTINGS_KEY, o as MESH_CA_KEY_REF, p as CLUSTER_SECRET_REJECTED_EXIT_CODE, r as DEFAULT_HUB_TLS_TCP_PORT, s as MESH_CA_SETTINGS_KEY, v as getProcessClusterSecretGate, x as setProcessClusterSecretGate, y as hashClusterSecret } from "./constants-D5z4EwY8.mjs";
30
- import { A as loadMeshCa, B as createCidrMatcher, C as caFingerprintCompact, D as generateMeshKeyPair, E as generateMeshCa, F as parseMeshAuthMode, H as parseCidrList, I as requestProcessMeshEnforcement, L as setProcessMeshAuthMode, M as getProcessMeshAuthMode, N as getProcessMeshAuthStatus, O as issueMeshLeafForNode, P as isMeshAuthMode, R as setProcessMeshAuthStatusSource, S as caFingerprint, SystemConfigAddon, T as exportMeshPrivateKeyPem, U as readCidrSetting, V as normalizeIp, _ as MeshCrl, a as setProcessMeshTlsEnabler, b as getProcessMeshCaState, c as mintMeshJoinToken, d as MESH_CLEAR_PORT_FIELD, f as applyProcessMeshClearPort, g as setProcessMeshClearPortControl, h as getProcessNodeMeshTransport, i as ServiceBroker, j as MESH_AUTH_MODE_FIELD, k as issueMeshLeafFromCsr, l as processJoinTokenJtiStore, m as decideMeshClearPortOff, n as TlsTcpTransporter, o as enrollMeshNode, p as checkProcessMeshClearPort, r as Errors, s as JoinTokenJtiStore, u as verifyMeshJoinToken, v as processMeshCrl, w as createMeshCsrPem, x as requireProcessMeshCa, y as MeshCaKeeper, z as setProcessMeshEnforcer } from "./builtins/system-config/system-config.addon.mjs";
30
+ import { A as loadMeshCa, B as MESH_AUTH_MODE_FIELD, C as caFingerprintCompact, D as generateMeshKeyPair, E as generateMeshCa, F as UnlockTicketJtiStore, G as requestProcessMeshEnforcement, H as getProcessMeshAuthStatus, I as isAllowedAgentDashboardReturnTo, J as setProcessMeshEnforcer, K as setProcessMeshAuthMode, L as mintAgentUiUnlockTicket, M as AGENT_UI_UNLOCK_DOMAIN, N as AGENT_UI_UNLOCK_TTL_MS, O as issueMeshLeafForNode, P as AGENT_UI_UNLOCK_VERSION, Q as readCidrSetting, R as parseAgentDashboardOrigins, S as caFingerprint, SystemConfigAddon, T as exportMeshPrivateKeyPem, U as isMeshAuthMode, V as getProcessMeshAuthMode, W as parseMeshAuthMode, X as normalizeIp, Y as createCidrMatcher, Z as parseCidrList, _ as MeshCrl, a as setProcessMeshTlsEnabler, b as getProcessMeshCaState, c as mintMeshJoinToken, d as MESH_CLEAR_PORT_FIELD, f as applyProcessMeshClearPort, g as setProcessMeshClearPortControl, h as getProcessNodeMeshTransport, i as ServiceBroker, j as AGENT_DASHBOARD_ORIGINS_FIELD, k as issueMeshLeafFromCsr, l as processJoinTokenJtiStore, m as decideMeshClearPortOff, n as TlsTcpTransporter, o as enrollMeshNode, p as checkProcessMeshClearPort, q as setProcessMeshAuthStatusSource, r as Errors, s as JoinTokenJtiStore, u as verifyMeshJoinToken, v as processMeshCrl, w as createMeshCsrPem, x as requireProcessMeshCa, y as MeshCaKeeper, z as verifyAgentUiUnlockTicket } from "./builtins/system-config/system-config.addon.mjs";
31
31
  import "./builtins/system-config/index.mjs";
32
32
  import { WinstonDestination, WinstonLoggingAddon } from "./builtins/winston-logging/index.mjs";
33
33
  import { C as resolveFormatRevision, S as isModelDownloaded, _ as downloadModel, a as createFileDataPlaneHandler, b as getModelFilePath, c as parseRangeHeader, d as ModelDownloadService, g as downloadFile, h as deleteModelFromDisk, i as startEventLoopStallMonitor, l as parseTokenizedUrl, m as collectModelFiles, n as STALL_TICK_MS, o as contentTypeFor, p as adoptModelRevisions, r as attributeStall, s as createAuthenticatedFileServer, t as STALL_REPORT_MS, u as resolveFilePath, v as ensureModel, w as revisionWeightUrl, y as fetchJson } from "./event-loop-stall-monitor-BtK_0Pjc.mjs";
@@ -13856,4 +13856,4 @@ var LifecycleJobEngine = class {
13856
13856
  }
13857
13857
  };
13858
13858
  //#endregion
13859
- export { ADMIN_TOKEN_MAX_LIFETIME_MS, ADMIN_TOKEN_PREFIX, AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, AGENT_REGISTER_NODE_ACTION, ALL_CAPABILITY_DEFINITIONS, ARRAY_BUFFER_CENSUS_COOLDOWN_MS, ARRAY_BUFFER_CENSUS_SUSTAIN_MS, ARRAY_BUFFER_CENSUS_THRESHOLD_MB, ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AdminTokenManager, AgentMeshAuthenticator, AlertCenterAddon, ApiKeyManager, AuthAuditLog, AuthManager, BackupDecryptError, BackupKeyFormatError, BackupKeyMismatchError, BackupKeyRequiredError, CAP_ACTION_OVERFLOW_LABEL, CAP_ACTION_UNLABELLED, CAP_METHOD_NOT_IMPLEMENTED, CA_COMMON_NAME, CA_VALIDITY_DAYS, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REF, CLUSTER_SECRET_REJECTED_EXIT_CODE, CLUSTER_SECRET_SETTINGS_KEY, CLUSTER_SECRET_UNAVAILABLE_TYPE, CORE_CAP_SERVICE_NAME, CalendarsAddon, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ComposerAddon, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CoreBlocksAddon, CustomActionRegistry, DECISIVE_HEAP_SPACES, DEFAULT_DATA_PATH, DEFAULT_HUB_TLS_TCP_PORT, DEFAULT_LAN_HTTP_PORT, DEFAULT_LOG_LEVEL, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EMPTY_CAP_ACTION_READING, EMPTY_SOCKET_DIRECTION, EMPTY_SOCKET_PLANE_BASELINE, EVENT_PLANE_TOP_N, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FIRST_BOOT_ADMIN_PASSWORD, FIRST_BOOT_ADMIN_USERNAME, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, GET_STATUS_METHOD, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HEAP_RECLAIM_MIN_INTERVAL_MS, HEAP_RECLAIM_STEADY_STATE_INTERVAL_MS, HEAP_RECLAIM_TRIGGER_MB, HEAP_SPACE_REPORT_MIN_MB, HEAP_WATCH_INTERVAL_MS, HEAP_WATCH_WARN_RATIO, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HUB_MAIN_HEAP_WATCH_LABEL, HUB_MAIN_RSS_BUDGET_MB, HUB_RSS_BUDGET_ENV, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JWT_SIGNING_KEY_REF, JobJournal, JoinTokenJtiStore, JwtSigningKeyUnavailableError, LEAF_RENEWAL_WINDOW_DAYS, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LoggingGate, LoginSessionManager, LokiDestination, LokiLoggingAddon, MAX_CAP_ACTIONS_PER_PEER, MAX_LEAF_VALIDITY_DAYS, MESH_ACTION_FORBIDDEN_TYPE, MESH_AUTH_MODE_FIELD, MESH_AUTO_ENFORCE_GRACE_MS, MESH_CA_FIELD, MESH_CA_KEY_REF, MESH_CA_SETTINGS_KEY, MESH_CLEAR_PORT_FIELD, MESH_JOIN_AUTHENTICATE_ACTION, MESH_JOIN_CHALLENGE_ACTION, MESH_JOIN_CHALLENGE_INVALID_TYPE, MESH_JOIN_CHALLENGE_TTL_MS, MESH_JOIN_PROTOCOL, MESH_NODE_UNAUTHENTICATED_TYPE, MESH_QUEUE_TOP_N, METHOD_ACCESS_MAP, MeshAuthenticator, MeshCaKeeper, MeshCrl, MeshJoinClient, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, OUTBOUND_QUEUE_COOLDOWN_MS, OUTBOUND_QUEUE_SUSTAIN_MS, OUTBOUND_QUEUE_THRESHOLD_BYTES, PARENT_ROUTED_PROVIDER_ADDON_ID, PYTHON_VERSION, PeerAnsweredError, PipelineRunner, PipelineValidator, PythonEnvManager, QUARANTINE_DIRNAME, REFRESH_TOKEN_PREFIX, RESTART_MARKER_FILE, RSS_BUDGET_REANNOUNCE_MIN_MS, RSS_BUDGET_RELEASE_RATIO, RUNNER_HEAP_WATCH_INTERVAL_MS, RUNNER_RSS_BUDGET_ENV, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, SERVER_AUTH_OID, SOCKET_PLANE_TOP_N, SOCKET_QUEUE_TOP_N, STALL_REPORT_MS, STALL_TICK_MS, STORE_READABLE_BOOTSTRAP_KEYS, SYSTEM_SECRET_OWNER, ScopedLogger, ScopedTokenManager, SettingsReadUnavailableError, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemBackupService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, TlsTcpTransporter, ToastService, UDS_NO_ROUTE_PREFIX, UNCLAIMED, UPLOADED_CA_FILE, USER_MANAGEMENT_INTERNAL, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, ZERO_LOOP_DELAY, __resetCapUsageRegistryForTests, __resetLoggingGateForTests, adaptBrokerToCluster, addonSettingsCapability, adoptModelRevisions, afterWorkerOnlineAnnounce, allFamiliesListenHost, answerClaimedGetStatus, applyCertificateToListener, applyLanHttp, applyProcessMeshClearPort, attachUserManagementInternal, attributeStall, bindPendingLanHttp, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildNpmChildEnv, buildStorageLocationRegistry, builderMountedCapNames, caFingerprint, caFingerprintCompact, callRegisterNodeWithRetry, callWithServiceDiscovery, canServeDataPlane, capActionLabel, capActionName, capActionSuffix, capBareAction, capServiceName, checkClusterSecret, checkProcessMeshClearPort, classifyAddonDir, classifyCapRoute, clearHttpsListener, clearPendingRestart, clearPlatformKeychainKey, closeLanHttp, clusterEventTopic, clusterSecretMatches, collectCertIdentity, collectModelFiles, computeHubJoinProof, computeNodeJoinProof, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonPeerBytesFacility, createAddonService, createArrayBufferCensusGate, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCapActionCensus, createCidrMatcher, createCoreCapService, createDeferredHeapWatchSink, createDoorSettingsView, createEventPlaneMeter, createEventPlaneReader, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createInspectorArrayBufferCensus, createKernelHwAccel, createLocalTransport, createLoopDelayMeter, createMeshAccessMiddleware, createMeshCsrPem, createMeshQueueReader, createOutboundQueueGate, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createSocketDirectionCounters, createSocketPlaneMeter, createSocketPlaneReader, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, createV8Reclaimer, decideMeshAutoEnforce, decideMeshClearPortOff, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, describeRss, detectWorkspacePackagesDir, deviceCapResolverLink, deviceOfGetStatus, diffCapActionCensus, diffEventPlane, diffSocketPlane, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, emitHeapDiagnosticReport, encodeFrame, enrollMeshNode, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, evaluateExistingCert, expandCapMethods, exportMeshPrivateKeyPem, fetchJson, findInPath, formatArrayBufferCensus, formatEventPlane, formatHeapSpaces, formatLogLine, formatMeshQueue, formatOutboundQueueVerdict, formatSocketPlane, generateBackupKeyPair, generateMeshCa, generateMeshKeyPair, getBrokerEventBus, getBrokerTlsTransporter, getCapUsageRegistry, getFfmpegDownloadUrl, getLoggingGate, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPlatformKeychainKey, getProcessClusterSecretGate, getProcessJwtSigningKeyState, getProcessMeshAuthMode, getProcessMeshAuthStatus, getProcessMeshCaState, getProcessNodeMeshTransport, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getUserManagementInternal, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, heapSpaceField, hostNodeIdOf, hubMainRssBudget, installManifestNativeDeps, installManifestPythonDeps, installManifestSystemDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcParentLink, isAddonDeploySource, isArrayBufferCensusReport, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isCredentialKeyName, isInfraCapability, isMeshAuthMode, isModelDownloaded, isOwnChildNodeId, isRemoteForbiddenAction, isRemoteForbiddenOnAgent, isSettingsReadUnavailable, isSourceNewer, isolatedBuiltinPhase, issueMeshLeafForNode, issueMeshLeafFromCsr, leafFingerprintOf, loadMeshCa, loadTlsCert, localEndpointPath, localProviderLink, meshErrorType, mintMeshJoinToken, mountNativeCapService, nextRssBudgetState, normalizeIp, overlayGetStatus, parseCapAction, parseCidrList, parseMeshAuthMode, parseRangeHeader, parseRssBudgetMb, parseTokenExpiry, parseTokenizedUrl, partitionIsolatedBuiltinIds, prepareBootTls, prepareRestoreArchive, processJoinTokenJtiStore, processJwtSigningKey, processMeshCrl, proxyToUpstream, quarantineAddonResidue, readBackupEncryptionHeader, readCidrSetting, readExtraSans, readHeapSpaces, readLanHttpState, readPendingRestart, readSocketQueues, readTlsAccessStatus, readTlsMode, readinessKey, reclaimIntervalMs, recordSocketFrame, redactLogEntry, redactLogMeta, redactLogText, regenerateCertificate, registerEventBusService, registerHttpsListener, registerLanHttpHandler, reissueTlsLeaf, reloadHttpsListener, requestProcessMeshEnforcement, requireProcessMeshCa, resolveFilePath, resolveFormatRevision, resolveHwAccel, resolveNpmInvocation, revertToGeneratedCertificate, revisionWeightUrl, runHubAddonBoot, runNpm, sampleSocketDirection, scheduleSelfRestart, scopeKey, scopesAllowAddon, scopesAllowDeviceCap, selectAddonResidue, selectReportedSpaces, serializeTypedArrays, servedHttpsLeafFingerprint, setHubConnected, setNodeEventInterest, setPlatformKeychainKey, setProcessClusterSecretGate, setProcessMeshAuthMode, setProcessMeshAuthStatusSource, setProcessMeshClearPortControl, setProcessMeshEnforcer, setProcessMeshTlsEnabler, setWrapperActive, shouldReclaim, signalAllRunnerGroups, socketDirectionBytes, socketDirectionMessages, splitPemCertificates, stampCallerAddonId, startEventLoopStallMonitor, startHeapWatch, startRunnerHeapWatch, staticJwtSigningKey, storeMayAnswer, storeUploadedCertificate, strandedMb, stripCamstackDeps, stripForgedIdentity, stripForwardedCaller, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, validateUploadedTls, verifyMeshJoinToken, waitUntilReady, writeExtraSans, writePendingRestart, writeTlsMode };
13859
+ export { ADMIN_TOKEN_MAX_LIFETIME_MS, ADMIN_TOKEN_PREFIX, AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_DASHBOARD_ORIGINS_FIELD, AGENT_READINESS_SERVICE_NAME, AGENT_REGISTER_NODE_ACTION, AGENT_UI_UNLOCK_DOMAIN, AGENT_UI_UNLOCK_TTL_MS, AGENT_UI_UNLOCK_VERSION, ALL_CAPABILITY_DEFINITIONS, ARRAY_BUFFER_CENSUS_COOLDOWN_MS, ARRAY_BUFFER_CENSUS_SUSTAIN_MS, ARRAY_BUFFER_CENSUS_THRESHOLD_MB, ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AdminTokenManager, AgentMeshAuthenticator, AlertCenterAddon, ApiKeyManager, AuthAuditLog, AuthManager, BackupDecryptError, BackupKeyFormatError, BackupKeyMismatchError, BackupKeyRequiredError, CAP_ACTION_OVERFLOW_LABEL, CAP_ACTION_UNLABELLED, CAP_METHOD_NOT_IMPLEMENTED, CA_COMMON_NAME, CA_VALIDITY_DAYS, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REF, CLUSTER_SECRET_REJECTED_EXIT_CODE, CLUSTER_SECRET_SETTINGS_KEY, CLUSTER_SECRET_UNAVAILABLE_TYPE, CORE_CAP_SERVICE_NAME, CalendarsAddon, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ComposerAddon, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CoreBlocksAddon, CustomActionRegistry, DECISIVE_HEAP_SPACES, DEFAULT_DATA_PATH, DEFAULT_HUB_TLS_TCP_PORT, DEFAULT_LAN_HTTP_PORT, DEFAULT_LOG_LEVEL, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EMPTY_CAP_ACTION_READING, EMPTY_SOCKET_DIRECTION, EMPTY_SOCKET_PLANE_BASELINE, EVENT_PLANE_TOP_N, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FIRST_BOOT_ADMIN_PASSWORD, FIRST_BOOT_ADMIN_USERNAME, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, GET_STATUS_METHOD, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HEAP_RECLAIM_MIN_INTERVAL_MS, HEAP_RECLAIM_STEADY_STATE_INTERVAL_MS, HEAP_RECLAIM_TRIGGER_MB, HEAP_SPACE_REPORT_MIN_MB, HEAP_WATCH_INTERVAL_MS, HEAP_WATCH_WARN_RATIO, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HUB_MAIN_HEAP_WATCH_LABEL, HUB_MAIN_RSS_BUDGET_MB, HUB_RSS_BUDGET_ENV, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JWT_SIGNING_KEY_REF, JobJournal, JoinTokenJtiStore, JwtSigningKeyUnavailableError, LEAF_RENEWAL_WINDOW_DAYS, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LoggingGate, LoginSessionManager, LokiDestination, LokiLoggingAddon, MAX_CAP_ACTIONS_PER_PEER, MAX_LEAF_VALIDITY_DAYS, MESH_ACTION_FORBIDDEN_TYPE, MESH_AUTH_MODE_FIELD, MESH_AUTO_ENFORCE_GRACE_MS, MESH_CA_FIELD, MESH_CA_KEY_REF, MESH_CA_SETTINGS_KEY, MESH_CLEAR_PORT_FIELD, MESH_JOIN_AUTHENTICATE_ACTION, MESH_JOIN_CHALLENGE_ACTION, MESH_JOIN_CHALLENGE_INVALID_TYPE, MESH_JOIN_CHALLENGE_TTL_MS, MESH_JOIN_PROTOCOL, MESH_NODE_UNAUTHENTICATED_TYPE, MESH_QUEUE_TOP_N, METHOD_ACCESS_MAP, MeshAuthenticator, MeshCaKeeper, MeshCrl, MeshJoinClient, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, OUTBOUND_QUEUE_COOLDOWN_MS, OUTBOUND_QUEUE_SUSTAIN_MS, OUTBOUND_QUEUE_THRESHOLD_BYTES, PARENT_ROUTED_PROVIDER_ADDON_ID, PYTHON_VERSION, PeerAnsweredError, PipelineRunner, PipelineValidator, PythonEnvManager, QUARANTINE_DIRNAME, REFRESH_TOKEN_PREFIX, RESTART_MARKER_FILE, RSS_BUDGET_REANNOUNCE_MIN_MS, RSS_BUDGET_RELEASE_RATIO, RUNNER_HEAP_WATCH_INTERVAL_MS, RUNNER_RSS_BUDGET_ENV, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, SERVER_AUTH_OID, SOCKET_PLANE_TOP_N, SOCKET_QUEUE_TOP_N, STALL_REPORT_MS, STALL_TICK_MS, STORE_READABLE_BOOTSTRAP_KEYS, SYSTEM_SECRET_OWNER, ScopedLogger, ScopedTokenManager, SettingsReadUnavailableError, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemBackupService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, TlsTcpTransporter, ToastService, UDS_NO_ROUTE_PREFIX, UNCLAIMED, UPLOADED_CA_FILE, USER_MANAGEMENT_INTERNAL, UdsLocalTransportClient, UdsLocalTransportServer, UnlockTicketJtiStore, UserManager, WinstonDestination, WinstonLoggingAddon, ZERO_LOOP_DELAY, __resetCapUsageRegistryForTests, __resetLoggingGateForTests, adaptBrokerToCluster, addonSettingsCapability, adoptModelRevisions, afterWorkerOnlineAnnounce, allFamiliesListenHost, answerClaimedGetStatus, applyCertificateToListener, applyLanHttp, applyProcessMeshClearPort, attachUserManagementInternal, attributeStall, bindPendingLanHttp, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildNpmChildEnv, buildStorageLocationRegistry, builderMountedCapNames, caFingerprint, caFingerprintCompact, callRegisterNodeWithRetry, callWithServiceDiscovery, canServeDataPlane, capActionLabel, capActionName, capActionSuffix, capBareAction, capServiceName, checkClusterSecret, checkProcessMeshClearPort, classifyAddonDir, classifyCapRoute, clearHttpsListener, clearPendingRestart, clearPlatformKeychainKey, closeLanHttp, clusterEventTopic, clusterSecretMatches, collectCertIdentity, collectModelFiles, computeHubJoinProof, computeNodeJoinProof, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonPeerBytesFacility, createAddonService, createArrayBufferCensusGate, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCapActionCensus, createCidrMatcher, createCoreCapService, createDeferredHeapWatchSink, createDoorSettingsView, createEventPlaneMeter, createEventPlaneReader, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createInspectorArrayBufferCensus, createKernelHwAccel, createLocalTransport, createLoopDelayMeter, createMeshAccessMiddleware, createMeshCsrPem, createMeshQueueReader, createOutboundQueueGate, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createSocketDirectionCounters, createSocketPlaneMeter, createSocketPlaneReader, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, createV8Reclaimer, decideMeshAutoEnforce, decideMeshClearPortOff, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, describeRss, detectWorkspacePackagesDir, deviceCapResolverLink, deviceOfGetStatus, diffCapActionCensus, diffEventPlane, diffSocketPlane, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, emitHeapDiagnosticReport, encodeFrame, enrollMeshNode, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, evaluateExistingCert, expandCapMethods, exportMeshPrivateKeyPem, fetchJson, findInPath, formatArrayBufferCensus, formatEventPlane, formatHeapSpaces, formatLogLine, formatMeshQueue, formatOutboundQueueVerdict, formatSocketPlane, generateBackupKeyPair, generateMeshCa, generateMeshKeyPair, getBrokerEventBus, getBrokerTlsTransporter, getCapUsageRegistry, getFfmpegDownloadUrl, getLoggingGate, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPlatformKeychainKey, getProcessClusterSecretGate, getProcessJwtSigningKeyState, getProcessMeshAuthMode, getProcessMeshAuthStatus, getProcessMeshCaState, getProcessNodeMeshTransport, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getUserManagementInternal, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, heapSpaceField, hostNodeIdOf, hubMainRssBudget, installManifestNativeDeps, installManifestPythonDeps, installManifestSystemDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcParentLink, isAddonDeploySource, isAllowedAgentDashboardReturnTo, isArrayBufferCensusReport, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isCredentialKeyName, isInfraCapability, isMeshAuthMode, isModelDownloaded, isOwnChildNodeId, isRemoteForbiddenAction, isRemoteForbiddenOnAgent, isSettingsReadUnavailable, isSourceNewer, isolatedBuiltinPhase, issueMeshLeafForNode, issueMeshLeafFromCsr, leafFingerprintOf, loadMeshCa, loadTlsCert, localEndpointPath, localProviderLink, meshErrorType, mintAgentUiUnlockTicket, mintMeshJoinToken, mountNativeCapService, nextRssBudgetState, normalizeIp, overlayGetStatus, parseAgentDashboardOrigins, parseCapAction, parseCidrList, parseMeshAuthMode, parseRangeHeader, parseRssBudgetMb, parseTokenExpiry, parseTokenizedUrl, partitionIsolatedBuiltinIds, prepareBootTls, prepareRestoreArchive, processJoinTokenJtiStore, processJwtSigningKey, processMeshCrl, proxyToUpstream, quarantineAddonResidue, readBackupEncryptionHeader, readCidrSetting, readExtraSans, readHeapSpaces, readLanHttpState, readPendingRestart, readSocketQueues, readTlsAccessStatus, readTlsMode, readinessKey, reclaimIntervalMs, recordSocketFrame, redactLogEntry, redactLogMeta, redactLogText, regenerateCertificate, registerEventBusService, registerHttpsListener, registerLanHttpHandler, reissueTlsLeaf, reloadHttpsListener, requestProcessMeshEnforcement, requireProcessMeshCa, resolveFilePath, resolveFormatRevision, resolveHwAccel, resolveNpmInvocation, revertToGeneratedCertificate, revisionWeightUrl, runHubAddonBoot, runNpm, sampleSocketDirection, scheduleSelfRestart, scopeKey, scopesAllowAddon, scopesAllowDeviceCap, selectAddonResidue, selectReportedSpaces, serializeTypedArrays, servedHttpsLeafFingerprint, setHubConnected, setNodeEventInterest, setPlatformKeychainKey, setProcessClusterSecretGate, setProcessMeshAuthMode, setProcessMeshAuthStatusSource, setProcessMeshClearPortControl, setProcessMeshEnforcer, setProcessMeshTlsEnabler, setWrapperActive, shouldReclaim, signalAllRunnerGroups, socketDirectionBytes, socketDirectionMessages, splitPemCertificates, stampCallerAddonId, startEventLoopStallMonitor, startHeapWatch, startRunnerHeapWatch, staticJwtSigningKey, storeMayAnswer, storeUploadedCertificate, strandedMb, stripCamstackDeps, stripForgedIdentity, stripForwardedCaller, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, validateUploadedTls, verifyAgentUiUnlockTicket, verifyMeshJoinToken, waitUntilReady, writeExtraSans, writePendingRestart, writeTlsMode };
@@ -69,6 +69,8 @@ export type { MeshJoinAuthenticateParams, MeshJoinAuthenticateResult, MeshJoinCh
69
69
  export { getProcessMeshAuthMode, getProcessMeshAuthStatus, isMeshAuthMode, MESH_AUTH_MODE_FIELD, parseMeshAuthMode, requestProcessMeshEnforcement, setProcessMeshAuthMode, setProcessMeshAuthStatusSource, setProcessMeshEnforcer, } from './moleculer/mesh-auth-mode.js';
70
70
  export { decideMeshAutoEnforce, MESH_AUTO_ENFORCE_GRACE_MS, } from './moleculer/mesh-auto-enforce.js';
71
71
  export type { MeshAutoEnforceDecision, MeshAutoEnforceInput, } from './moleculer/mesh-auto-enforce.js';
72
+ export { AGENT_DASHBOARD_ORIGINS_FIELD, AGENT_UI_UNLOCK_DOMAIN, AGENT_UI_UNLOCK_TTL_MS, AGENT_UI_UNLOCK_VERSION, isAllowedAgentDashboardReturnTo, mintAgentUiUnlockTicket, parseAgentDashboardOrigins, UnlockTicketJtiStore, verifyAgentUiUnlockTicket, } from './moleculer/agent-ui-unlock.js';
73
+ export type { AgentUiUnlockPayload, MintAgentUiUnlockTicketInput, VerifyAgentUiUnlockTicketOptions, } from './moleculer/agent-ui-unlock.js';
72
74
  export type { MeshAuthMode, MeshAuthStatus, MeshEnforcer, MeshNodeAuthEntry, MeshNodeAuthState, } from './moleculer/mesh-auth-mode.js';
73
75
  export { serializeTypedArrays } from './moleculer/typed-array-serde.js';
74
76
  export { createReadinessService, createReadinessServiceForRegistry, AGENT_READINESS_SERVICE_NAME, } from './moleculer/readiness-service.js';
@@ -0,0 +1,52 @@
1
+ export declare const AGENT_UI_UNLOCK_DOMAIN = "camstack-agent-ui-unlock/v1";
2
+ export declare const AGENT_UI_UNLOCK_VERSION: 1;
3
+ /** Ticket lifetime — long enough for a redirect hop, short enough to steal poorly. */
4
+ export declare const AGENT_UI_UNLOCK_TTL_MS: number;
5
+ /**
6
+ * Cluster settings key: extra origins allowed as `returnTo` for Continue-with-hub
7
+ * (NPM hostnames that are not `:4444` on a LAN IP). Comma-separated origins.
8
+ */
9
+ export declare const AGENT_DASHBOARD_ORIGINS_FIELD = "agentDashboardOrigins";
10
+ export interface AgentUiUnlockPayload {
11
+ readonly v: typeof AGENT_UI_UNLOCK_VERSION;
12
+ readonly nodeId: string;
13
+ /** Epoch ms. */
14
+ readonly exp: number;
15
+ readonly jti: string;
16
+ }
17
+ /** In-memory spent `jti` until `exp` (ms). */
18
+ export declare class UnlockTicketJtiStore {
19
+ private readonly spent;
20
+ isSpent(jti: string, nowMs?: number): boolean;
21
+ /** Record a jti as used until `exp`. Returns false if already spent. */
22
+ consume(jti: string, exp: number, nowMs?: number): boolean;
23
+ /** Test helper. */
24
+ size(): number;
25
+ private gc;
26
+ }
27
+ export interface MintAgentUiUnlockTicketInput {
28
+ /** `hashClusterSecret(secret)` — never the raw secret. */
29
+ readonly secretHash: string;
30
+ readonly nodeId: string;
31
+ readonly ttlMs?: number;
32
+ readonly nowMs?: number;
33
+ readonly jti?: string;
34
+ }
35
+ export declare function mintAgentUiUnlockTicket(input: MintAgentUiUnlockTicketInput): string;
36
+ export interface VerifyAgentUiUnlockTicketOptions {
37
+ readonly expectedNodeId: string;
38
+ readonly nowMs?: number;
39
+ }
40
+ /**
41
+ * Verify a ticket under `secretHash`. Returns the payload, or `null` when the
42
+ * signature, shape, expiry or nodeId fails (no reason string — refuse quietly).
43
+ */
44
+ export declare function verifyAgentUiUnlockTicket(secretHash: string, ticket: string, options: VerifyAgentUiUnlockTicketOptions): AgentUiUnlockPayload | null;
45
+ /**
46
+ * True when `returnTo` may receive a ticket redirect. Default: any http(s)
47
+ * origin whose port is 4444. Extra origins come from
48
+ * {@link AGENT_DASHBOARD_ORIGINS_FIELD} (exact origin match).
49
+ */
50
+ export declare function isAllowedAgentDashboardReturnTo(returnTo: string, extraOrigins?: readonly string[]): boolean;
51
+ /** Parse a stored comma/whitespace-separated origins list. */
52
+ export declare function parseAgentDashboardOrigins(stored: unknown): readonly string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.385",
3
+ "version": "1.2.387",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",