@camstack/system 1.2.141 → 1.2.143

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/trigger-engine.d.ts +7 -81
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.js +29 -109
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +26 -106
  20. package/dist/builtins/hub-forwarder/index.js +1 -1
  21. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  25. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  26. package/dist/builtins/local-network/local-network.addon.js +1 -1
  27. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  28. package/dist/builtins/loki-logging/index.js +1 -1
  29. package/dist/builtins/loki-logging/index.mjs +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  32. package/dist/builtins/platform-probe/index.js +1 -1
  33. package/dist/builtins/platform-probe/index.mjs +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  36. package/dist/builtins/snapshot/index.js +1 -1
  37. package/dist/builtins/snapshot/index.mjs +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  39. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  44. package/dist/builtins/system-config/system-config.addon.js +1 -1
  45. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  46. package/dist/builtins/winston-logging/index.js +1 -1
  47. package/dist/builtins/winston-logging/index.mjs +1 -1
  48. package/dist/{dist-BNhvXrQk.js → dist-CQs1b7uh.js} +838 -78
  49. package/dist/{dist-6mn3LQvB.mjs → dist-CVuVKA-O.mjs} +803 -79
  50. package/dist/index.d.ts +3 -0
  51. package/dist/index.js +127 -3
  52. package/dist/index.mjs +124 -4
  53. package/dist/logging/log-manager.d.ts +16 -0
  54. package/dist/logging/logging-gate.d.ts +32 -0
  55. package/dist/logging/logging-gate.singleton.d.ts +5 -0
  56. package/dist/{manifest-python-deps-DBQarwp9.js → manifest-python-deps-B_mCU6gz.js} +25 -2
  57. package/dist/{manifest-python-deps-D3zYlMEa.mjs → manifest-python-deps-BuhBXCg0.mjs} +25 -2
  58. package/dist/{retired-settings-keys-SYwVv_Ym.mjs → retired-settings-keys-DnMYSIEr.mjs} +1 -1
  59. package/dist/{retired-settings-keys-DVgdWTd7.js → retired-settings-keys-Dvei9nhG.js} +1 -1
  60. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -57,6 +57,9 @@ export type { ILogDestination } from './logging/log-manager.js';
57
57
  export { LogManager } from './logging/log-manager.js';
58
58
  export type { LogEntry, LogFilter, LogLevel } from './logging/log-ring-buffer.js';
59
59
  export { LogRingBuffer } from './logging/log-ring-buffer.js';
60
+ export type { LevelBearing } from './logging/logging-gate.js';
61
+ export { DEFAULT_LOG_LEVEL, LoggingGate } from './logging/logging-gate.js';
62
+ export { __resetLoggingGateForTests, getLoggingGate, } from './logging/logging-gate.singleton.js';
60
63
  export type { IScopedLogger } from './logging/scoped-logger.js';
61
64
  export { ScopedLogger } from './logging/scoped-logger.js';
62
65
  export type { ClientNetworkStats, DeviceNetworkStats, INetworkQualityTracker, StreamNetworkStats, } from './network/network-quality.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_dist = require("./dist-BNhvXrQk.js");
3
+ const require_dist = require("./dist-CQs1b7uh.js");
4
4
  const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
5
5
  require("./builtins/alerts/index.js");
6
6
  const require_formatter = require("./formatter-DqAKDlvN.js");
@@ -26,7 +26,7 @@ require("./builtins/system-config/index.js");
26
26
  const require_builtins_winston_logging_index = require("./builtins/winston-logging/index.js");
27
27
  const require_file_data_plane = require("./file-data-plane-DO8KbxCe.js");
28
28
  const require_tls$1 = require("./tls-u8QCJCFE.js");
29
- const require_manifest_python_deps = require("./manifest-python-deps-DBQarwp9.js");
29
+ const require_manifest_python_deps = require("./manifest-python-deps-B_mCU6gz.js");
30
30
  const require_resource_monitor = require("./resource-monitor-CdnzxBLP.js");
31
31
  const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
32
32
  let zod = require("zod");
@@ -696,6 +696,109 @@ var LogRingBuffer = class {
696
696
  }
697
697
  };
698
698
  //#endregion
