@camstack/system 1.2.378 → 1.2.379

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 (93) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/auth/auth-manager.d.ts +10 -2
  6. package/dist/auth/jwt-signing-key.d.ts +42 -0
  7. package/dist/auth/user-manager.d.ts +19 -19
  8. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  9. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  10. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  11. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  12. package/dist/builtins/alerts/alerts.addon.js +1 -1
  13. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  14. package/dist/builtins/autotrack/index.js +1 -1
  15. package/dist/builtins/autotrack/index.mjs +1 -1
  16. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  17. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  18. package/dist/builtins/calendars/calendars.addon.js +1 -1
  19. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  20. package/dist/builtins/camera-grid/index.js +1 -1
  21. package/dist/builtins/camera-grid/index.mjs +1 -1
  22. package/dist/builtins/composer/composer.addon.js +2 -2
  23. package/dist/builtins/composer/composer.addon.mjs +2 -2
  24. package/dist/builtins/console-logging/index.js +1 -1
  25. package/dist/builtins/console-logging/index.mjs +1 -1
  26. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  27. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  28. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  29. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  30. package/dist/builtins/hub-forwarder/index.js +1 -1
  31. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  32. package/dist/builtins/keychain/index.js +34 -4
  33. package/dist/builtins/keychain/index.mjs +34 -4
  34. package/dist/builtins/keychain/keychain-service.d.ts +14 -2
  35. package/dist/builtins/keychain/system-held-secrets.d.ts +11 -0
  36. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  37. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  38. package/dist/builtins/local-auth/admin-account-boot.d.ts +1 -1
  39. package/dist/builtins/local-auth/auth-config-reader.d.ts +9 -7
  40. package/dist/builtins/local-auth/jwt-signing-key-keeper.d.ts +30 -0
  41. package/dist/builtins/local-auth/local-auth.addon.d.ts +2 -6
  42. package/dist/builtins/local-auth/local-auth.addon.js +127 -221
  43. package/dist/builtins/local-auth/local-auth.addon.mjs +126 -220
  44. package/dist/builtins/local-network/local-network.addon.js +1 -1
  45. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  46. package/dist/builtins/loki-logging/index.js +1 -1
  47. package/dist/builtins/loki-logging/index.mjs +1 -1
  48. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  49. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  50. package/dist/builtins/platform-probe/index.js +1 -1
  51. package/dist/builtins/platform-probe/index.mjs +1 -1
  52. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  53. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  54. package/dist/builtins/snapshot/index.js +1 -1
  55. package/dist/builtins/snapshot/index.mjs +1 -1
  56. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  57. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  58. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  59. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  60. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  61. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  62. package/dist/builtins/system-config/cluster-secret-keeper.d.ts +36 -0
  63. package/dist/builtins/system-config/system-config.addon.d.ts +13 -0
  64. package/dist/builtins/system-config/system-config.addon.js +244 -53
  65. package/dist/builtins/system-config/system-config.addon.mjs +244 -54
  66. package/dist/builtins/winston-logging/index.js +1 -1
  67. package/dist/builtins/winston-logging/index.mjs +1 -1
  68. package/dist/{child-cap-dispatch-C9lmrWFT.js → child-cap-dispatch-CZeRUgYF.js} +1 -1
  69. package/dist/{child-cap-dispatch-DpHovtQd.mjs → child-cap-dispatch-CpjohToR.mjs} +1 -1
  70. package/dist/cluster-secret-BDue-uJj.mjs +83 -0
  71. package/dist/cluster-secret-Dg0OOOSr.js +148 -0
  72. package/dist/{composition-sources-CxhGdtdh.js → composition-sources-CHvLMrFI.js} +1 -1
  73. package/dist/{composition-sources-BLBd338N.mjs → composition-sources-DT_uJwI3.mjs} +1 -1
  74. package/dist/{dist-Bvl5JgNt.mjs → dist-B9oFw2jh.mjs} +58 -2
  75. package/dist/{dist-KxsOMQik.js → dist-Cc6KOdY-.js} +69 -1
  76. package/dist/{event-loop-stall-monitor-D8rqSWfW.mjs → event-loop-stall-monitor-anUH2IE1.mjs} +14 -14
  77. package/dist/{event-loop-stall-monitor-CbSmCWkp.js → event-loop-stall-monitor-mk6WGxir.js} +14 -14
  78. package/dist/index.d.ts +4 -2
  79. package/dist/index.js +192 -62
  80. package/dist/index.mjs +171 -55
  81. package/dist/jwt-signing-key-C2rE3wH6.js +174 -0
  82. package/dist/jwt-signing-key-Qhde8r82.mjs +127 -0
  83. package/dist/kernel/config-manager.d.ts +1 -1
  84. package/dist/kernel/config-schema.d.ts +4 -11
  85. package/dist/kernel/index.d.ts +2 -1
  86. package/dist/kernel/moleculer/broker-factory.d.ts +0 -2
  87. package/dist/kernel/moleculer/cluster-secret.d.ts +31 -0
  88. package/dist/kernel/moleculer/hub-service.d.ts +7 -4
  89. package/dist/kernel/moleculer/node-registry.d.ts +1 -1
  90. package/dist/kernel/system-settings-policy.d.ts +7 -9
  91. package/dist/{retired-settings-keys-BAcfvFtm.mjs → retired-settings-keys-DlZ3VV6x.mjs} +1 -1
  92. package/dist/{retired-settings-keys-gTsZfpmP.js → retired-settings-keys-YOOKLA3v.js} +1 -1
  93. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_child_cap_dispatch = require("./child-cap-dispatch-C9lmrWFT.js");
