@camstack/system 1.2.168 → 1.2.170

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 (60) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  14. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  25. package/dist/builtins/local-network/local-network.addon.js +1 -1
  26. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  27. package/dist/builtins/loki-logging/index.js +1 -1
  28. package/dist/builtins/loki-logging/index.mjs +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +1 -1
  36. package/dist/builtins/snapshot/index.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  41. package/dist/builtins/storage-orchestrator/data-store-dispatch.d.ts +18 -6
  42. package/dist/builtins/storage-orchestrator/hub-settings-forward.d.ts +68 -0
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +27 -4
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +236 -60
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +236 -60
  46. package/dist/builtins/system-config/system-config.addon.js +1 -1
  47. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  48. package/dist/builtins/winston-logging/index.js +1 -1
  49. package/dist/builtins/winston-logging/index.mjs +1 -1
  50. package/dist/{dist-DwaeuknI.mjs → dist-1DEQFmTq.mjs} +167 -6
  51. package/dist/{dist-DCdtLXgx.js → dist-DsoVfJSR.js} +167 -6
  52. package/dist/hub-cap-forward-BdT320dq.mjs +53 -0
  53. package/dist/hub-cap-forward-DmHNjbB0.js +70 -0
  54. package/dist/index.js +6 -5
  55. package/dist/index.mjs +3 -2
  56. package/dist/{manifest-system-deps-D52_8SN1.mjs → manifest-system-deps-Da6sme9C.mjs} +3 -54
  57. package/dist/{manifest-system-deps-Bi7oyXcN.js → manifest-system-deps-ir_XTNpe.js} +2 -71
  58. package/dist/{retired-settings-keys-CB3wWpuC.mjs → retired-settings-keys-ChS1Q2Kv.mjs} +1 -1
  59. package/dist/{retired-settings-keys-DobfRRgq.js → retired-settings-keys-DTE5k4lU.js} +1 -1
  60. package/package.json +1 -1
@@ -3,7 +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-DCdtLXgx.js");
6
+ const require_dist = require("../../dist-DsoVfJSR.js");
7
+ const require_hub_cap_forward = require("../../hub-cap-forward-DmHNjbB0.js");
7
8
  let zod = require("zod");
8
9
  let node_crypto = require("node:crypto");
9
10
  let node_fs_promises = require("node:fs/promises");