699
+ //#region src/logging/logging-gate.ts
700
+ /**
701
+ * The in-memory MIRROR of the logging settings document, read on the log path.
702
+ *
703
+ * ## Why a mirror and not the settings store
704
+ *
705
+ * The document (`system.getLoggingSettings` / `setLoggingSettings`) is the
706
+ * durable AUTHORITY. It is a row in `system-settings`, reachable only through
707
+ * an async cap call — and this gate is consulted once per emitted log line. A
708
+ * hot path cannot await a store, so the store is mirrored here and the mirror
709
+ * is refreshed OFF the hot path, on a write and once at boot.
710
+ *
711
+ * ## Cost when nothing is set
712
+ *
713
+ * {@link LoggingGate.restrictsDestinations} is a plain boolean FIELD, and it is
714
+ * the FIRST statement of {@link LoggingGate.allowsDestination} — before the
715
+ * entry is touched at all. A default hub sets no level, the field stays
716
+ * `false`, and every line costs one boolean read and a return: no rank lookup,
717
+ * no property access, no allocation. `logging-gate.spec.ts` pins this with an
718
+ * entry whose `level` getter THROWS, so moving the guard below a field access
719
+ * fails the spec instead of taxing every line the hub emits.
720
+ *
721
+ * ## A failed read changes nothing (D49)
722
+ *
723
+ * Nothing here reads the store. The only way the mirror moves is an explicit
724
+ * {@link LoggingGate.setDestinationFloor}, and the service that owns the
725
+ * document calls it ONLY with a value it actually read. A store read that
726
+ * fails leaves the mirror exactly as it was: it neither raises a level nor
727
+ * lowers one, and a diagnostic armed before the failure stays armed.
728
+ *
729
+ * ## What the floor governs, and what it deliberately does not
730
+ *
731
+ * The floor gates the fan-out to DESTINATIONS (console, rotated files, Loki,
732
+ * the hub forwarder). It does NOT gate the in-memory ring buffer: the ring
733
+ * keeps everything it has room for and prunes by level only under pressure,
734
+ * which the plan
735
+ * (`docs/design/plans/2026-08-26-logging-per-componente.md`) settles as the
736
+ * better policy — an admission level for the ring would make the log viewer go
737
+ * blind exactly when an operator raises the level to quieten a disk.
738
+ *
739
+ * A destination's own level still applies on top. The two compose as
740
+ * "most restrictive wins", which is a hierarchy and not a second authority:
741
+ * the floor is node-wide, the destination level is that destination's own, and
742
+ * neither can be read as overriding the other.
743
+ */
744
+ /** What a hub emits when nothing is configured anywhere. */
745
+ var DEFAULT_LOG_LEVEL = "debug";
746
+ var LoggingGate = class {
747
+ /** The ONLY field an unrestricted log path reads. */
748
+ restricted = false;
749
+ floorRank = require_dist.LOG_LEVEL_RANK[DEFAULT_LOG_LEVEL];
750
+ floor = DEFAULT_LOG_LEVEL;
751
+ /** The effective node-wide level currently mirrored. */
752
+ get destinationFloor() {
753
+ return this.floor;
754
+ }
755
+ /** True only when the mirrored level is stricter than the default. */
756
+ get restrictsDestinations() {
757
+ return this.restricted;
758
+ }
759
+ /**
760
+ * May this entry be fanned out to the log destinations?
761
+ *
762
+ * The boolean field is read FIRST. Do not move it below anything.
763
+ */
764
+ allowsDestination(entry) {
765
+ if (!this.restricted) return true;
766
+ return require_dist.LOG_LEVEL_RANK[entry.level] >= this.floorRank;
767
+ }
768
+ /**
769
+ * Refresh the mirror. Called off the hot path — on an operator write and
770
+ * once at boot — and never with a value that was not actually read.
771
+ */
772
+ setDestinationFloor(level) {
773
+ this.floor = level;
774
+ this.floorRank = require_dist.LOG_LEVEL_RANK[level];
775
+ this.restricted = this.floorRank > require_dist.LOG_LEVEL_RANK[DEFAULT_LOG_LEVEL];
776
+ }
777
+ };
778
+ //#endregion
779
+ //#region src/logging/logging-gate.singleton.ts
780
+ /**
781
+ * Process-wide holder for the {@link LoggingGate}.
782
+ *
783
+ * Two call sites that never meet need the SAME instance: `LogManager` reads it
784
+ * on every emitted line, and the `system` cap provider — which `trpc.router.ts`
785
+ * rebuilds PER REQUEST — refreshes it whenever the document is written. A gate
786
+ * built inside that per-request factory would be refreshed and collected,
787
+ * which is the shape of a knob that never does anything.
788
+ *
789
+ * Same idiom as `http-request-census.singleton.ts`.
790
+ */
791
+ var instance = null;
792
+ /** The process-wide logging gate. Created unrestricted on first use. */
793
+ function getLoggingGate() {
794
+ instance ??= new LoggingGate();
795
+ return instance;
796
+ }
797
+ /** Test-only: drop the instance so a spec starts from an unrestricted gate. */
798
+ function __resetLoggingGateForTests() {
799
+ instance = null;
800
+ }
801
+ //#endregion
699
802
  //#region src/logging/partitioned-log-buffer.ts
700
803
  /** Bucket key for entries that carry no resolvable `addonId` tag. */
701
804
  var NO_ADDON_KEY = "";
