@camstack/system 1.2.444 → 1.2.445

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 (142) hide show
  1. package/dist/addon-runner.js +40 -31
  2. package/dist/addon-runner.mjs +41 -32
  3. package/dist/{agent-https-DlpTMDqA.js → agent-https-CiOW0-sj.js} +810 -12
  4. package/dist/{agent-https-U0d4uvn2.mjs → agent-https-D4Q8iFNd.mjs} +697 -13
  5. package/dist/auth/admin-token-manager.d.ts +9 -3
  6. package/dist/auth/api-key-manager.d.ts +9 -3
  7. package/dist/auth/auth-audit-log.d.ts +9 -3
  8. package/dist/auth/device-credential-manager.d.ts +5 -1
  9. package/dist/auth/external-identity-manager.d.ts +8 -21
  10. package/dist/auth/login-session-manager.d.ts +11 -3
  11. package/dist/auth/scoped-token-manager.d.ts +8 -52
  12. package/dist/auth/totp-manager.d.ts +11 -3
  13. package/dist/auth/user-manager.d.ts +10 -17
  14. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  15. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  16. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  17. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  18. package/dist/builtins/alerts/alert-store.d.ts +32 -0
  19. package/dist/builtins/alerts/alerts.addon.d.ts +9 -2
  20. package/dist/builtins/alerts/alerts.addon.js +289 -150
  21. package/dist/builtins/alerts/alerts.addon.mjs +288 -150
  22. package/dist/builtins/alerts/record-kinds.d.ts +15 -0
  23. package/dist/builtins/autotrack/index.js +1 -1
  24. package/dist/builtins/autotrack/index.mjs +1 -1
  25. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +1 -0
  26. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +134 -193
  27. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +134 -193
  28. package/dist/builtins/backup-orchestrator/destination-policy.d.ts +4 -2
  29. package/dist/builtins/backup-orchestrator/encryption-key-store.d.ts +4 -2
  30. package/dist/builtins/backup-orchestrator/record-kinds.d.ts +58 -0
  31. package/dist/builtins/backup-orchestrator/schedule-store.d.ts +4 -2
  32. package/dist/builtins/backup-orchestrator/seed-marker-store.d.ts +4 -2
  33. package/dist/builtins/calendars/calendar-prefs-store.d.ts +13 -5
  34. package/dist/builtins/calendars/calendars.addon.d.ts +2 -2
  35. package/dist/builtins/calendars/calendars.addon.js +67 -64
  36. package/dist/builtins/calendars/calendars.addon.mjs +59 -64
  37. package/dist/builtins/calendars/record-kinds.d.ts +18 -0
  38. package/dist/builtins/camera-grid/index.js +1 -1
  39. package/dist/builtins/camera-grid/index.mjs +1 -1
  40. package/dist/builtins/cluster-agent/record-kinds.d.ts +36 -0
  41. package/dist/builtins/composer/composer.addon.js +2 -2
  42. package/dist/builtins/composer/composer.addon.mjs +2 -2
  43. package/dist/builtins/console-logging/index.js +1 -1
  44. package/dist/builtins/console-logging/index.mjs +1 -1
  45. package/dist/builtins/core-blocks/block-settings-store.d.ts +8 -4
  46. package/dist/builtins/core-blocks/core-block-store.d.ts +8 -16
  47. package/dist/builtins/core-blocks/core-blocks.addon.js +85 -112
  48. package/dist/builtins/core-blocks/core-blocks.addon.mjs +86 -113
  49. package/dist/builtins/core-blocks/index.mjs +1 -1
  50. package/dist/builtins/core-blocks/record-kinds.d.ts +26 -0
  51. package/dist/builtins/device-manager/device-manager.addon.js +111 -187
  52. package/dist/builtins/device-manager/device-manager.addon.mjs +110 -187
  53. package/dist/builtins/device-manager/device-tombstone-store.d.ts +9 -48
  54. package/dist/builtins/device-manager/record-kinds.d.ts +14 -0
  55. package/dist/builtins/hub-forwarder/index.js +1 -1
  56. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  57. package/dist/builtins/keychain/index.js +152 -22
  58. package/dist/builtins/keychain/index.mjs +152 -22
  59. package/dist/builtins/keychain/keychain-service.d.ts +3 -2
  60. package/dist/builtins/keychain/keychain-store.d.ts +13 -2
  61. package/dist/builtins/keychain/record-kinds.d.ts +16 -0
  62. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  63. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  64. package/dist/builtins/local-auth/auth-schema.d.ts +3 -1
  65. package/dist/builtins/local-auth/local-auth.addon.js +2903 -2123
  66. package/dist/builtins/local-auth/local-auth.addon.mjs +2904 -2124
  67. package/dist/builtins/local-auth/oauth-session-manager.d.ts +8 -48
  68. package/dist/builtins/local-auth/record-kinds.d.ts +141 -0
  69. package/dist/builtins/local-network/local-network.addon.js +2 -2
  70. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  71. package/dist/builtins/loki-logging/index.js +1 -1
  72. package/dist/builtins/loki-logging/index.mjs +1 -1
  73. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  74. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  75. package/dist/builtins/platform-probe/index.js +1 -1
  76. package/dist/builtins/platform-probe/index.mjs +1 -1
  77. package/dist/builtins/record-entries/index.js +1 -1
  78. package/dist/builtins/record-entries/index.mjs +1 -1
  79. package/dist/builtins/records/legacy-table-row-count.d.ts +5 -0
  80. package/dist/builtins/records/phase6-declare-gate.d.ts +28 -0
  81. package/dist/builtins/records-isolated.d.ts +10 -0
  82. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  83. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  84. package/dist/builtins/snapshot/index.js +1 -1
  85. package/dist/builtins/snapshot/index.mjs +1 -1
  86. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  87. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  88. package/dist/builtins/sqlite-storage/index.js +1 -1
  89. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  90. package/dist/builtins/sqlite-storage/integration-record-kinds.d.ts +30 -0
  91. package/dist/builtins/sqlite-storage/integration-registry.d.ts +10 -16
  92. package/dist/builtins/sqlite-storage/records/count-settings-array-legacy.d.ts +5 -0
  93. package/dist/builtins/sqlite-storage/records/count-settings-brokers-legacy.d.ts +6 -0
  94. package/dist/builtins/sqlite-storage/records/records-engine-types.d.ts +5 -0
  95. package/dist/builtins/sqlite-storage/records/records-engine.d.ts +4 -0
  96. package/dist/builtins/sqlite-storage/records/records-provider.d.ts +5 -1
  97. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +17 -0
  98. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +111 -3
  99. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +111 -3
  100. package/dist/builtins/sqlite-storage/storage-admin-provider.d.ts +9 -1
  101. package/dist/builtins/storage-orchestrator/drain-state-store.d.ts +5 -2
  102. package/dist/builtins/storage-orchestrator/location-store.d.ts +7 -2
  103. package/dist/builtins/storage-orchestrator/record-kinds.d.ts +38 -0
  104. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +2 -0
  105. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +196 -5
  106. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +195 -6
  107. package/dist/builtins/system-config/record-kinds.d.ts +32 -0
  108. package/dist/builtins/system-config/system-config.addon.js +53 -618
  109. package/dist/builtins/system-config/system-config.addon.mjs +29 -577
  110. package/dist/builtins/winston-logging/index.js +1 -1
  111. package/dist/builtins/winston-logging/index.mjs +1 -1
  112. package/dist/{composition-sources-C3rKncIF.mjs → composition-sources-BMt5cFYb.mjs} +1 -1
  113. package/dist/{composition-sources-NAPZddic.js → composition-sources-D3fi9Xns.js} +1 -1
  114. package/dist/{constants-BKX2QnIg.mjs → constants-BMHCxRzb.mjs} +1 -1
  115. package/dist/{constants-BXiNEIeV.js → constants-Br879Axi.js} +1 -1
  116. package/dist/{device-ca-DT9X2S6d.js → device-ca-CnJ_va7i.js} +1 -1
  117. package/dist/{device-ca-jxjSL8Ov.mjs → device-ca-T8osSMos.mjs} +1 -1
  118. package/dist/{dist-DERK-gP5.mjs → dist-BNunRyXT.mjs} +174 -58
  119. package/dist/{dist-D1vECb_6.js → dist-DgOG8flk.js} +233 -57
  120. package/dist/file-host-worker.js +1 -1
  121. package/dist/file-host-worker.mjs +1 -1
  122. package/dist/{hub-mdns-ciao-Cah9GCIg.js → hub-mdns-ciao-BIkTHq-n.js} +2 -2
  123. package/dist/{hub-mdns-ciao-CRaC97V0.mjs → hub-mdns-ciao-D16oRQ9K.mjs} +2 -2
  124. package/dist/index.d.ts +7 -0
  125. package/dist/index.js +3093 -2820
  126. package/dist/index.mjs +3065 -2809
  127. package/dist/kernel/index.d.ts +2 -0
  128. package/dist/kernel/moleculer/event-bus.d.ts +3 -1
  129. package/dist/kernel/moleculer/mesh-mtls/mesh-certificate-registry.d.ts +5 -1
  130. package/dist/kernel/moleculer/remote-logger.d.ts +2 -2
  131. package/dist/kernel/transport/cap-route-resolver.d.ts +9 -38
  132. package/dist/phase6-declare-gate-Ci1-8uUn.js +98 -0
  133. package/dist/phase6-declare-gate-tkKOEitw.mjs +93 -0
  134. package/dist/read-all-rows-CVoO1ONM.js +29 -0
  135. package/dist/read-all-rows-TaPqdWNF.mjs +25 -0
  136. package/dist/records/read-all-rows.d.ts +6 -0
  137. package/dist/{restore-archive-daKyE24F.mjs → restore-archive-DR9MtO7J.mjs} +339 -9
  138. package/dist/{restore-archive-Dy-qKTdF.js → restore-archive-DjJnCgap.js} +353 -5
  139. package/dist/tls/agent-https-names.d.ts +9 -13
  140. package/dist/{wal-maintenance-dAv4IdzC.js → wal-maintenance-CODGqhKY.js} +0 -0
  141. package/dist/{wal-maintenance-zRc24mOC.mjs → wal-maintenance-CfvgtQ7C.mjs} +0 -0
  142. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