2
+ const require_child_cap_dispatch = require("./child-cap-dispatch-CZeRUgYF.js");
3
3
  const require_custom_action_registry = require("./custom-action-registry-acBY3mHA.js");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { Ft as installManifestNativeDeps, H as createUdsLoggerWithControl, It as resolveAddonClass, St as validateProviderRegistrations, U as LocalChildClient, _n as startRunnerHeapWatch, _t as getWorkerNativeCapProvider, bt as setWorkerNativeCapsChangeListener, f as installManifestSystemDeps, g as createUdsAddonContext, i as killOwnRunnerGroupAfterParentLoss, in as RUNNER_RSS_BUDGET_ENV, kn as emitHeapDiagnosticReport, n as createChildCapDispatch, p as installManifestPythonDeps, u as startRunnerParentDeathWatchdog, vt as getWorkerNativeCapSnapshot } from "./child-cap-dispatch-DpHovtQd.mjs";
1
+ import { Ft as installManifestNativeDeps, H as createUdsLoggerWithControl, It as resolveAddonClass, St as validateProviderRegistrations, U as LocalChildClient, _n as startRunnerHeapWatch, _t as getWorkerNativeCapProvider, bt as setWorkerNativeCapsChangeListener, f as installManifestSystemDeps, g as createUdsAddonContext, i as killOwnRunnerGroupAfterParentLoss, in as RUNNER_RSS_BUDGET_ENV, kn as emitHeapDiagnosticReport, n as createChildCapDispatch, p as installManifestPythonDeps, u as startRunnerParentDeathWatchdog, vt as getWorkerNativeCapSnapshot } from "./child-cap-dispatch-CpjohToR.mjs";
2
2
  import { t as CustomActionRegistry } from "./custom-action-registry-B4Cf4O_X.mjs";
3
3
  import { register } from "node:module";
4
4
  import * as path$1 from "node:path";
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("./chunk-Cek0wNdY.js");
3
- const require_event_loop_stall_monitor = require("./event-loop-stall-monitor-CbSmCWkp.js");
3
+ const require_event_loop_stall_monitor = require("./event-loop-stall-monitor-mk6WGxir.js");
4
4
  const require_custom_action_registry = require("./custom-action-registry-acBY3mHA.js");
5
5
  const require_snapshot_courtesy = require("./snapshot-courtesy-D_C5-nC0.js");
6
6
  exports.COURTESY_DEFAULT_WIDTH = require_snapshot_courtesy.COURTESY_DEFAULT_WIDTH;
@@ -1,4 +1,4 @@
1
- import { C as resolveFormatRevision, S as isModelDownloaded, a as createFileDataPlaneHandler, b as getModelFilePath, c as parseRangeHeader, d as ModelDownloadService, f as ModelDownloadHttpError, g as downloadFile, h as deleteModelFromDisk, i as startEventLoopStallMonitor, m as collectModelFiles, n as STALL_TICK_MS, o as contentTypeFor, p as adoptModelRevisions, r as attributeStall, t as STALL_REPORT_MS, v as ensureModel, x as isModelDownloadHttpError } from "./event-loop-stall-monitor-D8rqSWfW.mjs";
1
+ import { C as resolveFormatRevision, S as isModelDownloaded, a as createFileDataPlaneHandler, b as getModelFilePath, c as parseRangeHeader, d as ModelDownloadService, f as ModelDownloadHttpError, g as downloadFile, h as deleteModelFromDisk, i as startEventLoopStallMonitor, m as collectModelFiles, n as STALL_TICK_MS, o as contentTypeFor, p as adoptModelRevisions, r as attributeStall, t as STALL_REPORT_MS, v as ensureModel, x as isModelDownloadHttpError } from "./event-loop-stall-monitor-anUH2IE1.mjs";
2
2
  import { t as CustomActionRegistry } from "./custom-action-registry-B4Cf4O_X.mjs";
3
3
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg, t as COURTESY_DEFAULT_WIDTH } from "./snapshot-courtesy-CyFs1M3I.mjs";
4
4
  export { COURTESY_DEFAULT_WIDTH, CustomActionRegistry, ModelDownloadHttpError, ModelDownloadService, STALL_REPORT_MS, STALL_TICK_MS, adoptModelRevisions, attributeStall, collectModelFiles, contentTypeFor, courtesyCacheKey, createFileDataPlaneHandler, deleteModelFromDisk, downloadFile, ensureModel, getModelFilePath, isModelDownloadHttpError, isModelDownloaded, parseRangeHeader, renderCourtesyJpeg, renderCourtesyOverlayJpeg, resolveFormatRevision, startEventLoopStallMonitor };
@@ -1,5 +1,6 @@
1
1
  import { IScopedLogger, TokenPayload, SsoBridgeClaimsSchema } from '@camstack/types';
2
2
  import { z } from 'zod';
3
+ import { JwtSigningKeySource } from './jwt-signing-key.js';
3
4
  export type { TokenPayload };