@@ -1036,6 +1139,16 @@ var LogManager = class {
1036
1139
  */
1037
1140
  bootstrapConsole = null;
1038
1141
  /**
1142
+ * The node-wide level floor, mirrored from the logging settings document.
1143
+ *
1144
+ * Consulted on EVERY emitted line, so it is a boolean field read and nothing
1145
+ * else while nothing is configured — see {@link LoggingGate}. It gates the
1146
+ * DESTINATIONS only: the ring buffer keeps the line either way, because a
1147
+ * floor that also blinded the log viewer would make raising the level to
1148
+ * quieten a disk cost the operator the one window they read incidents from.
1149
+ */
1150
+ gate = getLoggingGate();
1151
+ /**
1039
1152
  * `perAddonCapacity` bounds EACH addon's bucket, not the total — a chatty addon
1040
1153
  * evicts only its own lines, so quiet addons keep their sparse history.
1041
1154
  *
@@ -1070,7 +1183,7 @@ var LogManager = class {
1070
1183
  return new ScopedLogger(scope, (entry) => {
1071
1184
  const enriched = this.enrichDeviceName(entry);
1072
1185
  if (this.isDestinationEnabled?.("log-buffer") !== false) this.ringBuffer.push(enriched);
1073
- for (const entry of this.destinations) {
1186
+ if (this.gate.allowsDestination(enriched)) for (const entry of this.destinations) {
1074
1187
  if (entry.addonId !== void 0 && this.isDestinationEnabled?.(entry.addonId) === false) continue;
1075
1188
  entry.dest.write(enriched);
1076
1189
  }
@@ -1135,6 +1248,13 @@ var LogManager = class {
1135
1248
  reconfigureBuffer(options) {
1136
1249
  this.ringBuffer.reconfigure(options);
1137
1250
  }
1251
+ /**
1252
+ * Swap the node-wide level gate. Production uses the process singleton;
1253
+ * a spec passes its own so two suites cannot arm each other's floor.
1254
+ */
1255
+ setLoggingGate(gate) {
1256
+ this.gate = gate;
1257
+ }
1138
1258
  /** Set the per-destination enablement check (see {@link isDestinationEnabled}). */
1139
1259
  setDestinationEnabledCheck(check) {
1140
1260
  this.isDestinationEnabled = check ?? void 0;
@@ -95154,6 +95274,7 @@ exports.CoreBlocksAddon = require_builtins_core_blocks_core_blocks_addon.CoreBlo
95154
95274
  exports.CustomActionRegistry = require_custom_action_registry.CustomActionRegistry;
95155
95275
  exports.DEFAULT_DATA_PATH = DEFAULT_DATA_PATH;
95156
95276
  exports.DEFAULT_LAN_HTTP_PORT = require_tls$1.DEFAULT_LAN_HTTP_PORT;
95277
+ exports.DEFAULT_LOG_LEVEL = DEFAULT_LOG_LEVEL;
95157
95278
  exports.DEVICE_SETTINGS_CONTRIBUTION_METHODS = require_dist.DEVICE_SETTINGS_CONTRIBUTION_METHODS;
95158
95279
  exports.DEVICE_STATUS_METHOD = require_dist.DEVICE_STATUS_METHOD;
95159
95280
  exports.DataPlaneRegistry = DataPlaneRegistry;
@@ -95193,6 +95314,7 @@ exports.LocalChildClient = require_manifest_python_deps.LocalChildClient;
95193
95314
  exports.LocalChildRegistry = require_manifest_python_deps.LocalChildRegistry;
95194
95315
  exports.LogManager = LogManager;
95195
95316
  exports.LogRingBuffer = LogRingBuffer;
95317
+ exports.LoggingGate = LoggingGate;
95196
95318
  exports.LokiDestination = require_builtins_loki_logging_index.LokiDestination$1;
95197
95319
  exports.LokiLoggingAddon = require_builtins_loki_logging_index.LokiLoggingAddon$1;
95198
95320
  exports.MAX_LEAF_VALIDITY_DAYS = require_tls$1.MAX_LEAF_VALIDITY_DAYS;
@@ -95243,6 +95365,7 @@ exports.UserManager = require_builtins_local_auth_local_auth_addon.UserManager;
95243
95365
  exports.WinstonDestination = require_builtins_winston_logging_index.WinstonDestination$1;
95244
95366
  exports.WinstonLoggingAddon = require_builtins_winston_logging_index.WinstonLoggingAddon$1;
95245
95367
  exports.__resetCapUsageRegistryForTests = require_manifest_python_deps.__resetCapUsageRegistryForTests;
95368
+ exports.__resetLoggingGateForTests = __resetLoggingGateForTests;
95246
95369
  exports.adaptBrokerToCluster = require_manifest_python_deps.adaptBrokerToCluster;
95247
95370
  exports.addonSettingsCapability = require_dist.addonSettingsCapability;
95248
95371
  exports.allFamiliesListenHost = require_tls$1.allFamiliesListenHost;
@@ -95364,6 +95487,7 @@ Object.defineProperty(exports, "getFfmpegDownloadUrl", {
95364
95487
  return _camstack_types_node.getFfmpegDownloadUrl;
95365
95488
  }
95366
95489
  });
95490
+ exports.getLoggingGate = getLoggingGate;
95367
95491
  exports.getModelFilePath = require_file_data_plane.getModelFilePath;
95368
95492
  exports.getMoleculerEventStats = require_manifest_python_deps.getMoleculerEventStats;
95369
95493
  exports.getOrInitReadinessRegistry = require_manifest_python_deps.getOrInitReadinessRegistry;
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { At as asJsonObject$1, B as extractNestedAddonId, Bt as parseJsonObject, Ct as DEVICE_STATUS_METHOD, Dt as ReadinessRegistry, H as isArrayOutputSchema, Ht as readinessKey, It as expandCapMethods, J as lifecycleJobSchema, K as isVoidInput, Kt as EventCategory$1, Mt as asString$1, Nt as createEvent, Ot as ReadinessTimeoutError, Pt as emitDownForOwnedCaps, Q as looseSchema, St as DEVICE_SETTINGS_CONTRIBUTION_METHODS, U as isCollectionArrayMethod, Ut as resolveCapMount, Vt as parseJsonUnknown$1, W as isObjectInput, Wt as scopeKey, Z as logLevelAtMost, at as resolveMethodAuth, ct as scopesAllowDeviceCap, d as METHOD_ACCESS_MAP, f as RUNTIME_DEFAULTS, it as procedureAuthKey, jt as asNumber, q as kebabToCamel, st as scopesAllowAddon, t as ALL_CAPABILITY_DEFINITIONS, tt as objectInputDeclaresAddonId, xt as DATAPLANE_SECRET_HEADER$1, y as addonSettingsCapability, yt as errMsg$1 } from "./dist-6mn3LQvB.mjs";
2
+ import { $ as kebabToCamel, Bt as emitDownForOwnedCaps, C as addonSettingsCapability, Dt as DATAPLANE_SECRET_HEADER$1, G as extractNestedAddonId, Ht as expandCapMethods, It as asJsonObject$1, J as isCollectionArrayMethod, Jt as readinessKey, Kt as parseJsonObject, Lt as asNumber, Nt as ReadinessRegistry, Ot as DEVICE_SETTINGS_CONTRIBUTION_METHODS, Pt as ReadinessTimeoutError, Q as isVoidInput, Qt as EventCategory$1, Rt as asString$1, Tt as errMsg$1, Xt as scopeKey, Y as isObjectInput, Yt as resolveCapMount, dt as resolveMethodAuth, et as lifecycleJobSchema, f as LOG_LEVEL_RANK, it as looseSchema, kt as DEVICE_STATUS_METHOD, m as RUNTIME_DEFAULTS, mt as scopesAllowDeviceCap, p as METHOD_ACCESS_MAP, pt as scopesAllowAddon, q as isArrayOutputSchema, qt as parseJsonUnknown$1, rt as logLevelAtMost, st as objectInputDeclaresAddonId, t as ALL_CAPABILITY_DEFINITIONS, ut as procedureAuthKey, zt as createEvent } from "./dist-CVuVKA-O.mjs";
3
3
  import { AlertCenterAddon } from "./builtins/alerts/alerts.addon.mjs";
4
4
  import "./builtins/alerts/index.mjs";
5
5
  import { t as formatLogLine } from "./formatter-B7qW8bPJ.mjs";
@@ -25,7 +25,7 @@ import "./builtins/system-config/index.mjs";
25
25
  import { WinstonDestination, WinstonLoggingAddon } from "./builtins/winston-logging/index.mjs";
26
26
  import { a as parseTokenizedUrl, c as collectModelFiles, d as downloadModel, f as ensureModel, h as isModelDownloaded, i as parseRangeHeader, l as deleteModelFromDisk, m as getModelFilePath, n as contentTypeFor, o as resolveFilePath, p as fetchJson, r as createAuthenticatedFileServer, s as ModelDownloadService, t as createFileDataPlaneHandler, u as downloadFile } from "./file-data-plane-BhKdxJgf.mjs";
27
27
  import { C as registerLanHttpHandler, S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, b as bindPendingLanHttp, c as loadTlsCert, d as collectCertIdentity, f as CA_VALIDITY_DAYS, g as evaluateExistingCert, h as SERVER_AUTH_OID, i as writeExtraSans, l as reissueTlsLeaf, m as MAX_LEAF_VALIDITY_DAYS, n as readTlsAccessStatus, o as validateUploadedTls, p as LEAF_RENEWAL_WINDOW_DAYS, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, u as CA_COMMON_NAME, v as allFamiliesListenHost, x as closeLanHttp, y as applyLanHttp } from "./tls-CQhPGSJm.mjs";
28
- import { $ as buildUdsNativeCapProxy, A as createParentUnownedCallHandler, At as createV8Reclaimer, B as AGENT_CAP_FWD_SERVICE, C as brokerTransportLink, Ct as createAddonDataPlaneFacility, D as HUB_CAP_FWD_ACTION, Dt as HEAP_WATCH_WARN_RATIO, E as localProviderLink, Et as HEAP_WATCH_INTERVAL_MS, F as createUdsLoggerWithControl, G as createLocalTransport, H as CapRouteError, I as LocalChildClient, J as SocketChannel, K as UdsLocalTransportClient, L as LocalChildRegistry, M as createUdsEventBus, Mt as startHeapWatch, N as udsChildLogToWorkerEntry, Nt as startRunnerHeapWatch, O as HUB_CAP_FWD_SERVICE, Ot as RUNNER_HEAP_WATCH_INTERVAL_MS, P as createUdsLogger, Pt as strandedMb, Q as buildNativeCapProxy, R as UDS_NO_ROUTE_PREFIX, S as brokerCallForCap, St as runNpm, T as ipcParentLink, Tt as HEAP_RECLAIM_TRIGGER_MB, U as classifyCapRoute, V as CapRouteResolver, W as callWithServiceDiscovery, X as FrameDecoder, Y as localEndpointPath, Z as encodeFrame, _ as createKernelHwAccel, _t as CapabilityUnavailableError, a as getWorkerDeviceRegistry, at as createAddonService, b as __resetCapUsageRegistryForTests, bt as resolveAddonClass, c as setHubConnected, ct as capActionName, d as getBrokerEventBus, dt as capServiceName, et as createBrokerDeviceManagerApi, f as getMoleculerEventStats, ft as parseCapAction, g as AddonDepsManager, gt as CapabilityHandle, h as subscribePassthrough, ht as DeviceRegistry, i as createUdsAddonContext, j as createUdsEventBridge, jt as shouldReclaim, k as createHubCapForwardService, kt as buildHeapSample, l as EVENT_TOPIC_PREFIX, lt as capActionSuffix, m as setNodeEventInterest, mt as serializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as validateProviderRegistrations, p as registerEventBusService, pt as deserializeTypedArrays, q as UdsLocalTransportServer, r as createAddonContext, rt as mountNativeCapService, s as getOrInitReadinessRegistryForClient, st as NATIVE_PROVIDER_SERVICE_INFIX, t as installManifestPythonDeps, u as clusterEventTopic, ut as capBareAction, v as resolveHwAccel, vt as copyBundledNativeModules, w as buildLinkChain, wt as HEAP_RECLAIM_MIN_INTERVAL_MS, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as installManifestNativeDeps, z as AGENT_CAP_FWD_ACTION } from "./manifest-python-deps-D3zYlMEa.mjs";
28
+ import { $ as buildUdsNativeCapProxy, A as createParentUnownedCallHandler, At as createV8Reclaimer, B as AGENT_CAP_FWD_SERVICE, C as brokerTransportLink, Ct as createAddonDataPlaneFacility, D as HUB_CAP_FWD_ACTION, Dt as HEAP_WATCH_WARN_RATIO, E as localProviderLink, Et as HEAP_WATCH_INTERVAL_MS, F as createUdsLoggerWithControl, G as createLocalTransport, H as CapRouteError, I as LocalChildClient, J as SocketChannel, K as UdsLocalTransportClient, L as LocalChildRegistry, M as createUdsEventBus, Mt as startHeapWatch, N as udsChildLogToWorkerEntry, Nt as startRunnerHeapWatch, O as HUB_CAP_FWD_SERVICE, Ot as RUNNER_HEAP_WATCH_INTERVAL_MS, P as createUdsLogger, Pt as strandedMb, Q as buildNativeCapProxy, R as UDS_NO_ROUTE_PREFIX, S as brokerCallForCap, St as runNpm, T as ipcParentLink, Tt as HEAP_RECLAIM_TRIGGER_MB, U as classifyCapRoute, V as CapRouteResolver, W as callWithServiceDiscovery, X as FrameDecoder, Y as localEndpointPath, Z as encodeFrame, _ as createKernelHwAccel, _t as CapabilityUnavailableError, a as getWorkerDeviceRegistry, at as createAddonService, b as __resetCapUsageRegistryForTests, bt as resolveAddonClass, c as setHubConnected, ct as capActionName, d as getBrokerEventBus, dt as capServiceName, et as createBrokerDeviceManagerApi, f as getMoleculerEventStats, ft as parseCapAction, g as AddonDepsManager, gt as CapabilityHandle, h as subscribePassthrough, ht as DeviceRegistry, i as createUdsAddonContext, j as createUdsEventBridge, jt as shouldReclaim, k as createHubCapForwardService, kt as buildHeapSample, l as EVENT_TOPIC_PREFIX, lt as capActionSuffix, m as setNodeEventInterest, mt as serializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as validateProviderRegistrations, p as registerEventBusService, pt as deserializeTypedArrays, q as UdsLocalTransportServer, r as createAddonContext, rt as mountNativeCapService, s as getOrInitReadinessRegistryForClient, st as NATIVE_PROVIDER_SERVICE_INFIX, t as installManifestPythonDeps, u as clusterEventTopic, ut as capBareAction, v as resolveHwAccel, vt as copyBundledNativeModules, w as buildLinkChain, wt as HEAP_RECLAIM_MIN_INTERVAL_MS, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as installManifestNativeDeps, z as AGENT_CAP_FWD_ACTION } from "./manifest-python-deps-BuhBXCg0.mjs";
29
29
  import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BWmQ5i-o.mjs";
30
30
  import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
31
31
  import { z } from "zod";
@@ -689,6 +689,109 @@ var LogRingBuffer = class {
689
689
  }
690
690
  };
