@camstack/system 1.2.115 → 1.2.117

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/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  15. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  17. package/dist/builtins/hub-forwarder/index.js +1 -1
  18. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  19. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  20. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  21. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  22. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  23. package/dist/builtins/local-network/ipv6-lan.d.ts +26 -0
  24. package/dist/builtins/local-network/local-hostname.d.ts +15 -0
  25. package/dist/builtins/local-network/local-network.addon.d.ts +59 -21
  26. package/dist/builtins/local-network/local-network.addon.js +369 -50
  27. package/dist/builtins/local-network/local-network.addon.mjs +365 -51
  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 +2 -2
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  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-Drb54SZD.mjs → dist-D4Vmj8iw.mjs} +151 -9
  49. package/dist/{dist-iAwSA2_f.js → dist-DMEGlqg6.js} +151 -9
  50. package/dist/http/lan-http-bind.d.ts +30 -0
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.js +30 -472
  53. package/dist/index.mjs +11 -468
  54. package/dist/lan-http-bind-DmgpFP6_.js +890 -0
  55. package/dist/lan-http-bind-jKrj6OjQ.mjs +752 -0
  56. package/dist/{retired-settings-keys-Dz5-7gjt.mjs → retired-settings-keys--aM_vGF9.mjs} +1 -1
  57. package/dist/{retired-settings-keys-OfhqQio4.js → retired-settings-keys-DET4YkAU.js} +1 -1
  58. package/dist/tls/cert-manager.d.ts +11 -0
  59. package/dist/tls/index.d.ts +3 -1
  60. package/dist/tls/tls-status.d.ts +16 -0
  61. package/dist/tls/uploaded-cert.d.ts +14 -0
  62. package/package.json +1 -1
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-iAwSA2_f.js");
3
+ const require_dist = require("./dist-DMEGlqg6.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");
@@ -27,6 +27,7 @@ const require_builtins_winston_logging_index = require("./builtins/winston-loggi
27
27
  const require_file_data_plane = require("./file-data-plane-DUHPHa-Y.js");
28
28
  const require_manifest_python_deps = require("./manifest-python-deps-CwBbX4Ut.js");
29
29
  const require_resource_monitor = require("./resource-monitor-CdnzxBLP.js");
30
+ const require_lan_http_bind = require("./lan-http-bind-DmgpFP6_.js");
30
31
  const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
31
32
  let zod = require("zod");
32
33
  let node_crypto = require("node:crypto");
@@ -46,7 +47,6 @@ let node_http = require("node:http");
46
47
  let node_vm = require("node:vm");
47
48
  node_vm = require_chunk.__toESM(node_vm);
48
49
  let _camstack_types_addon = require("@camstack/types/addon");
49
- let node_net = require("node:net");
50
50
  let node_url = require("node:url");
51
51
  let node_events = require("node:events");
52
52
  let node_stream = require("node:stream");
@@ -1516,7 +1516,7 @@ var PipelineValidator = class {
1516
1516
  };
1517
1517
  //#endregion
1518
1518
  //#region src/python/python-env-manager.ts
1519
- var execFileAsync$4 = (0, node_util.promisify)(node_child_process.execFile);
1519
+ var execFileAsync$3 = (0, node_util.promisify)(node_child_process.execFile);
1520
1520
  var PythonEnvManager = class {
1521
1521
  venvPath;
1522
1522
  cachedProbe = null;
@@ -1526,12 +1526,12 @@ var PythonEnvManager = class {
1526
1526
  async probe() {
1527
1527
  if (this.cachedProbe) return this.cachedProbe;
1528
1528
  for (const cmd of ["python3", "python"]) try {
1529
- const { stdout } = await execFileAsync$4(cmd, ["--version"]);
1529
+ const { stdout } = await execFileAsync$3(cmd, ["--version"]);
1530
1530
  const version = stdout.trim().replace("Python ", "");
1531
1531
  const major = parseInt(version.split(".")[0] ?? "0", 10);
1532
1532
  const minor = parseInt(version.split(".")[1] ?? "0", 10);
1533
1533
  if (major < 3 || major === 3 && minor < 10) continue;
1534
- const { stdout: pathOut } = await execFileAsync$4(cmd, ["-c", "import sys; print(sys.executable)"]);
1534
+ const { stdout: pathOut } = await execFileAsync$3(cmd, ["-c", "import sys; print(sys.executable)"]);
1535
1535
  this.cachedProbe = {
1536
1536
  available: true,
1537
1537
  version,
@@ -1547,13 +1547,13 @@ var PythonEnvManager = class {
1547
1547
  async ensure(options) {
1548
1548
  const probe = await this.probe();
1549
1549
  if (!probe.available || !probe.path) throw new Error("Python 3.10+ is required but not found on this system");
1550
- if (!node_fs.existsSync(node_path.join(this.venvPath, "bin", "python"))) await execFileAsync$4(probe.path, [
1550
+ if (!node_fs.existsSync(node_path.join(this.venvPath, "bin", "python"))) await execFileAsync$3(probe.path, [
1551
1551
  "-m",
1552
1552
  "venv",
1553
1553
  this.venvPath
1554
1554
  ]);
1555
1555
  const venvPython = node_path.join(this.venvPath, "bin", "python");
1556
- if (options.packages.length > 0) await execFileAsync$4(venvPython, [
1556
+ if (options.packages.length > 0) await execFileAsync$3(venvPython, [
1557
1557
  "-m",
1558
1558
  "pip",
1559
1559
  "install",
@@ -2018,462 +2018,6 @@ var StorageManager = class {
2018
2018
  }
2019
2019
  };
2020
2020
  //#endregion
2021
- //#region src/tls/cert-evaluation.ts
2022
- /**
2023
- * The regeneration rule (D227), as a pure function.
2024
- *
2025
- * `ensureTlsCert` reissues **only** when the material on disk cannot do
2026
- * its job. Never because it is old, never because the machine picked up
2027
- * an address the certificate does not need, never because a new build
2028
- * shipped. The decision is a function of (files on disk, machine
2029
- * identity, clock) alone, so the same inputs always produce the same
2030
- * answer — which is what makes a reissue happen once and then stop.
2031
- */
2032
- /** OID `id-kp-serverAuth`. Apple's trustd requires it on the leaf. */
2033
- var SERVER_AUTH_OID = "1.3.6.1.5.5.7.3.1";
2034
- /**
2035
- * Apple's `SecPolicyCheckOtherTrustValidityPeriod` ceiling is 825 days;
2036
- * the CA/Browser Forum's recommended maximum is 397. We sit at 397.
2037
- */
2038
- var MAX_LEAF_VALIDITY_DAYS = 397;
2039
- /** Reissue this long before the leaf dies, so nobody serves an expired one. */
2040
- var LEAF_RENEWAL_WINDOW_DAYS = 30;
2041
- /** A local root outlives many leaves — it is the anchor an operator trusts. */
2042
- var CA_VALIDITY_DAYS = 3650;
2043
- var MS_PER_DAY = 864e5;
2044
- /** Reasons that invalidate the root as well as the leaf. */
2045
- var REASONS_REQUIRING_NEW_CA = [
2046
- "missing",
2047
- "unreadable",
2048
- "no-local-ca",
2049
- "ca-expiring"
2050
- ];
2051
- function reasonRequiresNewCa(reason) {
2052
- return REASONS_REQUIRING_NEW_CA.includes(reason);
2053
- }
2054
- function parse$1(pem) {
2055
- try {
2056
- return new node_crypto.X509Certificate(pem);
2057
- } catch {
2058
- return null;
2059
- }
2060
- }
2061
- function daysBetween(from, to) {
2062
- return (to.getTime() - from.getTime()) / MS_PER_DAY;
2063
- }
2064
- function hasServerAuthEku(leaf) {
2065
- const eku = leaf.keyUsage;
2066
- return eku !== void 0 && eku.length === 1 && eku[0] === "1.3.6.1.5.5.7.3.1";
2067
- }
2068
- function keyMatches(leaf, keyPem) {
2069
- try {
2070
- return leaf.checkPrivateKey((0, node_crypto.createPrivateKey)(keyPem));
2071
- } catch {
2072
- return false;
2073
- }
2074
- }
2075
- function coversIdentity(leaf, identity) {
2076
- for (const name of identity.requiredDnsNames) if (leaf.checkHost(name) === void 0) return false;
2077
- for (const ip of identity.requiredIpAddresses) if (leaf.checkIP(ip) === void 0) return false;
2078
- return true;
2079
- }
2080
- /**
2081
- * `null` when the material on disk is fit to keep serving.
2082
- *
2083
- * Order matters only for the quality of the reported reason; any
2084
- * non-null answer produces the same action.
2085
- */
2086
- function evaluateExistingCert(input) {
2087
- const leaf = parse$1(input.chainPem);
2088
- const ca = parse$1(input.caPem);
2089
- if (leaf === null || ca === null) return "unreadable";
2090
- if (daysBetween(input.now, new Date(ca.validTo)) < 427) return "ca-expiring";
2091
- if (!keyMatches(leaf, input.keyPem)) return "key-mismatch";
2092
- if (daysBetween(input.now, new Date(leaf.validTo)) < 30) return "expiring";
2093
- if (!hasServerAuthEku(leaf)) return "missing-server-auth-eku";
2094
- if (leaf.ca || !leaf.checkIssued(ca) || !leaf.verify(ca.publicKey)) return "not-issued-by-local-ca";
2095
- if (daysBetween(new Date(leaf.validFrom), new Date(leaf.validTo)) > 398) return "validity-too-long";
2096
- if (!coversIdentity(leaf, input.identity)) return "san-coverage-gap";
2097
- return null;
2098
- }
2099
- //#endregion
2100
- //#region src/tls/cert-identity.ts
2101
- /**
2102
- * What names and addresses the hub's certificate has to answer for.
2103
- *
2104
- * Split out of `cert-manager.ts` because it is pure and because the
2105
- * regeneration rule (D227) is decided entirely by the difference between
2106
- * *this* and what the certificate on disk already carries.
2107
- */
2108
- /**
2109
- * Interfaces whose addresses are certified but never gate a reissue:
2110
- * container bridges, VPN tunnels and mesh overlays hand out addresses
2111
- * that change without the hub moving.
2112
- */
2113
- var VOLATILE_INTERFACE_PREFIXES = [
2114
- "docker",
2115
- "br-",
2116
- "veth",
2117
- "virbr",
2118
- "cni",
2119
- "flannel",
2120
- "tun",
2121
- "utun",
2122
- "tap",
2123
- "wg",
2124
- "zt",
2125
- "tailscale",
2126
- "ppp",
2127
- "awdl",
2128
- "llw"
2129
- ];
2130
- /** A DNS name we are willing to hand to openssl. */
2131
- var DNS_NAME_PATTERN = /^[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?$/;
2132
- function isIpv4(value) {
2133
- return (0, node_net.isIP)(value) === 4;
2134
- }
2135
- function isIpv6(value) {
2136
- return (0, node_net.isIP)(value) === 6;
2137
- }
2138
- /**
2139
- * `fe80::/10` and `169.254.0.0/16`. An IPv6 link-local address needs a
2140
- * zone index to be usable and a SAN cannot carry one, so such an entry
2141
- * can never match anything — it is noise that makes the certificate look
2142
- * broader than it is.
2143
- */
2144
- function isLinkLocal(value) {
2145
- if (isIpv4(value)) return value.startsWith("169.254.");
2146
- if (!isIpv6(value)) return false;
2147
- const firstGroup = value.split(":")[0] ?? "";
2148
- if (firstGroup === "") return false;
2149
- const parsed = Number.parseInt(firstGroup, 16);
2150
- return Number.isFinite(parsed) && (parsed & 65472) === 65152;
2151
- }
2152
- /** `fc00::/7` — the IPv6 range that behaves like RFC 1918 does for v4. */
2153
- function isUniqueLocalIpv6(value) {
2154
- if (!isIpv6(value)) return false;
2155
- const firstGroup = value.split(":")[0] ?? "";
2156
- const parsed = Number.parseInt(firstGroup, 16);
2157
- return Number.isFinite(parsed) && (parsed & 65024) === 64512;
2158
- }
2159
- function isVolatileInterface(name) {
2160
- const lower = name.toLowerCase();
2161
- return VOLATILE_INTERFACE_PREFIXES.some((prefix) => lower.startsWith(prefix));
2162
- }
2163
- /**
2164
- * A global-scope IPv6 address is NOT a stable identity: SLAAC privacy
2165
- * extensions rotate temporary addresses roughly daily, and `os` gives us
2166
- * no way to tell a temporary address from a permanent one. Gating on
2167
- * one would reissue the certificate every day.
2168
- */
2169
- function isStableGateAddress(address) {
2170
- if (isLinkLocal(address)) return false;
2171
- if (isIpv4(address)) return true;
2172
- return isUniqueLocalIpv6(address);
2173
- }
2174
- function addSan(dns, ips, value) {
2175
- const trimmed = value.trim();
2176
- if (trimmed === "") return;
2177
- if ((0, node_net.isIP)(trimmed) !== 0) {
2178
- if (!isLinkLocal(trimmed)) ips.add(trimmed);
2179
- return;
2180
- }
2181
- if (DNS_NAME_PATTERN.test(trimmed)) dns.add(trimmed);
2182
- }
2183
- /**
2184
- * Snapshot the machine's identity. Called once per `ensureTlsCert`; the
2185
- * result is both the SAN list for a new leaf and the yardstick the
2186
- * existing leaf is measured against.
2187
- */
2188
- function collectCertIdentity(options) {
2189
- const dnsNames = /* @__PURE__ */ new Set();
2190
- const ipAddresses = new Set(["127.0.0.1", "::1"]);
2191
- const requiredDnsNames = /* @__PURE__ */ new Set();
2192
- const requiredIpAddresses = new Set(["127.0.0.1"]);
2193
- for (const name of [
2194
- "localhost",
2195
- options.commonName,
2196
- node_os.hostname()
2197
- ]) {
2198
- addSan(dnsNames, ipAddresses, name);
2199
- addSan(requiredDnsNames, requiredIpAddresses, name);
2200
- }
2201
- for (const [ifaceName, addrs] of Object.entries(node_os.networkInterfaces())) for (const addr of addrs ?? []) {
2202
- if (addr.internal) continue;
2203
- if (isLinkLocal(addr.address)) continue;
2204
- ipAddresses.add(addr.address);
2205
- if (isVolatileInterface(ifaceName)) continue;
2206
- if (isStableGateAddress(addr.address)) requiredIpAddresses.add(addr.address);
2207
- }
2208
- for (const san of options.extraSans) {
2209
- addSan(dnsNames, ipAddresses, san);
2210
- addSan(requiredDnsNames, requiredIpAddresses, san);
2211
- }
2212
- return {
2213
- dnsNames: [...dnsNames],
2214
- ipAddresses: [...ipAddresses],
2215
- requiredDnsNames: [...requiredDnsNames],
2216
- requiredIpAddresses: [...requiredIpAddresses]
2217
- };
2218
- }
2219
- //#endregion
2220
- //#region src/tls/cert-openssl.ts
2221
- /**
2222
- * The openssl invocations. Isolated so `cert-manager.ts` reads as policy
2223
- * and this file reads as mechanics.
2224
- */
2225
- var execFileAsync$3 = (0, node_util.promisify)(node_child_process.execFile);
2226
- /** Subject of the local root. Distinct from the leaf's CN by construction. */
2227
- var CA_COMMON_NAME = "CamStack Local CA";
2228
- function sanValue(identity) {
2229
- const parts = [];
2230
- for (const dns of identity.dnsNames) parts.push(`DNS:${dns}`);
2231
- for (const ip of identity.ipAddresses) if ((0, node_net.isIP)(ip) !== 0) parts.push(`IP:${ip}`);
2232
- return parts.join(",");
2233
- }
2234
- /**
2235
- * A local root: 10 years, `pathlen:0` so it can sign leaves and nothing
2236
- * else, and **no EKU** — Apple rejects an anchor asserting
2237
- * `anyExtendedKeyUsage`, and a root with no EKU constrains nothing.
2238
- */
2239
- async function generateCa(tmpDir) {
2240
- const caCertPath = (0, node_path.join)(tmpDir, "ca.crt");
2241
- const caKeyPath = (0, node_path.join)(tmpDir, "ca.key");
2242
- await execFileAsync$3("openssl", [
2243
- "req",
2244
- "-x509",
2245
- "-newkey",
2246
- "rsa:2048",
2247
- "-nodes",
2248
- "-sha256",
2249
- "-days",
2250
- String(CA_VALIDITY_DAYS),
2251
- "-keyout",
2252
- caKeyPath,
2253
- "-out",
2254
- caCertPath,
2255
- "-subj",
2256
- `/CN=${CA_COMMON_NAME}`,
2257
- "-addext",
2258
- "basicConstraints=critical,CA:TRUE,pathlen:0",
2259
- "-addext",
2260
- "keyUsage=critical,keyCertSign,cRLSign",
2261
- "-addext",
2262
- "subjectKeyIdentifier=hash"
2263
- ]);
2264
- await (0, node_fs_promises.chmod)(caKeyPath, 384);
2265
- return {
2266
- caCertPath,
2267
- caKeyPath
2268
- };
2269
- }
2270
- /**
2271
- * A server leaf signed by the local root: `CA:FALSE`, and
2272
- * `extendedKeyUsage=serverAuth` — not `anyExtendedKeyUsage`, which
2273
- * Apple's `testAppTrustRoot_AnyEKU_AfterJul2019` asserts must fail.
2274
- */
2275
- async function issueLeaf(tmpDir, ca, identity, commonName, validDays) {
2276
- const keyPath = (0, node_path.join)(tmpDir, "leaf.key");
2277
- const csrPath = (0, node_path.join)(tmpDir, "leaf.csr");
2278
- const certPath = (0, node_path.join)(tmpDir, "leaf.crt");
2279
- const extPath = (0, node_path.join)(tmpDir, "leaf.ext");
2280
- await (0, node_fs_promises.writeFile)(extPath, [
2281
- "basicConstraints=critical,CA:FALSE",
2282
- "keyUsage=critical,digitalSignature,keyEncipherment",
2283
- "extendedKeyUsage=serverAuth",
2284
- "subjectKeyIdentifier=hash",
2285
- "authorityKeyIdentifier=keyid,issuer",
2286
- `subjectAltName=${sanValue(identity)}`,
2287
- ""
2288
- ].join("\n"), "utf-8");
2289
- await execFileAsync$3("openssl", [
2290
- "req",
2291
- "-new",
2292
- "-newkey",
2293
- "rsa:2048",
2294
- "-nodes",
2295
- "-sha256",
2296
- "-keyout",
2297
- keyPath,
2298
- "-out",
2299
- csrPath,
2300
- "-subj",
2301
- `/CN=${commonName}`
2302
- ]);
2303
- await execFileAsync$3("openssl", [
2304
- "x509",
2305
- "-req",
2306
- "-in",
2307
- csrPath,
2308
- "-CA",
2309
- ca.caCertPath,
2310
- "-CAkey",
2311
- ca.caKeyPath,
2312
- "-set_serial",
2313
- `0x00${(0, node_crypto.randomBytes)(16).toString("hex")}`,
2314
- "-days",
2315
- String(validDays),
2316
- "-sha256",
2317
- "-extfile",
2318
- extPath,
2319
- "-out",
2320
- certPath
2321
- ]);
2322
- await (0, node_fs_promises.chmod)(keyPath, 384);
2323
- await (0, node_fs_promises.rm)(csrPath, { force: true });
2324
- await (0, node_fs_promises.rm)(extPath, { force: true });
2325
- return {
2326
- certPath,
2327
- keyPath
2328
- };
2329
- }
2330
- //#endregion
2331
- //#region src/tls/cert-manager.ts
2332
- var DEFAULT_COMMON_NAME = "camstack.local";
2333
- /**
2334
- * Ensure the hub has a usable TLS identity under `<dataDir>/tls`, made of
2335
- * a long-lived local **root** (`camstack-ca.crt`) and a short **leaf**
2336
- * chained to it (`camstack.crt` = leaf + root, `camstack.key`).
2337
- *
2338
- * ## The regeneration rule (D227)
2339
- *
2340
- * Reissue happens when, and only when, the material on disk **cannot do
2341
- * its job**:
2342
- *
2343
- * - it is absent, unreadable, or the key does not match the certificate;
2344
- * - the root is absent (every pre-2026-08 install: one self-signed
2345
- * `CA:TRUE` leaf and no root) or is inside its renewal window;
2346
- * - the leaf is inside its 30-day renewal window;
2347
- * - the leaf lacks `extendedKeyUsage = serverAuth` — Apple's trustd
2348
- * rejects an app-supplied anchor without it;
2349
- * - the leaf is not actually issued by the root on disk;
2350
- * - the leaf's validity span exceeds {@link MAX_LEAF_VALIDITY_DAYS};
2351
- * - **a name or address the hub must answer for is missing from the SAN
2352
- * list** — a DHCP move, a new interface or a new hostname.
2353
- *
2354
- * And explicitly NOT when: the certificate names an address the machine
2355
- * no longer has (a superset still works — the check is one-directional),
2356
- * a volatile interface (docker/VPN/mesh) or a global-scope IPv6 address
2357
- * changed (SLAAC privacy addresses rotate daily), or a new build shipped.
2358
- *
2359
- * Every trigger is derived from the files plus the machine, so once a
2360
- * reissue clears them it clears them for good: the next boot returns
2361
- * `generated: false`. A certificate that moved on every boot would
2362
- * invalidate every trust an operator granted by hand — worse than the
2363
- * bugs this rule fixes.
2364
- *
2365
- * **The root is rotated only for root-level reasons.** SAN drift rotates
2366
- * the leaf alone, so trust granted to `camstack-ca.crt` survives every
2367
- * address change the hub will ever see.
2368
- */
2369
- async function ensureTlsCert(dataDir, options) {
2370
- const tlsDir = (0, node_path.join)(dataDir, "tls");
2371
- const paths = {
2372
- certPath: (0, node_path.join)(tlsDir, "camstack.crt"),
2373
- keyPath: (0, node_path.join)(tlsDir, "camstack.key"),
2374
- caCertPath: (0, node_path.join)(tlsDir, "camstack-ca.crt"),
2375
- caKeyPath: (0, node_path.join)(tlsDir, "camstack-ca.key")
2376
- };
2377
- const commonName = options?.commonName ?? DEFAULT_COMMON_NAME;
2378
- const validDays = Math.min(options?.validDays ?? 397, 397);
2379
- const identity = collectCertIdentity({
2380
- commonName,
2381
- extraSans: options?.extraSans ?? []
2382
- });
2383
- const reason = decideRegeneration(paths, identity);
2384
- if (reason === null) return describe(paths, false, null, false, null);
2385
- const previousFingerprint = readFingerprint(paths.certPath);
2386
- const caRotated = reasonRequiresNewCa(reason);
2387
- (0, node_fs.mkdirSync)(tlsDir, { recursive: true });
2388
- await regenerate(paths, identity, commonName, validDays, caRotated);
2389
- return describe(paths, true, reason, caRotated, previousFingerprint);
2390
- }
2391
- /** Fingerprint of whatever is on disk, or `null` if there is nothing readable. */
2392
- function readFingerprint(certPath) {
2393
- try {
2394
- return new node_crypto.X509Certificate((0, node_fs.readFileSync)(certPath)).fingerprint256;
2395
- } catch {
2396
- return null;
2397
- }
2398
- }
2399
- function decideRegeneration(paths, identity) {
2400
- if (!(0, node_fs.existsSync)(paths.certPath) || !(0, node_fs.existsSync)(paths.keyPath)) return "missing";
2401
- if (!(0, node_fs.existsSync)(paths.caCertPath) || !(0, node_fs.existsSync)(paths.caKeyPath)) return "no-local-ca";
2402
- try {
2403
- return evaluateExistingCert({
2404
- chainPem: (0, node_fs.readFileSync)(paths.certPath, "utf-8"),
2405
- keyPem: (0, node_fs.readFileSync)(paths.keyPath, "utf-8"),
2406
- caPem: (0, node_fs.readFileSync)(paths.caCertPath, "utf-8"),
2407
- identity,
2408
- now: /* @__PURE__ */ new Date()
2409
- });
2410
- } catch {
2411
- return "unreadable";
2412
- }
2413
- }
2414
- /**
2415
- * Build every file in a scratch directory, then move them into place.
2416
- * A crash mid-way leaves the previous material untouched; a crash
2417
- * between the moves leaves a key/certificate mismatch, which the
2418
- * evaluator reports as `key-mismatch` and heals on the next boot.
2419
- */
2420
- async function regenerate(paths, identity, commonName, validDays, newCa) {
2421
- const scratch = await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "camstack-tls-"));
2422
- try {
2423
- const ca = newCa ? await generateCa(scratch) : {
2424
- caCertPath: paths.caCertPath,
2425
- caKeyPath: paths.caKeyPath
2426
- };
2427
- const leaf = await issueLeaf(scratch, ca, identity, commonName, validDays);
2428
- const chainPath = (0, node_path.join)(scratch, "chain.crt");
2429
- await (0, node_fs_promises.writeFile)(chainPath, `${(0, node_fs.readFileSync)(leaf.certPath, "utf-8").trimEnd()}\n${(0, node_fs.readFileSync)(ca.caCertPath, "utf-8").trimEnd()}\n`, "utf-8");
2430
- if (newCa) {
2431
- await (0, node_fs_promises.copyFile)(ca.caCertPath, `${paths.caCertPath}.new`);
2432
- await (0, node_fs_promises.copyFile)(ca.caKeyPath, `${paths.caKeyPath}.new`);
2433
- (0, node_fs.renameSync)(`${paths.caCertPath}.new`, paths.caCertPath);
2434
- (0, node_fs.renameSync)(`${paths.caKeyPath}.new`, paths.caKeyPath);
2435
- await (0, node_fs_promises.chmod)(paths.caKeyPath, 384);
2436
- }
2437
- await (0, node_fs_promises.copyFile)(leaf.keyPath, `${paths.keyPath}.new`);
2438
- await (0, node_fs_promises.copyFile)(chainPath, `${paths.certPath}.new`);
2439
- (0, node_fs.renameSync)(`${paths.keyPath}.new`, paths.keyPath);
2440
- (0, node_fs.renameSync)(`${paths.certPath}.new`, paths.certPath);
2441
- await (0, node_fs_promises.chmod)(paths.keyPath, 384);
2442
- } finally {
2443
- await (0, node_fs_promises.rm)(scratch, {
2444
- recursive: true,
2445
- force: true
2446
- });
2447
- }
2448
- }
2449
- function describe(paths, generated, reason, caRotated, previousFingerprintSha256) {
2450
- const leaf = new node_crypto.X509Certificate((0, node_fs.readFileSync)(paths.certPath));
2451
- const ca = new node_crypto.X509Certificate((0, node_fs.readFileSync)(paths.caCertPath));
2452
- const san = leaf.subjectAltName ?? "";
2453
- return {
2454
- ...paths,
2455
- generated,
2456
- reason,
2457
- caRotated,
2458
- fingerprintSha256: leaf.fingerprint256,
2459
- previousFingerprintSha256,
2460
- caFingerprintSha256: ca.fingerprint256,
2461
- validTo: new Date(leaf.validTo).toISOString(),
2462
- sans: san === "" ? [] : san.split(", ")
2463
- };
2464
- }
2465
- /**
2466
- * Load TLS cert+key from files. Returns Buffers suitable for Node.js TLS
2467
- * options. `certPath` is a full chain, so the listener presents the local
2468
- * root alongside the leaf.
2469
- */
2470
- function loadTlsCert(certPath, keyPath) {
2471
- return {
2472
- cert: (0, node_fs.readFileSync)(certPath),
2473
- key: (0, node_fs.readFileSync)(keyPath)
2474
- };
2475
- }
2476
- //#endregion
2477
2021
  //#region src/builtins/sqlite-storage/integration-registry.ts
2478
2022
  function serializeSetting(value) {
2479
2023
  if (typeof value === "number") return {
@@ -95294,8 +94838,8 @@ exports.AddonRouteRegistry = AddonRouteRegistry;
95294
94838
  exports.AlertCenterAddon = require_builtins_alerts_alerts_addon.AlertCenterAddon;
95295
94839
  exports.ApiKeyManager = require_builtins_local_auth_local_auth_addon.ApiKeyManager;
95296
94840
  exports.AuthManager = require_builtins_local_auth_local_auth_addon.AuthManager;
95297
- exports.CA_COMMON_NAME = CA_COMMON_NAME;
95298
- exports.CA_VALIDITY_DAYS = CA_VALIDITY_DAYS;
94841
+ exports.CA_COMMON_NAME = require_lan_http_bind.CA_COMMON_NAME;
94842
+ exports.CA_VALIDITY_DAYS = require_lan_http_bind.CA_VALIDITY_DAYS;
95299
94843
  exports.CLUSTER_SECRET_MISMATCH_TYPE = CLUSTER_SECRET_MISMATCH_TYPE;
95300
94844
  exports.CLUSTER_SECRET_REJECTED_EXIT_CODE = CLUSTER_SECRET_REJECTED_EXIT_CODE;
95301
94845
  exports.CORE_CAP_SERVICE_NAME = CORE_CAP_SERVICE_NAME;
@@ -95312,6 +94856,7 @@ exports.ConsoleLoggingAddon = require_builtins_console_logging_index.ConsoleLogg
95312
94856
  exports.CoreBlocksAddon = require_builtins_core_blocks_core_blocks_addon.CoreBlocksAddon;
95313
94857
  exports.CustomActionRegistry = require_custom_action_registry.CustomActionRegistry;
95314
94858
  exports.DEFAULT_DATA_PATH = DEFAULT_DATA_PATH;
94859
+ exports.DEFAULT_LAN_HTTP_PORT = require_lan_http_bind.DEFAULT_LAN_HTTP_PORT;
95315
94860
  exports.DEVICE_SETTINGS_CONTRIBUTION_METHODS = require_dist.DEVICE_SETTINGS_CONTRIBUTION_METHODS;
95316
94861
  exports.DEVICE_STATUS_METHOD = require_dist.DEVICE_STATUS_METHOD;
95317
94862
  exports.DataPlaneRegistry = DataPlaneRegistry;
@@ -95342,7 +94887,7 @@ exports.HubNodeRegistry = HubNodeRegistry;
95342
94887
  exports.INFRA_CAPABILITIES = INFRA_CAPABILITIES;
95343
94888
  exports.IntegrationRegistry = IntegrationRegistry;
95344
94889
  exports.JobJournal = JobJournal;
95345
- exports.LEAF_RENEWAL_WINDOW_DAYS = LEAF_RENEWAL_WINDOW_DAYS;
94890
+ exports.LEAF_RENEWAL_WINDOW_DAYS = require_lan_http_bind.LEAF_RENEWAL_WINDOW_DAYS;
95346
94891
  exports.LifecycleJobEngine = LifecycleJobEngine;
95347
94892
  exports.LifecycleStateMachine = LifecycleStateMachine;
95348
94893
  exports.LivenessMonitorAddon = require_builtins_liveness_monitor_liveness_monitor_addon.LivenessMonitorAddon;
@@ -95353,7 +94898,7 @@ exports.LogManager = LogManager;
95353
94898
  exports.LogRingBuffer = LogRingBuffer;
95354
94899
  exports.LokiDestination = require_builtins_loki_logging_index.LokiDestination$1;
95355
94900
  exports.LokiLoggingAddon = require_builtins_loki_logging_index.LokiLoggingAddon$1;
95356
- exports.MAX_LEAF_VALIDITY_DAYS = MAX_LEAF_VALIDITY_DAYS;
94901
+ exports.MAX_LEAF_VALIDITY_DAYS = require_lan_http_bind.MAX_LEAF_VALIDITY_DAYS;
95357
94902
  exports.METHOD_ACCESS_MAP = require_dist.METHOD_ACCESS_MAP;
95358
94903
  exports.ModelDownloadService = require_file_data_plane.ModelDownloadService;
95359
94904
  exports.NATIVE_PROVIDER_SERVICE_INFIX = require_manifest_python_deps.NATIVE_PROVIDER_SERVICE_INFIX;
@@ -95379,7 +94924,7 @@ exports.ReadinessRegistry = require_dist.ReadinessRegistry;
95379
94924
  exports.ReadinessTimeoutError = require_dist.ReadinessTimeoutError;
95380
94925
  exports.ReplEngine = ReplEngine;
95381
94926
  exports.RingBuffer = RingBuffer;
95382
- exports.SERVER_AUTH_OID = SERVER_AUTH_OID;
94927
+ exports.SERVER_AUTH_OID = require_lan_http_bind.SERVER_AUTH_OID;
95383
94928
  exports.ScopedLogger = ScopedLogger;
95384
94929
  exports.ScopedTokenManager = require_builtins_local_auth_local_auth_addon.ScopedTokenManager;
95385
94930
  exports.SocketChannel = require_manifest_python_deps.SocketChannel;
@@ -95403,6 +94948,9 @@ exports.WinstonLoggingAddon = require_builtins_winston_logging_index.WinstonLogg
95403
94948
  exports.__resetCapUsageRegistryForTests = require_manifest_python_deps.__resetCapUsageRegistryForTests;
95404
94949
  exports.adaptBrokerToCluster = require_manifest_python_deps.adaptBrokerToCluster;
95405
94950
  exports.addonSettingsCapability = require_dist.addonSettingsCapability;
94951
+ exports.allFamiliesListenHost = require_lan_http_bind.allFamiliesListenHost;
94952
+ exports.applyLanHttp = require_lan_http_bind.applyLanHttp;
94953
+ exports.bindPendingLanHttp = require_lan_http_bind.bindPendingLanHttp;
95406
94954
  exports.bootstrapSchema = bootstrapSchema;
95407
94955
  exports.brokerCallForCap = require_manifest_python_deps.brokerCallForCap;
95408
94956
  exports.brokerTransportLink = require_manifest_python_deps.brokerTransportLink;
@@ -95429,9 +94977,10 @@ exports.capServiceName = require_manifest_python_deps.capServiceName;
95429
94977
  exports.classifyAddonDir = classifyAddonDir;
95430
94978
  exports.classifyCapRoute = require_manifest_python_deps.classifyCapRoute;
95431
94979
  exports.clearPendingRestart = clearPendingRestart;
94980
+ exports.closeLanHttp = require_lan_http_bind.closeLanHttp;
95432
94981
  exports.clusterEventTopic = require_manifest_python_deps.clusterEventTopic;
95433
94982
  exports.clusterSecretMatches = clusterSecretMatches;
95434
- exports.collectCertIdentity = collectCertIdentity;
94983
+ exports.collectCertIdentity = require_lan_http_bind.collectCertIdentity;
95435
94984
  exports.collectModelFiles = require_file_data_plane.collectModelFiles;
95436
94985
  exports.contentTypeFor = require_file_data_plane.contentTypeFor;
95437
94986
  exports.copyDirRecursive = copyDirRecursive;
@@ -95497,8 +95046,8 @@ Object.defineProperty(exports, "ensurePython", {
95497
95046
  return _camstack_types_node.ensurePython;
95498
95047
  }
95499
95048
  });
95500
- exports.ensureTlsCert = ensureTlsCert;
95501
- exports.evaluateExistingCert = evaluateExistingCert;
95049
+ exports.ensureTlsCert = require_lan_http_bind.ensureTlsCert;
95050
+ exports.evaluateExistingCert = require_lan_http_bind.evaluateExistingCert;
95502
95051
  exports.expandCapMethods = require_dist.expandCapMethods;
95503
95052
  exports.fetchJson = require_file_data_plane.fetchJson;
95504
95053
  Object.defineProperty(exports, "findInPath", {
@@ -95562,7 +95111,7 @@ exports.isCollectionArrayMethod = require_dist.isCollectionArrayMethod;
95562
95111
  exports.isInfraCapability = isInfraCapability;
95563
95112
  exports.isModelDownloaded = require_file_data_plane.isModelDownloaded;
95564
95113
  exports.isSourceNewer = isSourceNewer;
95565
- exports.loadTlsCert = loadTlsCert;
95114
+ exports.loadTlsCert = require_lan_http_bind.loadTlsCert;
95566
95115
  exports.localEndpointPath = require_manifest_python_deps.localEndpointPath;
95567
95116
  exports.localProviderLink = require_manifest_python_deps.localProviderLink;
95568
95117
  exports.mountNativeCapService = require_manifest_python_deps.mountNativeCapService;
@@ -95571,9 +95120,15 @@ exports.parseRangeHeader = require_file_data_plane.parseRangeHeader;
95571
95120
  exports.parseTokenizedUrl = require_file_data_plane.parseTokenizedUrl;
95572
95121
  exports.proxyToUpstream = proxyToUpstream;
95573
95122
  exports.quarantineAddonResidue = quarantineAddonResidue;
95123
+ exports.readExtraSans = require_lan_http_bind.readExtraSans;
95124
+ exports.readLanHttpState = require_lan_http_bind.readLanHttpState;
95574
95125
  exports.readPendingRestart = readPendingRestart;
95126
+ exports.readTlsAccessStatus = require_lan_http_bind.readTlsAccessStatus;
95127
+ exports.readTlsMode = require_lan_http_bind.readTlsMode;
95575
95128
  exports.readinessKey = require_dist.readinessKey;
95576
95129
  exports.registerEventBusService = require_manifest_python_deps.registerEventBusService;
95130
+ exports.registerLanHttpHandler = require_lan_http_bind.registerLanHttpHandler;
95131
+ exports.reissueTlsLeaf = require_lan_http_bind.reissueTlsLeaf;
95577
95132
  exports.resolveFilePath = require_file_data_plane.resolveFilePath;
95578
95133
  exports.resolveHwAccel = require_manifest_python_deps.resolveHwAccel;
95579
95134
  exports.resolveNpmInvocation = require_manifest_python_deps.resolveNpmInvocation;
@@ -95594,4 +95149,7 @@ exports.stripCamstackDeps = stripCamstackDeps;
95594
95149
  exports.subscribePassthrough = require_manifest_python_deps.subscribePassthrough;
95595
95150
  exports.udsChildLogToWorkerEntry = require_manifest_python_deps.udsChildLogToWorkerEntry;
95596
95151
  exports.validateProviderRegistrations = require_manifest_python_deps.validateProviderRegistrations;
95152
+ exports.validateUploadedTls = require_lan_http_bind.validateUploadedTls;
95153
+ exports.writeExtraSans = require_lan_http_bind.writeExtraSans;
95597
95154
  exports.writePendingRestart = writePendingRestart;
95155
+ exports.writeTlsMode = require_lan_http_bind.writeTlsMode;