4
5
  export type AuthConfigReader = {
5
6
  /**
@@ -13,7 +14,7 @@ export type AuthConfigReader = {
13
14
  };
14
15
  export declare class AuthManager {
15
16
  private readonly config;
16
- private readonly jwtSecret;
17
+ private readonly signingKey;
17
18
  private readonly logger;
18
19
  /**
19
20
  * Offending `auth.tokenExpiry` values already reported by this instance.
@@ -25,7 +26,14 @@ export declare class AuthManager {
25
26
  * rest resolve silently to the same default until the value changes.
26
27
  */
27
28
  private readonly reportedInvalidExpiries;
28
- constructor(config: AuthConfigReader, logger?: IScopedLogger);
29
+ /**
30
+ * @param signingKey where the session signing key comes from — the process
31
+ * slot `local-auth` fills from the keychain (D797). Never config, never
32
+ * env; read on every mint and verify, and it THROWS while unavailable.
33
+ */
34
+ constructor(config: AuthConfigReader, logger?: IScopedLogger, signingKey?: JwtSigningKeySource);
35
+ /** The current signing key; throws `JwtSigningKeyUnavailableError` while there is none. */
36
+ private get jwtSecret();
29
37
  signToken(payload: Omit<TokenPayload, 'iat' | 'exp'>): string;
30
38
  /**
31
39
  * The operator's `auth.tokenExpiry`, or `undefined` → DEFAULT_TOKEN_EXPIRY.
@@ -0,0 +1,42 @@
1
+ import { SecretsAccess } from '@camstack/types';
2
+ /** The keychain owner of every system-held secret. */
3
+ export declare const SYSTEM_SECRET_OWNER = "system";
4
+ /** `secret://system/global/jwtSigningKey`. */
5
+ export declare const JWT_SIGNING_KEY_REF: string;
6
+ export type JwtSigningKeyState = {
7
+ readonly state: 'ready';
8
+ readonly key: string;
9
+ } | {
10
+ readonly state: 'unavailable';
11
+ readonly reason: string;
12
+ };
13
+ /** Sessions cannot be minted or verified: the signing key is not loaded. */
14
+ export declare class JwtSigningKeyUnavailableError extends Error {
15
+ readonly reason: string;
16
+ constructor(reason: string);
17
+ }
18
+ /** Where an `AuthManager` gets its key. `current()` throws while there is none. */
19
+ export interface JwtSigningKeySource {
20
+ current(): string;
21
+ }
22
+ /** Register the loaded (or unloadable) key. Called by the hub's `local-auth`. */
23
+ export declare function setProcessJwtSigningKey(state: JwtSigningKeyState): void;
24
+ /** Forget the key (tests, shutdown). */
25
+ export declare function clearProcessJwtSigningKey(): void;
26
+ export declare function getProcessJwtSigningKeyState(): JwtSigningKeyState;
27
+ /** The process slot — the default source of every `AuthManager`. */
28
+ export declare const processJwtSigningKey: JwtSigningKeySource;
29
+ /** A fixed key — tests, and nothing else. */
30
+ export declare function staticJwtSigningKey(key: string): JwtSigningKeySource;
31
+ /** 32 random bytes, hex. */
32
+ export declare function generateJwtSigningKey(): string;
33
+ export interface LoadJwtSigningKeyDeps {
34
+ /** A keychain handle acting for owner `system`. */
35
+ readonly secrets: SecretsAccess;
36
+ readonly generate?: () => string;
37
+ }
38
+ /**
39
+ * Read the key from the keychain; generate and store one ONLY when the
40
+ * keychain answers that none exists. Never throws.
41
+ */
42
+ export declare function loadJwtSigningKey(deps: LoadJwtSigningKeyDeps): Promise<JwtSigningKeyState>;
@@ -10,14 +10,20 @@ interface CreateUserInput {
10
10
  scopes?: TokenScope[];
11
11
  }
12
12
  type UpdatableUserFields = Partial<Pick<UserRecord, 'isAdmin' | 'allowedProviders' | 'allowedDevices' | 'scopes'>>;
13
+ /**
14
+ * The account a fresh hub seeds on its first boot (D797). Not configurable —
15
+ * no env variable, no config.yaml key: the first sign-in with this password
16
+ * forces a change (admin-ui `mustChangePassword`), and an existing user is
17
+ * never overwritten (D728).
18
+ */
19
+ export declare const FIRST_BOOT_ADMIN_USERNAME = "admin";
20
+ export declare const FIRST_BOOT_ADMIN_PASSWORD = "changeme";
13
21
  export interface EnsureAdminOptions {
14
22
  /** The operator's explicit, one-shot recovery request. Never a default. */
15
23
  readonly resetPassword?: boolean;
16
24
  }
17
25
  /** What `ensureAdminExists` did — every branch is named so the caller can log it. */
18
26
  export type AdminSeedOutcome = {
19
- readonly kind: 'not-configured';
20
- } | {
21
27
  readonly kind: 'seeded';
22
28
  readonly username: string;
23
29
  } | {
@@ -33,15 +39,10 @@ export type AdminSeedOutcome = {
33
39
  export interface UserStorageAccess {
34
40
  getStore(): SettingsStoreClient;
35
41
  }
36
- export interface UserConfigReader {
37
- /** See `AuthConfigReader.get` — a reader answers only the paths it resolved. */
38
- get<T>(path: string): T | undefined;
39
- }
40
42
  export declare class UserManager {
41
43
  private readonly storageAccess;
42
44
  private readonly auth;
43
- private readonly config;
44
- constructor(storageAccess: UserStorageAccess, auth: AuthManager, config: UserConfigReader);
45
+ constructor(storageAccess: UserStorageAccess, auth: AuthManager);
45
46
  private get store();
46
47
  create(input: CreateUserInput): Promise<UserRecord>;
47
48
  findByUsername(username: string): Promise<UserRecord | null>;
@@ -52,20 +53,19 @@ export declare class UserManager {
52
53
  delete(id: string): Promise<void>;
53
54
  resetPassword(id: string, newPassword: string): Promise<void>;
54
55
  /**
55
- * Seed the configured admin (`auth.adminUsername` / `auth.adminPassword`,
56
- * i.e. `CAMSTACK_ADMIN_USER` / `CAMSTACK_ADMIN_PASS`) — on FIRST boot only.
56
+ * Seed {@link FIRST_BOOT_ADMIN_USERNAME} / {@link FIRST_BOOT_ADMIN_PASSWORD}
57
+ * — on FIRST boot only, i.e. when no admin account exists.
57
58
  *
58
- * The configured password is a seed, never an authority. Once any admin
59
- * exists, this never writes a password: the operator's own change in the
60
- * admin UI is the truth from then on. This method used to compare the stored
61
- * hash with the configured password on every boot and rewrite it when they
62
- * differed, so a password changed in the UI went back to the configured one
63
- * (`changeme` on a Mac hub and on any container without `CAMSTACK_ADMIN_PASS`)
64
- * at the next restart.
59
+ * The seed is never an authority. Once any admin exists, this never writes a
60
+ * password: the operator's own change in the admin UI is the truth from then
61
+ * on (D728). This method once compared the stored hash with a configured
62
+ * password on every boot and rewrote it when they differed, so a password
63
+ * changed in the UI went back at the next restart.
65
64
  *
66
65
  * `resetPassword: true` is the one explicit exception — the operator's
67
- * one-shot recovery request (see `admin-password-reset.ts`): the configured
68
- * admin's password is set back to the configured one.
66
+ * one-shot recovery request (`RESET_ADMIN_PASSWORD`, see
67
+ * `admin-account-boot.ts`): the `admin` user's password goes back to
68
+ * `changeme`, and the next sign-in forces a change again.
69
69
  */
70
70
  ensureAdminExists(options?: EnsureAdminOptions): Promise<AdminSeedOutcome>;
71
71
  /**
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Gr as EventCategory, W as addonPagesCapability, cr as errMsg, lr as BaseAddon } from "../../dist-Bvl5JgNt.mjs";
1
+ import { W as addonPagesCapability, cr as errMsg, lr as BaseAddon, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Gr as EventCategory, K as addonWidgetsCapability, cr as errMsg, lr as BaseAddon } from "../../dist-Bvl5JgNt.mjs";
1
+ import { K as addonWidgetsCapability, cr as errMsg, lr as BaseAddon, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  //#region src/builtins/alerts/device-label.ts
8
8
  /**
9
9
  * Operator-facing device label for alert messages.
@@ -1,4 +1,4 @@
1
- import { Gr as EventCategory, Sr as createEvent, cr as errMsg, lr as BaseAddon, n as AlertSchema, q as alertsCapability } from "../../dist-Bvl5JgNt.mjs";
1
+ import { Cr as createEvent, cr as errMsg, lr as BaseAddon, n as AlertSchema, q as alertsCapability, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
2
2
  //#region src/builtins/alerts/device-label.ts
3
3
  /**
4
4
  * Operator-facing device label for alert messages.
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  //#region src/builtins/autotrack/autotrack-config.ts
8
8
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
9
9
  deadZoneFraction: .5,
@@ -1,4 +1,4 @@
1
- import { Gr as EventCategory, S as FIRST_LEVEL_MACRO_CLASSES, X as bareAddonId, cr as errMsg, gn as ptzAutotrackCapability, kr as isEvent, lr as BaseAddon } from "../../dist-Bvl5JgNt.mjs";
1
+ import { Ar as isEvent, S as FIRST_LEVEL_MACRO_CLASSES, X as bareAddonId, cr as errMsg, gn as ptzAutotrackCapability, lr as BaseAddon, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
2
2
  //#region src/builtins/autotrack/autotrack-config.ts
3
3
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
4
4
  deadZoneFraction: .5,
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let buffer = require("buffer");
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { Gr as EventCategory, In as storageOccupancyCapability, Ur as toNodeId, Y as backupCapability, g as DATABASE_SNAPSHOT_SUFFIX, jr as nodePin, lr as BaseAddon, nn as mayWriteToLocation } from "../../dist-Bvl5JgNt.mjs";
1
+ import { Gr as toNodeId, In as storageOccupancyCapability, Mr as nodePin, Y as backupCapability, g as DATABASE_SNAPSHOT_SUFFIX, lr as BaseAddon, nn as mayWriteToLocation, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { Buffer as Buffer$1 } from "buffer";
4
4
  import * as fsp from "node:fs/promises";
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let zod = require("zod");
9
9
  //#region src/builtins/calendars/calendar-color.ts
@@ -1,4 +1,4 @@
1
- import { et as calendarsCapability, lr as BaseAddon, p as CalendarPreferencesSchema } from "../../dist-Bvl5JgNt.mjs";
1
+ import { et as calendarsCapability, lr as BaseAddon, p as CalendarPreferencesSchema } from "../../dist-B9oFw2jh.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/builtins/calendars/calendar-color.ts
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  let zod = require("zod");
8
8
  let _camstack_types_node = require("@camstack/types/node");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Dr as hydrateSchema, Gr as EventCategory, Jn as buildFfmpegArgs, M as MOTION_CLOSE_AFTER_MS, Nn as snapshotCapability, Rn as streamCatalogCapability, Sr as createEvent, ar as DeviceFeature, b as DeclaredDevices, cr as errMsg, i as BaseDevice, lr as BaseAddon, mt as declarationOwnerNodeId, on as motionCapability, rn as memoizePinnedFfmpeg, sr as DeviceType, tt as cameraGridLayoutCapability, y as DERIVED_CAMERA_SILENCED_CAP_NAMES } from "../../dist-Bvl5JgNt.mjs";
1
+ import { Cr as createEvent, Jn as buildFfmpegArgs, M as MOTION_CLOSE_AFTER_MS, Nn as snapshotCapability, Or as hydrateSchema, Rn as streamCatalogCapability, ar as DeviceFeature, b as DeclaredDevices, cr as errMsg, i as BaseDevice, lr as BaseAddon, mt as declarationOwnerNodeId, on as motionCapability, qr as EventCategory, rn as memoizePinnedFfmpeg, sr as DeviceType, tt as cameraGridLayoutCapability, y as DERIVED_CAMERA_SILENCED_CAP_NAMES } from "../../dist-B9oFw2jh.mjs";
2
2
  import { z } from "zod";
3
3
  import { FfmpegProcess } from "@camstack/types/node";
4
4
  import { join } from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-KxsOMQik.js");
7
- const require_composition_sources = require("../../composition-sources-CxhGdtdh.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
+ const require_composition_sources = require("../../composition-sources-CHvLMrFI.js");
8
8
  let zod = require("zod");
9
9
  //#region src/builtins/shared/device-state-changed.ts
10
10
  /** Structural narrowing helper for untrusted bus payloads. */
@@ -1,5 +1,5 @@
1
- import { $n as compositionItemFieldPath, Et as evaluateCompositionField, Gr as EventCategory, Q as buildCapabilityLookup, Qn as compositionFeatureEnabled, Sr as createEvent, Xn as composedDeviceRef, Yn as cloneStatefulExpressionMemory, Zn as composedDeviceStableId, b as DeclaredDevices, cr as errMsg, er as compositionSliceKey, i as BaseDevice, ir as toExpressionValue, it as compositionFieldKey, kt as expressionContainsBuiltinCall, l as COMPOSED_ITEM_FIXED_FIELDS, lr as BaseAddon, nr as createStatefulExpressionMemory, pn as planComposition, qn as COMPOSER_ADDON_ID, rr as disarmRoseMemory, rt as composerCapability, sr as DeviceType, t as ALL_CAPABILITY_DEFINITIONS, tr as compositionSourceKey } from "../../dist-Bvl5JgNt.mjs";
2
- import { n as normalizedNativeOwner } from "../../composition-sources-BLBd338N.mjs";
1
+ import { $n as compositionItemFieldPath, Cr as createEvent, Et as evaluateCompositionField, Q as buildCapabilityLookup, Qn as compositionFeatureEnabled, Xn as composedDeviceRef, Yn as cloneStatefulExpressionMemory, Zn as composedDeviceStableId, b as DeclaredDevices, cr as errMsg, er as compositionSliceKey, i as BaseDevice, ir as toExpressionValue, it as compositionFieldKey, kt as expressionContainsBuiltinCall, l as COMPOSED_ITEM_FIXED_FIELDS, lr as BaseAddon, nr as createStatefulExpressionMemory, pn as planComposition, qn as COMPOSER_ADDON_ID, qr as EventCategory, rr as disarmRoseMemory, rt as composerCapability, sr as DeviceType, t as ALL_CAPABILITY_DEFINITIONS, tr as compositionSourceKey } from "../../dist-B9oFw2jh.mjs";
2
+ import { n as normalizedNativeOwner } from "../../composition-sources-DT_uJwI3.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/builtins/shared/device-state-changed.ts
5
5
  /** Structural narrowing helper for untrusted bus payloads. */
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { Zt as logDestinationCapability, lr as BaseAddon } from "../../dist-Bvl5JgNt.mjs";
1
+ import { Zt as logDestinationCapability, lr as BaseAddon } from "../../dist-B9oFw2jh.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-KxsOMQik.js");
4
- const require_composition_sources = require("../../composition-sources-CxhGdtdh.js");
3
+ const require_dist = require("../../dist-Cc6KOdY-.js");
4
+ const require_composition_sources = require("../../composition-sources-CHvLMrFI.js");
5
5
  let node_crypto = require("node:crypto");
6
6
  let _camstack_types_node = require("@camstack/types/node");
7
7
  let node_fs_promises = require("node:fs/promises");
@@ -1,5 +1,5 @@
1
- import { Dr as hydrateSchema, Q as buildCapabilityLookup, Sn as resolveCoreBlockFiles, Vn as validateComposition, Xn as composedDeviceRef, at as compositionSourceRefs, b as DeclaredDevices, cr as errMsg, ct as coreBlockIdFromAddonId, d as CORE_BLOCKS_ADDON_ID, f as CORE_BLOCK_ADDON_PREFIX, lr as BaseAddon, lt as coreBlockSourceKind, m as CoreBlockSchema, ot as coreBlockAddonId, pn as planComposition, sn as normalizeCoreBlockWrite, st as coreBlockComposition, t as ALL_CAPABILITY_DEFINITIONS, tr as compositionSourceKey, u as COMPOSER_RUNNER_ID, ut as coreBlocksCapability, vn as replacedFieldKeys, vt as describeComposableCaps, xn as resolveCoreBlockEntry } from "../../dist-Bvl5JgNt.mjs";
2
- import { t as describeCompositionSources } from "../../composition-sources-BLBd338N.mjs";
1
+ import { Or as hydrateSchema, Q as buildCapabilityLookup, Sn as resolveCoreBlockFiles, Vn as validateComposition, Xn as composedDeviceRef, at as compositionSourceRefs, b as DeclaredDevices, cr as errMsg, ct as coreBlockIdFromAddonId, d as CORE_BLOCKS_ADDON_ID, f as CORE_BLOCK_ADDON_PREFIX, lr as BaseAddon, lt as coreBlockSourceKind, m as CoreBlockSchema, ot as coreBlockAddonId, pn as planComposition, sn as normalizeCoreBlockWrite, st as coreBlockComposition, t as ALL_CAPABILITY_DEFINITIONS, tr as compositionSourceKey, u as COMPOSER_RUNNER_ID, ut as coreBlocksCapability, vn as replacedFieldKeys, vt as describeComposableCaps, xn as resolveCoreBlockEntry } from "../../dist-B9oFw2jh.mjs";
2
+ import { t as describeCompositionSources } from "../../composition-sources-DT_uJwI3.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { customizationBlockName } from "@camstack/types/node";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-KxsOMQik.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-gTsZfpmP.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-YOOKLA3v.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 { $ as buildStreamParamsConfigSchema, A as MINUTES_PER_DAY, C as FieldClaimModeSchema, Dn as runtimeStatePolicyFor, Gr as EventCategory, Hr as sleep$1, Kt as legacyAutoLinkIds, L as STREAM_PROFILE_META, Mn as siteZoneKey, Nt as formatMinutes, O as LocationIconIdSchema, Or as isDeviceConfigCap, Ot as expandContainer, Rr as resolveSecretMarkers, St as deviceStatusCapability, Tt as enumerateSchemaFields, Vr as secretTreeFromPaths, Wr as transformSecretWrite, Xt as locationIconKey, ar as DeviceFeature, bt as deviceManagerCapability, c as CAP_NAMES_WITH_STATUS, cn as normalizeUnit, cr as errMsg, dn as parseStreamParamsFormPatch, dt as createDeviceExtensionProvider, gr as WELL_KNOWN_TAB_MAP, gt as defineExtensionSlotProvider, lr as BaseAddon, o as CAMERA_SWITCH_ORDER, or as DeviceRole, sr as DeviceType, t as ALL_CAPABILITY_DEFINITIONS, v as DAY_NAMES, w as FieldClaimSchema, wt as enumerateItemArrayFields, x as DeviceStatusSchema, xr as collectZodSecretPaths, xt as deviceStateCapability, yt as deviceExtensionCapability } from "../../dist-Bvl5JgNt.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-BAcfvFtm.mjs";
1
+ import { $ as buildStreamParamsConfigSchema, A as MINUTES_PER_DAY, C as FieldClaimModeSchema, Dn as runtimeStatePolicyFor, Hr as secretTreeFromPaths, Kr as transformSecretWrite, Kt as legacyAutoLinkIds, L as STREAM_PROFILE_META, Mn as siteZoneKey, Nt as formatMinutes, O as LocationIconIdSchema, Ot as expandContainer, Sr as collectZodSecretPaths, St as deviceStatusCapability, Tt as enumerateSchemaFields, Wr as sleep$1, Xt as locationIconKey, ar as DeviceFeature, bt as deviceManagerCapability, c as CAP_NAMES_WITH_STATUS, cn as normalizeUnit, cr as errMsg, dn as parseStreamParamsFormPatch, dt as createDeviceExtensionProvider, gr as WELL_KNOWN_TAB_MAP, gt as defineExtensionSlotProvider, kr as isDeviceConfigCap, lr as BaseAddon, o as CAMERA_SWITCH_ORDER, or as DeviceRole, qr as EventCategory, sr as DeviceType, t as ALL_CAPABILITY_DEFINITIONS, v as DAY_NAMES, w as FieldClaimSchema, wt as enumerateItemArrayFields, x as DeviceStatusSchema, xt as deviceStateCapability, yt as deviceExtensionCapability, zr as resolveSecretMarkers } from "../../dist-B9oFw2jh.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-DlZ3VV6x.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
@@ -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-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { Zt as logDestinationCapability, lr as BaseAddon } from "../../dist-Bvl5JgNt.mjs";
1
+ import { Zt as logDestinationCapability, lr as BaseAddon } from "../../dist-B9oFw2jh.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -3,7 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-KxsOMQik.js");
6
+ const require_dist = require("../../dist-Cc6KOdY-.js");
7
+ const require_jwt_signing_key = require("../../jwt-signing-key-C2rE3wH6.js");
8
+ const require_cluster_secret = require("../../cluster-secret-Dg0OOOSr.js");
7
9
  const require_platform_key_source = require("../../platform-key-source-Di7HrlGR.js");
8
10
  let node_crypto = require("node:crypto");
9
11
  let node_path = require("node:path");
@@ -635,6 +637,14 @@ var KeychainRepository = class {
635
637
  return rows.length;
636
638
  }
637
639
  };
640
+ //#endregion
641
+ //#region src/builtins/keychain/system-held-secrets.ts
642
+ /** The refs `keychain-admin.setSystemSecret` may write, by name. */
643
+ var SYSTEM_HELD_SECRET_REFS = { "jwt-signing-key": require_jwt_signing_key.JWT_SIGNING_KEY_REF };
644
+ /** System refs no blob points at — in use for as long as they exist. */
645
+ var PROCESS_HELD_SECRET_REFS = new Set([require_jwt_signing_key.JWT_SIGNING_KEY_REF]);
646
+ /** `system-settings` keys that may hold a system-owned secret's marker. */
647
+ var SYSTEM_SECRET_SETTINGS_KEYS = [require_cluster_secret.CLUSTER_SECRET_SETTINGS_KEY];
638
648
  var KeychainLockedError = class extends Error {
639
649
  reason;
640
650
  constructor(reason) {
@@ -845,7 +855,11 @@ var KeychainService = class {
845
855
  if (parts === null || !this.owns(caller, parts.owner)) throw new KeychainForbiddenError(ref, caller);
846
856
  return parts.owner;
847
857
  }
848
- async put(caller, ref, value) {
858
+ /**
859
+ * @param actor who the audit names — the owner addon by default; the
860
+ * operator (`user:<id>`) for {@link setSystemSecret}.
861
+ */
862
+ async put(caller, ref, value, actor = actorOfAddon(caller)) {
849
863
  const owner = this.ownerOf(ref, caller);
850
864
  const unlocked = this.requireReady();
851
865
  if (value.length === 0) throw new Error("keychain.put: an empty value is not stored — delete the ref");
@@ -871,10 +885,10 @@ var KeychainService = class {
871
885
  version,
872
886
  createdAt: existing?.createdAt ?? t,
873
887
  updatedAt: t,
874
- updatedBy: actorOfAddon(caller),
888
+ updatedBy: actor,
875
889
  lastReadAt: existing?.lastReadAt ?? null
876
890
  });
877
- await this.audit("put", actorOfAddon(caller), ref, `version=${version}`);
891
+ await this.audit("put", actor, ref, `version=${version}`);
878
892
  this.deps.events.emit({
879
893
  ref,
880
894
  owner,
@@ -1004,6 +1018,14 @@ var KeychainService = class {
1004
1018
  usage: usage.get(row.ref) ?? "unknown"
1005
1019
  }));
1006
1020
  }
1021
+ /**
1022
+ * The operator replaces a system-held secret no form edits (D797) — the
1023
+ * session signing key. Stored under owner `system`, audited as the user.
1024
+ */
1025
+ async setSystemSecret(userId, name, value) {
1026
+ const ref = SYSTEM_HELD_SECRET_REFS[name];
1027
+ return this.put(require_jwt_signing_key.SYSTEM_SECRET_OWNER, ref, value, `user:${userId}`);
1028
+ }
1007
1029
  async listAudit(limit) {
1008
1030
  return this.deps.repo.listAudit(limit);
1009
1031
  }
@@ -1243,6 +1265,13 @@ async function prefixRows(door, collection, prefix) {
1243
1265
  }
1244
1266
  /** The blobs that may hold the marker of a ref in `scope`; `null` = cannot tell. */
1245
1267
  async function blobsFor(door, owner, scope) {
1268
+ if (owner === "system" && scope.kind === "global") return [[...PROCESS_HELD_SECRET_REFS].map((ref) => ({
1269
+ $secret: ref,
1270
+ v: 1
1271
+ })), ...await Promise.all(SYSTEM_SECRET_SETTINGS_KEYS.map((key) => door.get({
1272
+ collection: "system-settings",
1273
+ key
1274
+ })))];
1246
1275
  switch (scope.kind) {
1247
1276
  case "global":
1248
1277
  case "node": {
@@ -1424,6 +1453,7 @@ var KeychainAddon = class extends require_dist.BaseAddon {
1424
1453
  listSecrets: () => service.listSecrets(),
1425
1454
  listAudit: (input) => service.listAudit(input.limit ?? 100),
1426
1455
  reveal: (input) => service.reveal(input.caller.userId, input.ref),
1456
+ setSystemSecret: (input) => service.setSystemSecret(input.caller.userId, input.name, input.value),
1427
1457
  deleteOrphans: (input) => service.deleteOrphans(input.caller.userId, input.refs),
1428
1458
  rotateMasterKey: (input) => service.rotateMasterKey(input.caller.userId),
1429
1459
  purgeRetiredKeys: async (input) => ({ purged: await service.purgeRetiredKeys(input.caller.userId) }),
@@ -1,4 +1,6 @@
1
- import { Er as formatSecretScope, Fr as parseSecretRef, Gr as EventCategory, Gt as keychainCapability, Mr as normalizeSecretOwner, Wt as keychainAdminCapability, lr as BaseAddon } from "../../dist-Bvl5JgNt.mjs";
1
+ import { Dr as formatSecretScope, Gt as keychainCapability, Ir as parseSecretRef, Nr as normalizeSecretOwner, Wt as keychainAdminCapability, lr as BaseAddon, qr as EventCategory } from "../../dist-B9oFw2jh.mjs";
2
+ import { r as SYSTEM_SECRET_OWNER, t as JWT_SIGNING_KEY_REF } from "../../jwt-signing-key-Qhde8r82.mjs";
3
+ import { i as CLUSTER_SECRET_SETTINGS_KEY } from "../../cluster-secret-BDue-uJj.mjs";
2
4
  import { n as getPlatformKeychainKey } from "../../platform-key-source-BWlq0jjn.mjs";
3
5
  import { createCipheriv, createDecipheriv, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
4
6
  import * as path$1 from "node:path";
@@ -629,6 +631,14 @@ var KeychainRepository = class {
629
631
  return rows.length;
630
632
  }
631
633
  };
634
+ //#endregion
635
+ //#region src/builtins/keychain/system-held-secrets.ts
636
+ /** The refs `keychain-admin.setSystemSecret` may write, by name. */
637
+ var SYSTEM_HELD_SECRET_REFS = { "jwt-signing-key": JWT_SIGNING_KEY_REF };
638
+ /** System refs no blob points at — in use for as long as they exist. */
639
+ var PROCESS_HELD_SECRET_REFS = new Set([JWT_SIGNING_KEY_REF]);
640
+ /** `system-settings` keys that may hold a system-owned secret's marker. */
641
+ var SYSTEM_SECRET_SETTINGS_KEYS = [CLUSTER_SECRET_SETTINGS_KEY];
632
642
  var KeychainLockedError = class extends Error {
633
643
  reason;
634
644
  constructor(reason) {
@@ -839,7 +849,11 @@ var KeychainService = class {
839
849
  if (parts === null || !this.owns(caller, parts.owner)) throw new KeychainForbiddenError(ref, caller);
840
850
  return parts.owner;
841
851
  }
842
- async put(caller, ref, value) {
852
+ /**
853
+ * @param actor who the audit names — the owner addon by default; the
854
+ * operator (`user:<id>`) for {@link setSystemSecret}.
855
+ */
856
+ async put(caller, ref, value, actor = actorOfAddon(caller)) {
843
857
  const owner = this.ownerOf(ref, caller);
844
858
  const unlocked = this.requireReady();
845
859
  if (value.length === 0) throw new Error("keychain.put: an empty value is not stored — delete the ref");
@@ -865,10 +879,10 @@ var KeychainService = class {
865
879
  version,
866
880
  createdAt: existing?.createdAt ?? t,
867
881
  updatedAt: t,
868
- updatedBy: actorOfAddon(caller),
882
+ updatedBy: actor,
869
883
  lastReadAt: existing?.lastReadAt ?? null
870
884
  });
871
- await this.audit("put", actorOfAddon(caller), ref, `version=${version}`);
885
+ await this.audit("put", actor, ref, `version=${version}`);
872
886
  this.deps.events.emit({
873
887
  ref,
874
888
  owner,
@@ -998,6 +1012,14 @@ var KeychainService = class {
998
1012
  usage: usage.get(row.ref) ?? "unknown"
999
1013
  }));
1000
1014
  }
1015
+ /**
1016
+ * The operator replaces a system-held secret no form edits (D797) — the
1017
+ * session signing key. Stored under owner `system`, audited as the user.
1018
+ */
1019
+ async setSystemSecret(userId, name, value) {
1020
+ const ref = SYSTEM_HELD_SECRET_REFS[name];
1021
+ return this.put(SYSTEM_SECRET_OWNER, ref, value, `user:${userId}`);
1022
+ }
1001
1023
  async listAudit(limit) {
1002
1024
  return this.deps.repo.listAudit(limit);
1003
1025
  }
@@ -1237,6 +1259,13 @@ async function prefixRows(door, collection, prefix) {
1237
1259
  }
1238
1260
  /** The blobs that may hold the marker of a ref in `scope`; `null` = cannot tell. */
1239
1261
  async function blobsFor(door, owner, scope) {
1262
+ if (owner === "system" && scope.kind === "global") return [[...PROCESS_HELD_SECRET_REFS].map((ref) => ({
1263
+ $secret: ref,
1264
+ v: 1
1265
+ })), ...await Promise.all(SYSTEM_SECRET_SETTINGS_KEYS.map((key) => door.get({
1266
+ collection: "system-settings",
1267
+ key
1268
+ })))];
1240
1269
  switch (scope.kind) {
1241
1270
  case "global":
1242
1271
  case "node": {
@@ -1418,6 +1447,7 @@ var KeychainAddon = class extends BaseAddon {
1418
1447
  listSecrets: () => service.listSecrets(),
1419
1448
  listAudit: (input) => service.listAudit(input.limit ?? 100),
1420
1449
  reveal: (input) => service.reveal(input.caller.userId, input.ref),
1450
+ setSystemSecret: (input) => service.setSystemSecret(input.caller.userId, input.name, input.value),
1421
1451
  deleteOrphans: (input) => service.deleteOrphans(input.caller.userId, input.refs),
1422
1452
  rotateMasterKey: (input) => service.rotateMasterKey(input.caller.userId),
1423
1453
  purgeRetiredKeys: async (input) => ({ purged: await service.purgeRetiredKeys(input.caller.userId) }),
@@ -1,4 +1,4 @@
1
- import { EventCategory, KeychainAuditEntry, KeychainKeySource, KeychainResolveResult, KeychainSecretMeta, KeychainSecretUsage, KeychainState, KeychainStatus, KeychainUnavailableReason } from '@camstack/types';
1
+ import { EventCategory, KeychainAuditEntry, KeychainKeySource, KeychainResolveResult, KeychainSecretMeta, KeychainSecretUsage, KeychainState, KeychainStatus, KeychainUnavailableReason, SystemHeldSecretName } from '@camstack/types';
2
2
  import { KeychainRepository, KeychainSecretRow } from './keychain-store.js';
3
3
  import { KeySourceCandidate } from './key-sources.js';
4
4
  /** `lastReadAt` is written at most this often per ref (hub I/O is the bottleneck). */
@@ -54,7 +54,11 @@ export declare class KeychainService {
54
54
  /** May `caller` touch values owned by `owner`? */
55
55
  owns(caller: string | null, owner: string): boolean;
56
56
  private ownerOf;
57
- put(caller: string | null, ref: string, value: string): Promise<{
57
+ /**
58
+ * @param actor who the audit names — the owner addon by default; the
59
+ * operator (`user:<id>`) for {@link setSystemSecret}.
60
+ */
61
+ put(caller: string | null, ref: string, value: string, actor?: string): Promise<{
58
62
  ref: string;
59
63
  version: number;
60
64
  }>;
@@ -66,6 +70,14 @@ export declare class KeychainService {
66
70
  listSecrets(): Promise<readonly (KeychainSecretMeta & {
67
71
  usage: KeychainSecretUsage;
68
72
  })[]>;
73
+ /**
74
+ * The operator replaces a system-held secret no form edits (D797) — the
75
+ * session signing key. Stored under owner `system`, audited as the user.
76
+ */
77
+ setSystemSecret(userId: string, name: SystemHeldSecretName, value: string): Promise<{
78
+ ref: string;
79
+ version: number;
80
+ }>;
69
81
  listAudit(limit: number): Promise<readonly KeychainAuditEntry[]>;
70
82
  reveal(userId: string, ref: string): Promise<{
71
83
  value: string;