691
691
  //#endregion
692
+ //#region src/logging/logging-gate.ts
693
+ /**
694
+ * The in-memory MIRROR of the logging settings document, read on the log path.
695
+ *
696
+ * ## Why a mirror and not the settings store
697
+ *
698
+ * The document (`system.getLoggingSettings` / `setLoggingSettings`) is the
699
+ * durable AUTHORITY. It is a row in `system-settings`, reachable only through
700
+ * an async cap call — and this gate is consulted once per emitted log line. A
701
+ * hot path cannot await a store, so the store is mirrored here and the mirror
702
+ * is refreshed OFF the hot path, on a write and once at boot.
703
+ *
704
+ * ## Cost when nothing is set
705
+ *
706
+ * {@link LoggingGate.restrictsDestinations} is a plain boolean FIELD, and it is
707
+ * the FIRST statement of {@link LoggingGate.allowsDestination} — before the
708
+ * entry is touched at all. A default hub sets no level, the field stays
709
+ * `false`, and every line costs one boolean read and a return: no rank lookup,
710
+ * no property access, no allocation. `logging-gate.spec.ts` pins this with an
711
+ * entry whose `level` getter THROWS, so moving the guard below a field access
712
+ * fails the spec instead of taxing every line the hub emits.
713
+ *
714
+ * ## A failed read changes nothing (D49)
715
+ *
716
+ * Nothing here reads the store. The only way the mirror moves is an explicit
717
+ * {@link LoggingGate.setDestinationFloor}, and the service that owns the
718
+ * document calls it ONLY with a value it actually read. A store read that
719
+ * fails leaves the mirror exactly as it was: it neither raises a level nor
720
+ * lowers one, and a diagnostic armed before the failure stays armed.
721
+ *
722
+ * ## What the floor governs, and what it deliberately does not
723
+ *
724
+ * The floor gates the fan-out to DESTINATIONS (console, rotated files, Loki,
725
+ * the hub forwarder). It does NOT gate the in-memory ring buffer: the ring
726
+ * keeps everything it has room for and prunes by level only under pressure,
727
+ * which the plan
728
+ * (`docs/design/plans/2026-08-26-logging-per-componente.md`) settles as the
729
+ * better policy — an admission level for the ring would make the log viewer go
730
+ * blind exactly when an operator raises the level to quieten a disk.
731
+ *
732
+ * A destination's own level still applies on top. The two compose as
733
+ * "most restrictive wins", which is a hierarchy and not a second authority:
734
+ * the floor is node-wide, the destination level is that destination's own, and
735
+ * neither can be read as overriding the other.
736
+ */
737
+ /** What a hub emits when nothing is configured anywhere. */
738
+ var DEFAULT_LOG_LEVEL = "debug";
739
+ var LoggingGate = class {
740
+ /** The ONLY field an unrestricted log path reads. */
741
+ restricted = false;
742
+ floorRank = LOG_LEVEL_RANK[DEFAULT_LOG_LEVEL];
743
+ floor = DEFAULT_LOG_LEVEL;
744
+ /** The effective node-wide level currently mirrored. */
745
+ get destinationFloor() {
746
+ return this.floor;
747
+ }
748
+ /** True only when the mirrored level is stricter than the default. */
749
+ get restrictsDestinations() {
750
+ return this.restricted;
751
+ }
752
+ /**
753
+ * May this entry be fanned out to the log destinations?
754
+ *
755
+ * The boolean field is read FIRST. Do not move it below anything.
756
+ */
757
+ allowsDestination(entry) {
758
+ if (!this.restricted) return true;
759
+ return LOG_LEVEL_RANK[entry.level] >= this.floorRank;
760
+ }
761
+ /**
762
+ * Refresh the mirror. Called off the hot path — on an operator write and
763
+ * once at boot — and never with a value that was not actually read.
764
+ */
765
+ setDestinationFloor(level) {
766
+ this.floor = level;
767
+ this.floorRank = LOG_LEVEL_RANK[level];
768
+ this.restricted = this.floorRank > LOG_LEVEL_RANK[DEFAULT_LOG_LEVEL];
769
+ }
770
+ };
771
+ //#endregion
772
+ //#region src/logging/logging-gate.singleton.ts
773
+ /**
774
+ * Process-wide holder for the {@link LoggingGate}.
775
+ *
776
+ * Two call sites that never meet need the SAME instance: `LogManager` reads it
777
+ * on every emitted line, and the `system` cap provider — which `trpc.router.ts`
778
+ * rebuilds PER REQUEST — refreshes it whenever the document is written. A gate
779
+ * built inside that per-request factory would be refreshed and collected,
780
+ * which is the shape of a knob that never does anything.
781
+ *
782
+ * Same idiom as `http-request-census.singleton.ts`.
783
+ */
784
+ var instance = null;
785
+ /** The process-wide logging gate. Created unrestricted on first use. */
786
+ function getLoggingGate() {
787
+ instance ??= new LoggingGate();
788
+ return instance;
789
+ }
790
+ /** Test-only: drop the instance so a spec starts from an unrestricted gate. */
791
+ function __resetLoggingGateForTests() {
792
+ instance = null;
793
+ }
794
+ //#endregion
692
795
  //#region src/logging/partitioned-log-buffer.ts
