@camstack/system 1.2.380 → 1.2.382

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 (117) hide show
  1. package/dist/addon-runner.js +4 -2
  2. package/dist/addon-runner.mjs +4 -2
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/auth/admin-token-manager.d.ts +75 -0
  6. package/dist/auth/api-key-manager.d.ts +1 -14
  7. package/dist/auth/auth-audit-log.d.ts +59 -0
  8. package/dist/auth/auth-manager.d.ts +28 -16
  9. package/dist/auth/cidr.d.ts +22 -0
  10. package/dist/auth/login-session-manager.d.ts +158 -0
  11. package/dist/auth/user-management-internal.d.ts +69 -0
  12. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  13. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  14. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  15. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  16. package/dist/builtins/alerts/alerts.addon.js +1 -1
  17. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  18. package/dist/builtins/autotrack/index.js +1 -1
  19. package/dist/builtins/autotrack/index.mjs +1 -1
  20. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +14 -0
  21. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +291 -3631
  22. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +284 -3617
  23. package/dist/builtins/backup-orchestrator/encryption-key-store.d.ts +36 -0
  24. package/dist/builtins/backup-orchestrator/manifest-store.d.ts +19 -1
  25. package/dist/builtins/backup-orchestrator/restore-archive.d.ts +24 -0
  26. package/dist/builtins/calendars/calendars.addon.js +1 -1
  27. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  28. package/dist/builtins/camera-grid/index.js +1 -1
  29. package/dist/builtins/camera-grid/index.mjs +1 -1
  30. package/dist/builtins/composer/composer.addon.js +2 -2
  31. package/dist/builtins/composer/composer.addon.mjs +2 -2
  32. package/dist/builtins/console-logging/index.js +1 -1
  33. package/dist/builtins/console-logging/index.mjs +1 -1
  34. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  35. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  36. package/dist/builtins/device-manager/device-aggregation.d.ts +12 -0
  37. package/dist/builtins/device-manager/device-form-secrets.d.ts +25 -0
  38. package/dist/builtins/device-manager/device-manager.addon.d.ts +2 -0
  39. package/dist/builtins/device-manager/device-manager.addon.js +165 -26
  40. package/dist/builtins/device-manager/device-manager.addon.mjs +165 -26
  41. package/dist/builtins/hub-forwarder/index.js +1 -1
  42. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  43. package/dist/builtins/keychain/index.js +1108 -733
  44. package/dist/builtins/keychain/index.mjs +1108 -733
  45. package/dist/builtins/keychain/keychain-reset-trigger.d.ts +25 -0
  46. package/dist/builtins/keychain/keychain-service.d.ts +36 -3
  47. package/dist/builtins/keychain/keychain-store.d.ts +24 -1
  48. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  49. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  50. package/dist/builtins/local-auth/local-auth.addon.d.ts +16 -0
  51. package/dist/builtins/local-auth/local-auth.addon.js +1021 -118
  52. package/dist/builtins/local-auth/local-auth.addon.mjs +1034 -141
  53. package/dist/builtins/local-network/local-network.addon.js +3 -3
  54. package/dist/builtins/local-network/local-network.addon.mjs +3 -3
  55. package/dist/builtins/loki-logging/index.js +97 -29
  56. package/dist/builtins/loki-logging/index.mjs +97 -29
  57. package/dist/builtins/loki-logging/loki-logging.addon.d.ts +7 -23
  58. package/dist/builtins/loki-logging/retire-enabled-flag.d.ts +23 -0
  59. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  60. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  61. package/dist/builtins/platform-probe/index.js +1 -1
  62. package/dist/builtins/platform-probe/index.mjs +1 -1
  63. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  64. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  65. package/dist/builtins/snapshot/index.js +1 -1
  66. package/dist/builtins/snapshot/index.mjs +1 -1
  67. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  68. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  69. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  70. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  71. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +2 -2
  72. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +2 -2
  73. package/dist/builtins/system-backup/backup-encryption.d.ts +57 -0
  74. package/dist/builtins/system-backup/system-backup.service.d.ts +16 -1
  75. package/dist/builtins/system-config/cluster-secret-keeper.d.ts +7 -0
  76. package/dist/builtins/system-config/system-config.addon.d.ts +13 -3
  77. package/dist/builtins/system-config/system-config.addon.js +324 -38
  78. package/dist/builtins/system-config/system-config.addon.mjs +312 -39
  79. package/dist/builtins/winston-logging/index.js +1 -1
  80. package/dist/builtins/winston-logging/index.mjs +1 -1
  81. package/dist/{child-cap-dispatch-CZeRUgYF.js → child-cap-dispatch-BrMdKHfp.js} +6 -3
  82. package/dist/{child-cap-dispatch-CpjohToR.mjs → child-cap-dispatch-WsYPNvnK.mjs} +10 -7
  83. package/dist/{cluster-secret-Dg0OOOSr.js → cluster-secret-BEPLh3LL.js} +1 -1
  84. package/dist/{cluster-secret-BDue-uJj.mjs → cluster-secret-DhPdZ0VA.mjs} +1 -1
  85. package/dist/{composition-sources-CHvLMrFI.js → composition-sources-C6ZynCZ2.js} +1 -1
  86. package/dist/{composition-sources-DT_uJwI3.mjs → composition-sources-XqRX51db.mjs} +1 -1
  87. package/dist/{dist-B9oFw2jh.mjs → dist-CP5TTjwd.mjs} +1713 -157
  88. package/dist/{dist-Cc6KOdY-.js → dist-tnAO12ak.js} +1754 -156
  89. package/dist/{event-loop-stall-monitor-mk6WGxir.js → event-loop-stall-monitor-9kR0sAPS.js} +15 -15
  90. package/dist/{event-loop-stall-monitor-anUH2IE1.mjs → event-loop-stall-monitor-WkssIXjq.mjs} +15 -15
  91. package/dist/{hub-cap-forward-CH6_VWVO.mjs → hub-cap-forward-CDiN1p98.mjs} +18 -2
  92. package/dist/{hub-cap-forward-COCmYaup.js → hub-cap-forward-W8oEm4ME.js} +23 -1
  93. package/dist/index.d.ts +16 -1
  94. package/dist/index.js +3341 -79
  95. package/dist/index.mjs +3567 -354
  96. package/dist/{jwt-signing-key-Qhde8r82.mjs → jwt-signing-key-CAbozwvF.mjs} +1 -1
  97. package/dist/{jwt-signing-key-C2rE3wH6.js → jwt-signing-key-Dgf8sIXY.js} +1 -1
  98. package/dist/kernel/index.d.ts +12 -0
  99. package/dist/kernel/moleculer/broker-factory.d.ts +8 -1
  100. package/dist/kernel/moleculer/child-addon-call-dispatch.d.ts +1 -0
  101. package/dist/kernel/moleculer/hub-service.d.ts +9 -0
  102. package/dist/kernel/moleculer/mesh-access-middleware.d.ts +28 -0
  103. package/dist/kernel/moleculer/mesh-auth-mode.d.ts +64 -0
  104. package/dist/kernel/moleculer/mesh-authenticator.d.ts +71 -0
  105. package/dist/kernel/moleculer/mesh-auto-enforce.d.ts +26 -0
  106. package/dist/kernel/moleculer/mesh-join-client.d.ts +51 -0
  107. package/dist/kernel/moleculer/mesh-join-proof.d.ts +39 -0
  108. package/dist/kernel/system-settings-policy.d.ts +4 -3
  109. package/dist/kernel/transport/hub-cap-forward.d.ts +14 -3
  110. package/dist/kernel/transport/index.d.ts +1 -1
  111. package/dist/restore-archive-BV39nY46.mjs +4045 -0
  112. package/dist/restore-archive-CbvrfPuB.js +4139 -0
  113. package/dist/{retired-settings-keys-YOOKLA3v.js → retired-settings-keys-4bHpL-0m.js} +1 -1
  114. package/dist/{retired-settings-keys-DlZ3VV6x.mjs → retired-settings-keys-CzpIkwS-.mjs} +1 -1
  115. package/dist/{tls-CJHogwTl.mjs → tls-4cRH24Id.mjs} +3 -3
  116. package/package.json +1 -1
  117. package/dist/{tls-CCMX7MjX.js → tls-CljF057D.js} +1 -1
