@camstack/system 1.2.438 → 1.2.440

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 (120) hide show
  1. package/dist/addon-runner.js +743 -79
  2. package/dist/addon-runner.mjs +743 -79
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/{agent-https-BZIwS4xF.js → agent-https-DlpTMDqA.js} +252 -22
  6. package/dist/{agent-https-BPfHrI4c.mjs → agent-https-U0d4uvn2.mjs} +211 -23
  7. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  8. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  9. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  10. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  11. package/dist/builtins/alerts/alerts.addon.js +1 -1
  12. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  13. package/dist/builtins/autotrack/index.js +1 -1
  14. package/dist/builtins/autotrack/index.mjs +1 -1
  15. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  16. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  17. package/dist/builtins/calendars/calendars.addon.js +1 -1
  18. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  19. package/dist/builtins/camera-grid/index.js +1 -1
  20. package/dist/builtins/camera-grid/index.mjs +1 -1
  21. package/dist/builtins/composer/composer.addon.js +2 -2
  22. package/dist/builtins/composer/composer.addon.mjs +2 -2
  23. package/dist/builtins/console-logging/index.js +1 -1
  24. package/dist/builtins/console-logging/index.mjs +1 -1
  25. package/dist/builtins/core-blocks/block-settings-store.d.ts +1 -1
  26. package/dist/builtins/core-blocks/core-blocks.addon.js +77 -11
  27. package/dist/builtins/core-blocks/core-blocks.addon.mjs +77 -12
  28. package/dist/builtins/core-blocks/index.mjs +1 -1
  29. package/dist/builtins/device-manager/device-manager.addon.js +197 -60
  30. package/dist/builtins/device-manager/device-manager.addon.mjs +196 -61
  31. package/dist/builtins/device-manager/device-removal-hooks.d.ts +0 -11
  32. package/dist/builtins/device-manager/device-tombstone-store.d.ts +12 -2
  33. package/dist/builtins/hub-forwarder/index.js +1 -1
  34. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  35. package/dist/builtins/keychain/index.js +29 -11
  36. package/dist/builtins/keychain/index.mjs +29 -11
  37. package/dist/builtins/keychain/keychain-service.d.ts +8 -0
  38. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  39. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  40. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  41. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  42. package/dist/builtins/local-network/local-network.addon.js +7 -2
  43. package/dist/builtins/local-network/local-network.addon.mjs +7 -2
  44. package/dist/builtins/loki-logging/index.js +1 -1
  45. package/dist/builtins/loki-logging/index.mjs +1 -1
  46. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  47. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  48. package/dist/builtins/platform-probe/index.js +1 -1
  49. package/dist/builtins/platform-probe/index.mjs +1 -1
  50. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  51. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  52. package/dist/builtins/snapshot/index.js +1 -1
  53. package/dist/builtins/snapshot/index.mjs +1 -1
  54. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  55. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  56. package/dist/builtins/sqlite-storage/index.js +1 -1
  57. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  58. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +38 -7
  59. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +69 -3
  60. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +69 -3
  61. package/dist/builtins/sqlite-storage/storage-admin-provider.d.ts +31 -0
  62. package/dist/builtins/storage-orchestrator/hub-settings-forward.d.ts +3 -3
  63. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +4 -4
  64. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +4 -4
  65. package/dist/builtins/system-config/system-config.addon.js +4 -4
  66. package/dist/builtins/system-config/system-config.addon.mjs +285 -285
  67. package/dist/builtins/winston-logging/index.js +1 -1
  68. package/dist/builtins/winston-logging/index.mjs +1 -1
  69. package/dist/{composition-sources-DpHGCjdL.mjs → composition-sources-CS0D7imL.mjs} +1 -1
  70. package/dist/{composition-sources-DI3Z83vG.js → composition-sources-cBG8fBTu.js} +1 -1
  71. package/dist/{constants-DQK705Sb.mjs → constants-CIzPjw7L.mjs} +1 -1
  72. package/dist/{constants-wkcn5Ij3.js → constants-CRI3QzT4.js} +1 -1
  73. package/dist/{device-ca-DvIZjXIl.mjs → device-ca-DLKXYseS.mjs} +2 -2
  74. package/dist/{device-ca-BSvkXqyI.js → device-ca-DNHjts4_.js} +2 -2
  75. package/dist/{dist-Dc8GKC5H.mjs → dist-9fcsrFU3.mjs} +271 -32
  76. package/dist/{dist-GgG-7amw.js → dist-BKhlcYz6.js} +288 -31
  77. package/dist/file-host-worker.js +1 -1
  78. package/dist/file-host-worker.mjs +1 -1
  79. package/dist/{hub-mdns-ciao-CBLvq2oj.js → hub-mdns-ciao-3Zc9G9MS.js} +377 -69
  80. package/dist/{hub-mdns-ciao-DMlQTdPU.mjs → hub-mdns-ciao-Dy2YjRqC.mjs} +344 -72
  81. package/dist/index.js +372 -339
  82. package/dist/index.mjs +361 -341
  83. package/dist/kernel/addon-settings-store.d.ts +43 -0
  84. package/dist/kernel/config-manager.d.ts +0 -19
  85. package/dist/kernel/device-settings-store.d.ts +74 -0
  86. package/dist/kernel/legacy-blob-guard.d.ts +58 -0
  87. package/dist/kernel/moleculer/runner-addon-settings.d.ts +34 -0
  88. package/dist/kernel/moleculer/runner-device-settings.d.ts +39 -0
  89. package/dist/kernel/prefix-walk.d.ts +39 -0
  90. package/dist/kernel/sections-store.d.ts +20 -0
  91. package/dist/kernel/settings-door-view.d.ts +2 -2
  92. package/dist/kernel/settings-view-types.d.ts +5 -30
  93. package/dist/kernel/store-settings-view.d.ts +0 -3
  94. package/dist/{lib-NNxHSOIE.js → lib-8oZrELLm.js} +1 -1
  95. package/dist/{lib-BeGzVQcd.mjs → lib-DK38ompL.mjs} +1 -1
  96. package/dist/prefix-range-D_KueXbQ.js +27 -0
  97. package/dist/prefix-range-Dl0znowN.mjs +22 -0
  98. package/dist/prefix-walk-DAOBSyH1.mjs +88 -0
  99. package/dist/prefix-walk-Dv6MLZlM.js +99 -0
  100. package/dist/{restore-archive-CTRz8v9o.js → restore-archive-49aZwkp8.js} +1 -1
  101. package/dist/{restore-archive-DPg9duVY.mjs → restore-archive-DgvDecXM.mjs} +82 -82
  102. package/dist/{retired-settings-keys-Is0zO8gX.mjs → retired-settings-keys-Bhwe4ymb.mjs} +2 -2
  103. package/dist/{retired-settings-keys-Dr_4lfKu.js → retired-settings-keys-D_VwZiDt.js} +10 -4
  104. package/dist/tls/agent-https.d.ts +2 -2
  105. package/dist/tls/boot-tls.d.ts +6 -0
  106. package/dist/tls/cert-evaluation.d.ts +1 -1
  107. package/dist/tls/cert-issue.d.ts +5 -1
  108. package/dist/tls/cert-manager.d.ts +18 -0
  109. package/dist/tls/cert-name-constraints.d.ts +53 -0
  110. package/dist/tls/index.d.ts +5 -4
  111. package/dist/tls/tls-actions.d.ts +27 -7
  112. package/dist/tls/tls-status.d.ts +22 -0
  113. package/dist/tls/uploaded-cert.d.ts +36 -4
  114. package/dist/{wal-maintenance-Bx3lcVA7.mjs → wal-maintenance-BZGmF-3U.mjs} +0 -0
  115. package/dist/{wal-maintenance-mjB2i-VR.js → wal-maintenance-C041zn6V.js} +0 -0
  116. package/dist/{x509.es-r7Ht1thA.js → x509.es-C4cOo48M.js} +36 -0
  117. package/dist/{x509.es-CDvj67p1.mjs → x509.es-DK__ETIe.mjs} +1 -1
  118. package/package.json +7 -2
  119. package/dist/{custom-action-registry-Bf_YnagS.js → custom-action-registry-BKmoaqG0.js} +1 -1
  120. package/dist/{custom-action-registry-BQnbN0DP.mjs → custom-action-registry-CyUuhATc.mjs} +1 -1