693
796
  /** Bucket key for entries that carry no resolvable `addonId` tag. */
694
797
  var NO_ADDON_KEY = "";
@@ -1029,6 +1132,16 @@ var LogManager = class {
1029
1132
  */
1030
1133
  bootstrapConsole = null;
1031
1134
  /**
1135
+ * The node-wide level floor, mirrored from the logging settings document.
1136
+ *
1137
+ * Consulted on EVERY emitted line, so it is a boolean field read and nothing
1138
+ * else while nothing is configured — see {@link LoggingGate}. It gates the
1139
+ * DESTINATIONS only: the ring buffer keeps the line either way, because a
1140
+ * floor that also blinded the log viewer would make raising the level to
1141
+ * quieten a disk cost the operator the one window they read incidents from.
1142
+ */
1143
+ gate = getLoggingGate();
1144
+ /**
1032
1145
  * `perAddonCapacity` bounds EACH addon's bucket, not the total — a chatty addon
1033
1146
  * evicts only its own lines, so quiet addons keep their sparse history.
1034
1147
  *
@@ -1063,7 +1176,7 @@ var LogManager = class {
1063
1176
  return new ScopedLogger(scope, (entry) => {
1064
1177
  const enriched = this.enrichDeviceName(entry);
1065
1178
  if (this.isDestinationEnabled?.("log-buffer") !== false) this.ringBuffer.push(enriched);
1066
- for (const entry of this.destinations) {
1179
+ if (this.gate.allowsDestination(enriched)) for (const entry of this.destinations) {
1067
1180
  if (entry.addonId !== void 0 && this.isDestinationEnabled?.(entry.addonId) === false) continue;
1068
1181
  entry.dest.write(enriched);
1069
1182
  }
@@ -1128,6 +1241,13 @@ var LogManager = class {
1128
1241
  reconfigureBuffer(options) {
1129
1242
  this.ringBuffer.reconfigure(options);
1130
1243
  }
1244
+ /**
1245
+ * Swap the node-wide level gate. Production uses the process singleton;
1246
+ * a spec passes its own so two suites cannot arm each other's floor.
1247
+ */
1248
+ setLoggingGate(gate) {
1249
+ this.gate = gate;
1250
+ }
1131
1251
  /** Set the per-destination enablement check (see {@link isDestinationEnabled}). */
1132
1252
  setDestinationEnabledCheck(check) {
1133
1253
  this.isDestinationEnabled = check ?? void 0;
@@ -95113,4 +95233,4 @@ var LifecycleJobEngine = class {
95113
95233
  }
95114
95234
  };
95115
95235
  //#endregion
95116
- export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, ALL_CAPABILITY_DEFINITIONS, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CA_COMMON_NAME, CA_VALIDITY_DAYS, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CoreBlocksAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DEFAULT_LAN_HTTP_PORT, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HEAP_RECLAIM_MIN_INTERVAL_MS, HEAP_RECLAIM_TRIGGER_MB, HEAP_WATCH_INTERVAL_MS, HEAP_WATCH_WARN_RATIO, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LEAF_RENEWAL_WINDOW_DAYS, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LokiDestination, LokiLoggingAddon, MAX_LEAF_VALIDITY_DAYS, METHOD_ACCESS_MAP, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, QUARANTINE_DIRNAME, RESTART_MARKER_FILE, RUNNER_HEAP_WATCH_INTERVAL_MS, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, SERVER_AUTH_OID, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, addonSettingsCapability, allFamiliesListenHost, applyLanHttp, bindPendingLanHttp, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, canServeDataPlane, capActionName, capActionSuffix, capBareAction, capServiceName, classifyAddonDir, classifyCapRoute, clearPendingRestart, closeLanHttp, clusterEventTopic, clusterSecretMatches, collectCertIdentity, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createDoorSettingsView, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, createV8Reclaimer, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, evaluateExistingCert, expandCapMethods, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcParentLink, isAddonDeploySource, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, isolatedBuiltinPhase, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, partitionIsolatedBuiltinIds, proxyToUpstream, quarantineAddonResidue, readExtraSans, readLanHttpState, readPendingRestart, readTlsAccessStatus, readTlsMode, readinessKey, registerEventBusService, registerLanHttpHandler, reissueTlsLeaf, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runHubAddonBoot, runNpm, scheduleSelfRestart, scopeKey, scopesAllowAddon, scopesAllowDeviceCap, selectAddonResidue, serializeTypedArrays, setHubConnected, setNodeEventInterest, shouldReclaim, startHeapWatch, startRunnerHeapWatch, strandedMb, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, validateUploadedTls, waitUntilReady, writeExtraSans, writePendingRestart, writeTlsMode };
95236
+ export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, ALL_CAPABILITY_DEFINITIONS, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CA_COMMON_NAME, CA_VALIDITY_DAYS, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CoreBlocksAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DEFAULT_LAN_HTTP_PORT, DEFAULT_LOG_LEVEL, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HEAP_RECLAIM_MIN_INTERVAL_MS, HEAP_RECLAIM_TRIGGER_MB, HEAP_WATCH_INTERVAL_MS, HEAP_WATCH_WARN_RATIO, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LEAF_RENEWAL_WINDOW_DAYS, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LoggingGate, LokiDestination, LokiLoggingAddon, MAX_LEAF_VALIDITY_DAYS, METHOD_ACCESS_MAP, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, QUARANTINE_DIRNAME, RESTART_MARKER_FILE, RUNNER_HEAP_WATCH_INTERVAL_MS, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, SERVER_AUTH_OID, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, __resetLoggingGateForTests, adaptBrokerToCluster, addonSettingsCapability, allFamiliesListenHost, applyLanHttp, bindPendingLanHttp, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, canServeDataPlane, capActionName, capActionSuffix, capBareAction, capServiceName, classifyAddonDir, classifyCapRoute, clearPendingRestart, closeLanHttp, clusterEventTopic, clusterSecretMatches, collectCertIdentity, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createDoorSettingsView, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, createV8Reclaimer, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, evaluateExistingCert, expandCapMethods, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getLoggingGate, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcParentLink, isAddonDeploySource, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, isolatedBuiltinPhase, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, partitionIsolatedBuiltinIds, proxyToUpstream, quarantineAddonResidue, readExtraSans, readLanHttpState, readPendingRestart, readTlsAccessStatus, readTlsMode, readinessKey, registerEventBusService, registerLanHttpHandler, reissueTlsLeaf, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runHubAddonBoot, runNpm, scheduleSelfRestart, scopeKey, scopesAllowAddon, scopesAllowDeviceCap, selectAddonResidue, serializeTypedArrays, setHubConnected, setNodeEventInterest, shouldReclaim, startHeapWatch, startRunnerHeapWatch, strandedMb, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, validateUploadedTls, waitUntilReady, writeExtraSans, writePendingRestart, writeTlsMode };
@@ -1,4 +1,5 @@
1
1
  import { LogEntry, LogFilter, LogLevel } from './log-ring-buffer.js';
