@cryptiklemur/lattice 5.1.1 → 5.2.0

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.
@@ -404,12 +404,22 @@ export async function startDaemon(portOverride, tlsOverride) {
404
404
  cert: readFileSync(certs.cert),
405
405
  key: readFileSync(certs.key),
406
406
  };
407
- log.server("TLS enabled");
407
+ log.server("TLS enabled (cert: %s)", certs.cert);
408
408
  }
409
409
  catch (err) {
410
410
  console.error("[lattice] Failed to load TLS certs, falling back to HTTP:", err);
411
411
  }
412
412
  }
413
+ if (tlsOptions) {
414
+ var certsDir = join(getLatticeHome(), "certs");
415
+ log.server("TLS cert: %s", join(certsDir, "cert.pem"));
416
+ log.server("To trust the self-signed cert:");
417
+ log.server(" Linux: sudo cp %s /usr/local/share/ca-certificates/lattice.crt && sudo update-ca-certificates", join(certsDir, "cert.pem"));
418
+ log.server(" macOS: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain %s", join(certsDir, "cert.pem"));
419
+ log.server(" Or use Tailscale HTTPS (no trust needed):");
420
+ log.server(" tailscale cert $(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\\.$//')");
421
+ log.server(" Then copy: cp <hostname>.crt %s && cp <hostname>.key %s", join(certsDir, "cert.pem"), join(certsDir, "key.pem"));
422
+ }
413
423
  var protocol = tlsOptions ? "https" : "http";
414
424
  var httpServer = tlsOptions
415
425
  ? createHttpsServer(tlsOptions, app)
