@camstack/system 1.2.444 → 1.2.445
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.
- package/dist/addon-runner.js +40 -31
- package/dist/addon-runner.mjs +41 -32
- package/dist/{agent-https-DlpTMDqA.js → agent-https-CiOW0-sj.js} +810 -12
- package/dist/{agent-https-U0d4uvn2.mjs → agent-https-D4Q8iFNd.mjs} +697 -13
- package/dist/auth/admin-token-manager.d.ts +9 -3
- package/dist/auth/api-key-manager.d.ts +9 -3
- package/dist/auth/auth-audit-log.d.ts +9 -3
- package/dist/auth/device-credential-manager.d.ts +5 -1
- package/dist/auth/external-identity-manager.d.ts +8 -21
- package/dist/auth/login-session-manager.d.ts +11 -3
- package/dist/auth/scoped-token-manager.d.ts +8 -52
- package/dist/auth/totp-manager.d.ts +11 -3
- package/dist/auth/user-manager.d.ts +10 -17
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alert-store.d.ts +32 -0
- package/dist/builtins/alerts/alerts.addon.d.ts +9 -2
- package/dist/builtins/alerts/alerts.addon.js +289 -150
- package/dist/builtins/alerts/alerts.addon.mjs +288 -150
- package/dist/builtins/alerts/record-kinds.d.ts +15 -0
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +1 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +134 -193
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +134 -193
- package/dist/builtins/backup-orchestrator/destination-policy.d.ts +4 -2
- package/dist/builtins/backup-orchestrator/encryption-key-store.d.ts +4 -2
- package/dist/builtins/backup-orchestrator/record-kinds.d.ts +58 -0
- package/dist/builtins/backup-orchestrator/schedule-store.d.ts +4 -2
- package/dist/builtins/backup-orchestrator/seed-marker-store.d.ts +4 -2
- package/dist/builtins/calendars/calendar-prefs-store.d.ts +13 -5
- package/dist/builtins/calendars/calendars.addon.d.ts +2 -2
- package/dist/builtins/calendars/calendars.addon.js +67 -64
- package/dist/builtins/calendars/calendars.addon.mjs +59 -64
- package/dist/builtins/calendars/record-kinds.d.ts +18 -0
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/cluster-agent/record-kinds.d.ts +36 -0
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/block-settings-store.d.ts +8 -4
- package/dist/builtins/core-blocks/core-block-store.d.ts +8 -16
- package/dist/builtins/core-blocks/core-blocks.addon.js +85 -112
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +86 -113
- package/dist/builtins/core-blocks/index.mjs +1 -1
- package/dist/builtins/core-blocks/record-kinds.d.ts +26 -0
- package/dist/builtins/device-manager/device-manager.addon.js +111 -187
- package/dist/builtins/device-manager/device-manager.addon.mjs +110 -187
- package/dist/builtins/device-manager/device-tombstone-store.d.ts +9 -48
- package/dist/builtins/device-manager/record-kinds.d.ts +14 -0
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +152 -22
- package/dist/builtins/keychain/index.mjs +152 -22
- package/dist/builtins/keychain/keychain-service.d.ts +3 -2
- package/dist/builtins/keychain/keychain-store.d.ts +13 -2
- package/dist/builtins/keychain/record-kinds.d.ts +16 -0
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/auth-schema.d.ts +3 -1
- package/dist/builtins/local-auth/local-auth.addon.js +2903 -2123
- package/dist/builtins/local-auth/local-auth.addon.mjs +2904 -2124
- package/dist/builtins/local-auth/oauth-session-manager.d.ts +8 -48
- package/dist/builtins/local-auth/record-kinds.d.ts +141 -0
- package/dist/builtins/local-network/local-network.addon.js +2 -2
- package/dist/builtins/local-network/local-network.addon.mjs +2 -2
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/record-entries/index.js +1 -1
- package/dist/builtins/record-entries/index.mjs +1 -1
- package/dist/builtins/records/legacy-table-row-count.d.ts +5 -0
- package/dist/builtins/records/phase6-declare-gate.d.ts +28 -0
- package/dist/builtins/records-isolated.d.ts +10 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/index.js +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/integration-record-kinds.d.ts +30 -0
- package/dist/builtins/sqlite-storage/integration-registry.d.ts +10 -16
- package/dist/builtins/sqlite-storage/records/count-settings-array-legacy.d.ts +5 -0
- package/dist/builtins/sqlite-storage/records/count-settings-brokers-legacy.d.ts +6 -0
- package/dist/builtins/sqlite-storage/records/records-engine-types.d.ts +5 -0
- package/dist/builtins/sqlite-storage/records/records-engine.d.ts +4 -0
- package/dist/builtins/sqlite-storage/records/records-provider.d.ts +5 -1
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +17 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +111 -3
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +111 -3
- package/dist/builtins/sqlite-storage/storage-admin-provider.d.ts +9 -1
- package/dist/builtins/storage-orchestrator/drain-state-store.d.ts +5 -2
- package/dist/builtins/storage-orchestrator/location-store.d.ts +7 -2
- package/dist/builtins/storage-orchestrator/record-kinds.d.ts +38 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +2 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +196 -5
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +195 -6
- package/dist/builtins/system-config/record-kinds.d.ts +32 -0
- package/dist/builtins/system-config/system-config.addon.js +53 -618
- package/dist/builtins/system-config/system-config.addon.mjs +29 -577
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{composition-sources-C3rKncIF.mjs → composition-sources-BMt5cFYb.mjs} +1 -1
- package/dist/{composition-sources-NAPZddic.js → composition-sources-D3fi9Xns.js} +1 -1
- package/dist/{constants-BKX2QnIg.mjs → constants-BMHCxRzb.mjs} +1 -1
- package/dist/{constants-BXiNEIeV.js → constants-Br879Axi.js} +1 -1
- package/dist/{device-ca-DT9X2S6d.js → device-ca-CnJ_va7i.js} +1 -1
- package/dist/{device-ca-jxjSL8Ov.mjs → device-ca-T8osSMos.mjs} +1 -1
- package/dist/{dist-DERK-gP5.mjs → dist-BNunRyXT.mjs} +174 -58
- package/dist/{dist-D1vECb_6.js → dist-DgOG8flk.js} +233 -57
- package/dist/file-host-worker.js +1 -1
- package/dist/file-host-worker.mjs +1 -1
- package/dist/{hub-mdns-ciao-Cah9GCIg.js → hub-mdns-ciao-BIkTHq-n.js} +2 -2
- package/dist/{hub-mdns-ciao-CRaC97V0.mjs → hub-mdns-ciao-D16oRQ9K.mjs} +2 -2
- package/dist/index.d.ts +7 -0
- package/dist/index.js +3093 -2820
- package/dist/index.mjs +3065 -2809
- package/dist/kernel/index.d.ts +2 -0
- package/dist/kernel/moleculer/event-bus.d.ts +3 -1
- package/dist/kernel/moleculer/mesh-mtls/mesh-certificate-registry.d.ts +5 -1
- package/dist/kernel/moleculer/remote-logger.d.ts +2 -2
- package/dist/kernel/transport/cap-route-resolver.d.ts +9 -38
- package/dist/phase6-declare-gate-Ci1-8uUn.js +98 -0
- package/dist/phase6-declare-gate-tkKOEitw.mjs +93 -0
- package/dist/read-all-rows-CVoO1ONM.js +29 -0
- package/dist/read-all-rows-TaPqdWNF.mjs +25 -0
- package/dist/records/read-all-rows.d.ts +6 -0
- package/dist/{restore-archive-daKyE24F.mjs → restore-archive-DR9MtO7J.mjs} +339 -9
- package/dist/{restore-archive-Dy-qKTdF.js → restore-archive-DjJnCgap.js} +353 -5
- package/dist/tls/agent-https-names.d.ts +9 -13
- package/dist/{wal-maintenance-dAv4IdzC.js → wal-maintenance-CODGqhKY.js} +0 -0
- package/dist/{wal-maintenance-zRc24mOC.mjs → wal-maintenance-CfvgtQ7C.mjs} +0 -0
- package/package.json +1 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
+
const require_dist = require("./dist-DgOG8flk.js");
|
|
2
3
|
const require_x509_es = require("./x509.es-C4cOo48M.js");
|
|
4
|
+
const require_constants = require("./constants-Br879Axi.js");
|
|
5
|
+
let zod = require("zod");
|
|
3
6
|
let node_crypto = require("node:crypto");
|
|
4
7
|
let _camstack_types_node = require("@camstack/types/node");
|
|
5
8
|
let node_net = require("node:net");
|
|
@@ -7,14 +10,665 @@ let node_path = require("node:path");
|
|
|
7
10
|
let node_os = require("node:os");
|
|
8
11
|
node_os = require_chunk.__toESM(node_os);
|
|
9
12
|
let node_fs = require("node:fs");
|
|
10
|
-
//#region src/
|
|
13
|
+
//#region src/kernel/moleculer/mesh-mtls/mesh-ca.ts
|
|
14
|
+
/**
|
|
15
|
+
* Hub mesh CA — EC P-256, key in the keychain, cert in `cluster.meshCa`.
|
|
16
|
+
*
|
|
17
|
+
* Distinct from the HTTPS local CA (`tls/cert-issue.ts`, RSA): the mesh CA
|
|
18
|
+
* signs node leaves (CN = nodeID) and unlock-ticket JWS (ES256). Issuance uses
|
|
19
|
+
* `@peculiar/x509` over WebCrypto — same library the HTTPS path already uses
|
|
20
|
+
* (installed `1.14.3`; registry latest at adoption time was `2.1.0` — stay on
|
|
21
|
+
* the pinned range already shipping).
|
|
22
|
+
*/
|
|
23
|
+
require_x509_es.cryptoProvider.set(node_crypto.webcrypto);
|
|
24
|
+
var MS_PER_DAY$2 = 864e5;
|
|
25
|
+
/** Key generation — EC P-256 (plan). WebCrypto DOM names are not in @types/node. */
|
|
26
|
+
var MESH_KEY_ALGORITHM = {
|
|
27
|
+
name: "ECDSA",
|
|
28
|
+
namedCurve: "P-256"
|
|
29
|
+
};
|
|
30
|
+
/** Signing — ECDSA with SHA-256. */
|
|
31
|
+
var MESH_SIGNING_ALGORITHM = {
|
|
32
|
+
name: "ECDSA",
|
|
33
|
+
hash: "SHA-256"
|
|
34
|
+
};
|
|
35
|
+
/** A node id: letters, digits, `.`, `_`, `-`; at most 63 characters; starts with a letter or digit. */
|
|
36
|
+
var NODE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,62}$/i;
|
|
37
|
+
/** Ids no agent may take: the hub's own, whatever the case (D857). */
|
|
38
|
+
var RESERVED_NODE_IDS = new Set(["hub"]);
|
|
39
|
+
/** True for an id an agent may carry: the pattern, and not a reserved name. */
|
|
40
|
+
function isValidAgentNodeId(nodeId) {
|
|
41
|
+
return NODE_ID_PATTERN.test(nodeId) && !RESERVED_NODE_IDS.has(nodeId.toLowerCase());
|
|
42
|
+
}
|
|
43
|
+
function freshSerial$1() {
|
|
44
|
+
return `00${(0, node_crypto.randomBytes)(16).toString("hex")}`;
|
|
45
|
+
}
|
|
46
|
+
function toPkcs8Pem$1(pkcs8) {
|
|
47
|
+
return `${require_x509_es.PemConverter.encode(pkcs8, "PRIVATE KEY")}\n`;
|
|
48
|
+
}
|
|
49
|
+
function toCertPem$1(cert) {
|
|
50
|
+
return `${cert.toString("pem")}\n`;
|
|
51
|
+
}
|
|
52
|
+
async function exportKeyPem$1(key) {
|
|
53
|
+
return toPkcs8Pem$1(await node_crypto.webcrypto.subtle.exportKey("pkcs8", key));
|
|
54
|
+
}
|
|
55
|
+
/** SHA-256 fingerprint of the DER cert, colon-hex uppercase (Node style). */
|
|
56
|
+
function caFingerprint(certPem) {
|
|
57
|
+
return new node_crypto.X509Certificate(certPem).fingerprint256;
|
|
58
|
+
}
|
|
59
|
+
/** Compact fingerprint (lowercase hex, no colons). */
|
|
60
|
+
function caFingerprintCompact(certPem) {
|
|
61
|
+
return (0, node_crypto.createHash)("sha256").update(new node_crypto.X509Certificate(certPem).raw).digest("hex");
|
|
62
|
+
}
|
|
63
|
+
async function generateMeshCa(now = /* @__PURE__ */ new Date()) {
|
|
64
|
+
const keys = await node_crypto.webcrypto.subtle.generateKey(MESH_KEY_ALGORITHM, true, ["sign", "verify"]);
|
|
65
|
+
const extensions = [
|
|
66
|
+
new require_x509_es.BasicConstraintsExtension(true, 0, true),
|
|
67
|
+
new require_x509_es.KeyUsagesExtension(require_x509_es.KeyUsageFlags.keyCertSign | require_x509_es.KeyUsageFlags.cRLSign, true),
|
|
68
|
+
await require_x509_es.SubjectKeyIdentifierExtension.create(keys.publicKey)
|
|
69
|
+
];
|
|
70
|
+
const cert = await require_x509_es.X509CertificateGenerator.createSelfSigned({
|
|
71
|
+
serialNumber: freshSerial$1(),
|
|
72
|
+
name: `CN=${require_constants.MESH_CA_COMMON_NAME}`,
|
|
73
|
+
notBefore: require_x509_es.backdatedNotBefore(now),
|
|
74
|
+
notAfter: new Date(now.getTime() + require_constants.MESH_CA_VALIDITY_DAYS * MS_PER_DAY$2),
|
|
75
|
+
keys,
|
|
76
|
+
signingAlgorithm: MESH_SIGNING_ALGORITHM,
|
|
77
|
+
extensions
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
certPem: toCertPem$1(cert),
|
|
81
|
+
keyPem: await exportKeyPem$1(keys.privateKey),
|
|
82
|
+
cert,
|
|
83
|
+
privateKey: keys.privateKey,
|
|
84
|
+
publicKey: keys.publicKey
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
async function loadMeshCa(certPem, keyPem) {
|
|
88
|
+
const cert = new require_x509_es.X509Certificate(certPem);
|
|
89
|
+
return {
|
|
90
|
+
certPem,
|
|
91
|
+
keyPem,
|
|
92
|
+
cert,
|
|
93
|
+
privateKey: await node_crypto.webcrypto.subtle.importKey("pkcs8", require_x509_es.PemConverter.decodeFirst(keyPem), MESH_KEY_ALGORITHM, true, ["sign"]),
|
|
94
|
+
publicKey: await cert.publicKey.export(node_crypto.webcrypto)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Issue a leaf from a CSR. CN must be present; when `expectedNodeId` is set
|
|
99
|
+
* it must match. Validity: 30 days.
|
|
100
|
+
*/
|
|
101
|
+
async function issueMeshLeafFromCsr(ca, csrPem, opts = {}) {
|
|
102
|
+
const csr = new require_x509_es.Pkcs10CertificateRequest(csrPem);
|
|
103
|
+
if (!await csr.verify(node_crypto.webcrypto)) throw new Error("CSR signature is invalid");
|
|
104
|
+
const cn = subjectCn(csr.subject);
|
|
105
|
+
if (cn === null || cn.length === 0) throw new Error("CSR subject has no CN");
|
|
106
|
+
if (opts.allowReserved !== true && !isValidAgentNodeId(cn)) throw new Error(`CSR CN '${cn}' is not an id an agent may carry`);
|
|
107
|
+
if (opts.expectedNodeId !== void 0 && opts.expectedNodeId !== cn) throw new Error(`CSR CN '${cn}' does not match the token nodeId '${opts.expectedNodeId}'`);
|
|
108
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
109
|
+
const validDays = opts.validDays ?? 30;
|
|
110
|
+
const serialNumber = freshSerial$1();
|
|
111
|
+
const notAfter = new Date(now.getTime() + validDays * MS_PER_DAY$2);
|
|
112
|
+
const extensions = [
|
|
113
|
+
new require_x509_es.BasicConstraintsExtension(false, void 0, true),
|
|
114
|
+
new require_x509_es.KeyUsagesExtension(require_x509_es.KeyUsageFlags.digitalSignature, true),
|
|
115
|
+
new require_x509_es.ExtendedKeyUsageExtension([require_constants.SERVER_AUTH_OID, require_constants.CLIENT_AUTH_OID]),
|
|
116
|
+
await require_x509_es.SubjectKeyIdentifierExtension.create(csr.publicKey),
|
|
117
|
+
await require_x509_es.AuthorityKeyIdentifierExtension.create(ca.cert)
|
|
118
|
+
];
|
|
119
|
+
return {
|
|
120
|
+
certPem: toCertPem$1(await require_x509_es.X509CertificateGenerator.create({
|
|
121
|
+
serialNumber,
|
|
122
|
+
subject: `CN=${cn}`,
|
|
123
|
+
issuer: ca.cert.subject,
|
|
124
|
+
notBefore: require_x509_es.backdatedNotBefore(now),
|
|
125
|
+
notAfter,
|
|
126
|
+
publicKey: csr.publicKey,
|
|
127
|
+
signingKey: ca.privateKey,
|
|
128
|
+
signingAlgorithm: MESH_SIGNING_ALGORITHM,
|
|
129
|
+
extensions
|
|
130
|
+
})),
|
|
131
|
+
serialNumber,
|
|
132
|
+
notAfter,
|
|
133
|
+
commonName: cn
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** Issue a leaf for a known nodeID with a freshly generated key pair (tests / hub self). */
|
|
137
|
+
async function issueMeshLeafForNode(ca, nodeId, opts = {}) {
|
|
138
|
+
const keys = await node_crypto.webcrypto.subtle.generateKey(MESH_KEY_ALGORITHM, true, ["sign", "verify"]);
|
|
139
|
+
return {
|
|
140
|
+
...await issueMeshLeafFromCsr(ca, await createMeshCsrPem(keys, nodeId), {
|
|
141
|
+
expectedNodeId: nodeId,
|
|
142
|
+
now: opts.now,
|
|
143
|
+
validDays: opts.validDays,
|
|
144
|
+
allowReserved: true
|
|
145
|
+
}),
|
|
146
|
+
keyPem: await exportKeyPem$1(keys.privateKey)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function subjectCn(subject) {
|
|
150
|
+
return /(?:^|,)\s*CN\s*=\s*([^,]+)/i.exec(subject)?.[1]?.trim() ?? null;
|
|
151
|
+
}
|
|
152
|
+
async function createMeshCsrPem(keys, commonName) {
|
|
153
|
+
return `${(await require_x509_es.Pkcs10CertificateRequestGenerator.create({
|
|
154
|
+
name: `CN=${commonName}`,
|
|
155
|
+
keys,
|
|
156
|
+
signingAlgorithm: MESH_SIGNING_ALGORITHM
|
|
157
|
+
})).toString("pem")}\n`;
|
|
158
|
+
}
|
|
159
|
+
async function generateMeshKeyPair() {
|
|
160
|
+
return node_crypto.webcrypto.subtle.generateKey(MESH_KEY_ALGORITHM, true, ["sign", "verify"]);
|
|
161
|
+
}
|
|
162
|
+
async function exportMeshPrivateKeyPem(key) {
|
|
163
|
+
return exportKeyPem$1(key);
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/kernel/moleculer/mesh-mtls/mesh-certificate-registry.ts
|
|
167
|
+
/**
|
|
168
|
+
* The hub's record of every mesh leaf (D856): which certificate each node holds,
|
|
169
|
+
* when it expires, and which serials are revoked.
|
|
170
|
+
*
|
|
171
|
+
* Three ways a row appears:
|
|
172
|
+
* - `enroll` / `renewal` / `boot` - the hub issued it and wrote it down at the
|
|
173
|
+
* moment of issuing (the authoritative record);
|
|
174
|
+
* - `observed` - a node enrolled before this record existed (or by a path that
|
|
175
|
+
* did not write it) presented this leaf on its LIVE mutual-TLS connection.
|
|
176
|
+
*
|
|
177
|
+
* Revoked serials are persisted here and replayed into the in-memory CRL at boot,
|
|
178
|
+
* so a revocation survives a hub restart. Hub only: agents persist none of it.
|
|
179
|
+
*/
|
|
11
180
|
/**
|
|
12
181
|
* @durable class=config owner=system-config
|
|
13
|
-
* write="the hub records
|
|
14
|
-
* node
|
|
15
|
-
* retention="
|
|
16
|
-
*
|
|
182
|
+
* write="the hub records every mesh leaf it issues (enroll, renewal, boot) and every
|
|
183
|
+
* leaf a connected node is observed presenting; a revocation stamps revokedAt"
|
|
184
|
+
* retention="a revoked or expired row is kept 90 days past its end, then reaped; a
|
|
185
|
+
* live row is never deleted. Forgetting a node does not delete its rows."
|
|
17
186
|
*/
|
|
187
|
+
var MESH_NODE_CERTIFICATES_COLLECTION = "mesh_node_certificates";
|
|
188
|
+
var MESH_NODE_CERTIFICATES_COLUMNS = [
|
|
189
|
+
{
|
|
190
|
+
name: "id",
|
|
191
|
+
type: "TEXT",
|
|
192
|
+
primaryKey: true,
|
|
193
|
+
notNull: true
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "nodeId",
|
|
197
|
+
type: "TEXT",
|
|
198
|
+
notNull: true
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: "serial",
|
|
202
|
+
type: "TEXT",
|
|
203
|
+
notNull: true
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: "fingerprint",
|
|
207
|
+
type: "TEXT",
|
|
208
|
+
notNull: true
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: "notBefore",
|
|
212
|
+
type: "INTEGER",
|
|
213
|
+
notNull: true
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "notAfter",
|
|
217
|
+
type: "INTEGER",
|
|
218
|
+
notNull: true
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "issuedAt",
|
|
222
|
+
type: "INTEGER",
|
|
223
|
+
notNull: true
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "issuedVia",
|
|
227
|
+
type: "TEXT",
|
|
228
|
+
notNull: true
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: "revokedAt",
|
|
232
|
+
type: "INTEGER"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "revokedReason",
|
|
236
|
+
type: "TEXT"
|
|
237
|
+
}
|
|
238
|
+
];
|
|
239
|
+
var RETENTION_MS = 90 * 864e5;
|
|
240
|
+
/**
|
|
241
|
+
* A leaf the hub signed for a renewal that did not complete. It must not be the node's
|
|
242
|
+
* `current` leaf, but it is NOT on the CRL: the node may still hold it (the swap
|
|
243
|
+
* could have landed without the hub seeing it), and cutting it off would be the
|
|
244
|
+
* outage the hand-over exists to prevent. If the node is later seen presenting it, it
|
|
245
|
+
* is reinstated.
|
|
246
|
+
*/
|
|
247
|
+
var RENEWAL_ABANDONED = "renewal-abandoned";
|
|
248
|
+
var VIAS = [
|
|
249
|
+
"enroll",
|
|
250
|
+
"pairing",
|
|
251
|
+
"renewal",
|
|
252
|
+
"boot",
|
|
253
|
+
"observed"
|
|
254
|
+
];
|
|
255
|
+
/** CN, serial, fingerprint and validity of a leaf PEM (or DER). */
|
|
256
|
+
function describeMeshLeaf(cert) {
|
|
257
|
+
const x509 = new node_crypto.X509Certificate(cert);
|
|
258
|
+
return {
|
|
259
|
+
cn: subjectCn(x509.subject) ?? "",
|
|
260
|
+
serial: x509.serialNumber,
|
|
261
|
+
fingerprint: x509.fingerprint256,
|
|
262
|
+
notBefore: Date.parse(x509.validFrom),
|
|
263
|
+
notAfter: Date.parse(x509.validTo)
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function normalizeSerial(serial) {
|
|
267
|
+
return serial.replace(/^0x/i, "").replace(/:/g, "").toLowerCase();
|
|
268
|
+
}
|
|
269
|
+
function parse$1(data) {
|
|
270
|
+
const str = (k) => {
|
|
271
|
+
const v = data[k];
|
|
272
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
273
|
+
};
|
|
274
|
+
const num = (k) => {
|
|
275
|
+
const v = data[k];
|
|
276
|
+
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
277
|
+
};
|
|
278
|
+
const nodeId = str("nodeId");
|
|
279
|
+
const serial = str("serial");
|
|
280
|
+
const fingerprint = str("fingerprint");
|
|
281
|
+
const notBefore = num("notBefore");
|
|
282
|
+
const notAfter = num("notAfter");
|
|
283
|
+
const issuedAt = num("issuedAt");
|
|
284
|
+
if (nodeId === null || serial === null || fingerprint === null || notBefore === null || notAfter === null || issuedAt === null) return null;
|
|
285
|
+
return {
|
|
286
|
+
nodeId,
|
|
287
|
+
serial,
|
|
288
|
+
fingerprint,
|
|
289
|
+
notBefore,
|
|
290
|
+
notAfter,
|
|
291
|
+
issuedAt,
|
|
292
|
+
issuedVia: VIAS.find((v) => v === data["issuedVia"]) ?? "observed",
|
|
293
|
+
revokedAt: num("revokedAt"),
|
|
294
|
+
revokedReason: str("revokedReason")
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
var MeshCertificateRegistry = class {
|
|
298
|
+
store;
|
|
299
|
+
deps;
|
|
300
|
+
rows = /* @__PURE__ */ new Map();
|
|
301
|
+
constructor(store, deps = {}) {
|
|
302
|
+
this.store = store;
|
|
303
|
+
this.deps = deps;
|
|
304
|
+
}
|
|
305
|
+
recordToBody(record) {
|
|
306
|
+
return {
|
|
307
|
+
nodeId: record.nodeId,
|
|
308
|
+
serial: record.serial,
|
|
309
|
+
fingerprint: record.fingerprint,
|
|
310
|
+
notBefore: record.notBefore,
|
|
311
|
+
notAfter: record.notAfter,
|
|
312
|
+
issuedAt: record.issuedAt,
|
|
313
|
+
issuedVia: record.issuedVia,
|
|
314
|
+
revokedAt: record.revokedAt,
|
|
315
|
+
revokedReason: record.revokedReason
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
now() {
|
|
319
|
+
return (this.deps.now ?? Date.now)();
|
|
320
|
+
}
|
|
321
|
+
async load() {
|
|
322
|
+
const kind = this.deps.meshKind ?? null;
|
|
323
|
+
if (kind !== null) {
|
|
324
|
+
const page = await kind.query({ limit: 1e4 });
|
|
325
|
+
for (const row of page.records) {
|
|
326
|
+
const { fingerprint: _fp, ...body } = row.data;
|
|
327
|
+
const record = parse$1({
|
|
328
|
+
...body,
|
|
329
|
+
fingerprint: row.id
|
|
330
|
+
});
|
|
331
|
+
if (record !== null) this.rows.set(record.fingerprint, record);
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const rows = await this.store.query.query({ collection: MESH_NODE_CERTIFICATES_COLLECTION });
|
|
336
|
+
for (const row of rows) {
|
|
337
|
+
const record = parse$1(row.data);
|
|
338
|
+
if (record !== null) this.rows.set(record.fingerprint, record);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/** Write down a leaf the hub just issued. Idempotent per fingerprint. */
|
|
342
|
+
async recordIssued(nodeId, certPem, via) {
|
|
343
|
+
const leaf = describeMeshLeaf(certPem);
|
|
344
|
+
const existing = this.rows.get(leaf.fingerprint);
|
|
345
|
+
if (existing !== void 0) return existing;
|
|
346
|
+
const record = {
|
|
347
|
+
nodeId,
|
|
348
|
+
serial: leaf.serial,
|
|
349
|
+
fingerprint: leaf.fingerprint,
|
|
350
|
+
notBefore: leaf.notBefore,
|
|
351
|
+
notAfter: leaf.notAfter,
|
|
352
|
+
issuedAt: this.now(),
|
|
353
|
+
issuedVia: via,
|
|
354
|
+
revokedAt: null,
|
|
355
|
+
revokedReason: null
|
|
356
|
+
};
|
|
357
|
+
await this.insert(record);
|
|
358
|
+
this.deps.logger?.info("Mesh certificate recorded", {
|
|
359
|
+
tags: { nodeId },
|
|
360
|
+
meta: {
|
|
361
|
+
via,
|
|
362
|
+
serial: leaf.serial,
|
|
363
|
+
notAfter: new Date(leaf.notAfter).toISOString()
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
return record;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* A node was seen presenting `leaf` on its live connection. A leaf the hub already
|
|
370
|
+
* knows is left alone; an unknown one is recorded `observed` (a node enrolled
|
|
371
|
+
* before this record existed).
|
|
372
|
+
*/
|
|
373
|
+
async recordObserved(nodeId, leaf) {
|
|
374
|
+
const known = this.rows.get(leaf.fingerprint);
|
|
375
|
+
if (known !== void 0) {
|
|
376
|
+
if (known.revokedReason !== "renewal-abandoned") return known.revokedAt === null ? this.abandonRenewalsNewerThan(known) : false;
|
|
377
|
+
const reinstated = {
|
|
378
|
+
...known,
|
|
379
|
+
revokedAt: null,
|
|
380
|
+
revokedReason: null
|
|
381
|
+
};
|
|
382
|
+
this.rows.set(known.fingerprint, reinstated);
|
|
383
|
+
await this.persist(reinstated);
|
|
384
|
+
this.deps.logger?.warn("An abandoned renewal leaf is in use - reinstated", {
|
|
385
|
+
tags: { nodeId },
|
|
386
|
+
meta: { serial: known.serial }
|
|
387
|
+
});
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
const record = {
|
|
391
|
+
nodeId,
|
|
392
|
+
serial: leaf.serial,
|
|
393
|
+
fingerprint: leaf.fingerprint,
|
|
394
|
+
notBefore: leaf.notBefore,
|
|
395
|
+
notAfter: leaf.notAfter,
|
|
396
|
+
issuedAt: this.now(),
|
|
397
|
+
issuedVia: "observed",
|
|
398
|
+
revokedAt: null,
|
|
399
|
+
revokedReason: null
|
|
400
|
+
};
|
|
401
|
+
await this.insert(record);
|
|
402
|
+
this.deps.logger?.info("Mesh certificate observed on a live connection and recorded", {
|
|
403
|
+
tags: { nodeId },
|
|
404
|
+
meta: {
|
|
405
|
+
serial: leaf.serial,
|
|
406
|
+
fingerprint: leaf.fingerprint
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
list(nodeId) {
|
|
412
|
+
return [...this.rows.values()].filter((r) => nodeId === void 0 || r.nodeId === nodeId).toSorted((a, b) => b.issuedAt - a.issuedAt);
|
|
413
|
+
}
|
|
414
|
+
/** The newest non-revoked leaf of a node, or `undefined`. */
|
|
415
|
+
currentFor(nodeId) {
|
|
416
|
+
return this.list(nodeId).find((r) => r.revokedAt === null);
|
|
417
|
+
}
|
|
418
|
+
/** Every non-revoked leaf of a node (a renewal in flight has two). */
|
|
419
|
+
liveFor(nodeId) {
|
|
420
|
+
return this.list(nodeId).filter((r) => r.revokedAt === null);
|
|
421
|
+
}
|
|
422
|
+
/** Stamp a leaf revoked (and mirror it into `crl`). `false` when unknown or already revoked. */
|
|
423
|
+
async revoke(fingerprint, reason, crl) {
|
|
424
|
+
const record = this.rows.get(fingerprint);
|
|
425
|
+
if (record === void 0 || record.revokedAt !== null) return false;
|
|
426
|
+
const revoked = {
|
|
427
|
+
...record,
|
|
428
|
+
revokedAt: this.now(),
|
|
429
|
+
revokedReason: reason
|
|
430
|
+
};
|
|
431
|
+
this.rows.set(fingerprint, revoked);
|
|
432
|
+
crl?.revoke(record.serial);
|
|
433
|
+
await this.persist(revoked);
|
|
434
|
+
this.deps.logger?.warn("Mesh certificate revoked", {
|
|
435
|
+
tags: { nodeId: record.nodeId },
|
|
436
|
+
meta: {
|
|
437
|
+
serial: record.serial,
|
|
438
|
+
reason
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Operator revocation of a whole node: EVERY leaf of the node that is not already on the
|
|
445
|
+
* CRL goes on it - the current one, an observed one, a renewal one, and a leaf that was set
|
|
446
|
+
* aside (`renewal-abandoned`: a copy of it may sit on a stolen box, and it must not stay
|
|
447
|
+
* valid just because the hub once set it aside). Returns the serials put on the CRL.
|
|
448
|
+
* A leaf revoked this way is never reinstated by `recordObserved`.
|
|
449
|
+
*/
|
|
450
|
+
async revokeAllForNode(nodeId, reason, crl, keepFingerprint) {
|
|
451
|
+
const serials = [];
|
|
452
|
+
for (const record of this.list(nodeId)) {
|
|
453
|
+
if (keepFingerprint !== void 0 && record.fingerprint === keepFingerprint) continue;
|
|
454
|
+
if (record.revokedAt !== null && record.revokedReason !== "renewal-abandoned") continue;
|
|
455
|
+
const revoked = {
|
|
456
|
+
...record,
|
|
457
|
+
revokedAt: record.revokedAt ?? this.now(),
|
|
458
|
+
revokedReason: reason
|
|
459
|
+
};
|
|
460
|
+
this.rows.set(record.fingerprint, revoked);
|
|
461
|
+
crl.revoke(record.serial);
|
|
462
|
+
await this.persist(revoked);
|
|
463
|
+
serials.push(record.serial);
|
|
464
|
+
}
|
|
465
|
+
if (serials.length > 0) this.deps.logger?.warn("Every mesh certificate of the node revoked", {
|
|
466
|
+
tags: { nodeId },
|
|
467
|
+
meta: {
|
|
468
|
+
serials,
|
|
469
|
+
reason
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
return serials;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* At boot: a node with TWO live leaves had a renewal in flight when the hub stopped. The
|
|
476
|
+
* newer (renewal) one is set aside - it is not the node's current leaf until the node is
|
|
477
|
+
* seen presenting it, which reinstates it. It is NOT put on the CRL.
|
|
478
|
+
*/
|
|
479
|
+
async settleInterruptedRenewals() {
|
|
480
|
+
let settled = 0;
|
|
481
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
482
|
+
for (const record of this.rows.values()) {
|
|
483
|
+
if (record.revokedAt !== null) continue;
|
|
484
|
+
byNode.set(record.nodeId, [...byNode.get(record.nodeId) ?? [], record]);
|
|
485
|
+
}
|
|
486
|
+
for (const records of byNode.values()) {
|
|
487
|
+
if (records.length < 2) continue;
|
|
488
|
+
for (const record of records) if (record.issuedVia === "renewal" && await this.abandon(record.fingerprint)) settled += 1;
|
|
489
|
+
}
|
|
490
|
+
return settled;
|
|
491
|
+
}
|
|
492
|
+
/** Set a never-completed renewal leaf aside (not current, NOT on the CRL). */
|
|
493
|
+
/** Fingerprints of renewal leaves whose handover is running right now (this process only). */
|
|
494
|
+
renewalsInFlight = /* @__PURE__ */ new Set();
|
|
495
|
+
/**
|
|
496
|
+
* A renewal's leaf is "in flight" from signing until the handover ends. While it is, a node seen
|
|
497
|
+
* presenting its OLD leaf is simply not swapped yet: the new leaf is not set aside (that would
|
|
498
|
+
* flap set-aside / reinstate on the node's first connection with it).
|
|
499
|
+
*/
|
|
500
|
+
beginRenewal(fingerprint) {
|
|
501
|
+
this.renewalsInFlight.add(fingerprint);
|
|
502
|
+
}
|
|
503
|
+
endRenewal(fingerprint) {
|
|
504
|
+
this.renewalsInFlight.delete(fingerprint);
|
|
505
|
+
}
|
|
506
|
+
async abandonRenewalsNewerThan(presented) {
|
|
507
|
+
let changed = false;
|
|
508
|
+
for (const record of this.liveFor(presented.nodeId)) {
|
|
509
|
+
if (record.fingerprint === presented.fingerprint) continue;
|
|
510
|
+
if (record.issuedVia !== "renewal" || record.issuedAt <= presented.issuedAt) continue;
|
|
511
|
+
if (this.renewalsInFlight.has(record.fingerprint)) continue;
|
|
512
|
+
if (await this.abandon(record.fingerprint)) changed = true;
|
|
513
|
+
}
|
|
514
|
+
return changed;
|
|
515
|
+
}
|
|
516
|
+
async abandon(fingerprint) {
|
|
517
|
+
const record = this.rows.get(fingerprint);
|
|
518
|
+
if (record === void 0 || record.revokedAt !== null) return false;
|
|
519
|
+
const abandoned = {
|
|
520
|
+
...record,
|
|
521
|
+
revokedAt: this.now(),
|
|
522
|
+
revokedReason: RENEWAL_ABANDONED
|
|
523
|
+
};
|
|
524
|
+
this.rows.set(fingerprint, abandoned);
|
|
525
|
+
await this.persist(abandoned);
|
|
526
|
+
this.deps.logger?.warn("Renewal leaf set aside (not current, not on the CRL)", {
|
|
527
|
+
tags: { nodeId: record.nodeId },
|
|
528
|
+
meta: { serial: record.serial }
|
|
529
|
+
});
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
/** Replay every persisted revocation into `crl` (hub boot). Set-aside leaves are not revocations. */
|
|
533
|
+
restoreCrl(crl) {
|
|
534
|
+
let restored = 0;
|
|
535
|
+
for (const record of this.rows.values()) {
|
|
536
|
+
if (record.revokedAt === null || record.revokedReason === "renewal-abandoned") continue;
|
|
537
|
+
crl.revoke(record.serial);
|
|
538
|
+
restored += 1;
|
|
539
|
+
}
|
|
540
|
+
return restored;
|
|
541
|
+
}
|
|
542
|
+
/** Is `serial` listed as revoked here? */
|
|
543
|
+
isRevoked(serial) {
|
|
544
|
+
const wanted = normalizeSerial(serial);
|
|
545
|
+
for (const record of this.rows.values()) if (record.revokedAt !== null && record.revokedReason !== "renewal-abandoned" && normalizeSerial(record.serial) === wanted) return true;
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
async reap() {
|
|
549
|
+
const cutoff = this.now() - RETENTION_MS;
|
|
550
|
+
const dead = [...this.rows.values()].filter((r) => (r.revokedAt ?? r.notAfter) < cutoff);
|
|
551
|
+
for (const r of dead) {
|
|
552
|
+
this.rows.delete(r.fingerprint);
|
|
553
|
+
await this.deleteRow(r.fingerprint);
|
|
554
|
+
}
|
|
555
|
+
return dead.length;
|
|
556
|
+
}
|
|
557
|
+
async insert(record) {
|
|
558
|
+
this.rows.set(record.fingerprint, record);
|
|
559
|
+
const kind = this.deps.meshKind ?? null;
|
|
560
|
+
if (kind !== null) {
|
|
561
|
+
const parsed = systemConfigRecordKinds["mesh-node-certificates"].schema.safeParse(this.recordToBody(record));
|
|
562
|
+
if (!parsed.success) throw new Error(parsed.error.message);
|
|
563
|
+
await kind.put({
|
|
564
|
+
id: record.fingerprint,
|
|
565
|
+
data: parsed.data
|
|
566
|
+
});
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
await this.store.insert.mutate({
|
|
570
|
+
collection: MESH_NODE_CERTIFICATES_COLLECTION,
|
|
571
|
+
record: {
|
|
572
|
+
id: record.fingerprint,
|
|
573
|
+
data: { ...record }
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
async deleteRow(fingerprint) {
|
|
578
|
+
const kind = this.deps.meshKind ?? null;
|
|
579
|
+
if (kind !== null) {
|
|
580
|
+
await kind.delete(fingerprint);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
await this.store.delete.mutate({
|
|
584
|
+
collection: MESH_NODE_CERTIFICATES_COLLECTION,
|
|
585
|
+
key: fingerprint
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
async persist(record) {
|
|
589
|
+
try {
|
|
590
|
+
const kind = this.deps.meshKind ?? null;
|
|
591
|
+
if (kind !== null) {
|
|
592
|
+
const parsed = systemConfigRecordKinds["mesh-node-certificates"].schema.safeParse(this.recordToBody(record));
|
|
593
|
+
if (!parsed.success) throw new Error(parsed.error.message);
|
|
594
|
+
await kind.put({
|
|
595
|
+
id: record.fingerprint,
|
|
596
|
+
data: parsed.data
|
|
597
|
+
});
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
await this.store.update.mutate({
|
|
601
|
+
collection: MESH_NODE_CERTIFICATES_COLLECTION,
|
|
602
|
+
id: record.fingerprint,
|
|
603
|
+
data: { ...record }
|
|
604
|
+
});
|
|
605
|
+
} catch (error) {
|
|
606
|
+
this.deps.logger?.error("Mesh certificate write failed - the change holds until restart", {
|
|
607
|
+
tags: { nodeId: record.nodeId },
|
|
608
|
+
meta: { error: error instanceof Error ? error.message : String(error) }
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
var SLOT$1 = Symbol.for("camstack.mesh.certificate-registry");
|
|
614
|
+
function setProcessMeshCertificateRegistry(registry) {
|
|
615
|
+
if (registry === null) Reflect.deleteProperty(globalThis, SLOT$1);
|
|
616
|
+
else Reflect.set(globalThis, SLOT$1, registry);
|
|
617
|
+
}
|
|
618
|
+
function getProcessMeshCertificateRegistry() {
|
|
619
|
+
const value = Reflect.get(globalThis, SLOT$1);
|
|
620
|
+
return value !== null && typeof value === "object" && typeof Reflect.get(value, "recordIssued") === "function" ? value : null;
|
|
621
|
+
}
|
|
622
|
+
var MeshNodeCertificateBodySchema = zod.z.object({
|
|
623
|
+
nodeId: zod.z.string(),
|
|
624
|
+
serial: zod.z.string(),
|
|
625
|
+
fingerprint: zod.z.string(),
|
|
626
|
+
notBefore: zod.z.number(),
|
|
627
|
+
notAfter: zod.z.number(),
|
|
628
|
+
issuedAt: zod.z.number(),
|
|
629
|
+
issuedVia: zod.z.string(),
|
|
630
|
+
revokedAt: zod.z.number().nullish(),
|
|
631
|
+
revokedReason: zod.z.string().nullish()
|
|
632
|
+
});
|
|
633
|
+
var AgentHttpsNameBodySchema = zod.z.object({
|
|
634
|
+
names: zod.z.array(zod.z.string()).readonly(),
|
|
635
|
+
issuedAt: zod.z.number()
|
|
636
|
+
});
|
|
637
|
+
var _systemConfigRecordKinds = require_dist.defineRecordKinds({
|
|
638
|
+
"mesh-node-certificates": {
|
|
639
|
+
schema: MeshNodeCertificateBodySchema,
|
|
640
|
+
class: "config",
|
|
641
|
+
writeTrigger: "mesh pairing, renewal, revocation (CRL mirror refreshed from rows)",
|
|
642
|
+
device: "none",
|
|
643
|
+
at: "none",
|
|
644
|
+
indexes: [{
|
|
645
|
+
name: "node",
|
|
646
|
+
fields: ["nodeId"]
|
|
647
|
+
}, {
|
|
648
|
+
name: "fingerprint",
|
|
649
|
+
fields: ["fingerprint"]
|
|
650
|
+
}],
|
|
651
|
+
retention: {
|
|
652
|
+
owner: "system-config",
|
|
653
|
+
reason: "daily reap removes revoked/expired rows older than 90 days"
|
|
654
|
+
},
|
|
655
|
+
adoptsFrom: MESH_NODE_CERTIFICATES_COLLECTION
|
|
656
|
+
},
|
|
657
|
+
"agent-https-names": {
|
|
658
|
+
schema: AgentHttpsNameBodySchema,
|
|
659
|
+
class: "config",
|
|
660
|
+
writeTrigger: "agent HTTPS certificate issuance records the CN/SAN set",
|
|
661
|
+
device: "none",
|
|
662
|
+
at: "none",
|
|
663
|
+
retention: { none: "one row per node id" },
|
|
664
|
+
adoptsFrom: AGENT_HTTPS_NAMES_COLLECTION
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
var systemConfigRecordKinds = _systemConfigRecordKinds;
|
|
668
|
+
_systemConfigRecordKinds["mesh-node-certificates"];
|
|
669
|
+
_systemConfigRecordKinds["agent-https-names"];
|
|
670
|
+
//#endregion
|
|
671
|
+
//#region src/tls/agent-https-names.ts
|
|
18
672
|
var AGENT_HTTPS_NAMES_COLLECTION = "agent_https_names";
|
|
19
673
|
var AGENT_HTTPS_NAMES_COLUMNS = [
|
|
20
674
|
{
|
|
@@ -35,23 +689,34 @@ var AGENT_HTTPS_NAMES_COLUMNS = [
|
|
|
35
689
|
}
|
|
36
690
|
];
|
|
37
691
|
var AgentHttpsNames = class {
|
|
38
|
-
store;
|
|
39
692
|
byNode = /* @__PURE__ */ new Map();
|
|
40
|
-
|
|
41
|
-
|
|
693
|
+
kind;
|
|
694
|
+
store;
|
|
695
|
+
constructor(deps) {
|
|
696
|
+
if ("agentHttpsNamesKind" in deps) {
|
|
697
|
+
this.kind = deps.agentHttpsNamesKind;
|
|
698
|
+
this.store = deps.legacyStore ?? null;
|
|
699
|
+
} else {
|
|
700
|
+
this.kind = null;
|
|
701
|
+
this.store = deps;
|
|
702
|
+
}
|
|
42
703
|
}
|
|
43
704
|
async load() {
|
|
705
|
+
const kind = this.kind;
|
|
706
|
+
if (kind !== null) {
|
|
707
|
+
const page = await kind.query({ limit: 1e4 });
|
|
708
|
+
for (const row of page.records) this.byNode.set(row.id, row.data.names.map((n) => n.toLowerCase()));
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
44
711
|
const rows = await this.store.query.query({ collection: AGENT_HTTPS_NAMES_COLLECTION });
|
|
45
712
|
for (const row of rows) {
|
|
46
713
|
const names = row.data["names"];
|
|
47
714
|
if (Array.isArray(names)) this.byNode.set(row.id, names.filter((n) => typeof n === "string").map((n) => n.toLowerCase()));
|
|
48
715
|
}
|
|
49
716
|
}
|
|
50
|
-
/** The names certified to `nodeId` (its id and the address of its live mesh socket), or `null`. */
|
|
51
717
|
namesOf(nodeId) {
|
|
52
718
|
return this.byNode.get(nodeId) ?? null;
|
|
53
719
|
}
|
|
54
|
-
/** Every name certified to a node other than `nodeId`. */
|
|
55
720
|
takenByOthers(nodeId) {
|
|
56
721
|
const out = /* @__PURE__ */ new Set();
|
|
57
722
|
for (const [id, names] of this.byNode) {
|
|
@@ -60,9 +725,13 @@ var AgentHttpsNames = class {
|
|
|
60
725
|
}
|
|
61
726
|
return out;
|
|
62
727
|
}
|
|
63
|
-
/** Free every name certified to a node that was forgotten. */
|
|
64
728
|
async release(nodeId) {
|
|
65
729
|
if (!this.byNode.delete(nodeId)) return false;
|
|
730
|
+
const kind = this.kind;
|
|
731
|
+
if (kind !== null) {
|
|
732
|
+
await kind.delete(nodeId);
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
66
735
|
await this.store.delete.mutate({
|
|
67
736
|
collection: AGENT_HTTPS_NAMES_COLLECTION,
|
|
68
737
|
key: nodeId
|
|
@@ -73,9 +742,24 @@ var AgentHttpsNames = class {
|
|
|
73
742
|
const lowered = names.map((n) => n.toLowerCase());
|
|
74
743
|
const existed = this.byNode.has(nodeId);
|
|
75
744
|
this.byNode.set(nodeId, lowered);
|
|
745
|
+
const issuedAt = Date.now();
|
|
746
|
+
const body = {
|
|
747
|
+
names: lowered,
|
|
748
|
+
issuedAt
|
|
749
|
+
};
|
|
750
|
+
const kind = this.kind;
|
|
751
|
+
if (kind !== null) {
|
|
752
|
+
const parsed = systemConfigRecordKinds["agent-https-names"].schema.safeParse(body);
|
|
753
|
+
if (!parsed.success) throw new Error(parsed.error.message);
|
|
754
|
+
await kind.put({
|
|
755
|
+
id: nodeId,
|
|
756
|
+
data: parsed.data
|
|
757
|
+
});
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
76
760
|
const data = {
|
|
77
761
|
names: lowered,
|
|
78
|
-
issuedAt
|
|
762
|
+
issuedAt
|
|
79
763
|
};
|
|
80
764
|
if (existed) await this.store.update.mutate({
|
|
81
765
|
collection: AGENT_HTTPS_NAMES_COLLECTION,
|
|
@@ -993,6 +1677,24 @@ Object.defineProperty(exports, "MAX_LEAF_VALIDITY_DAYS", {
|
|
|
993
1677
|
return MAX_LEAF_VALIDITY_DAYS;
|
|
994
1678
|
}
|
|
995
1679
|
});
|
|
1680
|
+
Object.defineProperty(exports, "MESH_NODE_CERTIFICATES_COLLECTION", {
|
|
1681
|
+
enumerable: true,
|
|
1682
|
+
get: function() {
|
|
1683
|
+
return MESH_NODE_CERTIFICATES_COLLECTION;
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1686
|
+
Object.defineProperty(exports, "MESH_NODE_CERTIFICATES_COLUMNS", {
|
|
1687
|
+
enumerable: true,
|
|
1688
|
+
get: function() {
|
|
1689
|
+
return MESH_NODE_CERTIFICATES_COLUMNS;
|
|
1690
|
+
}
|
|
1691
|
+
});
|
|
1692
|
+
Object.defineProperty(exports, "MeshCertificateRegistry", {
|
|
1693
|
+
enumerable: true,
|
|
1694
|
+
get: function() {
|
|
1695
|
+
return MeshCertificateRegistry;
|
|
1696
|
+
}
|
|
1697
|
+
});
|
|
996
1698
|
Object.defineProperty(exports, "PERMITTED_DNS_SUFFIXES", {
|
|
997
1699
|
enumerable: true,
|
|
998
1700
|
get: function() {
|
|
@@ -1005,6 +1707,12 @@ Object.defineProperty(exports, "PERMITTED_IP_RANGES", {
|
|
|
1005
1707
|
return PERMITTED_IP_RANGES;
|
|
1006
1708
|
}
|
|
1007
1709
|
});
|
|
1710
|
+
Object.defineProperty(exports, "RENEWAL_ABANDONED", {
|
|
1711
|
+
enumerable: true,
|
|
1712
|
+
get: function() {
|
|
1713
|
+
return RENEWAL_ABANDONED;
|
|
1714
|
+
}
|
|
1715
|
+
});
|
|
1008
1716
|
Object.defineProperty(exports, "SERVER_AUTH_OID", {
|
|
1009
1717
|
enumerable: true,
|
|
1010
1718
|
get: function() {
|
|
@@ -1029,6 +1737,18 @@ Object.defineProperty(exports, "buildNameConstraintSet", {
|
|
|
1029
1737
|
return buildNameConstraintSet;
|
|
1030
1738
|
}
|
|
1031
1739
|
});
|
|
1740
|
+
Object.defineProperty(exports, "caFingerprint", {
|
|
1741
|
+
enumerable: true,
|
|
1742
|
+
get: function() {
|
|
1743
|
+
return caFingerprint;
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
Object.defineProperty(exports, "caFingerprintCompact", {
|
|
1747
|
+
enumerable: true,
|
|
1748
|
+
get: function() {
|
|
1749
|
+
return caFingerprintCompact;
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1032
1752
|
Object.defineProperty(exports, "certificateNames", {
|
|
1033
1753
|
enumerable: true,
|
|
1034
1754
|
get: function() {
|
|
@@ -1053,30 +1773,66 @@ Object.defineProperty(exports, "createAgentHttpsRequest", {
|
|
|
1053
1773
|
return createAgentHttpsRequest;
|
|
1054
1774
|
}
|
|
1055
1775
|
});
|
|
1776
|
+
Object.defineProperty(exports, "createMeshCsrPem", {
|
|
1777
|
+
enumerable: true,
|
|
1778
|
+
get: function() {
|
|
1779
|
+
return createMeshCsrPem;
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1056
1782
|
Object.defineProperty(exports, "describeHttpsLeaf", {
|
|
1057
1783
|
enumerable: true,
|
|
1058
1784
|
get: function() {
|
|
1059
1785
|
return describeHttpsLeaf;
|
|
1060
1786
|
}
|
|
1061
1787
|
});
|
|
1788
|
+
Object.defineProperty(exports, "describeMeshLeaf", {
|
|
1789
|
+
enumerable: true,
|
|
1790
|
+
get: function() {
|
|
1791
|
+
return describeMeshLeaf;
|
|
1792
|
+
}
|
|
1793
|
+
});
|
|
1062
1794
|
Object.defineProperty(exports, "evaluateExistingCert", {
|
|
1063
1795
|
enumerable: true,
|
|
1064
1796
|
get: function() {
|
|
1065
1797
|
return evaluateExistingCert;
|
|
1066
1798
|
}
|
|
1067
1799
|
});
|
|
1800
|
+
Object.defineProperty(exports, "exportMeshPrivateKeyPem", {
|
|
1801
|
+
enumerable: true,
|
|
1802
|
+
get: function() {
|
|
1803
|
+
return exportMeshPrivateKeyPem;
|
|
1804
|
+
}
|
|
1805
|
+
});
|
|
1068
1806
|
Object.defineProperty(exports, "generateCa", {
|
|
1069
1807
|
enumerable: true,
|
|
1070
1808
|
get: function() {
|
|
1071
1809
|
return generateCa;
|
|
1072
1810
|
}
|
|
1073
1811
|
});
|
|
1812
|
+
Object.defineProperty(exports, "generateMeshCa", {
|
|
1813
|
+
enumerable: true,
|
|
1814
|
+
get: function() {
|
|
1815
|
+
return generateMeshCa;
|
|
1816
|
+
}
|
|
1817
|
+
});
|
|
1818
|
+
Object.defineProperty(exports, "generateMeshKeyPair", {
|
|
1819
|
+
enumerable: true,
|
|
1820
|
+
get: function() {
|
|
1821
|
+
return generateMeshKeyPair;
|
|
1822
|
+
}
|
|
1823
|
+
});
|
|
1074
1824
|
Object.defineProperty(exports, "getProcessAgentHttpsNames", {
|
|
1075
1825
|
enumerable: true,
|
|
1076
1826
|
get: function() {
|
|
1077
1827
|
return getProcessAgentHttpsNames;
|
|
1078
1828
|
}
|
|
1079
1829
|
});
|
|
1830
|
+
Object.defineProperty(exports, "getProcessMeshCertificateRegistry", {
|
|
1831
|
+
enumerable: true,
|
|
1832
|
+
get: function() {
|
|
1833
|
+
return getProcessMeshCertificateRegistry;
|
|
1834
|
+
}
|
|
1835
|
+
});
|
|
1080
1836
|
Object.defineProperty(exports, "installAgentHttps", {
|
|
1081
1837
|
enumerable: true,
|
|
1082
1838
|
get: function() {
|
|
@@ -1107,6 +1863,12 @@ Object.defineProperty(exports, "isPrivateAddress", {
|
|
|
1107
1863
|
return isPrivateAddress;
|
|
1108
1864
|
}
|
|
1109
1865
|
});
|
|
1866
|
+
Object.defineProperty(exports, "isValidAgentNodeId", {
|
|
1867
|
+
enumerable: true,
|
|
1868
|
+
get: function() {
|
|
1869
|
+
return isValidAgentNodeId;
|
|
1870
|
+
}
|
|
1871
|
+
});
|
|
1110
1872
|
Object.defineProperty(exports, "issueAgentHttpsLeaf", {
|
|
1111
1873
|
enumerable: true,
|
|
1112
1874
|
get: function() {
|
|
@@ -1119,6 +1881,18 @@ Object.defineProperty(exports, "issueLeaf", {
|
|
|
1119
1881
|
return issueLeaf;
|
|
1120
1882
|
}
|
|
1121
1883
|
});
|
|
1884
|
+
Object.defineProperty(exports, "issueMeshLeafForNode", {
|
|
1885
|
+
enumerable: true,
|
|
1886
|
+
get: function() {
|
|
1887
|
+
return issueMeshLeafForNode;
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1890
|
+
Object.defineProperty(exports, "issueMeshLeafFromCsr", {
|
|
1891
|
+
enumerable: true,
|
|
1892
|
+
get: function() {
|
|
1893
|
+
return issueMeshLeafFromCsr;
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1122
1896
|
Object.defineProperty(exports, "loadAgentHttps", {
|
|
1123
1897
|
enumerable: true,
|
|
1124
1898
|
get: function() {
|
|
@@ -1131,6 +1905,12 @@ Object.defineProperty(exports, "loadCa", {
|
|
|
1131
1905
|
return loadCa;
|
|
1132
1906
|
}
|
|
1133
1907
|
});
|
|
1908
|
+
Object.defineProperty(exports, "loadMeshCa", {
|
|
1909
|
+
enumerable: true,
|
|
1910
|
+
get: function() {
|
|
1911
|
+
return loadMeshCa;
|
|
1912
|
+
}
|
|
1913
|
+
});
|
|
1134
1914
|
Object.defineProperty(exports, "pemDigest", {
|
|
1135
1915
|
enumerable: true,
|
|
1136
1916
|
get: function() {
|
|
@@ -1173,6 +1953,24 @@ Object.defineProperty(exports, "setProcessAgentHttpsNames", {
|
|
|
1173
1953
|
return setProcessAgentHttpsNames;
|
|
1174
1954
|
}
|
|
1175
1955
|
});
|
|
1956
|
+
Object.defineProperty(exports, "setProcessMeshCertificateRegistry", {
|
|
1957
|
+
enumerable: true,
|
|
1958
|
+
get: function() {
|
|
1959
|
+
return setProcessMeshCertificateRegistry;
|
|
1960
|
+
}
|
|
1961
|
+
});
|
|
1962
|
+
Object.defineProperty(exports, "subjectCn", {
|
|
1963
|
+
enumerable: true,
|
|
1964
|
+
get: function() {
|
|
1965
|
+
return subjectCn;
|
|
1966
|
+
}
|
|
1967
|
+
});
|
|
1968
|
+
Object.defineProperty(exports, "systemConfigRecordKinds", {
|
|
1969
|
+
enumerable: true,
|
|
1970
|
+
get: function() {
|
|
1971
|
+
return systemConfigRecordKinds;
|
|
1972
|
+
}
|
|
1973
|
+
});
|
|
1176
1974
|
Object.defineProperty(exports, "systemHostFacts", {
|
|
1177
1975
|
enumerable: true,
|
|
1178
1976
|
get: function() {
|