@@ -1,12 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-Cc6KOdY-.js");
4
- const require_tls = require("../../tls-CCMX7MjX.js");
3
+ const require_dist = require("../../dist-tnAO12ak.js");
4
+ const require_tls = require("../../tls-CljF057D.js");
5
5
  let zod = require("zod");
6
+ let node_net = require("node:net");
6
7
  let node_os = require("node:os");
7
8
  node_os = require_chunk.__toESM(node_os);
8
9
  let node_fs = require("node:fs");
9
- let node_net = require("node:net");
10
10
  //#region src/builtins/local-network/address-book.ts
11
11
  /**
12
12
  * The Network page's address book, as pure functions: custom addresses plus
@@ -1,9 +1,9 @@
1
- import { Cn as resolveHubAddressRoles, En as resolveNotificationTapAddress, Hn as validateCustomAddress, It as isAppLinkBase, Yt as localNetworkCapability, fn as pickAppLinkHub, lr as BaseAddon, nt as checkRoleAddress, qr as EventCategory, s as CAMSTACK_APP_LINK_ORIGIN, yn as resolveAppConnections, zt as isDefaultAppConnectionEndpoint } from "../../dist-B9oFw2jh.mjs";
2
- import { P as readLanHttpState, _ as writeExtraSans, h as readTlsMode, i as storeUploadedCertificate, j as applyLanHttp, k as DEFAULT_LAN_HTTP_PORT, m as readTlsAccessStatus, n as regenerateCertificate, p as readExtraSans, r as revertToGeneratedCertificate, t as applyCertificateToListener, u as servedHttpsLeafFingerprint, y as ensureTlsCert } from "../../tls-CJHogwTl.mjs";
1
+ import { Bt as isDefaultAppConnectionEndpoint, Dn as resolveNotificationTapAddress, Lt as isAppLinkBase, Un as validateCustomAddress, Xt as localNetworkCapability, bn as resolveAppConnections, ei as EventCategory, pn as pickAppLinkHub, rt as checkRoleAddress, s as CAMSTACK_APP_LINK_ORIGIN, ur as BaseAddon, wn as resolveHubAddressRoles } from "../../dist-CP5TTjwd.mjs";
2
+ import { P as readLanHttpState, _ as writeExtraSans, h as readTlsMode, i as storeUploadedCertificate, j as applyLanHttp, k as DEFAULT_LAN_HTTP_PORT, m as readTlsAccessStatus, n as regenerateCertificate, p as readExtraSans, r as revertToGeneratedCertificate, t as applyCertificateToListener, u as servedHttpsLeafFingerprint, y as ensureTlsCert } from "../../tls-4cRH24Id.mjs";
3
3
  import { z } from "zod";
4
+ import { isIP } from "node:net";
4
5
  import * as os from "node:os";
5
6
  import { readFileSync } from "node:fs";
6
- import { isIP } from "node:net";
7
7
  //#region src/builtins/local-network/address-book.ts
8
8
  /**
9
9
  * The Network page's address book, as pure functions: custom addresses plus
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Cc6KOdY-.js");
6
+ const require_dist = require("../../dist-tnAO12ak.js");
7
7
  //#region src/builtins/loki-logging/loki-payload.ts
8
8
  /**
9
9
  * Loki rejects a label name that is not a valid Prometheus label
@@ -293,10 +293,52 @@ var LokiDestination = class {
293
293
  }
294
294
  };
295
295
  //#endregion
296
+ //#region src/builtins/loki-logging/retire-enabled-flag.ts
297
+ /** The retired settings key. */
298
+ var RETIRED_ENABLED_KEY = "enabled";
299
+ /** Store key marking the retirement as done. Not a schema field. Pre-D777 name kept. */
300
+ var RETIREMENT_MARKER_KEY = "migratedEnabled";
301
+ function deferred(reason) {
302
+ return {
303
+ kind: "deferred",
304
+ reason
305
+ };
306
+ }
307
+ function isMarked(read) {
308
+ return read.kind === "present" && read.value === true;
309
+ }
310
+ async function retireEnabledFlag(deps) {
311
+ const marker = await deps.readStoredKey(RETIREMENT_MARKER_KEY);
312
+ if (marker.kind === "unreadable") return deferred("settings store unreadable (marker)");
313
+ if (isMarked(marker)) return { kind: "already-retired" };
314
+ const flag = await deps.readStoredKey(RETIRED_ENABLED_KEY);
315
+ if (flag.kind === "unreadable") return deferred("settings store unreadable (enabled)");
316
+ const wasOff = flag.kind === "present" && flag.value === false;
317
+ if (wasOff) try {
318
+ await deps.disableAtCapability();
319
+ } catch (err) {
320
+ return deferred(`capability toggle refused: ${err instanceof Error ? err.message : String(err)}`);
321
+ }
322
+ await deps.commit();
323
+ if (!isMarked(await deps.readStoredKey("migratedEnabled"))) return deferred("retirement write did not land");
324
+ return {
325
+ kind: "retired",
326
+ capDisabled: wasOff
327
+ };
328
+ }
329
+ //#endregion
296
330
  //#region src/builtins/loki-logging/loki-logging.addon.ts
