@camstack/system 1.2.113 → 1.2.115

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 (62) 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.d.ts +1 -1
  8. package/dist/builtins/alerts/alerts.addon.js +6 -1
  9. package/dist/builtins/alerts/alerts.addon.mjs +6 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  12. package/dist/builtins/console-logging/index.js +1 -1
  13. package/dist/builtins/console-logging/index.mjs +1 -1
  14. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  15. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  16. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  17. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  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 +3 -3
  26. package/dist/builtins/local-network/local-network.addon.d.ts +33 -1
  27. package/dist/builtins/local-network/local-network.addon.js +87 -2
  28. package/dist/builtins/local-network/local-network.addon.mjs +87 -3
  29. package/dist/builtins/loki-logging/index.js +1 -1
  30. package/dist/builtins/loki-logging/index.mjs +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  32. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  33. package/dist/builtins/platform-probe/index.js +1 -1
  34. package/dist/builtins/platform-probe/index.mjs +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  37. package/dist/builtins/snapshot/index.js +1 -1
  38. package/dist/builtins/snapshot/index.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  40. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  42. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  45. package/dist/builtins/system-config/system-config.addon.js +1 -1
  46. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  47. package/dist/builtins/winston-logging/index.js +1 -1
  48. package/dist/builtins/winston-logging/index.mjs +1 -1
  49. package/dist/{dist-Duz_uJSQ.mjs → dist-Drb54SZD.mjs} +89 -5
  50. package/dist/{dist-B2CtwVW2.js → dist-iAwSA2_f.js} +89 -5
  51. package/dist/index.js +438 -47
  52. package/dist/index.mjs +435 -50
  53. package/dist/{retired-settings-keys-BtVT2f6O.mjs → retired-settings-keys-Dz5-7gjt.mjs} +1 -1
  54. package/dist/{retired-settings-keys-CYDGGkyM.js → retired-settings-keys-OfhqQio4.js} +1 -1
  55. package/dist/tls/cert-evaluation.d.ts +41 -0
  56. package/dist/tls/cert-identity.d.ts +37 -0
  57. package/dist/tls/cert-manager.d.ts +60 -6
  58. package/dist/tls/cert-openssl.d.ts +23 -0
  59. package/dist/tls/index.d.ts +3 -1
  60. package/package.json +1 -1
  61. package/dist/{manifest-python-deps-MA_BZGBz.js → manifest-python-deps-CwBbX4Ut.js} +1 -1
  62. package/dist/{manifest-python-deps-Cb1ANt3i.mjs → manifest-python-deps-SGdbr9D5.mjs} +1 -1
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 asNumber, B as extractNestedAddonId, Bt as parseJsonUnknown$1, Dt as ReadinessTimeoutError, Et as ReadinessRegistry, Ft as expandCapMethods, Gt as EventCategory$1, H as isArrayOutputSchema, Ht as resolveCapMount, J as lifecycleJobSchema, K as isVoidInput, Mt as createEvent, Nt as emitDownForOwnedCaps, Q as looseSchema, St as DEVICE_STATUS_METHOD, U as isCollectionArrayMethod, Ut as scopeKey, Vt as readinessKey, W as isObjectInput, Z as logLevelAtMost, bt as DATAPLANE_SECRET_HEADER$1, d as METHOD_ACCESS_MAP, f as RUNTIME_DEFAULTS, it as procedureAuthKey, jt as asString$1, kt as asJsonObject$1, ot as scopesAllowAddon, q as kebabToCamel, st as scopesAllowDeviceCap, t as ALL_CAPABILITY_DEFINITIONS, tt as objectInputDeclaresAddonId, vt as errMsg$1, xt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, y as addonSettingsCapability, zt as parseJsonObject } from "./dist-Duz_uJSQ.mjs";
2
+ import { At as asNumber, B as extractNestedAddonId, Bt as parseJsonUnknown$1, Dt as ReadinessTimeoutError, Et as ReadinessRegistry, Ft as expandCapMethods, Gt as EventCategory$1, H as isArrayOutputSchema, Ht as resolveCapMount, J as lifecycleJobSchema, K as isVoidInput, Mt as createEvent, Nt as emitDownForOwnedCaps, Q as looseSchema, St as DEVICE_STATUS_METHOD, U as isCollectionArrayMethod, Ut as scopeKey, Vt as readinessKey, W as isObjectInput, Z as logLevelAtMost, bt as DATAPLANE_SECRET_HEADER$1, d as METHOD_ACCESS_MAP, f as RUNTIME_DEFAULTS, it as procedureAuthKey, jt as asString$1, kt as asJsonObject$1, ot as scopesAllowAddon, q as kebabToCamel, st as scopesAllowDeviceCap, t as ALL_CAPABILITY_DEFINITIONS, tt as objectInputDeclaresAddonId, vt as errMsg$1, xt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, y as addonSettingsCapability, zt as parseJsonObject } from "./dist-Drb54SZD.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";
@@ -24,24 +24,26 @@ import { SystemConfigAddon } from "./builtins/system-config/system-config.addon.
24
24
  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-CuE_hBli.mjs";
