@camstack/system 1.2.427 → 1.2.429

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 (100) hide show
  1. package/dist/agent-https-names-CpUSuP4w.js +120 -0
  2. package/dist/agent-https-names-uetr4OQl.mjs +91 -0
  3. package/dist/auth/auth-audit-log.d.ts +1 -1
  4. package/dist/auth/device-ca-store.d.ts +57 -0
  5. package/dist/auth/device-ca.d.ts +75 -0
  6. package/dist/auth/device-credential-manager.d.ts +154 -0
  7. package/dist/auth/login-session-manager.d.ts +14 -2
  8. package/dist/auth/user-management-internal.d.ts +10 -1
  9. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  10. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  11. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  12. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  13. package/dist/builtins/alerts/alerts.addon.js +1 -1
  14. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  15. package/dist/builtins/autotrack/index.js +1 -1
  16. package/dist/builtins/autotrack/index.mjs +1 -1
  17. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  18. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  19. package/dist/builtins/calendars/calendars.addon.js +1 -1
  20. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  21. package/dist/builtins/camera-grid/index.js +1 -1
  22. package/dist/builtins/camera-grid/index.mjs +1 -1
  23. package/dist/builtins/composer/composer.addon.js +2 -2
  24. package/dist/builtins/composer/composer.addon.mjs +2 -2
  25. package/dist/builtins/console-logging/index.js +1 -1
  26. package/dist/builtins/console-logging/index.mjs +1 -1
  27. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  28. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  29. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  30. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  31. package/dist/builtins/hub-forwarder/index.js +1 -1
  32. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  33. package/dist/builtins/keychain/index.js +10 -6
  34. package/dist/builtins/keychain/index.mjs +8 -4
  35. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  36. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  37. package/dist/builtins/local-auth/local-auth.addon.d.ts +6 -0
  38. package/dist/builtins/local-auth/local-auth.addon.js +955 -47
  39. package/dist/builtins/local-auth/local-auth.addon.mjs +946 -41
  40. package/dist/builtins/local-network/local-network.addon.js +2 -2
  41. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  42. package/dist/builtins/loki-logging/index.js +1 -1
  43. package/dist/builtins/loki-logging/index.mjs +1 -1
  44. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  45. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  46. package/dist/builtins/platform-probe/index.js +1 -1
  47. package/dist/builtins/platform-probe/index.mjs +1 -1
  48. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  49. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  50. package/dist/builtins/snapshot/index.js +1 -1
  51. package/dist/builtins/snapshot/index.mjs +1 -1
  52. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  53. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  54. package/dist/builtins/sqlite-storage/index.js +1 -1
  55. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  56. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +3 -3
  57. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +3 -3
  58. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  59. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  60. package/dist/builtins/system-config/system-config.addon.d.ts +18 -0
  61. package/dist/builtins/system-config/system-config.addon.js +834 -18
  62. package/dist/builtins/system-config/system-config.addon.mjs +819 -19
  63. package/dist/builtins/winston-logging/index.js +1 -1
  64. package/dist/builtins/winston-logging/index.mjs +1 -1
  65. package/dist/{composition-sources-DvCGZEUI.js → composition-sources-C2VNX6tF.js} +1 -1
  66. package/dist/{composition-sources-BQNxXQZG.mjs → composition-sources-SYtYuAgh.mjs} +1 -1
  67. package/dist/{constants-CR4If_lP.mjs → constants-CKuWoWvc.mjs} +1 -1
  68. package/dist/{constants-CCSJGnjs.js → constants-RI-hGP5S.js} +1 -1
  69. package/dist/device-ca-BWfdDB3k.js +462 -0
  70. package/dist/device-ca-R7DKKZL6.mjs +355 -0
  71. package/dist/{dist-BQG3QFg7.js → dist-Bgy_AmwX.js} +554 -0
  72. package/dist/{dist-DHSJV4Qc.mjs → dist-CSSfTM18.mjs} +507 -1
  73. package/dist/file-host-worker.js +1 -1
  74. package/dist/file-host-worker.mjs +1 -1
  75. package/dist/index.d.ts +4 -1
  76. package/dist/index.js +59 -12
  77. package/dist/index.mjs +10 -9
  78. package/dist/kernel/index.d.ts +2 -2
  79. package/dist/kernel/moleculer/mesh-mtls/index.d.ts +4 -1
  80. package/dist/kernel/moleculer/mesh-mtls/mesh-certificate-registry.d.ts +91 -0
  81. package/dist/kernel/moleculer/mesh-mtls/mesh-enroll.d.ts +4 -0
  82. package/dist/kernel/moleculer/mesh-mtls/mesh-peer-transport.d.ts +23 -0
  83. package/dist/kernel/moleculer/mesh-mtls/mesh-renewal.d.ts +126 -0
  84. package/dist/kernel/moleculer/mesh-mtls/mesh-tls-gate.d.ts +40 -0
  85. package/dist/kernel/moleculer/mesh-mtls/tls-tcp-transporter.d.ts +15 -0
  86. package/dist/{restore-archive-DH1Pxq_1.mjs → restore-archive-AIwMHO5_.mjs} +1 -1
  87. package/dist/{restore-archive-Dccm8lkA.js → restore-archive-BYUEMsog.js} +1 -1
  88. package/dist/{retired-settings-keys-BfGaSeQI.js → retired-settings-keys-BTPkRLTS.js} +1 -1
  89. package/dist/{retired-settings-keys-CPz7zpza.mjs → retired-settings-keys-H0rYXT27.mjs} +1 -1
  90. package/dist/tls/agent-https-names.d.ts +27 -0
  91. package/dist/tls/agent-https.d.ts +111 -0
  92. package/dist/tls/cert-issue.d.ts +7 -0
  93. package/dist/tls/index.d.ts +3 -0
  94. package/dist/{tls-BgImdxCO.js → tls-C5IYg3V5.js} +406 -0
  95. package/dist/{tls-CaOLZ09W.mjs → tls-CpcGkeJV.mjs} +314 -4
  96. package/dist/{wal-maintenance-DItkuOvu.js → wal-maintenance-BSGsxKW7.js} +0 -0
  97. package/dist/{wal-maintenance-Drqhcjit.mjs → wal-maintenance-DW0FfGV7.mjs} +0 -0
  98. package/package.json +4 -1
  99. package/dist/jwt-signing-key-BZm055IP.js +0 -174
  100. package/dist/jwt-signing-key-DL-qk1SM.mjs +0 -127