331
+ /** Backoff between retirement attempts — the first ones are refused until the
332
+ * provider registration reaches the hub. After the last, the next boot retries. */
333
+ var RETIREMENT_RETRY_DELAYS_MS = [
334
+ 1e3,
335
+ 2e3,
336
+ 5e3,
337
+ 1e4,
338
+ 3e4,
339
+ 6e4
340
+ ];
297
341
  var DEFAULTS = {
298
- enabled: true,
299
- migratedEnabled: false,
300
342
  url: "",
301
343
  job: "camstack",
302
344
  level: "info",
@@ -310,6 +352,8 @@ var DEFAULTS = {
310
352
  };
311
353
  var LokiLoggingAddon = class extends require_dist.BaseAddon {
312
354
  destination = null;
355
+ retirementTimer = null;
356
+ shuttingDown = false;
313
357
  constructor() {
314
358
  super({ ...DEFAULTS });
315
359
  }
@@ -317,48 +361,72 @@ var LokiLoggingAddon = class extends require_dist.BaseAddon {
317
361
  this.destination = new LokiDestination();
318
362
  await this.destination.initialize(this.destinationConfig());
319
363
  this.ctx.logger.info(this.config.url.length > 0 ? `Loki logging initialized → ${this.config.url}` : "Loki logging registered but inactive (no URL set)");
320
- await this.migrateRetiredEnabledFlag();
364
+ this.shuttingDown = false;
365
+ this.attemptEnabledFlagRetirement(0);
321
366
  return [{
322
367
  capability: require_dist.logDestinationCapability,
323
368
  provider: this.destination
324
369
  }];
325
370
  }
326
- /**
327
- * ONE-SHOT: carry a stored `enabled: false` over to the orchestrator's
328
- * per-destination toggle, then never look at the flag again.
329
- *
330
- * Without this, retiring the private setting would silently RE-ENABLE Loki
331
- * for every operator who had turned it off — shipping their logs to a host
332
- * they may no longer control. Best-effort: if the toggle call fails the
333
- * migration is simply retried next boot (the flag is only cleared on
334
- * success).
335
- */
336
- async migrateRetiredEnabledFlag() {
337
- if (this.config.migratedEnabled) return;
371
+ /** One attempt of the D777 carry-over; reschedules itself while deferred. */
372
+ async attemptEnabledFlagRetirement(attempt) {
373
+ if (this.shuttingDown) return;
374
+ let outcome;
338
375
  try {
339
- if (!this.config.enabled) {
340
- await this.ctx.api.addons.setCapabilityProviderEnabled.mutate({
341
- capName: "log-destination",
342
- addonId: this.ctx.id,
343
- enabled: false
344
- });
345
- this.ctx.logger.info("Loki logging: the retired `enabled: false` setting was carried over to the per-destination toggle — re-enable it there if you want Loki shipping again.");
346
- }
347
- await this.updateGlobalSettings({ migratedEnabled: true });
376
+ outcome = await retireEnabledFlag({
377
+ readStoredKey: (key) => this.readStoredGlobalKey(key),
378
+ disableAtCapability: async () => {
379
+ await this.ctx.api.addons.setCapabilityProviderEnabled.mutate({
380
+ capName: require_dist.logDestinationCapability.name,
381
+ addonId: this.ctx.id,
382
+ enabled: false
383
+ });
384
+ },
385
+ commit: async () => {
386
+ const patch = {
387
+ [RETIRED_ENABLED_KEY]: void 0,
388
+ [RETIREMENT_MARKER_KEY]: true
389
+ };
390
+ await this.updateGlobalSettings(patch);
391
+ }
392
+ });
348
393
  } catch (err) {
349
- this.ctx.logger.warn("Loki logging: enablement migration deferred to next boot", { meta: { error: err instanceof Error ? err.message : String(err) } });
394
+ outcome = {
395
+ kind: "deferred",
396
+ reason: err instanceof Error ? err.message : String(err)
397
+ };
398
+ }
399
+ if (outcome.kind === "retired" && outcome.capDisabled) this.ctx.logger.info("Loki logging: the retired `enabled: false` setting was carried over to the per-destination toggle — re-enable it there if you want Loki shipping again.");
400
+ if (outcome.kind !== "deferred") return;
401
+ const delay = RETIREMENT_RETRY_DELAYS_MS[attempt];
402
+ if (delay === void 0) {
403
+ this.ctx.logger.warn("Loki logging: enablement carry-over deferred to next boot", { meta: { reason: outcome.reason } });
404
+ return;
350
405
  }
406
+ this.ctx.logger.debug("Loki logging: enablement carry-over deferred", { meta: {
407
+ reason: outcome.reason,
408
+ attempt,
409
+ retryInMs: delay
410
+ } });
411
+ this.retirementTimer = setTimeout(() => {
412
+ this.retirementTimer = null;
413
+ this.attemptEnabledFlagRetirement(attempt + 1);
414
+ }, delay);
415
+ this.retirementTimer.unref?.();
351
416
  }
352
417
  async onShutdown() {
418
+ this.shuttingDown = true;
419
+ if (this.retirementTimer !== null) clearTimeout(this.retirementTimer);
420
+ this.retirementTimer = null;
353
421
  await this.destination?.shutdown();
354
422
  this.destination = null;
355
423
  }
356
424
  /**
357
- * React to a settings change (enable/disable, URL, level, batching) without a
425
+ * React to a settings change (URL, level, batching) without a
358
426
  * restart — the contract this addon advertises. `BaseAddon` has already
359
427
  * reloaded `this.config` by the time this runs; re-apply it to the live
360
- * destination so toggling `enabled` on actually starts pushing (and off stops
361
- * it). Without this the destination keeps its boot-time config forever.
428
+ * destination so setting or clearing the URL actually starts (or stops)
429
+ * pushing. Without this the destination keeps its boot-time config forever.
362
430
  */
363
431
  async onConfigChanged() {
364
432
  await this.destination?.reconfigure(this.destinationConfig());
@@ -1,4 +1,4 @@
1
- import { Zt as logDestinationCapability, lr as BaseAddon } from "../../dist-B9oFw2jh.mjs";
1
+ import { Qt as logDestinationCapability, ur as BaseAddon } from "../../dist-CP5TTjwd.mjs";
2
2
  //#region src/builtins/loki-logging/loki-payload.ts
3
3
  /**
4
4
  * Loki rejects a label name that is not a valid Prometheus label
@@ -288,10 +288,52 @@ var LokiDestination = class {
288
288
  }
289
289
  };
290
290
  //#endregion
291
+ //#region src/builtins/loki-logging/retire-enabled-flag.ts
292
+ /** The retired settings key. */
293
+ var RETIRED_ENABLED_KEY = "enabled";
294
+ /** Store key marking the retirement as done. Not a schema field. Pre-D777 name kept. */
295
+ var RETIREMENT_MARKER_KEY = "migratedEnabled";
296
+ function deferred(reason) {
297
+ return {
298
+ kind: "deferred",
299
+ reason
300
+ };
301
+ }
302
+ function isMarked(read) {
303
+ return read.kind === "present" && read.value === true;
304
+ }
305
+ async function retireEnabledFlag(deps) {
306
+ const marker = await deps.readStoredKey(RETIREMENT_MARKER_KEY);
307
+ if (marker.kind === "unreadable") return deferred("settings store unreadable (marker)");
308
+ if (isMarked(marker)) return { kind: "already-retired" };
309
+ const flag = await deps.readStoredKey(RETIRED_ENABLED_KEY);
310
+ if (flag.kind === "unreadable") return deferred("settings store unreadable (enabled)");
311
+ const wasOff = flag.kind === "present" && flag.value === false;
312
+ if (wasOff) try {
313
+ await deps.disableAtCapability();
314
+ } catch (err) {
315
+ return deferred(`capability toggle refused: ${err instanceof Error ? err.message : String(err)}`);
316
+ }
317
+ await deps.commit();
318
+ if (!isMarked(await deps.readStoredKey("migratedEnabled"))) return deferred("retirement write did not land");
319
+ return {
320
+ kind: "retired",
321
+ capDisabled: wasOff
322
+ };
323
+ }
324
+ //#endregion
291
325
  //#region src/builtins/loki-logging/loki-logging.addon.ts
326
+ /** Backoff between retirement attempts — the first ones are refused until the
327
+ * provider registration reaches the hub. After the last, the next boot retries. */
328
+ var RETIREMENT_RETRY_DELAYS_MS = [
329
+ 1e3,
330
+ 2e3,
331
+ 5e3,
332
+ 1e4,
333
+ 3e4,
334
+ 6e4
335
+ ];
292
336
  var DEFAULTS = {
293
- enabled: true,
294
- migratedEnabled: false,
295
337
  url: "",
296
338
  job: "camstack",
297
339
  level: "info",
@@ -305,6 +347,8 @@ var DEFAULTS = {
305
347
  };
306
348
  var LokiLoggingAddon = class extends BaseAddon {
307
349
  destination = null;
350
+ retirementTimer = null;
351
+ shuttingDown = false;
308
352
  constructor() {
309
353
  super({ ...DEFAULTS });
310
354
  }
@@ -312,48 +356,72 @@ var LokiLoggingAddon = class extends BaseAddon {
312
356
  this.destination = new LokiDestination();
313
357
  await this.destination.initialize(this.destinationConfig());
314
358
  this.ctx.logger.info(this.config.url.length > 0 ? `Loki logging initialized → ${this.config.url}` : "Loki logging registered but inactive (no URL set)");
315
- await this.migrateRetiredEnabledFlag();
359
+ this.shuttingDown = false;
360
+ this.attemptEnabledFlagRetirement(0);
316
361
  return [{
317
362
  capability: logDestinationCapability,
318
363
  provider: this.destination
319
364
  }];
320
365
  }
321
- /**
322
- * ONE-SHOT: carry a stored `enabled: false` over to the orchestrator's
323
- * per-destination toggle, then never look at the flag again.
324
- *
325
- * Without this, retiring the private setting would silently RE-ENABLE Loki
326
- * for every operator who had turned it off — shipping their logs to a host
327
- * they may no longer control. Best-effort: if the toggle call fails the
328
- * migration is simply retried next boot (the flag is only cleared on
329
- * success).
330
- */
331
- async migrateRetiredEnabledFlag() {
332
- if (this.config.migratedEnabled) return;
366
+ /** One attempt of the D777 carry-over; reschedules itself while deferred. */
367
+ async attemptEnabledFlagRetirement(attempt) {
368
+ if (this.shuttingDown) return;
369
+ let outcome;
333
370
  try {
334
- if (!this.config.enabled) {
335
- await this.ctx.api.addons.setCapabilityProviderEnabled.mutate({
336
- capName: "log-destination",
337
- addonId: this.ctx.id,
338
- enabled: false
339
- });
340
- this.ctx.logger.info("Loki logging: the retired `enabled: false` setting was carried over to the per-destination toggle — re-enable it there if you want Loki shipping again.");
341
- }
342
- await this.updateGlobalSettings({ migratedEnabled: true });
371
+ outcome = await retireEnabledFlag({
372
+ readStoredKey: (key) => this.readStoredGlobalKey(key),
373
+ disableAtCapability: async () => {
374
+ await this.ctx.api.addons.setCapabilityProviderEnabled.mutate({
375
+ capName: logDestinationCapability.name,
376
+ addonId: this.ctx.id,
377
+ enabled: false
378
+ });
379
+ },
380
+ commit: async () => {
381
+ const patch = {
382
+ [RETIRED_ENABLED_KEY]: void 0,
383
+ [RETIREMENT_MARKER_KEY]: true
384
+ };
385
+ await this.updateGlobalSettings(patch);
386
+ }
387
+ });
343
388
  } catch (err) {
344
- this.ctx.logger.warn("Loki logging: enablement migration deferred to next boot", { meta: { error: err instanceof Error ? err.message : String(err) } });
389
+ outcome = {
390
+ kind: "deferred",
391
+ reason: err instanceof Error ? err.message : String(err)
392
+ };
393
+ }
394
+ if (outcome.kind === "retired" && outcome.capDisabled) this.ctx.logger.info("Loki logging: the retired `enabled: false` setting was carried over to the per-destination toggle — re-enable it there if you want Loki shipping again.");
395
+ if (outcome.kind !== "deferred") return;
396
+ const delay = RETIREMENT_RETRY_DELAYS_MS[attempt];
397
+ if (delay === void 0) {
398
+ this.ctx.logger.warn("Loki logging: enablement carry-over deferred to next boot", { meta: { reason: outcome.reason } });
399
+ return;
345
400
  }
401
+ this.ctx.logger.debug("Loki logging: enablement carry-over deferred", { meta: {
402
+ reason: outcome.reason,
403
+ attempt,
404
+ retryInMs: delay
405
+ } });
406
+ this.retirementTimer = setTimeout(() => {
407
+ this.retirementTimer = null;
408
+ this.attemptEnabledFlagRetirement(attempt + 1);
409
+ }, delay);
410
+ this.retirementTimer.unref?.();
346
411
  }
347
412
  async onShutdown() {
413
+ this.shuttingDown = true;
414
+ if (this.retirementTimer !== null) clearTimeout(this.retirementTimer);
415
+ this.retirementTimer = null;
348
416
  await this.destination?.shutdown();
349
417
  this.destination = null;
350
418
  }
351
419
  /**
352
- * React to a settings change (enable/disable, URL, level, batching) without a
420
+ * React to a settings change (URL, level, batching) without a
353
421
  * restart — the contract this addon advertises. `BaseAddon` has already
354
422
  * reloaded `this.config` by the time this runs; re-apply it to the live
355
- * destination so toggling `enabled` on actually starts pushing (and off stops
356
- * it). Without this the destination keeps its boot-time config forever.
423
+ * destination so setting or clearing the URL actually starts (or stops)
424
+ * pushing. Without this the destination keeps its boot-time config forever.
357
425
  */
358
426
  async onConfigChanged() {
359
427
  await this.destination?.reconfigure(this.destinationConfig());
@@ -1,15 +1,6 @@
1
1
  import { LogLevel, ProviderRegistration, BaseAddon } from '@camstack/types';
2
2
  import { LokiDestination } from './loki-destination.js';
3
3
  interface LokiAddonConfig {
4
- /**
5
- * RETIRED (2026-07-30). Enablement is the orchestrator's per-destination
6
- * toggle now — the ONE truth for every log destination. Kept only so a
7
- * stored `false` can be migrated ONCE into a registry disable: without that,
8
- * an operator who had Loki off would silently start shipping logs after this
9
- * update. `migratedEnabled` marks the migration done; the field is then dead.
10
- */
11
- readonly enabled: boolean;
12
- readonly migratedEnabled: boolean;
13
4
  readonly url: string;
14
5
  readonly job: string;
15
6
  readonly level: LogLevel;
@@ -23,26 +14,19 @@ interface LokiAddonConfig {
23
14
  }
24
15
  export declare class LokiLoggingAddon extends BaseAddon<LokiAddonConfig> {
25
16
  private destination;
17
+ private retirementTimer;
18
+ private shuttingDown;
26
19
  constructor();
27
20
  protected onInitialize(): Promise<ProviderRegistration[]>;
28
- /**
29
- * ONE-SHOT: carry a stored `enabled: false` over to the orchestrator's
30
- * per-destination toggle, then never look at the flag again.
31
- *
32
- * Without this, retiring the private setting would silently RE-ENABLE Loki
33
- * for every operator who had turned it off — shipping their logs to a host
34
- * they may no longer control. Best-effort: if the toggle call fails the
35
- * migration is simply retried next boot (the flag is only cleared on
36
- * success).
37
- */
38
- private migrateRetiredEnabledFlag;
21
+ /** One attempt of the D777 carry-over; reschedules itself while deferred. */
22
+ private attemptEnabledFlagRetirement;
39
23
  protected onShutdown(): Promise<void>;
40
24
  /**
41
- * React to a settings change (enable/disable, URL, level, batching) without a
25
+ * React to a settings change (URL, level, batching) without a
42
26
  * restart — the contract this addon advertises. `BaseAddon` has already
43
27
  * reloaded `this.config` by the time this runs; re-apply it to the live
44
- * destination so toggling `enabled` on actually starts pushing (and off stops
45
- * it). Without this the destination keeps its boot-time config forever.
28
+ * destination so setting or clearing the URL actually starts (or stops)
29
+ * pushing. Without this the destination keeps its boot-time config forever.
46
30
  */
47
31
  protected onConfigChanged(): Promise<void>;
48
32
  /**
@@ -0,0 +1,23 @@
1
+ import { StoredKeyRead } from '@camstack/types';
2
+ /** The retired settings key. */
3
+ export declare const RETIRED_ENABLED_KEY = "enabled";
4
+ /** Store key marking the retirement as done. Not a schema field. Pre-D777 name kept. */
5
+ export declare const RETIREMENT_MARKER_KEY = "migratedEnabled";
6
+ export type RetirementOutcome = {
7
+ readonly kind: 'already-retired';
8
+ } | {
9
+ readonly kind: 'retired';
10
+ readonly capDisabled: boolean;
11
+ } | {
12
+ readonly kind: 'deferred';
13
+ readonly reason: string;
14
+ };
15
+ export interface RetireEnabledFlagDeps {
16
+ /** Three-way read of one key of the addon's own store. */
17
+ readStoredKey(key: string): Promise<StoredKeyRead>;
18
+ /** Disable this addon at the `log-destination` toggle. Throws when refused. */
19
+ disableAtCapability(): Promise<void>;
20
+ /** ONE store write: strip {@link RETIRED_ENABLED_KEY} and set {@link RETIREMENT_MARKER_KEY}. */
21
+ commit(): Promise<void>;
22
+ }
23
+ export declare function retireEnabledFlag(deps: RetireEnabledFlagDeps): Promise<RetirementOutcome>;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Cc6KOdY-.js");
6
+ const require_dist = require("../../dist-tnAO12ak.js");
7
7
  const require_proc_stat = require("../../proc-stat-DL-WUOhl.js");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_os = require("node:os");
@@ -1,4 +1,4 @@
1
- import { Cr as createEvent, Mt as foldSnapshotByFunction, _n as reducePoints, bn as resolveBucketMs, cr as errMsg, in as metricsProviderCapability, lr as BaseAddon, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
1
+ import { Nt as foldSnapshotByFunction, Tr as createEvent, an as metricsProviderCapability, ei as EventCategory, lr as errMsg, ur as BaseAddon, vn as reducePoints, xn as resolveBucketMs } from "../../dist-CP5TTjwd.mjs";
2
2
  import { a as parseProcStat, i as nodeProcReader, n as elapsedSecSince, o as parseProcStatusRss, r as lifetimeCpuPercent, t as commandFromCmdline } from "../../proc-stat-CbC55Kqi.mjs";
3
3
  import { access, readFile, readdir } from "node:fs/promises";
4
4
  import * as os from "node:os";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Cc6KOdY-.js");
6
+ const require_dist = require("../../dist-tnAO12ak.js");
7
7
  let node_os = require("node:os");
8
8
  node_os = require_chunk.__toESM(node_os);
9
9
  let node_fs = require("node:fs");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { An as scoreRuntimes, Ct as enumerateInferenceDevices, Tr as emitReadiness, cr as errMsg, lr as BaseAddon, mn as platformProbeCapability, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
2
+ import { Dr as emitReadiness, ei as EventCategory, hn as platformProbeCapability, jn as scoreRuntimes, lr as errMsg, ur as BaseAddon, wt as enumerateInferenceDevices } from "../../dist-CP5TTjwd.mjs";
3
3
  import * as os from "node:os";
4
4
  import * as fs from "node:fs";
5
5
  import { execFile } from "node:child_process";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Cc6KOdY-.js");
6
+ const require_dist = require("../../dist-tnAO12ak.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { lr as BaseAddon, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
1
+ import { ei as EventCategory, ur as BaseAddon } from "../../dist-CP5TTjwd.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Cc6KOdY-.js");
6
+ const require_dist = require("../../dist-tnAO12ak.js");
7
7
  const require_snapshot_courtesy = require("../../snapshot-courtesy-D_C5-nC0.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { Mr as nodePin, Nn as snapshotCapability, X as bareAddonId, Z as batteryCapability, _t as deriveBatteryPresence, a as BatteryStatusSchema, ar as DeviceFeature, cr as errMsg, lr as BaseAddon, qr as EventCategory, rn as memoizePinnedFfmpeg, sr as DeviceType, zn as streamQualityLabel } from "../../dist-B9oFw2jh.mjs";
1
+ import { Bn as streamQualityLabel, Pn as snapshotCapability, Q as batteryCapability, Rr as nodePin, Z as bareAddonId, a as BatteryStatusSchema, cr as DeviceType, ei as EventCategory, in as memoizePinnedFfmpeg, lr as errMsg, or as DeviceFeature, ur as BaseAddon, vt as deriveBatteryPresence } from "../../dist-CP5TTjwd.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CyFs1M3I.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Cc6KOdY-.js");
6
+ const require_dist = require("../../dist-tnAO12ak.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Ln as storageProviderCapability, jt as filesystemBrowseCapability, lr as BaseAddon } from "../../dist-B9oFw2jh.mjs";
1
+ import { Mt as filesystemBrowseCapability, Rn as storageProviderCapability, ur as BaseAddon } from "../../dist-CP5TTjwd.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Cc6KOdY-.js");
7
- const require_hub_cap_forward = require("../../hub-cap-forward-COCmYaup.js");
6
+ const require_dist = require("../../dist-tnAO12ak.js");
7
+ const require_hub_cap_forward = require("../../hub-cap-forward-W8oEm4ME.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
10
10
  let _camstack_types_node = require("@camstack/types/node");
@@ -1,5 +1,5 @@
1
- import { B as StorageLocationModeSchema, Bt as isLocationEnabled, D as LegacyStorageLocationDefaultSchema, Dt as evictionPolicyOfLocation, Fn as storageMigrationCapability, H as StorageMigrationJobSchema, Hr as secretTreeFromPaths, Kn as withLocationMode, Kr as transformSecretWrite, Pn as storageCapability, Pr as parseJsonObject, Pt as gbToBytes, V as StorageLocationTypeSchema, an as modeMayWrite, jn as settingsStoreCapability, jr as isSecretMarker, lr as BaseAddon, nn as mayWriteToLocation, pr as REDACTED_SECRET, qr as EventCategory, qt as legacyModeOf, tn as mayReadLocation, wn as resolveLocationMode, xr as collectSecretConfigKeys, z as StorageCleanupJobSchema, zr as resolveSecretMarkers } from "../../dist-B9oFw2jh.mjs";
2
- import { t as HUB_CAP_FWD_ACTION } from "../../hub-cap-forward-CH6_VWVO.mjs";
1
+ import { $r as transformSecretWrite, B as StorageCleanupJobSchema, Br as parseJsonObject, Cr as collectSecretConfigKeys, Fn as storageCapability, Fr as isSecretMarker, Ft as gbToBytes, Gr as resolveSecretMarkers, H as StorageLocationTypeSchema, In as storageMigrationCapability, Jr as secretTreeFromPaths, Jt as legacyModeOf, Mn as settingsStoreCapability, O as LegacyStorageLocationDefaultSchema, Ot as evictionPolicyOfLocation, Tn as resolveLocationMode, U as StorageMigrationJobSchema, V as StorageLocationModeSchema, Vt as isLocationEnabled, ei as EventCategory, mr as REDACTED_SECRET, nn as mayReadLocation, on as modeMayWrite, qn as withLocationMode, rn as mayWriteToLocation, ur as BaseAddon } from "../../dist-CP5TTjwd.mjs";
2
+ import { t as HUB_CAP_FWD_ACTION } from "../../hub-cap-forward-CDiN1p98.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { containsOrEquals, physicalRootOf } from "@camstack/types/node";
@@ -0,0 +1,57 @@
1
+ import { KeyObject } from 'node:crypto';
2
+ import { Transform } from 'node:stream';
3
+ export declare const BACKUP_ENCRYPTION_SCHEME = "x25519-hkdf-aes256gcm-stream-v1";
4
+ export declare const ENCRYPTED_BACKUP_SUFFIX = ".csbk";
5
+ export declare const BACKUP_ENCRYPTION_HEADER_BYTES: number;
6
+ export declare const DEFAULT_BACKUP_CHUNK_BYTES: number;
7
+ /** A key string that is not a backup key, or was mistyped. */
8
+ export declare class BackupKeyFormatError extends Error {
9
+ constructor(message: string);
10
+ }
11
+ /** The archive was encrypted to another backup key. */
12
+ export declare class BackupKeyMismatchError extends Error {
13
+ readonly archiveKeyId: string;
14
+ readonly givenKeyId: string;
15
+ constructor(archiveKeyId: string, givenKeyId: string);
16
+ }
17
+ /** The archive is damaged, truncated or tampered with. */
18
+ export declare class BackupDecryptError extends Error {
19
+ constructor(message: string);
20
+ }
21
+ export interface BackupKeyPair {
22
+ /** Shown ONCE to the operator; never stored on the hub. */
23
+ readonly secretKey: string;
24
+ /** What the hub keeps to encrypt. */
25
+ readonly publicKey: string;
26
+ /** 16 hex chars — names the key in the UI and in every archive header. */
27
+ readonly keyId: string;
28
+ }
29
+ export declare function generateBackupKeyPair(): BackupKeyPair;
30
+ export interface ParsedPublicKey {
31
+ readonly raw: Buffer;
32
+ readonly keyId: string;
33
+ }
34
+ export interface ParsedSecretKey {
35
+ readonly key: KeyObject;
36
+ readonly publicRaw: Buffer;
37
+ readonly keyId: string;
38
+ }
39
+ export declare function parseBackupPublicKey(text: string): ParsedPublicKey;
40
+ export declare function parseBackupSecretKey(text: string): ParsedSecretKey;
41
+ /** Encrypt a byte stream to the holder of `publicKey`'s secret key. */
42
+ export declare function createBackupEncryptStream(publicKey: string, opts?: {
43
+ readonly chunkBytes?: number;
44
+ }): Transform;
45
+ /** Decrypt a stream produced by {@link createBackupEncryptStream}. */
46
+ export declare function createBackupDecryptStream(secretKey: string): Transform;
47
+ /** The key id an encrypted archive names, or `null` for a plain (pre-encryption) archive. */
48
+ export declare function readBackupEncryptionHeader(file: string): Promise<{
49
+ readonly keyId: string;
50
+ } | null>;
51
+ /** File → file. Used by tests and the offline path; the hub streams in its pipeline. */
52
+ export declare function encryptBackupFile(input: string, output: string, publicKey: string): Promise<void>;
53
+ /**
54
+ * File → file. A failure of any kind removes the partial output: a restore
55
+ * must never find half a tar where the archive should be.
56
+ */
57
+ export declare function decryptBackupFile(input: string, output: string, secretKey: string): Promise<void>;