2
+ import { LoggingGate } from './logging-gate.js';
2
3
  import { PartitionedLogBufferOptions } from './partitioned-log-buffer.js';
3
4
  import { IScopedLogger } from './scoped-logger.js';
4
5
  export interface ILogDestination {
@@ -50,6 +51,16 @@ export declare class LogManager {
50
51
  * replay suppressed — when {@link STDOUT_DESTINATION_ADDON_ID} registers.
51
52
  */
52
53
  private bootstrapConsole;
54
+ /**
55
+ * The node-wide level floor, mirrored from the logging settings document.
56
+ *
57
+ * Consulted on EVERY emitted line, so it is a boolean field read and nothing
58
+ * else while nothing is configured — see {@link LoggingGate}. It gates the
59
+ * DESTINATIONS only: the ring buffer keeps the line either way, because a
60
+ * floor that also blinded the log viewer would make raising the level to
61
+ * quieten a disk cost the operator the one window they read incidents from.
62
+ */
63
+ private gate;
53
64
  /**
54
65
  * `perAddonCapacity` bounds EACH addon's bucket, not the total — a chatty addon
55
66
  * evicts only its own lines, so quiet addons keep their sparse history.
@@ -94,6 +105,11 @@ export declare class LogManager {
94
105
  readonly maxTotalEntries?: number | null;
95
106
  readonly pruneLevel?: LogLevel;
96
107
  }): void;
108
+ /**
109
+ * Swap the node-wide level gate. Production uses the process singleton;
110
+ * a spec passes its own so two suites cannot arm each other's floor.
111
+ */
112
+ setLoggingGate(gate: LoggingGate): void;
97
113
  /** Set the per-destination enablement check (see {@link isDestinationEnabled}). */
98
114
  setDestinationEnabledCheck(check: ((addonId: string) => boolean) | null): void;
99
115
  /**
@@ -0,0 +1,32 @@
1
+ import { LogLevel } from '@camstack/types';
2
+ /** What a hub emits when nothing is configured anywhere. */
3
+ export declare const DEFAULT_LOG_LEVEL: LogLevel;
4
+ /**
5
+ * The only thing the gate needs off an entry — and it must not read it while
6
+ * the gate is unrestricted. Deliberately structural so the spec can hand in an
7
+ * object whose getter throws.
8
+ */
9
+ export interface LevelBearing {
10
+ readonly level: LogLevel;
11
+ }
12
+ export declare class LoggingGate {
13
+ /** The ONLY field an unrestricted log path reads. */
14
+ private restricted;
15
+ private floorRank;
16
+ private floor;
17
+ /** The effective node-wide level currently mirrored. */
18
+ get destinationFloor(): LogLevel;
19
+ /** True only when the mirrored level is stricter than the default. */
20
+ get restrictsDestinations(): boolean;
21
+ /**
22
+ * May this entry be fanned out to the log destinations?
23
+ *
24
+ * The boolean field is read FIRST. Do not move it below anything.
25
+ */
26
+ allowsDestination(entry: LevelBearing): boolean;
27
+ /**
28
+ * Refresh the mirror. Called off the hot path — on an operator write and
29
+ * once at boot — and never with a value that was not actually read.
30
+ */
31
+ setDestinationFloor(level: LogLevel): void;
32
+ }
@@ -0,0 +1,5 @@
1
+ import { LoggingGate } from './logging-gate.js';
2
+ /** The process-wide logging gate. Created unrestricted on first use. */
3
+ export declare function getLoggingGate(): LoggingGate;
4
+ /** Test-only: drop the instance so a spec starts from an unrestricted gate. */
5
+ export declare function __resetLoggingGateForTests(): void;
@@ -7893,11 +7893,34 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
7893
7893
  };
7894
7894
  /**
7895
7895
  * Soft read for CONSUMERS: a failure reads as empty so a boot path degrades
7896
- * to defaults instead of throwing. Safe here precisely because the result is
7897
- * never written back {@link readBlobResult} is what the write paths use.
7896
+ * to defaults instead of throwing. The degradation is never SILENT a
7897
+ * consumer that got `{}` because the store did not answer looks identical to
7898
+ * one whose store is genuinely empty, and downstream that difference decides
7899
+ * whether an addon runs on the operator's configuration or on its
7900
+ * constructor defaults.
7901
+ *
7902
+ * "Safe because the result is never written back" is what this docblock used
7903
+ * to claim, and it was a leftover that read as verification.
7904
+ * `BaseAddon.resolveConfig` reads THIS function through
7905
+ * `ctx.settings.readAddonStore()`; the defaults it falls back to then become
7906
+ * the values the addon's next `updateGlobalSettings` patch carries into the
7907
+ * store. That is how the Home Assistant export lost its membership on
7908
+ * 2026-08-24 — see `brokerRefreshPatch` in the ha-export addon for the other
7909
+ * half of that story. The line below is the only warning that this window is
7910
+ * open at all.
7898
7911
  */
7899
7912
  const readBlob = async (coll, key, namespace) => {
7900
7913
  const r = await readBlobResult(coll, key, namespace);
7914
+ if (r.kind === "failed") {
7915
+ scopedLogger.error(`settings read FAILED (${coll}/${key}) — answering empty, this caller now sees DEFAULTS`, { meta: {
7916
+ addonId,
7917
+ collection: coll,
7918
+ key,
7919
+ namespace,
7920
+ error: r.error
7921
+ } });
7922
+ return {};
7923
+ }
7901
7924
  return r.kind === "value" ? r.value : {};
7902
7925
  };
7903
7926
  /** Read-modify-write that REFUSES to write when the read failed. */
@@ -7889,11 +7889,34 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
7889
7889
  };