27
- import { $ as buildNativeCapProxy, A as createHubCapForwardService, At as buildHeapSample, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as runNpm, D as localProviderLink, Dt as HEAP_WATCH_INTERVAL_MS, E as ipcParentLink, Et as HEAP_RECLAIM_TRIGGER_MB, F as createUdsLogger, Ft as strandedMb, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, Mt as shouldReclaim, N as createUdsEventBus, Nt as startHeapWatch, O as HUB_CAP_FWD_ACTION, Ot as HEAP_WATCH_WARN_RATIO, P as udsChildLogToWorkerEntry, Pt as startRunnerHeapWatch, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as resolveNpmInvocation, T as ipcChildLink, Tt as HEAP_RECLAIM_MIN_INTERVAL_MS, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, jt as createV8Reclaimer, k as HUB_CAP_FWD_SERVICE, kt as RUNNER_HEAP_WATCH_INTERVAL_MS, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveAddonClass, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-Cb1ANt3i.mjs";
27
+ import { $ as buildNativeCapProxy, A as createHubCapForwardService, At as buildHeapSample, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as runNpm, D as localProviderLink, Dt as HEAP_WATCH_INTERVAL_MS, E as ipcParentLink, Et as HEAP_RECLAIM_TRIGGER_MB, F as createUdsLogger, Ft as strandedMb, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, Mt as shouldReclaim, N as createUdsEventBus, Nt as startHeapWatch, O as HUB_CAP_FWD_ACTION, Ot as HEAP_WATCH_WARN_RATIO, P as udsChildLogToWorkerEntry, Pt as startRunnerHeapWatch, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as resolveNpmInvocation, T as ipcChildLink, Tt as HEAP_RECLAIM_MIN_INTERVAL_MS, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, jt as createV8Reclaimer, k as HUB_CAP_FWD_SERVICE, kt as RUNNER_HEAP_WATCH_INTERVAL_MS, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveAddonClass, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-SGdbr9D5.mjs";
28
28
  import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BWmQ5i-o.mjs";
29
29
  import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
30
30
  import { z } from "zod";
31
- import { X509Certificate, createHash, randomUUID, timingSafeEqual } from "node:crypto";
31
+ import { X509Certificate, createHash, createPrivateKey, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
32
32
  import { PYTHON_VERSION, buildBinaryPath, downloadBinary, ensureBinary, ensureFfmpeg, ensurePython, findInPath, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
33
- import { lstat, readdir, readlink, realpath } from "node:fs/promises";
33
+ import { chmod, copyFile, lstat, mkdtemp, readdir, readlink, realpath, rm, writeFile } from "node:fs/promises";
34
34
  import * as path$39 from "node:path";
35
35
  import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
36
36
  import { execFile, spawn } from "node:child_process";
37
37
  import * as util$10 from "node:util";
38
38
  import { promisify } from "node:util";
39
39
  import * as os$18 from "node:os";
40
+ import { tmpdir } from "node:os";
40
41
  import * as fs$17 from "node:fs";
41
- import { accessSync, constants, existsSync, mkdirSync, readFileSync } from "node:fs";
42
+ import { accessSync, constants, existsSync, mkdirSync, readFileSync, renameSync } from "node:fs";
42
43
  import { request } from "node:http";
43
44
  import * as vm from "node:vm";
44
45
  import { asJsonObject, errMsg, parseJsonUnknown } from "@camstack/types/addon";
46
+ import { isIP } from "node:net";
45
47
  import { fileURLToPath, pathToFileURL } from "node:url";
46
48
  import { EventEmitter } from "node:events";
47
49
  import As from "node:stream";
@@ -2010,71 +2012,454 @@ var StorageManager = class {
2010
2012
  }
2011
2013
  };
2012
2014
  //#endregion
2013
- //#region src/tls/cert-manager.ts
2014
- var execFileAsync$3 = promisify(execFile);
2015
+ //#region src/tls/cert-evaluation.ts
2015
2016
  /**
2016
- * Ensure a self-signed TLS certificate exists in the given directory.
2017
- * Generates one if missing. Returns paths to cert and key files.
2017
+ * The regeneration rule (D227), as a pure function.
2018
+ *
2019
+ * `ensureTlsCert` reissues **only** when the material on disk cannot do
2020
+ * its job. Never because it is old, never because the machine picked up
2021
+ * an address the certificate does not need, never because a new build
2022
+ * shipped. The decision is a function of (files on disk, machine
2023
+ * identity, clock) alone, so the same inputs always produce the same
2024
+ * answer — which is what makes a reissue happen once and then stop.
2018
2025
  */
2019
- async function ensureTlsCert(dataDir, options) {
2020
- const tlsDir = join(dataDir, "tls");
2021
- const certPath = join(tlsDir, "camstack.crt");
2022
- const keyPath = join(tlsDir, "camstack.key");
2023
- if (existsSync(certPath) && existsSync(keyPath)) try {
2024
- const x509 = new X509Certificate(readFileSync(certPath));
2025
- if (new Date(x509.validTo) > /* @__PURE__ */ new Date()) return {
2026
- certPath,
2027
- keyPath,
2028
- generated: false
2029
- };
2030
- } catch {}
2031
- mkdirSync(tlsDir, { recursive: true });
2032
- const cn = options?.commonName ?? "camstack.local";
2033
- const validDays = options?.validDays ?? 825;
2034
- const sanDns = new Set([
2026
+ /** OID `id-kp-serverAuth`. Apple's trustd requires it on the leaf. */
2027
+ var SERVER_AUTH_OID = "1.3.6.1.5.5.7.3.1";
2028
+ /**
2029
+ * Apple's `SecPolicyCheckOtherTrustValidityPeriod` ceiling is 825 days;
2030
+ * the CA/Browser Forum's recommended maximum is 397. We sit at 397.
2031
+ */
2032
+ var MAX_LEAF_VALIDITY_DAYS = 397;
2033
+ /** Reissue this long before the leaf dies, so nobody serves an expired one. */
2034
+ var LEAF_RENEWAL_WINDOW_DAYS = 30;
2035
+ /** A local root outlives many leaves — it is the anchor an operator trusts. */
2036
+ var CA_VALIDITY_DAYS = 3650;
2037
+ var MS_PER_DAY = 864e5;
2038
+ /** Reasons that invalidate the root as well as the leaf. */
2039
+ var REASONS_REQUIRING_NEW_CA = [
2040
+ "missing",
2041
+ "unreadable",
2042
+ "no-local-ca",
2043
+ "ca-expiring"
2044
+ ];
2045
+ function reasonRequiresNewCa(reason) {
2046
+ return REASONS_REQUIRING_NEW_CA.includes(reason);
2047
+ }
2048
+ function parse$2(pem) {
2049
+ try {
2050
+ return new X509Certificate(pem);
2051
+ } catch {
2052
+ return null;
2053
+ }
2054
+ }
2055
+ function daysBetween(from, to) {
2056
+ return (to.getTime() - from.getTime()) / MS_PER_DAY;
2057
+ }
2058
+ function hasServerAuthEku(leaf) {
2059
+ const eku = leaf.keyUsage;
2060
+ return eku !== void 0 && eku.length === 1 && eku[0] === "1.3.6.1.5.5.7.3.1";
2061
+ }
2062
+ function keyMatches(leaf, keyPem) {
2063
+ try {
2064
+ return leaf.checkPrivateKey(createPrivateKey(keyPem));
2065
+ } catch {
2066
+ return false;
2067
+ }
2068
+ }
2069
+ function coversIdentity(leaf, identity) {
2070
+ for (const name of identity.requiredDnsNames) if (leaf.checkHost(name) === void 0) return false;
2071
+ for (const ip of identity.requiredIpAddresses) if (leaf.checkIP(ip) === void 0) return false;
2072
+ return true;
2073
+ }
2074
+ /**
2075
+ * `null` when the material on disk is fit to keep serving.
2076
+ *
2077
+ * Order matters only for the quality of the reported reason; any
2078
+ * non-null answer produces the same action.
2079
+ */
2080
+ function evaluateExistingCert(input) {
2081
+ const leaf = parse$2(input.chainPem);
2082
+ const ca = parse$2(input.caPem);
2083
+ if (leaf === null || ca === null) return "unreadable";
2084
+ if (daysBetween(input.now, new Date(ca.validTo)) < 427) return "ca-expiring";
2085
+ if (!keyMatches(leaf, input.keyPem)) return "key-mismatch";
2086
+ if (daysBetween(input.now, new Date(leaf.validTo)) < 30) return "expiring";
2087
+ if (!hasServerAuthEku(leaf)) return "missing-server-auth-eku";
2088
+ if (leaf.ca || !leaf.checkIssued(ca) || !leaf.verify(ca.publicKey)) return "not-issued-by-local-ca";
2089
+ if (daysBetween(new Date(leaf.validFrom), new Date(leaf.validTo)) > 398) return "validity-too-long";
2090
+ if (!coversIdentity(leaf, input.identity)) return "san-coverage-gap";
2091
+ return null;
2092
+ }
2093
+ //#endregion
2094
+ //#region src/tls/cert-identity.ts
2095
+ /**
2096
+ * What names and addresses the hub's certificate has to answer for.
2097
+ *
2098
+ * Split out of `cert-manager.ts` because it is pure and because the
2099
+ * regeneration rule (D227) is decided entirely by the difference between
2100
+ * *this* and what the certificate on disk already carries.
2101
+ */
2102
+ /**
2103
+ * Interfaces whose addresses are certified but never gate a reissue:
2104
+ * container bridges, VPN tunnels and mesh overlays hand out addresses
2105
+ * that change without the hub moving.
2106
+ */
2107
+ var VOLATILE_INTERFACE_PREFIXES = [
2108
+ "docker",
2109
+ "br-",
2110
+ "veth",
2111
+ "virbr",
2112
+ "cni",
2113
+ "flannel",
2114
+ "tun",
2115
+ "utun",
2116
+ "tap",
2117
+ "wg",
2118
+ "zt",
2119
+ "tailscale",
2120
+ "ppp",
2121
+ "awdl",
2122
+ "llw"
2123
+ ];
2124
+ /** A DNS name we are willing to hand to openssl. */
2125
+ var DNS_NAME_PATTERN = /^[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?$/;
2126
+ function isIpv4(value) {
2127
+ return isIP(value) === 4;
2128
+ }
2129
+ function isIpv6(value) {
2130
+ return isIP(value) === 6;
2131
+ }
2132
+ /**
2133
+ * `fe80::/10` and `169.254.0.0/16`. An IPv6 link-local address needs a
2134
+ * zone index to be usable and a SAN cannot carry one, so such an entry
2135
+ * can never match anything — it is noise that makes the certificate look
2136
+ * broader than it is.
2137
+ */
2138
+ function isLinkLocal(value) {
2139
+ if (isIpv4(value)) return value.startsWith("169.254.");
2140
+ if (!isIpv6(value)) return false;
2141
+ const firstGroup = value.split(":")[0] ?? "";
2142
+ if (firstGroup === "") return false;
2143
+ const parsed = Number.parseInt(firstGroup, 16);
2144
+ return Number.isFinite(parsed) && (parsed & 65472) === 65152;
2145
+ }
2146
+ /** `fc00::/7` — the IPv6 range that behaves like RFC 1918 does for v4. */
2147
+ function isUniqueLocalIpv6(value) {
2148
+ if (!isIpv6(value)) return false;
2149
+ const firstGroup = value.split(":")[0] ?? "";
2150
+ const parsed = Number.parseInt(firstGroup, 16);
2151
+ return Number.isFinite(parsed) && (parsed & 65024) === 64512;
2152
+ }
2153
+ function isVolatileInterface(name) {
2154
+ const lower = name.toLowerCase();
2155
+ return VOLATILE_INTERFACE_PREFIXES.some((prefix) => lower.startsWith(prefix));
2156
+ }
2157
+ /**
2158
+ * A global-scope IPv6 address is NOT a stable identity: SLAAC privacy
2159
+ * extensions rotate temporary addresses roughly daily, and `os` gives us
2160
+ * no way to tell a temporary address from a permanent one. Gating on
2161
+ * one would reissue the certificate every day.
2162
+ */
2163
+ function isStableGateAddress(address) {
2164
+ if (isLinkLocal(address)) return false;
2165
+ if (isIpv4(address)) return true;
2166
+ return isUniqueLocalIpv6(address);
2167
+ }
2168
+ function addSan(dns, ips, value) {
2169
+ const trimmed = value.trim();
2170
+ if (trimmed === "") return;
2171
+ if (isIP(trimmed) !== 0) {
2172
+ if (!isLinkLocal(trimmed)) ips.add(trimmed);
2173
+ return;
2174
+ }
2175
+ if (DNS_NAME_PATTERN.test(trimmed)) dns.add(trimmed);
2176
+ }
2177
+ /**
2178
+ * Snapshot the machine's identity. Called once per `ensureTlsCert`; the
2179
+ * result is both the SAN list for a new leaf and the yardstick the
2180
+ * existing leaf is measured against.
2181
+ */
2182
+ function collectCertIdentity(options) {
2183
+ const dnsNames = /* @__PURE__ */ new Set();
2184
+ const ipAddresses = new Set(["127.0.0.1", "::1"]);
2185
+ const requiredDnsNames = /* @__PURE__ */ new Set();
2186
+ const requiredIpAddresses = new Set(["127.0.0.1"]);
2187
+ for (const name of [
2035
2188
  "localhost",
2036
- cn,
2189
+ options.commonName,
2037
2190
  os$18.hostname()
2038
- ]);
2039
- const sanIps = new Set(["127.0.0.1", "::1"]);
2040
- const interfaces = os$18.networkInterfaces();
2041
- for (const addrs of Object.values(interfaces)) {
2042
- if (!addrs) continue;
2043
- for (const addr of addrs) if (!addr.internal) sanIps.add(addr.address);
2044
- }
2045
- for (const san of options?.extraSans ?? []) if (san.match(/^\d+\.\d+\.\d+\.\d+$/) || san.includes(":")) sanIps.add(san);
2046
- else sanDns.add(san);
2047
- const sanParts = [];
2048
- for (const dns of sanDns) sanParts.push(`DNS:${dns}`);
2049
- for (const ip of sanIps) sanParts.push(`IP:${ip}`);
2050
- const sanString = sanParts.join(",");
2191
+ ]) {
2192
+ addSan(dnsNames, ipAddresses, name);
2193
+ addSan(requiredDnsNames, requiredIpAddresses, name);
2194
+ }
2195
+ for (const [ifaceName, addrs] of Object.entries(os$18.networkInterfaces())) for (const addr of addrs ?? []) {
2196
+ if (addr.internal) continue;
2197
+ if (isLinkLocal(addr.address)) continue;
2198
+ ipAddresses.add(addr.address);
2199
+ if (isVolatileInterface(ifaceName)) continue;
2200
+ if (isStableGateAddress(addr.address)) requiredIpAddresses.add(addr.address);
2201
+ }
2202
+ for (const san of options.extraSans) {
2203
+ addSan(dnsNames, ipAddresses, san);
2204
+ addSan(requiredDnsNames, requiredIpAddresses, san);
2205
+ }
2206
+ return {
2207
+ dnsNames: [...dnsNames],
2208
+ ipAddresses: [...ipAddresses],
2209
+ requiredDnsNames: [...requiredDnsNames],
2210
+ requiredIpAddresses: [...requiredIpAddresses]
2211
+ };
2212
+ }
2213
+ //#endregion
2214
+ //#region src/tls/cert-openssl.ts
2215
+ /**
2216
+ * The openssl invocations. Isolated so `cert-manager.ts` reads as policy
2217
+ * and this file reads as mechanics.
2218
+ */
2219
+ var execFileAsync$3 = promisify(execFile);
2220
+ /** Subject of the local root. Distinct from the leaf's CN by construction. */
2221
+ var CA_COMMON_NAME = "CamStack Local CA";
2222
+ function sanValue(identity) {
2223
+ const parts = [];
2224
+ for (const dns of identity.dnsNames) parts.push(`DNS:${dns}`);
2225
+ for (const ip of identity.ipAddresses) if (isIP(ip) !== 0) parts.push(`IP:${ip}`);
2226
+ return parts.join(",");
2227
+ }
2228
+ /**
2229
+ * A local root: 10 years, `pathlen:0` so it can sign leaves and nothing
2230
+ * else, and **no EKU** — Apple rejects an anchor asserting
2231
+ * `anyExtendedKeyUsage`, and a root with no EKU constrains nothing.
2232
+ */
2233
+ async function generateCa(tmpDir) {
2234
+ const caCertPath = join(tmpDir, "ca.crt");
2235
+ const caKeyPath = join(tmpDir, "ca.key");
2051
2236
  await execFileAsync$3("openssl", [
2052
2237
  "req",
2053
2238
  "-x509",
2054
2239
  "-newkey",
2055
2240
  "rsa:2048",
2241
+ "-nodes",
2242
+ "-sha256",
2243
+ "-days",
2244
+ String(CA_VALIDITY_DAYS),
2245
+ "-keyout",
2246
+ caKeyPath,
2247
+ "-out",
2248
+ caCertPath,
2249
+ "-subj",
2250
+ `/CN=${CA_COMMON_NAME}`,
2251
+ "-addext",
2252
+ "basicConstraints=critical,CA:TRUE,pathlen:0",
2253
+ "-addext",
2254
+ "keyUsage=critical,keyCertSign,cRLSign",
2255
+ "-addext",
2256
+ "subjectKeyIdentifier=hash"
2257
+ ]);
2258
+ await chmod(caKeyPath, 384);
2259
+ return {
2260
+ caCertPath,
2261
+ caKeyPath
2262
+ };
2263
+ }
2264
+ /**
2265
+ * A server leaf signed by the local root: `CA:FALSE`, and
2266
+ * `extendedKeyUsage=serverAuth` — not `anyExtendedKeyUsage`, which
2267
+ * Apple's `testAppTrustRoot_AnyEKU_AfterJul2019` asserts must fail.
2268
+ */
2269
+ async function issueLeaf(tmpDir, ca, identity, commonName, validDays) {
2270
+ const keyPath = join(tmpDir, "leaf.key");
2271
+ const csrPath = join(tmpDir, "leaf.csr");
2272
+ const certPath = join(tmpDir, "leaf.crt");
2273
+ const extPath = join(tmpDir, "leaf.ext");
2274
+ await writeFile(extPath, [
2275
+ "basicConstraints=critical,CA:FALSE",
2276
+ "keyUsage=critical,digitalSignature,keyEncipherment",
2277
+ "extendedKeyUsage=serverAuth",
2278
+ "subjectKeyIdentifier=hash",
2279
+ "authorityKeyIdentifier=keyid,issuer",
2280
+ `subjectAltName=${sanValue(identity)}`,
2281
+ ""
2282
+ ].join("\n"), "utf-8");
2283
+ await execFileAsync$3("openssl", [
2284
+ "req",
2285
+ "-new",
2286
+ "-newkey",
2287
+ "rsa:2048",
2288
+ "-nodes",
2289
+ "-sha256",
2056
2290
  "-keyout",
2057
2291
  keyPath,
2058
2292
  "-out",
2059
- certPath,
2293
+ csrPath,
2294
+ "-subj",
2295
+ `/CN=${commonName}`
2296
+ ]);
2297
+ await execFileAsync$3("openssl", [
2298
+ "x509",
2299
+ "-req",
2300
+ "-in",
2301
+ csrPath,
2302
+ "-CA",
2303
+ ca.caCertPath,
2304
+ "-CAkey",
2305
+ ca.caKeyPath,
2306
+ "-set_serial",
2307
+ `0x00${randomBytes(16).toString("hex")}`,
2060
2308
  "-days",
2061
2309
  String(validDays),
2062
- "-nodes",
2063
- "-subj",
2064
- `/CN=${cn}`,
2065
- "-addext",
2066
- `subjectAltName=${sanString}`
2310
+ "-sha256",
2311
+ "-extfile",
2312
+ extPath,
2313
+ "-out",
2314
+ certPath
2067
2315
  ]);
2068
- const { chmod } = await import("node:fs/promises");
2069
2316
  await chmod(keyPath, 384);
2317
+ await rm(csrPath, { force: true });
2318
+ await rm(extPath, { force: true });
2070
2319
  return {
2071
2320
  certPath,
2072
- keyPath,
2073
- generated: true
2321
+ keyPath
2322
+ };
2323
+ }
2324
+ //#endregion
2325
+ //#region src/tls/cert-manager.ts
2326
+ var DEFAULT_COMMON_NAME = "camstack.local";
2327
+ /**
2328
+ * Ensure the hub has a usable TLS identity under `<dataDir>/tls`, made of
2329
+ * a long-lived local **root** (`camstack-ca.crt`) and a short **leaf**
2330
+ * chained to it (`camstack.crt` = leaf + root, `camstack.key`).
2331
+ *
2332
+ * ## The regeneration rule (D227)
2333
+ *
2334
+ * Reissue happens when, and only when, the material on disk **cannot do
2335
+ * its job**:
2336
+ *
2337
+ * - it is absent, unreadable, or the key does not match the certificate;
2338
+ * - the root is absent (every pre-2026-08 install: one self-signed
2339
+ * `CA:TRUE` leaf and no root) or is inside its renewal window;
2340
+ * - the leaf is inside its 30-day renewal window;
2341
+ * - the leaf lacks `extendedKeyUsage = serverAuth` — Apple's trustd
2342
+ * rejects an app-supplied anchor without it;
2343
+ * - the leaf is not actually issued by the root on disk;
2344
+ * - the leaf's validity span exceeds {@link MAX_LEAF_VALIDITY_DAYS};
2345
+ * - **a name or address the hub must answer for is missing from the SAN
2346
+ * list** — a DHCP move, a new interface or a new hostname.
2347
+ *
2348
+ * And explicitly NOT when: the certificate names an address the machine
2349
+ * no longer has (a superset still works — the check is one-directional),
2350
+ * a volatile interface (docker/VPN/mesh) or a global-scope IPv6 address
2351
+ * changed (SLAAC privacy addresses rotate daily), or a new build shipped.
2352
+ *
2353
+ * Every trigger is derived from the files plus the machine, so once a
2354
+ * reissue clears them it clears them for good: the next boot returns
2355
+ * `generated: false`. A certificate that moved on every boot would
2356
+ * invalidate every trust an operator granted by hand — worse than the
2357
+ * bugs this rule fixes.
2358
+ *
2359
+ * **The root is rotated only for root-level reasons.** SAN drift rotates
2360
+ * the leaf alone, so trust granted to `camstack-ca.crt` survives every
2361
+ * address change the hub will ever see.
2362
+ */
2363
+ async function ensureTlsCert(dataDir, options) {
2364
+ const tlsDir = join(dataDir, "tls");
2365
+ const paths = {
2366
+ certPath: join(tlsDir, "camstack.crt"),
2367
+ keyPath: join(tlsDir, "camstack.key"),
2368
+ caCertPath: join(tlsDir, "camstack-ca.crt"),
2369
+ caKeyPath: join(tlsDir, "camstack-ca.key")
2370
+ };
2371
+ const commonName = options?.commonName ?? DEFAULT_COMMON_NAME;
2372
+ const validDays = Math.min(options?.validDays ?? 397, 397);
2373
+ const identity = collectCertIdentity({
2374
+ commonName,
2375
+ extraSans: options?.extraSans ?? []
2376
+ });
2377
+ const reason = decideRegeneration(paths, identity);
2378
+ if (reason === null) return describe(paths, false, null, false, null);
2379
+ const previousFingerprint = readFingerprint(paths.certPath);
2380
+ const caRotated = reasonRequiresNewCa(reason);
2381
+ mkdirSync(tlsDir, { recursive: true });
2382
+ await regenerate(paths, identity, commonName, validDays, caRotated);
2383
+ return describe(paths, true, reason, caRotated, previousFingerprint);
2384
+ }
2385
+ /** Fingerprint of whatever is on disk, or `null` if there is nothing readable. */
2386
+ function readFingerprint(certPath) {
2387
+ try {
2388
+ return new X509Certificate(readFileSync(certPath)).fingerprint256;
2389
+ } catch {
2390
+ return null;
2391
+ }
2392
+ }
2393
+ function decideRegeneration(paths, identity) {
2394
+ if (!existsSync(paths.certPath) || !existsSync(paths.keyPath)) return "missing";
2395
+ if (!existsSync(paths.caCertPath) || !existsSync(paths.caKeyPath)) return "no-local-ca";
2396
+ try {
2397
+ return evaluateExistingCert({
2398
+ chainPem: readFileSync(paths.certPath, "utf-8"),
2399
+ keyPem: readFileSync(paths.keyPath, "utf-8"),
2400
+ caPem: readFileSync(paths.caCertPath, "utf-8"),
2401
+ identity,
2402
+ now: /* @__PURE__ */ new Date()
2403
+ });
2404
+ } catch {
2405
+ return "unreadable";
2406
+ }
2407
+ }
2408
+ /**
2409
+ * Build every file in a scratch directory, then move them into place.
2410
+ * A crash mid-way leaves the previous material untouched; a crash
2411
+ * between the moves leaves a key/certificate mismatch, which the
2412
+ * evaluator reports as `key-mismatch` and heals on the next boot.
2413
+ */
2414
+ async function regenerate(paths, identity, commonName, validDays, newCa) {
2415
+ const scratch = await mkdtemp(join(tmpdir(), "camstack-tls-"));
2416
+ try {
2417
+ const ca = newCa ? await generateCa(scratch) : {
2418
+ caCertPath: paths.caCertPath,
2419
+ caKeyPath: paths.caKeyPath
2420
+ };
2421
+ const leaf = await issueLeaf(scratch, ca, identity, commonName, validDays);
2422
+ const chainPath = join(scratch, "chain.crt");
2423
+ await writeFile(chainPath, `${readFileSync(leaf.certPath, "utf-8").trimEnd()}\n${readFileSync(ca.caCertPath, "utf-8").trimEnd()}\n`, "utf-8");
2424
+ if (newCa) {
2425
+ await copyFile(ca.caCertPath, `${paths.caCertPath}.new`);
2426
+ await copyFile(ca.caKeyPath, `${paths.caKeyPath}.new`);
2427
+ renameSync(`${paths.caCertPath}.new`, paths.caCertPath);
2428
+ renameSync(`${paths.caKeyPath}.new`, paths.caKeyPath);
2429
+ await chmod(paths.caKeyPath, 384);
2430
+ }
2431
+ await copyFile(leaf.keyPath, `${paths.keyPath}.new`);
2432
+ await copyFile(chainPath, `${paths.certPath}.new`);
2433
+ renameSync(`${paths.keyPath}.new`, paths.keyPath);
2434
+ renameSync(`${paths.certPath}.new`, paths.certPath);
2435
+ await chmod(paths.keyPath, 384);
2436
+ } finally {
2437
+ await rm(scratch, {
2438
+ recursive: true,
2439
+ force: true
2440
+ });
2441
+ }
2442
+ }
2443
+ function describe(paths, generated, reason, caRotated, previousFingerprintSha256) {
2444
+ const leaf = new X509Certificate(readFileSync(paths.certPath));
2445
+ const ca = new X509Certificate(readFileSync(paths.caCertPath));
2446
+ const san = leaf.subjectAltName ?? "";
2447
+ return {
2448
+ ...paths,
2449
+ generated,
2450
+ reason,
2451
+ caRotated,
2452
+ fingerprintSha256: leaf.fingerprint256,
2453
+ previousFingerprintSha256,
2454
+ caFingerprintSha256: ca.fingerprint256,
2455
+ validTo: new Date(leaf.validTo).toISOString(),
2456
+ sans: san === "" ? [] : san.split(", ")
2074
2457
  };
2075
2458
  }
2076
2459
  /**
2077
- * Load TLS cert+key from files. Returns Buffers suitable for Node.js TLS options.
2460
+ * Load TLS cert+key from files. Returns Buffers suitable for Node.js TLS
2461
+ * options. `certPath` is a full chain, so the listener presents the local
2462
+ * root alongside the leaf.
2078
2463
  */
2079
2464
  function loadTlsCert(certPath, keyPath) {
2080
2465
  return {
@@ -94888,4 +95273,4 @@ var LifecycleJobEngine = class {
94888
95273
  }
94889
95274
  };
94890
95275
  //#endregion
94891
- 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, 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, 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, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LokiDestination, LokiLoggingAddon, 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, 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, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyAddonDir, classifyCapRoute, clearPendingRestart, clusterEventTopic, clusterSecretMatches, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, 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, expandCapMethods, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isAddonDeploySource, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, quarantineAddonResidue, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runNpm, scheduleSelfRestart, scopeKey, scopesAllowAddon, scopesAllowDeviceCap, selectAddonResidue, serializeTypedArrays, setHubConnected, setNodeEventInterest, shouldReclaim, startHeapWatch, startRunnerHeapWatch, strandedMb, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
95276
+ 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, 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, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyAddonDir, classifyCapRoute, clearPendingRestart, clusterEventTopic, clusterSecretMatches, collectCertIdentity, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, 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, ipcChildLink, ipcParentLink, isAddonDeploySource, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, quarantineAddonResidue, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runNpm, scheduleSelfRestart, scopeKey, scopesAllowAddon, scopesAllowDeviceCap, selectAddonResidue, serializeTypedArrays, setHubConnected, setNodeEventInterest, shouldReclaim, startHeapWatch, startRunnerHeapWatch, strandedMb, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
@@ -1,4 +1,4 @@
1
- import { kt as asJsonObject } from "./dist-Duz_uJSQ.mjs";
1
+ import { kt as asJsonObject } from "./dist-Drb54SZD.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-B2CtwVW2.js");
1
+ const require_dist = require("./dist-iAwSA2_f.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?
@@ -0,0 +1,41 @@
1
+ import { CertIdentity } from './cert-identity.js';
2
+ /** OID `id-kp-serverAuth`. Apple's trustd requires it on the leaf. */
3
+ export declare const SERVER_AUTH_OID = "1.3.6.1.5.5.7.3.1";
4
+ /**
5
+ * Apple's `SecPolicyCheckOtherTrustValidityPeriod` ceiling is 825 days;
6
+ * the CA/Browser Forum's recommended maximum is 397. We sit at 397.
7
+ */
8
+ export declare const MAX_LEAF_VALIDITY_DAYS = 397;
9
+ /** Reissue this long before the leaf dies, so nobody serves an expired one. */
10
+ export declare const LEAF_RENEWAL_WINDOW_DAYS = 30;
11
+ /** A local root outlives many leaves — it is the anchor an operator trusts. */
12
+ export declare const CA_VALIDITY_DAYS = 3650;
13
+ /**
14
+ * The CA must outlive any leaf it signs by a margin, or a leaf issued
15
+ * today expires after its own issuer.
16
+ */
17
+ export declare const CA_RENEWAL_WINDOW_DAYS: number;
18
+ /**
19
+ * Why a reissue is happening. Reported on `EnsureTlsResult` and logged
20
+ * verbatim — an operator who trusted the old certificate by hand learns
21
+ * from this string, not from a browser error.
22
+ */
23
+ export type CertRegenerationReason = 'missing' | 'unreadable' | 'no-local-ca' | 'ca-expiring' | 'key-mismatch' | 'expiring' | 'missing-server-auth-eku' | 'not-issued-by-local-ca' | 'validity-too-long' | 'san-coverage-gap';
24
+ export interface CertEvaluationInput {
25
+ /** Contents of `camstack.crt` — leaf first, then the issuing CA. */
26
+ readonly chainPem: string;
27
+ /** Contents of `camstack.key` — the leaf's private key. */
28
+ readonly keyPem: string;
29
+ /** Contents of `camstack-ca.crt` — the local root. */
30
+ readonly caPem: string;
31
+ readonly identity: CertIdentity;
32
+ readonly now: Date;
33
+ }
34
+ export declare function reasonRequiresNewCa(reason: CertRegenerationReason): boolean;
35
+ /**
36
+ * `null` when the material on disk is fit to keep serving.
37
+ *
38
+ * Order matters only for the quality of the reported reason; any
39
+ * non-null answer produces the same action.
40
+ */
41
+ export declare function evaluateExistingCert(input: CertEvaluationInput): CertRegenerationReason | null;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Names/addresses that go INTO the certificate, and the subset whose
3
+ * absence forces a reissue.
4
+ *
5
+ * The two are deliberately different sizes. Everything plausible is
6
+ * certified — costs nothing. Only a **stable** identity gates
7
+ * regeneration, because a gate that fires on a rotating address rewrites
8
+ * the certificate on every boot, which is a worse bug than the one the
9
+ * gate exists to fix.
10
+ */
11
+ export interface CertIdentity {
12
+ readonly dnsNames: readonly string[];
13
+ readonly ipAddresses: readonly string[];
14
+ readonly requiredDnsNames: readonly string[];
15
+ readonly requiredIpAddresses: readonly string[];
16
+ }
17
+ export interface CertIdentityOptions {
18
+ readonly commonName: string;
19
+ readonly extraSans: readonly string[];
20
+ }
21
+ export declare function isIpv4(value: string): boolean;
22
+ export declare function isIpv6(value: string): boolean;
23
+ /**
24
+ * `fe80::/10` and `169.254.0.0/16`. An IPv6 link-local address needs a
25
+ * zone index to be usable and a SAN cannot carry one, so such an entry
26
+ * can never match anything — it is noise that makes the certificate look
27
+ * broader than it is.
28
+ */
29
+ export declare function isLinkLocal(value: string): boolean;
30
+ /** `fc00::/7` — the IPv6 range that behaves like RFC 1918 does for v4. */
31
+ export declare function isUniqueLocalIpv6(value: string): boolean;
32
+ /**
33
+ * Snapshot the machine's identity. Called once per `ensureTlsCert`; the
34
+ * result is both the SAN list for a new leaf and the yardstick the
35
+ * existing leaf is measured against.
36
+ */
37
+ export declare function collectCertIdentity(options: CertIdentityOptions): CertIdentity;