@@ -302,7 +303,7 @@ var StorageMigrationCoordinator = class {
302
303
  if (!target) throw new Error(`Storage location "${targetId}" not found`);
303
304
  if (target.type !== storageClass) throw new Error(`Storage location "${targetId}" is type "${target.type}", expected "${storageClass}"`);
304
305
  if (source.id === target.id) throw new Error(namedSourceId ? `Source and destination are the same location ("${targetId}")` : `Storage location "${targetId}" is already the "${storageClass}" default`);
305
- const freezeSource = defaultLoc !== void 0 && source.id !== defaultLoc.id;
306
+ const freezeSource = defaultLoc === null || source.id !== defaultLoc.id;
306
307
  if (!MOVER_CLASSES.includes(storageClass)) throw new Error(`No mover owns "${storageClass}" — the migration can repoint its default but cannot move its bytes. Move it by hand, then repoint the default with upsertLocation.`);
307
308
  moves.push({
308
309
  storageClass,
@@ -1118,6 +1119,201 @@ function canProbeOccupancyLocally(locality) {
1118
1119
  return locality === true;
1119
1120
  }
1120
1121
  //#endregion
1122
+ //#region src/builtins/storage-orchestrator/data-store-dispatch.ts
1123
+ /**
1124
+ * Pick the target to serve a call.
1125
+ *
1126
+ * A LOCAL engine always wins: the remote is a fallback for a node that has
1127
+ * none, never a policy. Two cases are refused rather than guessed:
1128
+ *
1129
+ * - **none registered, and no remote** — resolving to a no-op would make
1130
+ * every read answer `undefined` and every write disappear, which reads
1131
+ * exactly like an empty database;
1132
+ * - **more than one engine** — there is no routing rule yet. The `engine`
1133
+ * hint on `declareCollection` is what will choose, and until it exists a
1134
+ * silent pick would send a collection to whichever engine happened to
1135
+ * register first. A remote does not rescue this: the ambiguity is local.
1136
+ */
1137
+ function resolveEngine(getEngines, getRemote) {
1138
+ const engines = getEngines();
1139
+ if (engines.length === 0) {
1140
+ const remote = getRemote?.() ?? null;
1141
+ if (remote) return remote;
1142
+ throw new Error("settings-store: no data-store-provider engine is registered — the data door has nothing behind it");
1143
+ }
1144
+ if (engines.length > 1) throw new Error(`settings-store: ${engines.length} data-store-provider engines are registered and there is no routing rule yet — declare the engine on the collection first`);
1145
+ const engine = engines[0];
1146
+ if (!engine) throw new Error("settings-store: data-store-provider collection yielded an empty entry");
1147
+ return engine;
1148
+ }
1149
+ /**
1150
+ * Build the `settings-store` provider that fronts the registered engines,
1151
+ * with an optional remote behind it for a node that registers none.
1152
+ *
1153
+ * `getEngines` and `getRemote` are read on every call, never captured:
1154
+ * engines register during boot and a runner respawn replaces the instance, so
1155
+ * a snapshot taken at construction time would go stale exactly when an addon
1156
+ * crashed and came back.
1157
+ */
1158
+ function createDataStoreDispatch(getEngines, getRemote) {
1159
+ const engine = async () => resolveEngine(getEngines, getRemote);
1160
+ return {
1161
+ get: async (input) => (await engine()).get(input),
1162
+ set: async (input) => (await engine()).set(input),
1163
+ query: async (input) => (await engine()).query(input),
1164
+ insert: async (input) => (await engine()).insert(input),
1165
+ insertMany: async (input) => (await engine()).insertMany(input),
1166
+ update: async (input) => (await engine()).update(input),
1167
+ delete: async (input) => (await engine()).delete(input),
1168
+ deleteWhere: async (input) => (await engine()).deleteWhere(input),
1169
+ updateWhere: async (input) => (await engine()).updateWhere(input),
1170
+ count: async (input) => (await engine()).count(input),
1171
+ aggregate: async (input) => (await engine()).aggregate(input),
1172
+ histogram: async (input) => (await engine()).histogram(input),
1173
+ isEmpty: async (input) => (await engine()).isEmpty(input),
1174
+ declareCollection: async (input) => (await engine()).declareCollection(input)
1175
+ };
1176
+ }
1177
+ //#endregion
1178
+ //#region src/builtins/storage-orchestrator/hub-settings-forward.ts
1179
+ /**
1180
+ * The data door with the HUB behind it — an agent's `settings-store` provider.
1181
+ *
1182
+ * ## Why this exists
1183
+ *
1184
+ * Persistence is the hub's. An agent runs no `data-store-provider` engine
1185
+ * (see `resolveAgentInfraCapabilities` in `server/backend/src/agent/`), so the
1186
+ * `settings-store` door the storage-orchestrator registers on an agent has
1187
+ * nothing local behind it. Rather than refuse — which would strand the four
1188
+ * infra addons that boot inside agent-main (`hub-forwarder`, `native-metrics`,
1189
+ * `platform-probe`, the orchestrator itself) with a `ctx.settings` that throws
1190
+ * on every read — the door FORWARDS into the hub's own routing.
1191
+ *
1192
+ * This is not a new transport. It is the same `$hub-cap-fwd.forward` a forked
1193
+ * addon on an agent has always used to reach a hub-hosted cap
1194
+ * (`kernel/transport/hub-cap-forward.ts` names `settings-store` in its own
1195
+ * header); agent-main's infra addons simply never had it, because they resolve
1196
+ * `ctx.api.settingsStore` through `localProviderLink` against the process's own
1197
+ * provider registry — which is exactly the local door.
1198
+ *
1199
+ * ## The payload crosses untouched
1200
+ *
1201
+ * The engine composes its table name from `namespace` + `collection` **out of
1202
+ * the payload**. A forwarder that injected, defaulted or stripped a namespace
1203
+ * would silently rename every table on the hub. Every method here is the same
1204
+ * forward, and the args are the caller's object verbatim — the same rule
1205
+ * `data-store-dispatch.ts` states for the local path, for the same reason.
1206
+ *
1207
+ * ## No second key space
1208
+ *
1209
+ * Per-node scoping already exists, one layer up, and it is per FIELD:
1210
+ * `BaseAddon` persists a `perNode: true` schema field as `<key>@<nodeId>`
1211
+ * inside the addon's single shared blob (`packages/types/src/addon/
1212
+ * per-node-store.ts`). The agent therefore reads and writes the SAME row the
1213
+ * hub's copy of that addon reads — `<addonId>:addon-settings` / `root` — and
1214
+ * the two agree by construction. Introducing a second, addon-level
1215
+ * `(nodeId, addonId)` row here would be a second authority over one fact,
1216
+ * which is the shape [D224](../../../../../docs/decisions/adr-0224-the-freshness-of-a-camera-fact-lives-in-its-provider.md)
1217
+ * forbids: the hub's copy would keep writing the bare row and nothing would
1218
+ * reconcile them.
1219
+ *
1220
+ * ## A hub NEVER forwards
1221
+ *
1222
+ * {@link nodeOwnsItsDataStore} gates construction. A hub that built one would
1223
+ * route its own door back into itself through `$hub-cap-fwd`, which resolves
1224
+ * `settings-store` to… this. The gate reads the same env var, with the same
1225
+ * default, as `settingsStoreIsAuthoritativeHere` — unset means hub, because
1226
+ * that is what the launcher defaults to.
1227
+ *
1228
+ * ## When the hub is down
1229
+ *
1230
+ * Every method REJECTS. It never answers "empty": `ctx.settings`' read path
1231
+ * (`addon-context-factory.ts`) distinguishes a MISS from a FAILURE precisely
1232
+ * so a failed read cannot make the next read-modify-write persist the patch
1233
+ * alone and destroy every field the patch does not mention. An empty answer
1234
+ * would be indistinguishable from an empty store; a rejection is not. The
1235
+ * operator's rule for this node is the same one that already governs it —
1236
+ * without the hub an agent has no work — so a rejection is the whole of the
1237
+ * degraded behaviour, and it is loud.
1238
+ */
1239
+ /**
1240
+ * Does THIS node hold its own data store?
1241
+ *
1242
+ * Unset or `'hub'` means hub — the launcher's own default, mirrored from
1243
+ * `resolveNodeRole` (`server/backend/src/node-role.ts`) the same way
1244
+ * `settingsStoreIsAuthoritativeHere` mirrors it, and duplicated rather than
1245
+ * imported for the same reason: `@camstack/system` is a dependency OF the
1246
+ * backend, not the other way round.
1247
+ *
1248
+ * Anything else is an agent, and an agent's answer is the hub's store.
1249
+ */
1250
+ function nodeOwnsItsDataStore(env) {
1251
+ const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
1252
+ return raw === "" || raw === "hub";
1253
+ }
1254
+ /**
1255
+ * Build the `settings-store` provider that forwards to the hub.
1256
+ *
1257
+ * Every method is the same forward under the CAP name — never the engine
1258
+ * name. The hub routes `settings-store` to its own door, which then picks its
1259
+ * local engine; asking for `data-store-provider` would step around the door on
1260
+ * the far side and lose whatever the door is for.
1261
+ */
1262
+ function createHubSettingsForward(forward) {
1263
+ const send = (method, args) => forward({
1264
+ capName: require_dist.settingsStoreCapability.name,
1265
+ method,
1266
+ args
1267
+ });
1268
+ return {
1269
+ get: (input) => send("get", input),
1270
+ set: (input) => send("set", input),
1271
+ query: (input) => send("query", input),
1272
+ insert: (input) => send("insert", input),
1273
+ insertMany: (input) => send("insertMany", input),
1274
+ update: (input) => send("update", input),
1275
+ delete: (input) => send("delete", input),
1276
+ deleteWhere: (input) => send("deleteWhere", input),
1277
+ updateWhere: (input) => send("updateWhere", input),
1278
+ count: (input) => send("count", input),
1279
+ aggregate: (input) => send("aggregate", input),
1280
+ histogram: (input) => send("histogram", input),
1281
+ isEmpty: (input) => send("isEmpty", input),
1282
+ declareCollection: (input) => send("declareCollection", input)
1283
+ };
1284
+ }
1285
+ /**
1286
+ * How long an agent waits for the hub to answer a settings call before the
1287
+ * door rejects. Matches the agent's own `forwardToHub` budget in
1288
+ * `server/backend/src/agent/main.ts` — the same hop, over the same action, so
1289
+ * a shorter one here would only manufacture a timeout the transport did not
1290
+ * have.
1291
+ */
1292
+ var HUB_SETTINGS_FORWARD_TIMEOUT_MS = 6e4;
1293
+ /**
1294
+ * The whole decision, in one place: does this node's data door have the hub
1295
+ * behind it, and how does it get there?
1296
+ *
1297
+ * `null` means "no remote" and the door keeps its old behaviour — it refuses
1298
+ * when no engine is registered. That is the answer on a hub (which IS the
1299
+ * store) and in any process with no cluster broker.
1300
+ *
1301
+ * Extracted from the addon so the gate is unit-testable without booting a
1302
+ * `BaseAddon` — the same reason `data-store-dispatch.ts` and
1303
+ * `provider-discovery.ts` exist.
1304
+ */
1305
+ function resolveHubSettingsForward(options) {
1306
+ if (nodeOwnsItsDataStore(options.env)) return null;
1307
+ const broker = options.broker;
1308
+ if (!broker) return null;
1309
+ options.logger.info("storage-orchestrator: no local data-store engine on this node — the data door forwards to the hub");
1310
+ return createHubSettingsForward((envelope) => broker.call(require_hub_cap_forward.HUB_CAP_FWD_ACTION, {
1311
+ capName: envelope.capName,
1312
+ method: envelope.method,
1313
+ args: envelope.args
1314
+ }, { timeout: HUB_SETTINGS_FORWARD_TIMEOUT_MS }));
1315
+ }
1316
+ //#endregion
1121
1317
  //#region src/builtins/storage-orchestrator/location-occupancy.ts
1122
1318
  /**
1123
1319
  * Delete-time occupancy of a location's on-disk root.
@@ -1190,57 +1386,6 @@ async function probeLocalDirectoryOccupancy(input) {
1190
1386
  return "empty";
1191
1387
  }
1192
1388
  //#endregion
1193
- //#region src/builtins/storage-orchestrator/data-store-dispatch.ts
1194
- /**
1195
- * Pick the engine to serve a call.
1196
- *
1197
- * Today the rule is "the only one there is". Two cases are refused rather
1198
- * than guessed:
1199
- *
1200
- * - **none registered** — resolving to a no-op would make every read
1201
- * answer `undefined` and every write disappear, which reads exactly
1202
- * like an empty database;
1203
- * - **more than one** — there is no routing rule yet. The `engine` hint
1204
- * on `declareCollection` is what will choose, and until it exists a
1205
- * silent pick would send a collection to whichever engine happened to
1206
- * register first.
1207
- */
1208
- function resolveEngine(getEngines) {
1209
- const engines = getEngines();
1210
- if (engines.length === 0) throw new Error("settings-store: no data-store-provider engine is registered — the data door has nothing behind it");
1211
- if (engines.length > 1) throw new Error(`settings-store: ${engines.length} data-store-provider engines are registered and there is no routing rule yet — declare the engine on the collection first`);
1212
- const engine = engines[0];
1213
- if (!engine) throw new Error("settings-store: data-store-provider collection yielded an empty entry");
1214
- return engine;
1215
- }
1216
- /**
1217
- * Build the `settings-store` provider that fronts the registered engines.
1218
- *
1219
- * `getEngines` is read on every call, never captured: engines register
1220
- * during boot and a runner respawn replaces the instance, so a snapshot
1221
- * taken at construction time would go stale exactly when an addon crashed
1222
- * and came back.
1223
- */
1224
- function createDataStoreDispatch(getEngines) {
1225
- const engine = async () => resolveEngine(getEngines);
1226
- return {
1227
- get: async (input) => (await engine()).get(input),
1228
- set: async (input) => (await engine()).set(input),
1229
- query: async (input) => (await engine()).query(input),
1230
- insert: async (input) => (await engine()).insert(input),
1231
- insertMany: async (input) => (await engine()).insertMany(input),
1232
- update: async (input) => (await engine()).update(input),
1233
- delete: async (input) => (await engine()).delete(input),
1234
- deleteWhere: async (input) => (await engine()).deleteWhere(input),
1235
- updateWhere: async (input) => (await engine()).updateWhere(input),
1236
- count: async (input) => (await engine()).count(input),
1237
- aggregate: async (input) => (await engine()).aggregate(input),
1238
- histogram: async (input) => (await engine()).histogram(input),
1239
- isEmpty: async (input) => (await engine()).isEmpty(input),
1240
- declareCollection: async (input) => (await engine()).declareCollection(input)
1241
- };
1242
- }
1243
- //#endregion
1244
1389
  //#region src/builtins/storage-orchestrator/location-secrets.ts
1245
1390
  /**
1246
1391
  * The config keys the ORCHESTRATOR itself owns, as opposed to the ones a
@@ -2485,7 +2630,6 @@ var StorageOrchestratorAddon = class extends require_dist.BaseAddon {
2485
2630
  pressureTimer = null;
2486
2631
  pressureSweepInFlight = false;
2487
2632
  migration = null;
2488
- cleanup = null;
2489
2633
  /**
2490
2634
  * Cached `providerId → nodeLocal` snapshot (SP1). Backs the orchestrator's
2491
2635
  * synchronous `NodeLocalResolver` — `getProviderInfo()` is async, so we
@@ -2791,7 +2935,6 @@ var StorageOrchestratorAddon = class extends require_dist.BaseAddon {
2791
2935
  now: () => Date.now(),
2792
2936
  newId: () => (0, node_crypto.randomUUID)()
2793
2937
  });
2794
- this.cleanup = cleanup;
2795
2938
  const migrationProvider = {
2796
2939
  plan: (input) => migration.plan(input),
2797
2940
  start: async (input) => ({ jobId: await migration.start(input) }),
@@ -2840,10 +2983,32 @@ var StorageOrchestratorAddon = class extends require_dist.BaseAddon {
2840
2983
  },
2841
2984
  {
2842
2985
  capability: require_dist.settingsStoreCapability,
2843
- provider: createDataStoreDispatch(getEngines)
2986
+ provider: createDataStoreDispatch(getEngines, () => this.hubSettingsForward())
2844
2987
  }
2845
2988
  ];
2846
2989
  }
2990
+ /**
2991
+ * The remote behind the data door on a node that owns no engine.
2992
+ *
2993
+ * `null` on a hub — the hub IS the store, and a hub that forwarded would
2994
+ * route its own door back into itself through `$hub-cap-fwd`. `null` too
2995
+ * when this process has no cluster broker (a forked worker, or a test
2996
+ * harness); the door then behaves exactly as it always did and refuses.
2997
+ *
2998
+ * Built once and memoised. It is only consulted when `getEngines()` is
2999
+ * empty, so on a hub — and on an agent rolled back to a local engine via
3000
+ * `CAMSTACK_AGENT_LOCAL_DATA_STORE` — the resolve never runs a second time.
3001
+ */
3002
+ hubForward = null;
3003
+ hubSettingsForward() {
3004
+ if (this.hubForward) return this.hubForward;
3005
+ this.hubForward = resolveHubSettingsForward({
3006
+ env: process.env,
3007
+ broker: this.ctx.kernel.cluster?.broker,
3008
+ logger: this.ctx.logger
3009
+ });
3010
+ return this.hubForward;
3011
+ }
2847
3012
  async onShutdown() {
2848
3013
  for (const dispose of this.seedSubscriptionDisposers) try {
2849
3014
  dispose();
@@ -2864,9 +3029,19 @@ var StorageOrchestratorAddon = class extends require_dist.BaseAddon {
2864
3029
  /**
2865
3030
  * Resolve the live engine from the capability registry and wrap it in a
2866
3031
  * `SqliteLocationStore`. Returns `null` when no engine has registered
2867
- * yet — the service falls back to in-memory only. Production boot order
2868
- * (`data-store-provider` precedes `storage` in `INFRA_CAPABILITIES`)
2869
- * makes this the rare path.
3032
+ * yet — the service falls back to in-memory only. On a HUB that is a
3033
+ * boot-order window: `data-store-provider` precedes `storage` in
3034
+ * `INFRA_CAPABILITIES`, so it is the rare path.
3035
+ *
3036
+ * On an AGENT it is permanent and intended, and the line says so. An agent
3037
+ * boots no engine (`resolveAgentInfraCapabilities`), and this store must NOT
3038
+ * be pointed at the hub-forwarding data door: `storage_locations` is keyed by
3039
+ * a bare location id (`recordings:default`, `eventMedia:default`, …) which
3040
+ * every node in the cluster shares, so an agent writing through the door
3041
+ * would land on the HUB's rows. The locations an agent needs are re-seeded
3042
+ * from its addon declarations at every boot; the operator's real, node-pinned
3043
+ * locations live in the hub's own table (`__nodeId` in the config blob) and
3044
+ * are dispatched to the agent's `storage-provider` from there.
2870
3045
  *
2871
3046
  * Reads the ENGINE, not the `settings-store` singleton: since D44 this
2872
3047
  * addon *is* that singleton, so asking the registry for it would hand
@@ -2878,7 +3053,8 @@ var StorageOrchestratorAddon = class extends require_dist.BaseAddon {
2878
3053
  if (!reg) return null;
2879
3054
  const engine = reg.getCollection(DATA_STORE_PROVIDER_CAP_NAME)[0];
2880
3055
  if (!engine) {
2881
- if (!quiet) this.ctx.logger.warn("storage-orchestrator: no data-store-provider engine yet — running in-memory until one registers");
3056
+ if (!quiet) if (nodeOwnsItsDataStore(process.env)) this.ctx.logger.warn("storage-orchestrator: no data-store-provider engine yet — running in-memory until one registers");
3057
+ else this.ctx.logger.info("storage-orchestrator: this node boots no data-store engine — storage locations are seeded from addon declarations at every boot, and node-pinned locations live on the hub");
2882
3058
  return null;
2883
3059
  }
2884
3060
  return new SqliteLocationStore(engine);