- import type { CollectionColumn, SettingsStoreClient } from "@camstack/types";
1
+ import type { CollectionColumn, RecordKindHandle, SettingsStoreClient } from "@camstack/types";
2
+ import type { AdminTokenBody } from "../builtins/local-auth/record-kinds.js";
2
3
  /**
3
4
  * @durable class=config owner=local-auth
4
5
  * write="an admin creates a token (after re-authentication); `lastUsedAt` is
@@ -19,10 +20,15 @@ export interface AdminTokenRecord {
19
20
  readonly lastUsedAt: number | null;
20
21
  readonly revokedAt: number | null;
21
22
  }
23
+ export interface AdminTokenRecordsDeps {
24
+ readonly adminTokensKind: RecordKindHandle<AdminTokenBody>;
25
+ readonly store?: SettingsStoreClient;
26
+ }
22
27
  export declare class AdminTokenManager {
23
- private readonly store;
24
28
  private readonly now;
25
- constructor(store: SettingsStoreClient, now?: () => number);
29
+ private readonly kind;
30
+ private readonly store;
31
+ constructor(deps: SettingsStoreClient | AdminTokenRecordsDeps, now?: () => number);
26
32
  /** Mint a token for `createdBy`. Refuses an expiry in the past or past 90 days. */
27
33
  create(input: {
28
34
  readonly createdBy: string;
@@ -1,11 +1,17 @@
1
- import type { ApiKeyRecord, SettingsStoreClient } from "@camstack/types";
1
+ import type { ApiKeyRecord, RecordKindHandle, SettingsStoreClient } from "@camstack/types";
2
+ import type { ApiKeyBody } from "../builtins/local-auth/record-kinds.js";
2
3
  export type { ApiKeyRecord };
3
4
  export interface ApiKeyStorageAccess {
4
5
  getStore(): SettingsStoreClient;
5
6
  }
7
+ export interface ApiKeyRecordsDeps {
8
+ readonly apiKeysKind: RecordKindHandle<ApiKeyBody>;
9
+ readonly legacyStore?: SettingsStoreClient;
10
+ }
6
11
  export declare class ApiKeyManager {
7
- private readonly storageAccess;
8
- constructor(storageAccess: ApiKeyStorageAccess);
12
+ private readonly kind;
13
+ private readonly legacyStore;
14
+ constructor(deps: ApiKeyStorageAccess | ApiKeyRecordsDeps);
9
15
  private get store();
10
16
  listAll(): Promise<Omit<ApiKeyRecord, "tokenHash">[]>;
11
17
  revoke(id: string): Promise<void>;
@@ -1,4 +1,5 @@
1
- import type { CollectionColumn, IScopedLogger, SettingsStoreClient } from "@camstack/types";
1
+ import type { CollectionColumn, IScopedLogger, RecordKindHandle, SettingsStoreClient } from "@camstack/types";
2
+ import type { AuthAuditBody } from "../builtins/local-auth/record-kinds.js";
2
3
  /**
3
4
  * @durable class=ledger owner=local-auth
4
5
  * write="one row per audited auth event: a second factor skipped from a trusted
@@ -22,11 +23,16 @@ export interface AuthAuditRecord extends AuthAuditEvent {
22
23
  readonly id: string;
23
24
  readonly at: number;
24
25
  }
26
+ export interface AuthAuditRecordsDeps {
27
+ readonly authAuditKind: RecordKindHandle<AuthAuditBody>;
28
+ readonly legacyStore?: SettingsStoreClient;
29
+ }
25
30
  export declare class AuthAuditLog {
26
- private readonly store;
27
31
  private readonly logger;
28
32
  private readonly now;
29
- constructor(store: SettingsStoreClient, logger?: IScopedLogger | null, now?: () => number);
33
+ private readonly kind;
34
+ private readonly store;
35
+ constructor(deps: SettingsStoreClient | AuthAuditRecordsDeps, logger?: IScopedLogger | null, now?: () => number);
30
36
  /** Record `event`. Never throws. */
31
37
  record(event: AuthAuditEvent): Promise<void>;
32
38
  /** Newest first, at most `limit` (≤ 500), optionally one user's. */
@@ -1,4 +1,5 @@
1
- import type { CollectionColumn, IScopedLogger, SettingsStoreClient } from "@camstack/types";
1
+ import type { CollectionColumn, IScopedLogger, RecordKindHandle, SettingsStoreClient } from "@camstack/types";
2
+ import type { DeviceCredentialBody } from "../builtins/local-auth/record-kinds.js";
2
3
  import { type DeviceCredential, type DevicePlatform, type DeviceSignaturePurpose } from "@camstack/types";
3
4
  import type { DeviceCaStore } from "./device-ca-store.js";
4
5
  /**
@@ -70,6 +71,7 @@ export interface DeviceCredentialManagerDeps {
70
71
  } | null>;
71
72
  /** Hub-wide switch for `enrollSelf`; absent = allowed. */
72
73
  readonly selfEnrollmentAllowed: () => Promise<boolean>;
74
+ readonly deviceCredentialsKind?: RecordKindHandle<DeviceCredentialBody> | null;
73
75
  readonly now?: () => number;
74
76
  readonly logger?: IScopedLogger;
75
77
  }
@@ -148,6 +150,8 @@ export declare class DeviceCredentialManager {
148
150
  toSummary(record: DeviceCredentialRecord, username: string | null): DeviceCredential;
149
151
  /** Delete rows revoked or expired more than 90 days ago. */
150
152
  reap(): Promise<number>;
153
+ private persistRecord;
154
+ private deleteRecord;
151
155
  private save;
152
156
  private refuse;
153
157
  private prune;
@@ -1,20 +1,11 @@
1
1
  /**
2
2
  * External sign-in identities linked to local users (D782).
3
- *
4
- * An OIDC login names an identity `(provider, subject)` — the provider
5
- * instance's login-method id (`auth-oidc/authentik`) and the IdP's `sub`. It
6
- * signs in ONLY the user its owner explicitly linked it to: no
7
- * auto-provisioning, no email matching, no group mapping.
8
- *
9
- * The row id is the identity itself (`JSON.stringify([provider, subject])`),
10
- * so the collection's primary key is what makes "one identity → one user" a
11
- * storage invariant rather than a check that a race could slip past.
12
3
  */
13
- import type { SettingsStoreClient } from "@camstack/types";
4
+ import type { RecordKindHandle, SettingsStoreClient } from "@camstack/types";
5
+ import type { ExternalIdentityBody } from "../builtins/local-auth/record-kinds.js";
14
6
  export interface ExternalIdentityRecord {
15
7
  readonly provider: string;
16
8
  readonly subject: string;
17
- /** Verified `iss` the identity was linked under. */
18
9
  readonly issuer: string;
19
10
  readonly userId: string;
20
11
  readonly label?: string;
@@ -24,32 +15,28 @@ export interface ExternalIdentityKey {
24
15
  readonly provider: string;
25
16
  readonly subject: string;
26
17
  }
27
- /** The identity is already linked to a different user. */
28
18
  export declare class ExternalIdentityConflictError extends Error {
29
19
  constructor();
30
20
  }
21
+ export interface ExternalIdentityRecordsDeps {
22
+ readonly identitiesKind: RecordKindHandle<ExternalIdentityBody>;
23
+ readonly legacyStore?: SettingsStoreClient;
24
+ }
31
25
  export declare class ExternalIdentityManager {
26
+ private readonly kind;
32
27
  private readonly store;
33
- constructor(store: SettingsStoreClient);
28
+ constructor(deps: SettingsStoreClient | ExternalIdentityRecordsDeps);
34
29
  private find;
35
- /** The link of `(provider, subject)`, or `null`. */
36
30
  findLink(provider: string, subject: string): Promise<ExternalIdentityRecord | null>;
37
- /** The user `(provider, subject)` is linked to, or `null`. */
38
31
  findUser(provider: string, subject: string): Promise<string | null>;
39
32
  listForUser(userId: string): Promise<readonly ExternalIdentityRecord[]>;
40
- /**
41
- * Link `(provider, subject)` to `userId`. Idempotent for the same user;
42
- * {@link ExternalIdentityConflictError} when another user holds it.
43
- */
44
33
  link(input: ExternalIdentityKey & {
45
34
  userId: string;
46
35
  issuer: string;
47
36
  label?: string;
48
37
  }): Promise<void>;
49
- /** Remove one link of `userId`; `false` when that user did not hold it. */
50
38
  unlink(input: ExternalIdentityKey & {
51
39
  userId: string;
52
40
  }): Promise<boolean>;
53
- /** Delete every link of a user being deleted. Returns how many were removed. */
54
41
  removeAllForUser(userId: string): Promise<number>;
55
42
  }
@@ -1,4 +1,5 @@
1
- import type { CollectionColumn, IScopedLogger, SettingsStoreClient } from "@camstack/types";
1
+ import type { CollectionColumn, IScopedLogger, RecordKindHandle, SettingsStoreClient } from "@camstack/types";
2
+ import type { LoginSessionBody, LoginSessionCutoffBody } from "../builtins/local-auth/record-kinds.js";
2
3
  /**
3
4
  * @durable class=ledger owner=local-auth
4
5
  * write="one row per login (password, passkey, SSO, handoff, session exchange);
@@ -73,15 +74,22 @@ export interface LoginSessionManagerOptions {
73
74
  readonly now?: () => number;
74
75
  readonly logger?: IScopedLogger;
75
76
  }
77
+ export interface LoginSessionRecordsDeps {
78
+ readonly sessionsKind: RecordKindHandle<LoginSessionBody>;
79
+ readonly cutoffsKind: RecordKindHandle<LoginSessionCutoffBody>;
80
+ readonly legacyStore?: SettingsStoreClient;
81
+ }
76
82
  export declare class LoginSessionManager {
77
- private readonly store;
78
83
  private readonly sessions;
79
84
  /** refresh hash (current or previous) → session id */
80
85
  private readonly byRefreshHash;
81
86
  private readonly cutoffs;
82
87
  private readonly now;
83
88
  private readonly logger;
84
- constructor(store: SettingsStoreClient, options?: LoginSessionManagerOptions);
89
+ private readonly sessionsKind;
90
+ private readonly cutoffsKind;
91
+ private readonly store;
92
+ constructor(deps: SettingsStoreClient | LoginSessionRecordsDeps, options?: LoginSessionManagerOptions);
85
93
  /** Load every row into the mirror. Call once at init, before serving. */
86
94
  load(): Promise<void>;
87
95
  create(input: CreateLoginSessionInput): Promise<{
@@ -1,69 +1,25 @@
1
- import type { ScopedToken, TokenScope, SettingsStoreClient } from "@camstack/types";
1
+ import type { RecordKindHandle, ScopedToken, TokenScope, SettingsStoreClient } from "@camstack/types";
2
+ import type { ScopedTokenBody } from "../builtins/local-auth/record-kinds.js";
3
+ export interface ScopedTokenRecordsDeps {
4
+ readonly scopedTokensKind: RecordKindHandle<ScopedTokenBody>;
5
+ readonly legacyStore?: SettingsStoreClient;
6
+ }
2
7
  /**
3
8
  * Manages scoped API tokens with restricted addon/route/capability access.
4
9
  */
5
10
  export declare class ScopedTokenManager {
11
+ private readonly kind;
6
12
  private readonly store;
7
- constructor(store: SettingsStoreClient);
13
+ constructor(deps: SettingsStoreClient | ScopedTokenRecordsDeps);
8
14
  create(userId: string, name: string, scopes: TokenScope[], expiresAt?: number): Promise<{
9
15
  token: string;
10
16
  record: ScopedToken;
11
17
  }>;
12
18
  validate(rawToken: string): Promise<ScopedToken | null>;
13
- /**
14
- * Coarse scope match — checks only `addonId` / `capability` (no
15
- * `route-prefix` after the caps-only migration). Used by addon-route
16
- * HTTP handlers that need a quick allow/deny before the per-method
17
- * `access` check kicks in via `METHOD_ACCESS_MAP` in
18
- * `protectedProcedure`.
19
- */
20
19
  matchesScope(token: ScopedToken, addonId?: string, capability?: string): boolean;
21
- /**
22
- * Delete the token row. `true` when a row was removed; `false` when the id
23
- * was already gone (unknown or previously revoked).
24
- */
25
20
  revoke(tokenId: string): Promise<boolean>;
26
21
  listForUser(userId: string): Promise<ScopedToken[]>;
27
22
  updateLastUsed(tokenId: string): Promise<void>;
28
- /**
29
- * Delete tokens whose expiry has passed.
30
- *
31
- * These were never usable — {@link validate} already rejects an expired
32
- * token — so this is hygiene rather than a hole being closed, and it is
33
- * worth being precise about what it actually buys:
34
- *
35
- * - `listForUser` returns rows with no expiry filter, so the operator's
36
- * token list showed dead tokens as if they were live. There was no way to
37
- * tell from the UI which ones still worked.
38
- * - a database that outlives its tokens keeps their hashes indefinitely.
39
- * 27 rows on the live hub had been unusable for months.
40
- *
41
- * A token with NO expiry (`expiresAt` null/absent) is a deliberate
42
- * never-expires token and MUST survive: `BETWEEN` never matches NULL in
43
- * SQL, which is the property this depends on and the spec pins against a
44
- * real SQLite.
45
- *
46
- * The bound is `nowMs - 1` because {@link validate} treats `now ===
47
- * expiresAt` as still valid; reaping at exactly the expiry instant would
48
- * delete a token that the same millisecond still accepts.
49
- *
50
- * Returns how many rows went.
51
- */
52
23
  reapExpired(nowMs?: number): Promise<number>;
53
- /**
54
- * One-shot migration: drop tokens whose owner can't be resolved.
55
- *
56
- * Two ways a token can end up orphan:
57
- * • Pre-fix tokens minted via the CLI were owned by the literal
58
- * string `"system"` (provider hardcode, fixed 2026-05-11).
59
- * • A user gets deleted but their tokens were not cascade-revoked.
60
- *
61
- * Either way the UI's `listScopedTokens({ userId: u.id })` never
62
- * returns them, so the operator can't revoke through the normal flow.
63
- * We sweep both classes by passing the live user-id set in.
64
- *
65
- * Idempotent: if no orphans exist the method is a no-op.
66
- * Returns the number of tokens removed.
67
- */
68
24
  cleanupOrphans(validUserIds: ReadonlySet<string>): Promise<number>;
69
25
  }
@@ -1,4 +1,5 @@
1
- import { type RawSecretMigrationReport, type IScopedLogger, type SecretsAccess, type SettingsStoreClient } from "@camstack/types";
1
+ import { type RawSecretMigrationReport, type IScopedLogger, type RecordKindHandle, type SecretsAccess, type SettingsStoreClient } from "@camstack/types";
2
+ import type { UserTotpBody } from "../builtins/local-auth/record-kinds.js";
2
3
  /**
3
4
  * The seed cannot be used right now. The message is what a not-yet-logged-in
4
5
  * caller sees, so it is generic; `reason` (keychain detail) is for the server log only.
@@ -19,16 +20,21 @@ export interface TotpStatus {
19
20
  readonly enabled: boolean;
20
21
  readonly confirmedAt: number | null;
21
22
  }
23
+ export interface TotpRecordsDeps {
24
+ readonly userTotpKind: RecordKindHandle<UserTotpBody>;
25
+ readonly legacyStore?: SettingsStoreClient;
26
+ }
22
27
  export declare class TotpManager {
23
- private readonly store;
24
28
  /** ±N steps of clock-skew tolerance, expressed in seconds for otplib v13's
25
29
  * per-call `epochTolerance`. Default window 1 = ±30 s. */
26
30
  private readonly epochTolerance;
27
31
  private readonly secrets;
28
32
  private readonly logger;
33
+ private readonly userTotpKind;
34
+ private readonly store;
29
35
  /** Per-user queue: setup / confirm / disable / migration never interleave on one user. */
30
36
  private readonly chains;
31
- constructor(store: SettingsStoreClient, opts?: {
37
+ constructor(deps: SettingsStoreClient | TotpRecordsDeps, opts?: {
32
38
  /** ±N 30-second windows tolerated. Default 1 = ±30 s clock skew. */
33
39
  readonly window?: number;
34
40
  /** Keychain handle for owner `system`; `null` = no keychain door (setup refuses). */
@@ -96,4 +102,6 @@ export declare class TotpManager {
96
102
  private moveRow;
97
103
  private readAll;
98
104
  private find;
105
+ private writeRow;
106
+ private removeRow;
99
107
  }
@@ -1,5 +1,6 @@
1
- import type { SettingsStoreClient, TokenScope, UserRecord } from "@camstack/types";
1
+ import type { RecordKindHandle, SettingsStoreClient, TokenScope, UserRecord } from "@camstack/types";
2
2
  import type { AuthManager } from "./auth-manager.js";
3
+ import type { UserBody } from "../builtins/local-auth/record-kinds.js";
3
4
  export type { UserRecord };
4
5
  interface CreateUserInput {
5
6
  username: string;
@@ -39,10 +40,17 @@ export type AdminSeedOutcome = {
39
40
  export interface UserStorageAccess {
40
41
  getStore(): SettingsStoreClient;
41
42
  }
43
+ export interface UserRecordsDeps {
44
+ readonly usersKind: RecordKindHandle<UserBody>;
45
+ /** Optional legacy store for tests that assert no declareCollection. */
46
+ readonly legacyStore?: SettingsStoreClient;
47
+ }
42
48
  export declare class UserManager {
43
49
  private readonly storageAccess;
44
50
  private readonly auth;
45
- constructor(storageAccess: UserStorageAccess, auth: AuthManager);
51
+ private readonly usersKind;
52
+ private readonly legacyStore;
53
+ constructor(storageAccess: UserStorageAccess | UserRecordsDeps, auth: AuthManager);
46
54
  private get store();
47
55
  create(input: CreateUserInput): Promise<UserRecord>;
48
56
  findByUsername(username: string): Promise<UserRecord | null>;
@@ -55,22 +63,7 @@ export declare class UserManager {
55
63
  /**
56
64
  * Seed {@link FIRST_BOOT_ADMIN_USERNAME} / {@link FIRST_BOOT_ADMIN_PASSWORD}
57
65
  * — on FIRST boot only, i.e. when no admin account exists.
58
- *
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.
64
- *
65
- * `resetPassword: true` is the one explicit exception — the operator's
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
66
  */
70
67
  ensureAdminExists(options?: EnsureAdminOptions): Promise<AdminSeedOutcome>;
71
- /**
72
- * Does `username` still sign in with `password`? A hash comparison — the
73
- * password is never logged or stored. `false` when the user does not exist.
74
- */
75
68
  hasPassword(username: string, password: string): Promise<boolean>;
76
69
  }
@@ -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-D1vECb_6.js");
6
+ const require_dist = require("../../dist-DgOG8flk.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 { $i as EventCategory, Fr as BaseAddon, Pr as errMsg, at as addonPagesCapability } from "../../dist-DERK-gP5.mjs";
1
+ import { Lr as errMsg, Rr as BaseAddon, la as EventCategory, lt as addonPagesCapability } from "../../dist-BNunRyXT.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-D1vECb_6.js");
6
+ const require_dist = require("../../dist-DgOG8flk.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 { $i as EventCategory, Fr as BaseAddon, Pr as errMsg, st as addonWidgetsCapability } from "../../dist-DERK-gP5.mjs";
1
+ import { Lr as errMsg, Rr as BaseAddon, dt as addonWidgetsCapability, la as EventCategory } from "../../dist-BNunRyXT.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";
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Alert persistence over the `alerts` record kind (phase 4) or legacy collection.
3
+ */
4
+ import type { Alert, RecordKindHandle, RecordsAccess, SettingsStoreClient } from "@camstack/types";
5
+ import { type AlertBody } from "./record-kinds.js";
6
+ export { ALERTS_COLLECTION } from "./record-kinds.js";
7
+ export type AlertRecord = {
8
+ readonly id: string;
9
+ readonly data: Record<string, unknown>;
10
+ };
11
+ export declare class AlertStore {
12
+ private readonly kind;
13
+ private readonly legacyStore;
14
+ constructor(kind: RecordKindHandle<AlertBody> | null, legacyStore: SettingsStoreClient | null);
15
+ static of(records: Pick<RecordsAccess, "kind">): AlertStore;
16
+ static legacy(store: SettingsStoreClient): AlertStore;
17
+ private legacyRows;
18
+ list(filter?: {
19
+ unreadOnly?: boolean;
20
+ limit?: number;
21
+ }): Promise<readonly AlertRecord[]>;
22
+ countUnread(): Promise<number>;
23
+ countAll(): Promise<number>;
24
+ getData(id: string): Promise<Record<string, unknown> | null>;
25
+ mergeUpdate(id: string, patch: Record<string, unknown>): Promise<void>;
26
+ persist(alert: Alert): Promise<void>;
27
+ putRaw(id: string, data: Record<string, unknown>): Promise<void>;
28
+ delete(id: string): Promise<void>;
29
+ listAllByCreatedAtAsc(): Promise<readonly AlertRecord[]>;
30
+ listRecent(limit: number): Promise<readonly AlertRecord[]>;
31
+ findWhereDataField(field: string, value: unknown): Promise<readonly AlertRecord[]>;
32
+ }
@@ -5,10 +5,15 @@ interface AlertCenterConfig {
5
5
  readonly retentionDays: number;
6
6
  /** Push critical system alerts (runner failed/refused, runners degraded) straight to the notifiers (D877). */
7
7
  readonly criticalPushEnabled: boolean;
8
- /** Comma-separated notifier target ids for that push; empty = every enabled operator-level target. */
9
- readonly criticalPushTargetIds: string;
8
+ /**
9
+ * Notifier target ids for that push (`notification-target-multiselect`).
10
+ * Empty = every enabled operator-level (shared) target. A legacy CSV string
11
+ * still stored in the addon store is accepted at read time.
12
+ */
13
+ readonly criticalPushTargetIds: readonly string[];
10
14
  }
11
15
  export declare class AlertCenterAddon extends BaseAddon<AlertCenterConfig> {
16
+ private alertStore;
12
17
  private unsubscribers;
13
18
  /** How long a burst of critical findings is gathered into one digest push. */
14
19
  private readonly pushCoalesceMs;
@@ -50,6 +55,8 @@ export declare class AlertCenterAddon extends BaseAddon<AlertCenterConfig> {
50
55
  private readonly pushQueue;
51
56
  private pushTimer;
52
57
  private pushPolicy;
58
+ /** Rewrite a legacy CSV `criticalPushTargetIds` string into `string[]`. */
59
+ private migrateCriticalPushTargetIds;
53
60
  private pushPorts;
54
61
  /**
55
62
  * Queue a critical finding for the next digest. A burst (every runner of a boot)