@lensmcp/cluster 1.16.14 → 1.16.16

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/server.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAkB,qBAAqB,EAAsB,MAAM,SAAS,CAAC;AAkDxH,wBAAsB,YAAY,CAChC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAqNxB"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/server.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAkB,qBAAqB,EAAsB,MAAM,SAAS,CAAC;AAkDxH,wBAAsB,YAAY,CAChC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAgPxB"}
@@ -110,7 +110,18 @@ async function startGateway(options, context) {
110
110
  const registry = new route_registry_1.RouteRegistry();
111
111
  registry.register({ wsKey, root: context.root, routes: discovered, services });
112
112
  const routes = []; // the LIVE table the request pipeline reads (a stable ref, mutated in place)
113
- const rebuildRoutes = () => (0, route_registry_1.rebuildRoutesInPlace)(routes, registry);
113
+ // Assigned after the TLS servers are built (below) re-mints the gateway cert with the CURRENT route
114
+ // hosts and hot-swaps it. Declared HERE (above rebuildRoutes) so rebuildRoutes can trigger it AFTER every
115
+ // live route change; a no-op until the TLS servers exist (the boot mint happens separately, below).
116
+ let refreshCert = () => { };
117
+ const rebuildRoutes = () => {
118
+ (0, route_registry_1.rebuildRoutesInPlace)(routes, registry);
119
+ // The cert tracks the live route hosts BY CONSTRUCTION: any register/unregister rebuilds the table then
120
+ // re-mints, so a dynamically-registered workspace's hostnames always join the SANs (no ERR_CERT_COMMON_
121
+ // NAME_INVALID). Wiring it here (not in the control onRegister) avoids the ordering trap where the cert
122
+ // was minted from the STALE table because onRegister ran before the control handler's rebuild.
123
+ refreshCert();
124
+ };
114
125
  rebuildRoutes();
115
126
  // --- per-service api keys (the gateway is the trust boundary) ---------------
116
127
  const { serviceKeys, keyToProject } = (0, service_keys_1.loadServiceKeys)(context.root, services);
@@ -147,7 +158,8 @@ async function startGateway(options, context) {
147
158
  killService: (svc, reason) => svcLayer.killSpawned(svc, reason),
148
159
  onRegister: (fragment) => {
149
160
  // Host the registered workspace's dashboard (appends its lensmcp.local/<key> route to the fragment
150
- // BEFORE the control handler's rebuild) and remember its reaper.
161
+ // BEFORE the control handler's rebuild) and remember its reaper. The cert re-mint happens in
162
+ // rebuildRoutes (called by the control handler right after this), once the full table is live.
151
163
  const reap = lens.hostWorkspaceDashboard(fragment);
152
164
  if (reap)
153
165
  dashboardReapers.set(fragment.wsKey, reap);
@@ -214,12 +226,15 @@ async function startGateway(options, context) {
214
226
  // --- TLS + listeners --------------------------------------------------------
215
227
  let pem;
216
228
  let caPath;
229
+ let mintCert;
217
230
  if (https) {
218
231
  // eslint-disable-next-line @typescript-eslint/no-require-imports
219
232
  const basicSsl = require('../../../basic-ssl');
220
233
  const cacheDir = (0, service_keys_1.gatewayCacheDir)(context.root);
221
- const hosts = routes.map((r) => r.host).filter((h) => !!h);
222
- pem = basicSsl.getCertificateSync(cacheDir, 'gateway', hosts);
234
+ // SANs = the CURRENT route hosts. `rt.routes` is rebuilt live on register, so a re-mint (refreshCert
235
+ // below) automatically includes a newly-registered workspace's hostnames.
236
+ mintCert = () => basicSsl.getCertificateSync(cacheDir, 'gateway', rt.routes.map((r) => r.host).filter((h) => !!h));
237
+ pem = mintCert();
223
238
  caPath = basicSsl.caCertPath(cacheDir);
224
239
  }
225
240
  const servers = [];
@@ -253,6 +268,18 @@ async function startGateway(options, context) {
253
268
  });
254
269
  servers.push(server);
255
270
  }
271
+ // Now that the TLS servers exist, wire the live cert refresh: on a workspace register/unregister the
272
+ // control endpoint calls this to re-mint the gateway cert with the CURRENT route hosts and hot-swap it
273
+ // into every HTTP/2 secure server (setSecureContext), so a dynamically-registered workspace's hostnames
274
+ // are covered (no ERR_CERT_COMMON_NAME_INVALID).
275
+ if (mintCert) {
276
+ refreshCert = () => {
277
+ const fresh = mintCert();
278
+ for (const s of servers) {
279
+ s.setSecureContext?.({ key: fresh, cert: fresh });
280
+ }
281
+ };
282
+ }
256
283
  console.log('[gateway] Routes:');
257
284
  for (const r of routes) {
258
285
  const dest = r.pool ? `pods@${r.pool.dir}` : r.target;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/cluster",
3
- "version": "1.16.14",
3
+ "version": "1.16.16",
4
4
  "description": "Run your Nx workspace as a local production cluster: pods on unix sockets with true HMR, one https gateway with per-project domains, scale-from-zero, autoscale, idle-kill, local-CA TLS — observed by the LensMCP lens.",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -65,8 +65,8 @@
65
65
  }
66
66
  },
67
67
  "dependencies": {
68
- "@lensmcp/node-instrumentation": "1.16.14",
69
- "@lensmcp/nx-plugin": "1.16.14",
68
+ "@lensmcp/node-instrumentation": "1.16.16",
69
+ "@lensmcp/nx-plugin": "1.16.16",
70
70
  "fork-ts-checker-webpack-plugin": "^9.0.0",
71
71
  "glob": "^11.0.0",
72
72
  "http-proxy": "^1.18.0",