@@ -459,6 +459,35 @@ async function issueLeaf(ca, identity, commonName, validDays, now) {
459
459
  keyPem: await exportKeyPem(keys.privateKey)
460
460
  };
461
461
  }
462
+ /**
463
+ * A server leaf for a key pair the CALLER holds (D856): an agent sends a CSR, the
464
+ * hub signs its public key under the local root. Same extensions as
465
+ * {@link issueLeaf} - `CA:FALSE`, serverAuth only - so a device that trusts the
466
+ * hub root trusts the agent. The private key never exists on the hub.
467
+ */
468
+ async function issueLeafFromCsr(ca, csrPem, identity, commonName, validDays, now) {
469
+ const csr = new require_x509_es.Pkcs10CertificateRequest(csrPem);
470
+ if (!await csr.verify(node_crypto.webcrypto)) throw new Error("CSR signature is invalid");
471
+ const extensions = [
472
+ new require_x509_es.BasicConstraintsExtension(false, void 0, true),
473
+ new require_x509_es.KeyUsagesExtension(require_x509_es.KeyUsageFlags.digitalSignature | require_x509_es.KeyUsageFlags.keyEncipherment, true),
474
+ new require_x509_es.ExtendedKeyUsageExtension([SERVER_AUTH_OID]),
475
+ await require_x509_es.SubjectKeyIdentifierExtension.create(csr.publicKey),
476
+ await require_x509_es.AuthorityKeyIdentifierExtension.create(ca.cert),
477
+ new require_x509_es.SubjectAlternativeNameExtension(subjectAltNames(identity))
478
+ ];
479
+ return toCertPem(await require_x509_es.X509CertificateGenerator.create({
480
+ serialNumber: freshSerial(),
481
+ subject: `CN=${commonName}`,
482
+ issuer: ca.cert.subject,
483
+ notBefore: now,
484
+ notAfter: new Date(now.getTime() + validDays * MS_PER_DAY),
485
+ publicKey: csr.publicKey,
486
+ signingKey: ca.privateKey,
487
+ signingAlgorithm: SIGNING_ALGORITHM,
488
+ extensions
489
+ }));
490
+ }
462
491
  //#endregion