@@ -1,4 +1,4 @@
1
- import { a as KeyUsagesExtension, c as Pkcs10CertificateRequestGenerator, d as X509Certificate$1, f as X509CertificateGenerator, g as backdatedNotBefore, i as KeyUsageFlags, l as SubjectAlternativeNameExtension, n as BasicConstraintsExtension, o as PemConverter, p as cryptoProvider, r as ExtendedKeyUsageExtension, s as Pkcs10CertificateRequest, t as AuthorityKeyIdentifierExtension, u as SubjectKeyIdentifierExtension } from "./x509.es-CDvj67p1.mjs";
1
+ import { S as backdatedNotBefore, _ as NameConstraints, a as KeyUsageFlags, c as Pkcs10CertificateRequest, d as SubjectKeyIdentifierExtension, f as X509Certificate$1, g as GeneralSubtrees, h as GeneralSubtree, i as Extension, l as Pkcs10CertificateRequestGenerator, m as cryptoProvider, n as BasicConstraintsExtension, o as KeyUsagesExtension, p as X509CertificateGenerator, r as ExtendedKeyUsageExtension, s as PemConverter, t as AuthorityKeyIdentifierExtension, u as SubjectAlternativeNameExtension, v as GeneralName, x as AsnConvert } from "./x509.es-DK__ETIe.mjs";
2
2
  import { X509Certificate, createHash, createPrivateKey, randomBytes, webcrypto } from "node:crypto";
3
3
  import { writeOwnerOnlyFileSync } from "@camstack/types/node";
4
4
  import { isIP } from "node:net";
@@ -99,6 +99,188 @@ function getProcessAgentHttpsNames() {
99
99
  return value !== null && typeof value === "object" && typeof Reflect.get(value, "takenByOthers") === "function" ? value : null;
100
100
  }
101
101
  //#endregion