@@ -141,6 +141,9 @@ switch (command) {
141
141
  case "config":
142
142
  runConfigInfo();
143
143
  break;
144
+ case "setup-tls":
145
+ await runSetupTls();
146
+ break;
144
147
  case "help":
145
148
  case "--help":
146
149
  case "-h":
@@ -269,6 +272,7 @@ function runHelp() {
269
272
  console.log(" logs Tail the daemon log");
270
273
  console.log(" open Open the UI in the browser");
271
274
  console.log(" config Show configuration paths and settings");
275
+ console.log(" setup-tls Set up HTTPS (Tailscale or self-signed)");
272
276
  console.log(" help Show this help message");
273
277
  console.log("");
274
278
  console.log(" Options:");
@@ -281,6 +285,68 @@ function runHelp() {
281
285
  console.log(" DEBUG Enable debug logging (e.g. DEBUG=lattice:*)");
282
286
  console.log("");
283
287
  }
288
+ async function runSetupTls() {
289
+ var { execSync: exec } = await import("node:child_process");
290
+ var certsDir = join(getLatticeHome(), "certs");
291
+ var certPath = join(certsDir, "cert.pem");
292
+ var keyPath = join(certsDir, "key.pem");
293
+ console.log("[lattice] TLS Setup");
294
+ console.log("");
295
+ var hasTailscale = false;
296
+ try {
297
+ exec("tailscale version", { stdio: "ignore" });
298
+ hasTailscale = true;
299
+ }
300
+ catch { }
301
+ if (hasTailscale) {
302
+ console.log(" Tailscale detected. To use trusted Tailscale HTTPS certs:");
303
+ console.log("");
304
+ try {
305
+ var dnsName = exec("tailscale status --json", { encoding: "utf-8" });
306
+ var hostname = JSON.parse(dnsName).Self.DNSName.replace(/\.$/, "");
307
+ console.log(" 1. Generate cert:");
308
+ console.log(" sudo tailscale cert --cert-file " + certPath + " --key-file " + keyPath + " " + hostname);
309
+ console.log("");
310
+ console.log(" 2. Enable TLS and restart:");
311
+ console.log(" lattice restart --tls");
312
+ console.log("");
313
+ console.log(" 3. Access at:");
314
+ console.log(" https://" + hostname + ":" + loadConfig().port);
315
+ }
316
+ catch {
317
+ console.log(" 1. Run: sudo tailscale cert --cert-file " + certPath + " --key-file " + keyPath + " <your-hostname>.ts.net");
318
+ console.log(" 2. Run: lattice restart --tls");
319
+ }
320
+ }
321
+ else {
322
+ console.log(" Tailscale not found. Using self-signed certificate.");
323
+ console.log("");
324
+ }
325
+ console.log("");
326
+ console.log(" ── Self-signed certificate ──");
327
+ console.log("");
328
+ if (existsSync(certPath)) {
329
+ console.log(" Cert exists: " + certPath);
330
+ console.log(" To regenerate: rm -rf " + certsDir + " && lattice restart --tls");
331
+ }
332
+ else {
333
+ console.log(" No cert yet. Run 'lattice restart --tls' to auto-generate one.");
334
+ }
335
+ console.log("");
336
+ console.log(" To trust the self-signed cert (removes browser warnings):");
337
+ console.log("");
338
+ console.log(" Linux:");
339
+ console.log(" sudo cp " + certPath + " /usr/local/share/ca-certificates/lattice.crt");
340
+ console.log(" sudo update-ca-certificates");
341
+ console.log("");
342
+ console.log(" macOS:");
343
+ console.log(" sudo security add-trusted-cert -d -r trustRoot \\");
344
+ console.log(" -k /Library/Keychains/System.keychain " + certPath);
345
+ console.log("");
346
+ console.log(" Windows (PowerShell as admin):");
347
+ console.log(" Import-Certificate -FilePath " + certPath + " -CertStoreLocation Cert:\\LocalMachine\\Root");
348
+ console.log("");
349
+ }
284
350
  async function runRestart() {
285
351
  var pid = readPid();
286
352
  if (pid !== null && isDaemonRunning(pid)) {
@@ -1,6 +1,7 @@
1
- import { existsSync, mkdirSync } from "node:fs";
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
+ import { networkInterfaces } from "node:os";
4
5
  import { getLatticeHome } from "./config.js";
5
6
  export function getCertsDir() {
6
7
  var certsDir = join(getLatticeHome(), "certs");
@@ -33,6 +34,20 @@ export function ensureCerts() {
33
34
  return { cert: certPath, key: keyPath };
34
35
  }
35
36
  console.log("[lattice] Generating self-signed TLS certificate...");
37
+ var sans = ["DNS:lattice", "DNS:localhost", "IP:127.0.0.1", "IP:::1"];
38
+ var ifaces = networkInterfaces();
39
+ for (var name in ifaces) {
40
+ var addrs = ifaces[name];
41
+ if (!addrs)
42
+ continue;
43
+ for (var i = 0; i < addrs.length; i++) {
44
+ if (!addrs[i].internal) {
45
+ sans.push("IP:" + addrs[i].address);
46
+ }
47
+ }
48
+ }
49
+ var extFile = join(certsDir, "openssl-san.cnf");
50
+ writeFileSync(extFile, "[req]\ndistinguished_name=dn\nx509_extensions=v3\nprompt=no\n[dn]\nCN=lattice\n[v3]\nsubjectAltName=" + sans.join(",") + "\n");
36
51
  var result = spawnSync("openssl", [
37
52
  "req", "-x509",
38
53
  "-newkey", "rsa:2048",
@@ -40,7 +55,7 @@ export function ensureCerts() {
40
55
  "-out", certPath,
41
56
  "-days", "365",
42
57
  "-nodes",
43
- "-subj", "/CN=lattice",
58
+ "-config", extFile,
44
59
  ], { encoding: "utf-8" });
45
60
  if (result.status !== 0) {
46
61
  throw new Error("[lattice] Failed to generate TLS certificates: " + (result.stderr || result.error?.message || "unknown error"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "5.1.1",
3
+ "version": "5.2.0",
4
4
  "description": "Multi-machine agentic dashboard for Claude Code. Monitor sessions, manage MCP servers and skills, orchestrate across mesh-networked nodes.",
5
5
  "license": "MIT",
6
6
  "author": "Aaron Scherer <me@aaronscherer.me>",