463
492
  //#region src/tls/cert-manager.ts
464
493
  var DEFAULT_COMMON_NAME = "camstack.local";
@@ -941,6 +970,293 @@ function applyCertificateToListener(dataDir) {
941
970
  return reloadHttpsListener(loadTlsCert(certPath, keyPath));
942
971
  }
943
972
  //#endregion
973
+ //#region src/tls/agent-https.ts
974
+ /**
975
+ * HTTPS for agents (D856): a leaf from the hub's LOCAL CA, so a device that
976
+ * trusts the hub root trusts the agents too.
977
+ *
978
+ * The agent generates its key and a CSR; the hub signs only the PUBLIC key under
979
+ * the local CA and returns the certificate - no private key ever crosses the mesh.
980
+ * The agent keeps ONE file, `tls/agent-https.json` (0600: key + leaf + the pinned CA),
981
+ * written atomically (temp file + rename) so a crash can never leave a key that does
982
+ * not match its certificate. It persists no database.
983
+ *
984
+ * What the hub is willing to certify (the smaller safe option). The local CA is trusted
985
+ * by every device that trusts the hub, so a compromised agent must not obtain a name it
986
+ * does not own. The hub signs ONLY what it knows by itself:
987
+ * - the agent's node id (unique in the cluster), when it is a local-only name;
988
+ * - the remote address of the agent's LIVE mesh socket as the hub sees it, when it is
989
+ * a private address - never an address the agent reports.
990
+ * Anything the agent reports (host names, interface addresses) is DROPPED and logged:
991
+ * extra self-reported names would need an explicit operator approval flow, which does
992
+ * not exist, so they are not certified. A name the hub's own certificate holds, or one
993
+ * already certified to another node, is never signed (a NAT'd pair of agents would
994
+ * otherwise share an address).
995
+ */
996
+ require_x509_es.cryptoProvider.set(node_crypto.webcrypto);
997
+ function agentHttpsPaths(dataDir) {
998
+ const dir = (0, node_path.join)(dataDir, "tls");
999
+ return {
1000
+ dir,
1001
+ bundlePath: (0, node_path.join)(dir, "agent-https.json"),
1002
+ certPath: (0, node_path.join)(dir, "agent.crt"),
1003
+ pendingKeyPath: (0, node_path.join)(dir, "agent.key.pending")
1004
+ };
1005
+ }
1006
+ /**
1007
+ * The material on disk, or `null` while no hub-issued leaf is installed. A bundle that
1008
+ * cannot be parsed is reported through `onCorrupt` and treated as absent: a bad file must
1009
+ * never take the agent API down.
1010
+ */
1011
+ function loadAgentHttps(dataDir, onCorrupt) {
1012
+ const { bundlePath } = agentHttpsPaths(dataDir);
1013
+ if (!(0, node_fs.existsSync)(bundlePath)) return null;
1014
+ try {
1015
+ const parsed = JSON.parse((0, node_fs.readFileSync)(bundlePath, "utf8"));
1016
+ const field = (name) => {
1017
+ const v = parsed !== null && typeof parsed === "object" ? Reflect.get(parsed, name) : null;
1018
+ return typeof v === "string" && v.length > 0 ? v : null;
1019
+ };
1020
+ const keyPem = field("keyPem");
1021
+ const certPem = field("certPem");
1022
+ const caCertPem = field("caCertPem");
1023
+ if (keyPem === null || certPem === null || caCertPem === null) {
1024
+ onCorrupt?.("the bundle is missing a field");
1025
+ return null;
1026
+ }
1027
+ if (!new node_crypto.X509Certificate(certPem).checkPrivateKey((0, node_crypto.createPrivateKey)(keyPem))) {
1028
+ onCorrupt?.("the certificate does not match the key");
1029
+ return null;
1030
+ }
1031
+ return {
1032
+ keyPem,
1033
+ certPem,
1034
+ caCertPem
1035
+ };
1036
+ } catch (error) {
1037
+ onCorrupt?.(error instanceof Error ? error.message : String(error));
1038
+ return null;
1039
+ }
1040
+ }
1041
+ function collectAgentNames(nodeId, hostFacts) {
1042
+ const identity = collectCertIdentity({
1043
+ commonName: nodeId,
1044
+ extraSans: [],
1045
+ hostFacts
1046
+ });
1047
+ return {
1048
+ hostnames: identity.dnsNames,
1049
+ addresses: identity.ipAddresses
1050
+ };
1051
+ }
1052
+ /** SHA-256 of the hub local CA this agent pinned, or `null` while none is. */
1053
+ function pinnedHttpsCaFingerprint(dataDir) {
1054
+ const material = loadAgentHttps(dataDir);
1055
+ return material === null ? null : new node_crypto.X509Certificate(material.caCertPem).fingerprint256;
1056
+ }
1057
+ /**
1058
+ * A fresh key pair (EC P-256, kept in `agent.key.pending`, 0600) and its CSR. The
1059
+ * key becomes `agent.key` only when the matching certificate is installed, so a
1060
+ * request that is never answered changes nothing that is being served.
1061
+ */
1062
+ async function createAgentHttpsRequest(dataDir, nodeId, names) {
1063
+ const paths = agentHttpsPaths(dataDir);
1064
+ (0, node_fs.mkdirSync)(paths.dir, { recursive: true });
1065
+ const keys = await node_crypto.webcrypto.subtle.generateKey({
1066
+ name: "ECDSA",
1067
+ namedCurve: "P-256"
1068
+ }, true, ["sign", "verify"]);
1069
+ const csr = await require_x509_es.Pkcs10CertificateRequestGenerator.create({
1070
+ name: `CN=${nodeId}`,
1071
+ keys,
1072
+ signingAlgorithm: {
1073
+ name: "ECDSA",
1074
+ hash: "SHA-256"
1075
+ }
1076
+ });
1077
+ const pkcs8 = await node_crypto.webcrypto.subtle.exportKey("pkcs8", keys.privateKey);
1078
+ (0, _camstack_types_node.writeOwnerOnlyFileSync)(paths.pendingKeyPath, `${require_x509_es.PemConverter.encode(pkcs8, "PRIVATE KEY")}\n`);
1079
+ return {
1080
+ csrPem: `${csr.toString("pem")}\n`,
1081
+ ...names
1082
+ };
1083
+ }
1084
+ var AgentHttpsInstallError = class extends Error {
1085
+ constructor(message) {
1086
+ super(message);
1087
+ this.name = "AgentHttpsInstallError";
1088
+ }
1089
+ };
1090
+ /**
1091
+ * Install the hub-signed leaf for the pending key. Refused when it is not for that key, when
1092
+ * it is not signed by the CA it came with, or - once a CA is pinned - when that CA is not the
1093
+ * one this agent already pinned (the CA that arrives with a later leaf is never trusted on its
1094
+ * own say: the first one came over the authenticated mesh, every later leaf must chain to it).
1095
+ * Key, leaf and CA are written as ONE file, atomically.
1096
+ */
1097
+ function installAgentHttps(dataDir, input) {
1098
+ const paths = agentHttpsPaths(dataDir);
1099
+ if (!(0, node_fs.existsSync)(paths.pendingKeyPath)) throw new AgentHttpsInstallError("no pending key: prepareHttpsCertificate was not called");
1100
+ const keyPem = (0, node_fs.readFileSync)(paths.pendingKeyPath, "utf8");
1101
+ let leaf;
1102
+ let ca;
1103
+ try {
1104
+ leaf = new node_crypto.X509Certificate(input.certPem);
1105
+ ca = new node_crypto.X509Certificate(input.caCertPem);
1106
+ } catch {
1107
+ throw new AgentHttpsInstallError("the certificate or the CA is not a valid PEM certificate");
1108
+ }
1109
+ if (!leaf.checkPrivateKey((0, node_crypto.createPrivateKey)(keyPem))) throw new AgentHttpsInstallError("the certificate is not for this node's pending key");
1110
+ const pinned = loadAgentHttps(dataDir);
1111
+ if (pinned !== null && input.caRotation !== true) {
1112
+ const pinnedCa = new node_crypto.X509Certificate(pinned.caCertPem);
1113
+ if (pinnedCa.fingerprint256 !== ca.fingerprint256 || !leaf.verify(pinnedCa.publicKey)) throw new AgentHttpsInstallError("the leaf does not chain to the hub local CA this agent pinned at its first install (if the hub rotated its local CA, renew HTTPS from the hub - it signals the rotation)");
1114
+ }
1115
+ if (!leaf.verify(ca.publicKey)) throw new AgentHttpsInstallError("the certificate is not signed by the CA that came with it");
1116
+ if (new Date(leaf.validTo).getTime() <= Date.now()) throw new AgentHttpsInstallError("the certificate is already expired");
1117
+ (0, node_fs.mkdirSync)(paths.dir, { recursive: true });
1118
+ const tmp = `${paths.bundlePath}.tmp`;
1119
+ (0, _camstack_types_node.writeOwnerOnlyFileSync)(tmp, JSON.stringify({
1120
+ keyPem,
1121
+ certPem: input.certPem,
1122
+ caCertPem: input.caCertPem
1123
+ }));
1124
+ (0, node_fs.renameSync)(tmp, paths.bundlePath);
1125
+ (0, node_fs.chmodSync)(paths.bundlePath, 384);
1126
+ writeFileAtomic(paths.certPath, input.certPem.endsWith("\n") ? input.certPem : `${input.certPem}\n`);
1127
+ return {
1128
+ keyPem,
1129
+ certPem: input.certPem,
1130
+ caCertPem: input.caCertPem
1131
+ };
1132
+ }
1133
+ function writeFileAtomic(path, content) {
1134
+ const tmp = `${path}.tmp`;
1135
+ (0, node_fs.writeFileSync)(tmp, content);
1136
+ (0, node_fs.renameSync)(tmp, path);
1137
+ }
1138
+ /** Fingerprint, issuer, SANs and validity of a served leaf. */
1139
+ function describeHttpsLeaf(certPem) {
1140
+ const x509 = new node_crypto.X509Certificate(certPem);
1141
+ return {
1142
+ fingerprintSha256: x509.fingerprint256,
1143
+ issuer: x509.issuer.replaceAll("\n", ", "),
1144
+ sans: (x509.subjectAltName ?? "").split(",").map((s) => s.trim()).filter((s) => s.length > 0),
1145
+ notBefore: Date.parse(x509.validFrom),
1146
+ notAfter: Date.parse(x509.validTo)
1147
+ };
1148
+ }
1149
+ var LOCAL_SUFFIXES = [
1150
+ ".local",
1151
+ ".lan",
1152
+ ".home.arpa",
1153
+ ".internal",
1154
+ ".localdomain"
1155
+ ];
1156
+ var LABEL_RE = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/i;
1157
+ /** Private-network IPv4/IPv6: RFC 1918, CGNAT (Tailscale), link-local, ULA. */
1158
+ function isPrivateAddress(address) {
1159
+ const family = (0, node_net.isIP)(address);
1160
+ if (family === 4) {
1161
+ const [a, b] = address.split(".").map(Number);
1162
+ return a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254;
1163
+ }
1164
+ if (family === 6) {
1165
+ const lower = address.toLowerCase();
1166
+ return lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
1167
+ }
1168
+ return false;
1169
+ }
1170
+ /** The default docker bridge network (172.17.0.0/16): identical on every host, so it names nothing. */
1171
+ function isContainerBridgeAddress(address) {
1172
+ return /^172\.17\.\d{1,3}\.\d{1,3}$/.test(address);
1173
+ }
1174
+ /** A name that cannot collide with public DNS. */
1175
+ function isLocalOnlyHostname(name) {
1176
+ const lower = name.toLowerCase();
1177
+ if (lower.length === 0 || lower.length > 253) return false;
1178
+ if (!lower.includes(".")) return LABEL_RE.test(lower);
1179
+ if (!LOCAL_SUFFIXES.some((suffix) => lower.endsWith(suffix))) return false;
1180
+ return lower.split(".").every((label) => LABEL_RE.test(label));
1181
+ }
1182
+ /**
1183
+ * The names the hub will sign for `nodeId`: its node id and the address of its live mesh
1184
+ * socket - nothing the agent says about itself. `excluded` holds every name already taken
1185
+ * (the hub's own certificate, other nodes' leaves); `reported` is what the agent claimed,
1186
+ * listed only so it can be logged as dropped.
1187
+ */
1188
+ function acceptAgentSans(input) {
1189
+ const excluded = input.excluded ?? /* @__PURE__ */ new Set();
1190
+ const taken = (name) => excluded.has(name.toLowerCase());
1191
+ const rejected = (input.reported ?? []).map((n) => `${n} (self-reported, not certified)`);
1192
+ const dns = [];
1193
+ const ips = [];
1194
+ const id = input.nodeId.toLowerCase();
1195
+ if (id !== "hub" && isLocalOnlyHostname(id) && !taken(id)) dns.push(id);
1196
+ else rejected.push(`${input.nodeId} (node id: not a usable name)`);
1197
+ const address = input.observedAddress?.replace(/^::ffff:/, "");
1198
+ if (address !== void 0) if (address.includes("%")) rejected.push(`${address} (observed address: zone-scoped, only the node id is certified)`);
1199
+ else if (isContainerBridgeAddress(address)) rejected.push(`${address} (observed address: a container bridge address, only the node id is certified)`);
1200
+ else if ((0, node_net.isIP)(address) !== 0 && isPrivateAddress(address) && !taken(address)) ips.push(address);
1201
+ else rejected.push(`${address} (observed address: not private or already certified)`);
1202
+ return {
1203
+ identity: {
1204
+ dnsNames: dns,
1205
+ ipAddresses: ips,
1206
+ requiredDnsNames: dns,
1207
+ requiredIpAddresses: ips
1208
+ },
1209
+ accepted: [...dns, ...ips],
1210
+ rejected
1211
+ };
1212
+ }
1213
+ /** The SAN values (lower-cased) of a certificate PEM: DNS names and IP addresses. */
1214
+ function certificateNames(certPem) {
1215
+ const names = /* @__PURE__ */ new Set();
1216
+ const san = new node_crypto.X509Certificate(certPem).subjectAltName ?? "";
1217
+ for (const part of san.split(",")) {
1218
+ const [, value] = part.trim().split(/:(.*)/s);
1219
+ if (value !== void 0 && value.length > 0) names.add(value.toLowerCase());
1220
+ }
1221
+ return names;
1222
+ }
1223
+ /**
1224
+ * Hub side: sign an agent's CSR under the local CA in `<hubDataDir>/tls`. Throws when
1225
+ * there is no local CA to sign with or no name survives the policy.
1226
+ */
1227
+ async function issueAgentHttpsLeaf(input) {
1228
+ const caCertPath = (0, node_path.join)(input.hubDataDir, "tls", "camstack-ca.crt");
1229
+ const caKeyPath = (0, node_path.join)(input.hubDataDir, "tls", "camstack-ca.key");
1230
+ const hubLeafPath = (0, node_path.join)(input.hubDataDir, "tls", "camstack.crt");
1231
+ if (!(0, node_fs.existsSync)(caCertPath) || !(0, node_fs.existsSync)(caKeyPath)) throw new Error("the hub has no local CA to sign with (no tls/camstack-ca.crt + key)");
1232
+ const excluded = new Set(input.takenByOthers ?? []);
1233
+ if ((0, node_fs.existsSync)(hubLeafPath)) for (const name of certificateNames((0, node_fs.readFileSync)(hubLeafPath, "utf8"))) excluded.add(name);
1234
+ const sans = acceptAgentSans({
1235
+ nodeId: input.nodeId,
1236
+ observedAddress: input.observedAddress,
1237
+ excluded,
1238
+ ...input.reportedNames !== void 0 ? { reported: input.reportedNames } : {}
1239
+ });
1240
+ if (sans.accepted.length === 0) throw new Error(`no name of node '${input.nodeId}' can be certified (${sans.rejected.join("; ") || "nothing offered"})`);
1241
+ const ca = await loadCa((0, node_fs.readFileSync)(caCertPath, "utf8"), (0, node_fs.readFileSync)(caKeyPath, "utf8"));
1242
+ return {
1243
+ certPem: await issueLeafFromCsr(ca, input.csrPem, sans.identity, input.nodeId, 397, input.now ?? /* @__PURE__ */ new Date()),
1244
+ caCertPem: ca.certPem,
1245
+ accepted: sans.accepted,
1246
+ rejected: sans.rejected
1247
+ };
1248
+ }
1249
+ /** SHA-256 of a PEM (for logs that must not print a certificate). */
1250
+ function pemDigest(pem) {
1251
+ return (0, node_crypto.createHash)("sha256").update(pem).digest("hex").slice(0, 16);
1252
+ }
1253
+ //#endregion
1254
+ Object.defineProperty(exports, "AgentHttpsInstallError", {
1255
+ enumerable: true,
1256
+ get: function() {
1257
+ return AgentHttpsInstallError;
1258
+ }
1259
+ });
944
1260
  Object.defineProperty(exports, "CA_COMMON_NAME", {
945
1261
  enumerable: true,
946
1262
  get: function() {
@@ -983,6 +1299,18 @@ Object.defineProperty(exports, "UPLOADED_CA_FILE", {
983
1299
  return UPLOADED_CA_FILE;
984
1300
  }
985
1301
  });
1302
+ Object.defineProperty(exports, "acceptAgentSans", {
1303
+ enumerable: true,
1304
+ get: function() {
1305
+ return acceptAgentSans;
1306
+ }
1307
+ });
1308
+ Object.defineProperty(exports, "agentHttpsPaths", {
1309
+ enumerable: true,
1310
+ get: function() {
1311
+ return agentHttpsPaths;
1312
+ }
1313
+ });
986
1314
  Object.defineProperty(exports, "allFamiliesListenHost", {
987
1315
  enumerable: true,
988
1316
  get: function() {
@@ -1007,6 +1335,12 @@ Object.defineProperty(exports, "bindPendingLanHttp", {
1007
1335
  return bindPendingLanHttp;
1008
1336
  }
1009
1337
  });
1338
+ Object.defineProperty(exports, "certificateNames", {
1339
+ enumerable: true,
1340
+ get: function() {
1341
+ return certificateNames;
1342
+ }
1343
+ });
1010
1344
  Object.defineProperty(exports, "clearHttpsListener", {
1011
1345
  enumerable: true,
1012
1346
  get: function() {
@@ -1019,12 +1353,30 @@ Object.defineProperty(exports, "closeLanHttp", {
1019
1353
  return closeLanHttp;
1020
1354
  }
1021
1355
  });
1356
+ Object.defineProperty(exports, "collectAgentNames", {
1357
+ enumerable: true,
1358
+ get: function() {
1359
+ return collectAgentNames;
1360
+ }
1361
+ });
1022
1362
  Object.defineProperty(exports, "collectCertIdentity", {
1023
1363
  enumerable: true,
1024
1364
  get: function() {
1025
1365
  return collectCertIdentity;
1026
1366
  }
1027
1367
  });
1368
+ Object.defineProperty(exports, "createAgentHttpsRequest", {
1369
+ enumerable: true,
1370
+ get: function() {
1371
+ return createAgentHttpsRequest;
1372
+ }
1373
+ });
1374
+ Object.defineProperty(exports, "describeHttpsLeaf", {
1375
+ enumerable: true,
1376
+ get: function() {
1377
+ return describeHttpsLeaf;
1378
+ }
1379
+ });
1028
1380
  Object.defineProperty(exports, "ensureTlsCert", {
1029
1381
  enumerable: true,
1030
1382
  get: function() {
@@ -1037,18 +1389,66 @@ Object.defineProperty(exports, "evaluateExistingCert", {
1037
1389
  return evaluateExistingCert;
1038
1390
  }
1039
1391
  });
1392
+ Object.defineProperty(exports, "installAgentHttps", {
1393
+ enumerable: true,
1394
+ get: function() {
1395
+ return installAgentHttps;
1396
+ }
1397
+ });
1398
+ Object.defineProperty(exports, "isContainerBridgeAddress", {
1399
+ enumerable: true,
1400
+ get: function() {
1401
+ return isContainerBridgeAddress;
1402
+ }
1403
+ });
1404
+ Object.defineProperty(exports, "isLocalOnlyHostname", {
1405
+ enumerable: true,
1406
+ get: function() {
1407
+ return isLocalOnlyHostname;
1408
+ }
1409
+ });
1410
+ Object.defineProperty(exports, "isPrivateAddress", {
1411
+ enumerable: true,
1412
+ get: function() {
1413
+ return isPrivateAddress;
1414
+ }
1415
+ });
1416
+ Object.defineProperty(exports, "issueAgentHttpsLeaf", {
1417
+ enumerable: true,
1418
+ get: function() {
1419
+ return issueAgentHttpsLeaf;
1420
+ }
1421
+ });
1040
1422
  Object.defineProperty(exports, "leafFingerprintOf", {
1041
1423
  enumerable: true,
1042
1424
  get: function() {
1043
1425
  return leafFingerprintOf;
1044
1426
  }
1045
1427
  });
1428
+ Object.defineProperty(exports, "loadAgentHttps", {
1429
+ enumerable: true,
1430
+ get: function() {
1431
+ return loadAgentHttps;
1432
+ }
1433
+ });
1046
1434
  Object.defineProperty(exports, "loadTlsCert", {
1047
1435
  enumerable: true,
1048
1436
  get: function() {
1049
1437
  return loadTlsCert;
1050
1438
  }
1051
1439
  });
1440
+ Object.defineProperty(exports, "pemDigest", {
1441
+ enumerable: true,
1442
+ get: function() {
1443
+ return pemDigest;
1444
+ }
1445
+ });
1446
+ Object.defineProperty(exports, "pinnedHttpsCaFingerprint", {
1447
+ enumerable: true,
1448
+ get: function() {
1449
+ return pinnedHttpsCaFingerprint;
1450
+ }
1451
+ });
1052
1452
  Object.defineProperty(exports, "prepareBootTls", {
1053
1453
  enumerable: true,
1054
1454
  get: function() {
@@ -1133,6 +1533,12 @@ Object.defineProperty(exports, "storeUploadedCertificate", {
1133
1533
  return storeUploadedCertificate;
1134
1534
  }
1135
1535
  });
1536
+ Object.defineProperty(exports, "systemHostFacts", {
1537
+ enumerable: true,
1538
+ get: function() {
1539
+ return systemHostFacts;
1540
+ }
1541
+ });
1136
1542
  Object.defineProperty(exports, "validateUploadedTls", {
1137
1543
  enumerable: true,
1138
1544
  get: function() {