7890
7890
  /**
7891
7891
  * Soft read for CONSUMERS: a failure reads as empty so a boot path degrades
7892
- * to defaults instead of throwing. Safe here precisely because the result is
7893
- * never written back {@link readBlobResult} is what the write paths use.
7892
+ * to defaults instead of throwing. The degradation is never SILENT a
7893
+ * consumer that got `{}` because the store did not answer looks identical to
7894
+ * one whose store is genuinely empty, and downstream that difference decides
7895
+ * whether an addon runs on the operator's configuration or on its
7896
+ * constructor defaults.
7897
+ *
7898
+ * "Safe because the result is never written back" is what this docblock used
7899
+ * to claim, and it was a leftover that read as verification.
7900
+ * `BaseAddon.resolveConfig` reads THIS function through
7901
+ * `ctx.settings.readAddonStore()`; the defaults it falls back to then become
7902
+ * the values the addon's next `updateGlobalSettings` patch carries into the
7903
+ * store. That is how the Home Assistant export lost its membership on
7904
+ * 2026-08-24 — see `brokerRefreshPatch` in the ha-export addon for the other
7905
+ * half of that story. The line below is the only warning that this window is
7906
+ * open at all.
7894
7907
  */
7895
7908
  const readBlob = async (coll, key, namespace) => {
7896
7909
  const r = await readBlobResult(coll, key, namespace);
7910
+ if (r.kind === "failed") {
7911
+ scopedLogger.error(`settings read FAILED (${coll}/${key}) — answering empty, this caller now sees DEFAULTS`, { meta: {
7912
+ addonId,
7913
+ collection: coll,
7914
+ key,
7915
+ namespace,
7916
+ error: r.error
7917
+ } });
7918
+ return {};
7919
+ }
7897
7920
  return r.kind === "value" ? r.value : {};
7898
7921
  };
7899
7922
  /** Read-modify-write that REFUSES to write when the read failed. */
@@ -1,4 +1,4 @@
1
- import { At as asJsonObject } from "./dist-6mn3LQvB.mjs";
1
+ import { It as asJsonObject } from "./dist-CVuVKA-O.mjs";
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BNhvXrQk.js");
1
+ const require_dist = require("./dist-CQs1b7uh.js");
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.141",
3
+ "version": "1.2.143",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",