102
+ //#region src/tls/cert-name-constraints.ts
103
+ /**
104
+ * Name constraints of the hub's generated local CA (D873).
105
+ *
106
+ * An unconstrained private CA vouches for EVERY site on the internet to any
107
+ * device that trusts it, so a leaked CA key would let its holder impersonate
108
+ * a bank. The generated CA therefore carries a CRITICAL `nameConstraints`
109
+ * extension (RFC 5280 4.2.1.10) that limits what it can ever certify to the
110
+ * private address space and local-only names. A verifier that does not
111
+ * understand a critical extension must reject the certificate, so the
112
+ * constraint cannot be silently ignored by a conforming client.
113
+ *
114
+ * Pure: no filesystem, no clock. `cert-issue.ts` embeds the extension,
115
+ * `cert-manager.ts` filters leaf names through {@link permitsIdentity}.
116
+ */
117
+ /** `id-ce-nameConstraints`. */
118
+ var NAME_CONSTRAINTS_OID = "2.5.29.30";
119
+ /** The address space the generated CA may certify. 100.64.0.0/10 is CGNAT, which Tailscale hands out. Loopback is for the hub's own `127.0.0.1` / `::1` SANs. */
120
+ var PERMITTED_IP_RANGES = [
121
+ "10.0.0.0/8",
122
+ "172.16.0.0/12",
123
+ "192.168.0.0/16",
124
+ "100.64.0.0/10",
125
+ "127.0.0.0/8",
126
+ "fc00::/7",
127
+ "::1/128"
128
+ ];
129
+ /**
130
+ * Local-only DNS suffixes: none of them is delegated in public DNS. A leading
131
+ * dot permits every name under the suffix and not the suffix itself.
132
+ */
133
+ var PERMITTED_DNS_SUFFIXES = [
134
+ ".local",
135
+ ".lan",
136
+ ".home.arpa",
137
+ ".internal",
138
+ ".localdomain",
139
+ "localhost"
140
+ ];
141
+ var LABEL_RE = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/i;
142
+ /** A name that cannot collide with public DNS: a single label, or under a local-only suffix. */
143
+ function isLocalOnlyHostname(name) {
144
+ const lower = name.toLowerCase();
145
+ if (lower.length === 0 || lower.length > 253) return false;
146
+ if (!lower.includes(".")) return LABEL_RE.test(lower);
147
+ if (!PERMITTED_DNS_SUFFIXES.some((suffix) => suffix.startsWith(".") && lower.endsWith(suffix))) return false;
148
+ return lower.split(".").every((label) => LABEL_RE.test(label));
149
+ }
150
+ /**
151
+ * The constraint set for a new CA. `hostnames` are the hub's own local-only
152
+ * names at creation time (a single-label OS hostname has no suffix to hide
153
+ * under), added as exact entries; anything that could collide with public DNS
154
+ * is dropped here, never constrained in.
155
+ */
156
+ function buildNameConstraintSet(hostnames) {
157
+ const dns = new Set(PERMITTED_DNS_SUFFIXES);
158
+ for (const name of hostnames) {
159
+ const lower = name.trim().toLowerCase();
160
+ if (isIP(lower) === 0 && isLocalOnlyHostname(lower)) dns.add(lower);
161
+ }
162
+ return {
163
+ ipRanges: PERMITTED_IP_RANGES,
164
+ dnsNames: [...dns],
165
+ critical: true
166
+ };
167
+ }
168
+ /** The `nameConstraints` extension for a CA: permitted subtrees only, marked critical. */
169
+ function createNameConstraintsExtension(set) {
170
+ const subtrees = [...set.ipRanges.map((range) => new GeneralSubtree({ base: new GeneralName({ iPAddress: range }) })), ...set.dnsNames.map((name) => new GeneralSubtree({ base: new GeneralName({ dNSName: name }) }))];
171
+ const value = AsnConvert.serialize(new NameConstraints({ permittedSubtrees: new GeneralSubtrees(subtrees) }));
172
+ return new Extension(NAME_CONSTRAINTS_OID, set.critical, value);
173
+ }
174
+ /** The CA's constraint set, or `null` when the certificate carries no `nameConstraints`. */
175
+ function readNameConstraints(cert) {
176
+ const ext = cert.extensions.find((e) => e.type === NAME_CONSTRAINTS_OID);
177
+ if (ext === void 0) return null;
178
+ const parsed = AsnConvert.parse(ext.value, NameConstraints);
179
+ const ipRanges = [];
180
+ const dnsNames = [];
181
+ for (const subtree of parsed.permittedSubtrees ?? []) {
182
+ if (subtree.base.iPAddress !== void 0) ipRanges.push(subtree.base.iPAddress);
183
+ if (subtree.base.dNSName !== void 0) dnsNames.push(subtree.base.dNSName.toLowerCase());
184
+ }
185
+ return {
186
+ ipRanges,
187
+ dnsNames,
188
+ critical: ext.critical
189
+ };
190
+ }
191
+ /**
192
+ * True when a CA is safe to keep as the hub's anchor: constrained to a
193
+ * non-empty permitted set AND marked critical. A non-critical constraint is
194
+ * ignored by clients that do not know the extension, which is the same as none.
195
+ */
196
+ function isConstrainedCa(cert) {
197
+ const set = readNameConstraints(cert);
198
+ return set !== null && set.critical && (set.ipRanges.length > 0 || set.dnsNames.length > 0);
199
+ }
200
+ function ipv4ToBigInt(address) {
201
+ return address.split(".").reduce((acc, octet) => acc << 8n | BigInt(Number(octet)), 0n);
202
+ }
203
+ function ipv6ToBigInt(address) {
204
+ const mapped = /^(.*:)(\d+\.\d+\.\d+\.\d+)$/.exec(address);
205
+ let text = address;
206
+ if (mapped !== null) {
207
+ const v4 = ipv4ToBigInt(mapped[2] ?? "0.0.0.0");
208
+ text = `${mapped[1] ?? ""}${(v4 >> 16n & 65535n).toString(16)}:${(v4 & 65535n).toString(16)}`;
209
+ }
210
+ const halves = text.split("::");
211
+ if (halves.length > 2) return null;
212
+ const head = (halves[0] ?? "") === "" ? [] : (halves[0] ?? "").split(":");
213
+ const tail = halves.length === 2 && (halves[1] ?? "") !== "" ? (halves[1] ?? "").split(":") : [];
214
+ const fill = halves.length === 2 ? 8 - head.length - tail.length : 0;
215
+ if (fill < 0 || halves.length === 1 && head.length !== 8) return null;
216
+ return [
217
+ ...head,
218
+ ...Array(fill).fill("0"),
219
+ ...tail
220
+ ].reduce((acc, group) => acc << 16n | BigInt(Number.parseInt(group, 16)), 0n);
221
+ }
222
+ function toBigInt(address) {
223
+ const family = isIP(address);
224
+ if (family === 4) return {
225
+ value: ipv4ToBigInt(address),
226
+ bits: 32
227
+ };
228
+ if (family === 6) {
229
+ const value = ipv6ToBigInt(address);
230
+ return value === null ? null : {
231
+ value,
232
+ bits: 128
233
+ };
234
+ }
235
+ return null;
236
+ }
237
+ function ipInRange(address, range) {
238
+ const [base, prefixText] = range.split("/");
239
+ if (base === void 0 || prefixText === void 0) return false;
240
+ const target = toBigInt(address);
241
+ const net = toBigInt(base);
242
+ const prefix = Number(prefixText);
243
+ if (target === null || net === null || target.bits !== net.bits) return false;
244
+ if (!Number.isInteger(prefix) || prefix < 0 || prefix > net.bits) return false;
245
+ const shift = BigInt(net.bits - prefix);
246
+ return target.value >> shift === net.value >> shift;
247
+ }
248
+ /** RFC 5280 DNS matching: a leading dot is a strict subtree, a bare name is itself and everything under it. */
249
+ function dnsPermitted(name, constraint) {
250
+ const lower = name.toLowerCase();
251
+ if (constraint.startsWith(".")) return lower.endsWith(constraint);
252
+ return lower === constraint || lower.endsWith(`.${constraint}`);
253
+ }
254
+ /** Is this SAN value (an IP literal or a DNS name) inside the permitted set? */
255
+ function permitsName(set, value) {
256
+ if (isIP(value) !== 0) return set.ipRanges.some((range) => ipInRange(value, range));
257
+ return set.dnsNames.some((constraint) => dnsPermitted(value, constraint));
258
+ }
259
+ /**
260
+ * The part of an identity the CA is allowed to certify. A leaf naming
261
+ * anything outside the constraints makes the WHOLE chain fail validation in
262
+ * OpenSSL, so such names are dropped (and reported), never signed. The
263
+ * required lists shrink the same way: otherwise a name the CA cannot certify
264
+ * would count as a permanent SAN gap and reissue the leaf on every boot.
265
+ */
266
+ function permitsIdentity(set, identity) {
267
+ const dropped = /* @__PURE__ */ new Set();
268
+ const keep = (values) => values.filter((value) => {
269
+ if (permitsName(set, value)) return true;
270
+ dropped.add(value);
271
+ return false;
272
+ });
273
+ return {
274
+ identity: {
275
+ dnsNames: keep(identity.dnsNames),
276
+ ipAddresses: keep(identity.ipAddresses),
277
+ requiredDnsNames: keep(identity.requiredDnsNames),
278
+ requiredIpAddresses: keep(identity.requiredIpAddresses)
279
+ },
280
+ dropped: [...dropped]
281
+ };
282
+ }
283
+ //#endregion
102
284
  //#region src/tls/cert-evaluation.ts
