@sksoftofficial/ocduet 0.2.2 → 0.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sksoftofficial/ocduet",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Duet for opencode: pair your phone with a local opencode TUI. Type on mobile, see it live on both screens. LAN-first, QR pairing, zero config.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -40,7 +40,9 @@ function registerOnce(url, desktopId, pubB64, sign, secret, certPem) {
40
40
  return new Promise((resolve, reject) => {
41
41
  const wsUrl = new URL("/desktop", url);
42
42
  wsUrl.protocol = wsUrl.protocol === "http:" ? "ws:" : "wss:";
43
- const tlsOpts = certPem ? { ca: certPem } : { rejectUnauthorized: false };
43
+ // pinned cert = the identity itself; hostname/SAN matching would always
44
+ // fail for a bare-IP relayer with a self-signed cert
45
+ const tlsOpts = certPem ? { ca: certPem, checkServerIdentity: () => undefined } : { rejectUnauthorized: false };
44
46
  const ws = new WebSocket(wsUrl, { ...tlsOpts, handshakeTimeout: 10_000 });
45
47
  const fail = (why) => { try { ws.close(); } catch {} reject(new Error(why)); };
46
48
  const timer = setTimeout(() => fail("register timeout"), 12_000);
@@ -33,7 +33,9 @@ export function readRelayConfig() {
33
33
  // auth still comes from the ed25519 register signature — the pin kills
34
34
  // silent MITM even without a domain.
35
35
  export function relayTlsOpts(cfg) {
36
- return cfg?.certPem ? { ca: cfg.certPem } : { rejectUnauthorized: false };
36
+ // when pinned, the exact cert IS the identity skip hostname/SAN matching,
37
+ // which can never pass for a bare-IP host anyway
38
+ return cfg?.certPem ? { ca: cfg.certPem, checkServerIdentity: () => undefined } : { rejectUnauthorized: false };
37
39
  }
38
40
 
39
41
  const state = { linked: false, connected: false, url: null, desktopId: null, since: null, lastError: null, reconnects: 0 };
package/src/relayer.js CHANGED
@@ -75,11 +75,17 @@ function ensureCert() {
75
75
  return { cert: fs.readFileSync(certP), key: fs.readFileSync(keyP) };
76
76
  }
77
77
  fs.mkdirSync(relayerDir(), { recursive: true });
78
+ // cover every address the box might be reached on — desktops link by bare IP
79
+ const ips = [];
80
+ for (const addrs of Object.values(os.networkInterfaces())) {
81
+ for (const a of addrs || []) if (a.family === "IPv4" && !a.internal) ips.push(a.address);
82
+ }
83
+ const san = ["DNS:localhost", "IP:127.0.0.1", ...ips.map((ip) => `IP:${ip}`)];
78
84
  const out = spawnSync("openssl", [
79
85
  "req", "-x509", "-newkey", "ec", "-pkeyopt", "ec_paramgen_curve:prime256v1",
80
86
  "-keyout", keyP, "-out", certP, "-days", "3650", "-nodes",
81
87
  "-subj", "/CN=ocduet-relayer",
82
- "-addext", "subjectAltName=DNS:localhost,IP:127.0.0.1",
88
+ "-addext", `subjectAltName=${san.join(",")}`,
83
89
  ], { stdio: "ignore" });
84
90
  if (out.status !== 0) throw new Error("openssl cert generation failed — is openssl installed?");
85
91
  try { fs.chmodSync(keyP, 0o600); } catch {}