103
285
  /**
104
286
  * The regeneration rule (D227), as a pure function.
@@ -127,7 +309,8 @@ var REASONS_REQUIRING_NEW_CA = [
127
309
  "missing",
128
310
  "unreadable",
129
311
  "no-local-ca",
130
- "ca-expiring"
312
+ "ca-expiring",
313
+ "ca-unconstrained"
131
314
  ];
132
315
  function reasonRequiresNewCa(reason) {
133
316
  return REASONS_REQUIRING_NEW_CA.includes(reason);
@@ -139,6 +322,14 @@ function parse(pem) {
139
322
  return null;
140
323
  }
141
324
  }
325
+ /** A root without a CRITICAL name constraint (every pre-D873 install) can vouch for any site. */
326
+ function caIsUnconstrained(caPem) {
327
+ try {
328
+ return !isConstrainedCa(new X509Certificate$1(caPem));
329
+ } catch {
330
+ return true;
331
+ }
332
+ }
142
333
  function daysBetween(from, to) {
143
334
  return (to.getTime() - from.getTime()) / MS_PER_DAY$1;
144
335
  }
@@ -169,6 +360,7 @@ function evaluateExistingCert(input) {
169
360
  const ca = parse(input.caPem);
170
361
  if (leaf === null || ca === null) return "unreadable";
171
362
  if (daysBetween(input.now, new Date(ca.validTo)) < 427) return "ca-expiring";
363
+ if (caIsUnconstrained(input.caPem)) return "ca-unconstrained";
172
364
  if (!keyMatches(leaf, input.keyPem)) return "key-mismatch";
173
365
  if (daysBetween(input.now, new Date(leaf.validTo)) < 30) return "expiring";
174
366
  if (!hasServerAuthEku(leaf)) return "missing-server-auth-eku";
@@ -381,13 +573,18 @@ function subjectAltNames(identity) {
381
573
  * A local root: 10 years, `pathlen:0` so it can sign leaves and nothing
382
574
  * else, and **no EKU** — Apple rejects an anchor asserting
383
575
  * `anyExtendedKeyUsage`, and a root with no EKU constrains nothing.
576
+ *
577
+ * It carries a CRITICAL `nameConstraints` extension (D873): private address
578
+ * ranges and local-only DNS names, plus `hostnames` (the hub's own single-label
579
+ * names at creation). A leaked key therefore cannot vouch for a public site.
384
580
  */
385
- async function generateCa(now) {
581
+ async function generateCa(now, hostnames = []) {
386
582
  const keys = await webcrypto.subtle.generateKey(SIGNING_ALGORITHM, true, ["sign", "verify"]);
387
583
  const extensions = [
388
584
  new BasicConstraintsExtension(true, 0, true),
389
585
  new KeyUsagesExtension(KeyUsageFlags.keyCertSign | KeyUsageFlags.cRLSign, true),
390
- await SubjectKeyIdentifierExtension.create(keys.publicKey)
586
+ await SubjectKeyIdentifierExtension.create(keys.publicKey),
587
+ createNameConstraintsExtension(buildNameConstraintSet(hostnames))
391
588
  ];
392
589
  const cert = await X509CertificateGenerator.createSelfSigned({
393
590
  serialNumber: freshSerial(),
@@ -650,14 +847,6 @@ function describeHttpsLeaf(certPem) {
650
847
  notAfter: Date.parse(x509.validTo)
651
848
  };
652
849
  }
653
- var LOCAL_SUFFIXES = [
654
- ".local",
655
- ".lan",
656
- ".home.arpa",
657
- ".internal",
658
- ".localdomain"
659
- ];
660
- var LABEL_RE = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/i;
661
850
  /** Private-network IPv4/IPv6: RFC 1918, CGNAT (Tailscale), link-local, ULA. */
662
851
  function isPrivateAddress(address) {
663
852
  const family = isIP(address);
@@ -675,14 +864,6 @@ function isPrivateAddress(address) {
675
864
  function isContainerBridgeAddress(address) {
676
865
  return /^172\.17\.\d{1,3}\.\d{1,3}$/.test(address);
677
866
  }
678
- /** A name that cannot collide with public DNS. */
679
- function isLocalOnlyHostname(name) {
680
- const lower = name.toLowerCase();
681
- if (lower.length === 0 || lower.length > 253) return false;
682
- if (!lower.includes(".")) return LABEL_RE.test(lower);
683
- if (!LOCAL_SUFFIXES.some((suffix) => lower.endsWith(suffix))) return false;
684
- return lower.split(".").every((label) => LABEL_RE.test(label));
685
- }
686
867
  /**
687
868
  * The names the hub will sign for `nodeId`: its node id and the address of its live mesh
688
869
  * socket - nothing the agent says about itself. `excluded` holds every name already taken
@@ -735,14 +916,21 @@ async function issueAgentHttpsLeaf(input) {
735
916
  if (!existsSync(caCertPath) || !existsSync(caKeyPath)) throw new Error("the hub has no local CA to sign with (no tls/camstack-ca.crt + key)");
736
917
  const excluded = new Set(input.takenByOthers ?? []);
737
918
  if (existsSync(hubLeafPath)) for (const name of certificateNames(readFileSync(hubLeafPath, "utf8"))) excluded.add(name);
738
- const sans = acceptAgentSans({
919
+ const ca = await loadCa(readFileSync(caCertPath, "utf8"), readFileSync(caKeyPath, "utf8"));
920
+ const policy = acceptAgentSans({
739
921
  nodeId: input.nodeId,
740
922
  observedAddress: input.observedAddress,
741
923
  excluded,
742
924
  ...input.reportedNames !== void 0 ? { reported: input.reportedNames } : {}
743
925
  });
926
+ const constraints = readNameConstraints(ca.cert);
927
+ const constrained = constraints === null ? null : permitsIdentity(constraints, policy.identity);
928
+ const sans = constrained === null ? policy : {
929
+ identity: constrained.identity,
930
+ accepted: policy.accepted.filter((name) => !constrained.dropped.includes(name)),
931
+ rejected: [...policy.rejected, ...constrained.dropped.map((name) => `${name} (outside the local CA's name constraints)`)]
932
+ };
744
933
  if (sans.accepted.length === 0) throw new Error(`no name of node '${input.nodeId}' can be certified (${sans.rejected.join("; ") || "nothing offered"})`);
745
- const ca = await loadCa(readFileSync(caCertPath, "utf8"), readFileSync(caKeyPath, "utf8"));
746
934
  return {
747
935
  certPem: await issueLeafFromCsr(ca, input.csrPem, sans.identity, input.nodeId, 397, input.now ?? /* @__PURE__ */ new Date()),
748
936
  caCertPem: ca.certPem,
@@ -755,4 +943,4 @@ function pemDigest(pem) {
755
943
  return createHash("sha256").update(pem).digest("hex").slice(0, 16);
756
944
  }
757
945
  //#endregion
758
- export { AgentHttpsNames as A, LEAF_RENEWAL_WINDOW_DAYS as C, reasonRequiresNewCa as D, evaluateExistingCert as E, setProcessAgentHttpsNames as M, AGENT_HTTPS_NAMES_COLLECTION as O, CA_VALIDITY_DAYS as S, SERVER_AUTH_OID as T, generateCa as _, collectAgentNames as a, collectCertIdentity as b, installAgentHttps as c, isPrivateAddress as d, issueAgentHttpsLeaf as f, CA_COMMON_NAME as g, pinnedHttpsCaFingerprint as h, certificateNames as i, getProcessAgentHttpsNames as j, AGENT_HTTPS_NAMES_COLUMNS as k, isContainerBridgeAddress as l, pemDigest as m, acceptAgentSans as n, createAgentHttpsRequest as o, loadAgentHttps as p, agentHttpsPaths as r, describeHttpsLeaf as s, AgentHttpsInstallError as t, isLocalOnlyHostname as u, issueLeaf as v, MAX_LEAF_VALIDITY_DAYS as w, systemHostFacts as x, loadCa as y };
946
+ export { isConstrainedCa as A, MAX_LEAF_VALIDITY_DAYS as C, PERMITTED_DNS_SUFFIXES as D, reasonRequiresNewCa as E, AGENT_HTTPS_NAMES_COLLECTION as F, AGENT_HTTPS_NAMES_COLUMNS as I, AgentHttpsNames as L, permitsIdentity as M, permitsName as N, PERMITTED_IP_RANGES as O, readNameConstraints as P, getProcessAgentHttpsNames as R, LEAF_RENEWAL_WINDOW_DAYS as S, evaluateExistingCert as T, issueLeaf as _, collectAgentNames as a, systemHostFacts as b, installAgentHttps as c, issueAgentHttpsLeaf as d, loadAgentHttps as f, generateCa as g, CA_COMMON_NAME as h, certificateNames as i, isLocalOnlyHostname as j, buildNameConstraintSet as k, isContainerBridgeAddress as l, pinnedHttpsCaFingerprint as m, acceptAgentSans as n, createAgentHttpsRequest as o, pemDigest as p, agentHttpsPaths as r, describeHttpsLeaf as s, AgentHttpsInstallError as t, isPrivateAddress as u, loadCa as v, SERVER_AUTH_OID as w, CA_VALIDITY_DAYS as x, collectCertIdentity as y, setProcessAgentHttpsNames as z };
@@ -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-GgG-7amw.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.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 { Dr as errMsg, Or as BaseAddon, nt as addonPagesCapability, yi as EventCategory } from "../../dist-Dc8GKC5H.mjs";
1
+ import { Ar as errMsg, Si as EventCategory, it as addonPagesCapability, jr as BaseAddon } from "../../dist-9fcsrFU3.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-GgG-7amw.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.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 { Dr as errMsg, Or as BaseAddon, it as addonWidgetsCapability, yi as EventCategory } from "../../dist-Dc8GKC5H.mjs";
1
+ import { Ar as errMsg, Si as EventCategory, jr as BaseAddon, ot as addonWidgetsCapability } from "../../dist-9fcsrFU3.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-GgG-7amw.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.js");
7
7
  //#region src/builtins/alerts/device-label.ts
8
8
  /**
9
9
  * Operator-facing device label for alert messages.
@@ -1,4 +1,4 @@
1
- import { Dr as errMsg, Or as BaseAddon, Wr as createEvent, at as alertsCapability, i as AlertSchema, yi as EventCategory } from "../../dist-Dc8GKC5H.mjs";
1
+ import { Ar as errMsg, Si as EventCategory, i as AlertSchema, jr as BaseAddon, qr as createEvent, st as alertsCapability } from "../../dist-9fcsrFU3.mjs";
2
2
  //#region src/builtins/alerts/device-label.ts
3
3
  /**
4
4
  * Operator-facing device label for alert messages.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-GgG-7amw.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.js");
7
7
  //#region src/builtins/autotrack/autotrack-config.ts
8
8
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
9
9
  deadZoneFraction: .5,
@@ -1,4 +1,4 @@
1
- import { $r as isEvent, Dr as errMsg, M as FIRST_LEVEL_MACRO_CLASSES, Mn as ptzAutotrackCapability, Or as BaseAddon, ct as bareAddonId, yi as EventCategory } from "../../dist-Dc8GKC5H.mjs";
1
+ import { Ar as errMsg, M as FIRST_LEVEL_MACRO_CLASSES, Pn as ptzAutotrackCapability, Si as EventCategory, jr as BaseAddon, ni as isEvent, ut as bareAddonId } from "../../dist-9fcsrFU3.mjs";
2
2
  //#region src/builtins/autotrack/autotrack-config.ts
3
3
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
4
4
  deadZoneFraction: .5,
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-GgG-7amw.js");
7
- const require_restore_archive = require("../../restore-archive-CTRz8v9o.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.js");
7
+ const require_restore_archive = require("../../restore-archive-49aZwkp8.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_fs_promises = require("node:fs/promises");
10
10
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,5 +1,5 @@
1
- import { Or as BaseAddon, er as storageOccupancyCapability, hi as toNodeId, ii as nodePin, st as backupCapability, vn as mayWriteToLocation, yi as EventCategory } from "../../dist-Dc8GKC5H.mjs";
2
- import { a as BackupSnapshotRefusedError, d as ENCRYPTED_BACKUP_SUFFIX, f as generateBackupKeyPair, i as BackupCancelledError, l as BackupKeyFormatError, m as parseBackupSecretKey, n as precheckRestoreKey, o as DEFAULT_BACKUP_DATABASES, p as parseBackupPublicKey, r as prepareRestoreArchive, s as SystemBackupService } from "../../restore-archive-DPg9duVY.mjs";
1
+ import { Si as EventCategory, bn as mayWriteToLocation, jr as BaseAddon, lt as backupCapability, rr as storageOccupancyCapability, si as nodePin, vi as toNodeId } from "../../dist-9fcsrFU3.mjs";
2
+ import { a as BackupSnapshotRefusedError, d as ENCRYPTED_BACKUP_SUFFIX, f as generateBackupKeyPair, i as BackupCancelledError, l as BackupKeyFormatError, m as parseBackupSecretKey, n as precheckRestoreKey, o as DEFAULT_BACKUP_DATABASES, p as parseBackupPublicKey, r as prepareRestoreArchive, s as SystemBackupService } from "../../restore-archive-DgvDecXM.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import * as fsp from "node:fs/promises";
5
5
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-GgG-7amw.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let zod = require("zod");
9
9
  //#region src/builtins/calendars/calendar-color.ts
@@ -1,4 +1,4 @@
1
- import { Or as BaseAddon, g as CalendarPreferencesSchema, mt as calendarsCapability } from "../../dist-Dc8GKC5H.mjs";
1
+ import { g as CalendarPreferencesSchema, gt as calendarsCapability, jr as BaseAddon } from "../../dist-9fcsrFU3.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/builtins/calendars/calendar-color.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-GgG-7amw.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.js");
7
7
  let zod = require("zod");
8
8
  let _camstack_types_node = require("@camstack/types/node");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { A as DeclaredDevices, Dr as errMsg, Er as DeviceType, H as MOTION_CLOSE_AFTER_MS, Or as BaseAddon, Sn as motionCapability, Wr as createEvent, Xn as snapshotCapability, Zr as hydrateSchema, ht as cameraGridLayoutCapability, kt as declarationOwnerNodeId, nr as streamCatalogCapability, o as BaseDevice, pr as buildFfmpegArgs, w as DERIVED_CAMERA_SILENCED_CAP_NAMES, wr as DeviceFeature, yi as EventCategory, yn as memoizePinnedFfmpeg } from "../../dist-Dc8GKC5H.mjs";
1
+ import { A as DeclaredDevices, Ar as errMsg, Dr as DeviceFeature, H as MOTION_CLOSE_AFTER_MS, Qn as snapshotCapability, Si as EventCategory, _t as cameraGridLayoutCapability, ar as streamCatalogCapability, ei as hydrateSchema, gr as buildFfmpegArgs, jr as BaseAddon, jt as declarationOwnerNodeId, kr as DeviceType, o as BaseDevice, qr as createEvent, w as DERIVED_CAMERA_SILENCED_CAP_NAMES, wn as motionCapability, xn as memoizePinnedFfmpeg } from "../../dist-9fcsrFU3.mjs";
2
2
  import { z } from "zod";
3
3
  import { FfmpegProcess } from "@camstack/types/node";
4
4
  import { join } from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-GgG-7amw.js");
7
- const require_composition_sources = require("../../composition-sources-DI3Z83vG.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.js");
7
+ const require_composition_sources = require("../../composition-sources-cBG8fBTu.js");
8
8
  let zod = require("zod");
9
9
  //#region src/builtins/shared/device-state-changed.ts
10
10
  /** Structural narrowing helper for untrusted bus payloads. */
@@ -1,5 +1,5 @@
1
- import { A as DeclaredDevices, Cr as toExpressionValue, Dr as errMsg, Er as DeviceType, Gt as expressionContainsBuiltinCall, Ht as evaluateCompositionField, Or as BaseAddon, Sr as disarmRoseMemory, Wr as createEvent, _r as compositionFeatureEnabled, br as compositionSourceKey, f as COMPOSED_ITEM_FIXED_FIELDS, fr as COMPOSER_ADDON_ID, gr as composedDeviceStableId, hr as composedDeviceRef, kn as planComposition, mr as cloneStatefulExpressionMemory, o as BaseDevice, r as ALL_CAPABILITY_DEFINITIONS, ut as buildCapabilityLookup, vr as compositionItemFieldPath, vt as composerCapability, xr as createStatefulExpressionMemory, yi as EventCategory, yr as compositionSliceKey, yt as compositionFieldKey } from "../../dist-Dc8GKC5H.mjs";
2
- import { n as normalizedNativeOwner } from "../../composition-sources-DpHGCjdL.mjs";
1
+ import { A as DeclaredDevices, Ar as errMsg, Cr as compositionSourceKey, Er as toExpressionValue, Si as EventCategory, Sr as compositionSliceKey, Tr as disarmRoseMemory, Wt as evaluateCompositionField, _r as cloneStatefulExpressionMemory, br as compositionFeatureEnabled, bt as composerCapability, f as COMPOSED_ITEM_FIXED_FIELDS, ft as buildCapabilityLookup, hr as COMPOSER_ADDON_ID, jn as planComposition, jr as BaseAddon, kr as DeviceType, o as BaseDevice, qr as createEvent, qt as expressionContainsBuiltinCall, r as ALL_CAPABILITY_DEFINITIONS, vr as composedDeviceRef, wr as createStatefulExpressionMemory, xr as compositionItemFieldPath, xt as compositionFieldKey, yr as composedDeviceStableId } from "../../dist-9fcsrFU3.mjs";
2
+ import { n as normalizedNativeOwner } from "../../composition-sources-CS0D7imL.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/builtins/shared/device-state-changed.ts
5
5
  /** Structural narrowing helper for untrusted bus payloads. */
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-GgG-7amw.js");
6
+ const require_dist = require("../../dist-BKhlcYz6.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { Or as BaseAddon, mn as logDestinationCapability } from "../../dist-Dc8GKC5H.mjs";
1
+ import { gn as logDestinationCapability, jr as BaseAddon } from "../../dist-9fcsrFU3.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -8,7 +8,7 @@
8
8
  * keeps any values the UI already wrote.
9
9
  *
10
10
  * On every patch the same values are mirrored into the block addon's
11
- * `addon-settings` namespace so a running script reads them the same way any
11
+ * `addon-settings` rows so a running script reads them the same way any
12
12
  * addon does: `ctx.settings.readAddonStore()`.
13
13
  */
14
14
  import type { SettingsStoreClient } from "@camstack/types";
@@ -1,7 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-GgG-7amw.js");
4
- const require_composition_sources = require("../../composition-sources-DI3Z83vG.js");
3
+ const require_dist = require("../../dist-BKhlcYz6.js");
4
+ const require_composition_sources = require("../../composition-sources-cBG8fBTu.js");
5
+ const require_prefix_range = require("../../prefix-range-D_KueXbQ.js");
6
+ const require_prefix_walk = require("../../prefix-walk-Dv6MLZlM.js");
5
7
  let node_crypto = require("node:crypto");
6
8
  let _camstack_types_node = require("@camstack/types/node");
7
9
  let node_fs_promises = require("node:fs/promises");
@@ -490,6 +492,72 @@ var BlockSupervisor = class {
490
492
  }
491
493
  };
492
494
  //#endregion
495
+ //#region src/kernel/addon-settings-store.ts
496
+ /** The collection every addon's settings share. Named nowhere else (with sections' own module). */
497
+ var ADDON_SETTINGS_COLLECTION = "addon-settings";
498
+ function createAddonSettingsStore(door, addonId) {
499
+ const prefix = `${addonId}.`;
500
+ const message = (err) => err instanceof Error ? err.message : String(err);
501
+ return {
502
+ async read() {
503
+ try {
504
+ const value = await require_prefix_walk.loadPrefixed(door, ADDON_SETTINGS_COLLECTION, prefix);
505
+ return Object.keys(value).length === 0 ? { kind: "absent" } : {
506
+ kind: "present",
507
+ value
508
+ };
509
+ } catch (err) {
510
+ return {
511
+ kind: "unreadable",
512
+ error: message(err)
513
+ };
514
+ }
515
+ },
516
+ async readKey(key) {
517
+ try {
518
+ const row = await door.get({
519
+ collection: ADDON_SETTINGS_COLLECTION,
520
+ key: `${prefix}${key}`
521
+ });
522
+ return row === void 0 || row === null ? { kind: "absent" } : {
523
+ kind: "present",
524
+ value: require_prefix_walk.unwrapStoredValue(row)
525
+ };
526
+ } catch (err) {
527
+ return {
528
+ kind: "unreadable",
529
+ error: message(err)
530
+ };
531
+ }
532
+ },
533
+ async merge(patch) {
534
+ const records = [];
535
+ const removed = [];
536
+ for (const [key, value] of Object.entries(patch)) if (value === void 0) removed.push(`${prefix}${key}`);
537
+ else records.push({
538
+ id: `${prefix}${key}`,
539
+ data: {
540
+ addonId,
541
+ key,
542
+ value
543
+ }
544
+ });
545
+ if (records.length === 0 && removed.length === 0) return;
546
+ if (require_prefix_range.prefixRange(prefix) === null) throw new Error(`addon settings: "${addonId}" cannot be expressed as an id range`);
547
+ await door.transact({ ops: [...removed.length > 0 ? [{
548
+ op: "deleteWhere",
549
+ collection: ADDON_SETTINGS_COLLECTION,
550
+ filter: { whereIn: { id: removed } }
551
+ }] : [], ...records.length > 0 ? [{
552
+ op: "upsertMany",
553
+ collection: ADDON_SETTINGS_COLLECTION,
554
+ conflict: ["id"],
555
+ records
556
+ }] : []] });
557
+ }
558
+ };
559
+ }
560
+ //#endregion
493
561
  //#region src/builtins/core-blocks/block-settings-store.ts
494
562
  /**
495
563
  * @durable class=config owner=core-blocks
@@ -550,15 +618,12 @@ var BlockSettingsStore = class {
550
618
  });
551
619
  }
552
620
  async mirrorToBlockAddon(blockId, values) {
553
- const namespace = require_dist.coreBlockAddonId(blockId);
554
- try {
555
- await this.store.set.mutate({
556
- namespace,
557
- collection: "addon-settings",
558
- key: "root",
559
- value: values
560
- });
561
- } catch {}
621
+ const store = this.store;
622
+ await createAddonSettingsStore({
623
+ query: (input) => store.query.query(input),
624
+ get: (input) => store.get.query(input),
625
+ transact: (input) => store.transact.mutate(input)
626
+ }, require_dist.coreBlockAddonId(blockId)).merge(values);
562
627
  }
563
628
  };
564
629
  //#endregion
@@ -2273,6 +2338,7 @@ exports.CoreBlocksAddon = CoreBlocksAddon;
2273
2338
  exports.blockAddonId = blockAddonId;
2274
2339
  exports.blockIdFromAddonId = blockIdFromAddonId;
2275
2340
  exports.compileBlock = compileBlock;
2341
+ exports.createAddonSettingsStore = createAddonSettingsStore;
2276
2342
  exports.extractSettingsSchema = extractSettingsSchema;
2277
2343
  exports.loadBlockTypeDefs = loadBlockTypeDefs;
2278
2344
  exports.materializeBlock = materializeBlock;