@log0u7/opencode-mesh 0.2.0 → 0.4.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/README.md +46 -13
  3. package/dist/lib/client.d.ts +2 -0
  4. package/dist/lib/client.d.ts.map +1 -1
  5. package/dist/lib/client.js +6 -1
  6. package/dist/lib/client.js.map +1 -1
  7. package/dist/lib/db.d.ts.map +1 -1
  8. package/dist/lib/db.js +26 -1
  9. package/dist/lib/db.js.map +1 -1
  10. package/dist/lib/discovery.d.ts +3 -0
  11. package/dist/lib/discovery.d.ts.map +1 -1
  12. package/dist/lib/discovery.js +33 -7
  13. package/dist/lib/discovery.js.map +1 -1
  14. package/dist/lib/identity.d.ts +10 -8
  15. package/dist/lib/identity.d.ts.map +1 -1
  16. package/dist/lib/identity.js +132 -32
  17. package/dist/lib/identity.js.map +1 -1
  18. package/dist/lib/locks.d.ts.map +1 -1
  19. package/dist/lib/locks.js +20 -12
  20. package/dist/lib/locks.js.map +1 -1
  21. package/dist/lib/peers.d.ts +13 -1
  22. package/dist/lib/peers.d.ts.map +1 -1
  23. package/dist/lib/peers.js +114 -18
  24. package/dist/lib/peers.js.map +1 -1
  25. package/dist/lib/serve.d.ts +2 -0
  26. package/dist/lib/serve.d.ts.map +1 -1
  27. package/dist/lib/serve.js +62 -71
  28. package/dist/lib/serve.js.map +1 -1
  29. package/dist/lib/spawn.d.ts +14 -4
  30. package/dist/lib/spawn.d.ts.map +1 -1
  31. package/dist/lib/spawn.js +210 -21
  32. package/dist/lib/spawn.js.map +1 -1
  33. package/dist/lib/sqlite.d.ts.map +1 -1
  34. package/dist/lib/sqlite.js +15 -4
  35. package/dist/lib/sqlite.js.map +1 -1
  36. package/dist/lib/workers.d.ts +2 -0
  37. package/dist/lib/workers.d.ts.map +1 -1
  38. package/dist/lib/workers.js +45 -12
  39. package/dist/lib/workers.js.map +1 -1
  40. package/dist/lib/worktree.d.ts +22 -4
  41. package/dist/lib/worktree.d.ts.map +1 -1
  42. package/dist/lib/worktree.js +54 -6
  43. package/dist/lib/worktree.js.map +1 -1
  44. package/dist/plugin-v2.d.ts.map +1 -1
  45. package/dist/plugin-v2.js +65 -337
  46. package/dist/plugin-v2.js.map +1 -1
  47. package/dist/plugin.d.ts +2 -6
  48. package/dist/plugin.d.ts.map +1 -1
  49. package/dist/plugin.js +119 -265
  50. package/dist/plugin.js.map +1 -1
  51. package/dist/tools.d.ts +30 -0
  52. package/dist/tools.d.ts.map +1 -0
  53. package/dist/tools.js +315 -0
  54. package/dist/tools.js.map +1 -0
  55. package/dist/types/peer.d.ts +2 -0
  56. package/dist/types/peer.d.ts.map +1 -1
  57. package/package.json +10 -8
@@ -1,58 +1,158 @@
1
- import { randomUUID } from "node:crypto";
1
+ import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, randomUUID, sign, verify, } from "node:crypto";
2
2
  import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
3
  import { hostname as osHostname } from "node:os";
4
- import { join } from "node:path";
4
+ import { dirname, join } from "node:path";
5
5
  export function loadOrCreateIdentity(dataDir) {
6
6
  const file = join(dataDir, "node.json");
7
- const existing = tryRead(file);
7
+ const existing = readIdentity(file);
8
8
  if (existing) {
9
- return withFingerprint(existing);
9
+ // A file written by <= 0.2.0 carries either no keys or a dead Ed25519
10
+ // JWK keypair nothing ever used. The node keeps its node_id (peers stay
11
+ // recognised), and the file is rewritten to the canonical shape with a
12
+ // fresh signing keypair so pairing proofs can be produced.
13
+ if (existing.migrated)
14
+ writeIdentity(file, existing.identity);
15
+ return withFingerprint(existing.identity);
10
16
  }
11
- return withFingerprint(generate(dataDir, file));
17
+ const identity = {
18
+ ...freshKeys(),
19
+ node_id: randomUUID(),
20
+ hostname: hostName(),
21
+ };
22
+ writeIdentity(file, identity);
23
+ return withFingerprint(identity);
12
24
  }
13
- function tryRead(file) {
25
+ function readIdentity(file) {
26
+ let raw;
14
27
  try {
15
- const raw = JSON.parse(readFileSync(file, "utf8"));
16
- if (typeof raw.node_id === "string" && raw.public_key && raw.private_key) {
17
- return raw;
18
- }
19
- return null;
28
+ raw = readFileSync(file, "utf8");
29
+ }
30
+ catch (error) {
31
+ if (error.code === "ENOENT")
32
+ return null;
33
+ throw new Error(`cannot read identity file ${file}: ${errorMessage(error)}`);
34
+ }
35
+ let parsed;
36
+ try {
37
+ parsed = JSON.parse(raw);
20
38
  }
21
39
  catch {
22
- return null;
40
+ throw corrupt(file, "is not valid JSON");
41
+ }
42
+ const record = (parsed ?? {});
43
+ if (typeof record.node_id !== "string" || record.node_id.length === 0) {
44
+ throw corrupt(file, "has no node_id");
45
+ }
46
+ const identity = {
47
+ node_id: record.node_id,
48
+ hostname: hostName(record.hostname),
49
+ public_key: typeof record.public_key === "string" ? record.public_key : "",
50
+ private_key: typeof record.private_key === "string" ? record.private_key : "",
51
+ };
52
+ const keysUsable = isSpki(identity.public_key) && isPkcs8(identity.private_key);
53
+ const migrated = !keysUsable || Object.keys(record).some((key) => !KNOWN_KEYS.has(key));
54
+ if (!keysUsable) {
55
+ // Replace dead or missing keys; the node_id above is never regenerated, so
56
+ // peers keep recognising this node through the migration.
57
+ Object.assign(identity, freshKeys());
23
58
  }
59
+ return { identity, migrated };
60
+ }
61
+ const KNOWN_KEYS = new Set(["node_id", "hostname", "public_key", "private_key"]);
62
+ // A corrupt identity file would make the node silently mint a new id, breaking
63
+ // every peer that recognises it and orphaning queued mail. Refuse to start and
64
+ // hand the operator a recovery path instead of reminting behind their back.
65
+ function corrupt(file, why) {
66
+ return new Error(`identity file ${file} ${why}; refusing to mint a new node id. ` +
67
+ `Move it aside to start with a fresh identity: mv '${file}' '${file}.bak'`);
24
68
  }
25
- function generate(dataDir, file) {
26
- // Bun implements node:crypto, so this works under OpenCode's runtime and Node.
27
- const { generateKeyPairSync } = require("node:crypto");
69
+ function freshKeys() {
28
70
  const { publicKey, privateKey } = generateKeyPairSync("ed25519");
29
- const publicJwk = publicKey.export({ format: "jwk" });
30
- const privateJwk = privateKey.export({ format: "jwk" });
31
- const identity = {
32
- node_id: randomUUID(),
33
- hostname: hostName(),
34
- public_key: publicJwk,
35
- private_key: privateJwk,
71
+ return {
72
+ public_key: Buffer.from(publicKey.export({ type: "spki", format: "der" })).toString("base64"),
73
+ private_key: Buffer.from(privateKey.export({ type: "pkcs8", format: "der" })).toString("base64"),
36
74
  };
37
- mkdirSync(dataDir, { recursive: true, mode: 0o700 });
38
- // Atomic write: temp file + rename, so a crash never leaves a truncated keypair.
75
+ }
76
+ function isSpki(value) {
77
+ try {
78
+ const key = createPublicKey({
79
+ key: Buffer.from(value, "base64"),
80
+ format: "der",
81
+ type: "spki",
82
+ });
83
+ // Only the algorithm we sign with: a hand-edited RSA key must not pass
84
+ // validation and then break `sign(null, ...)` at send time.
85
+ return key.asymmetricKeyType === "ed25519";
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ }
91
+ function isPkcs8(value) {
92
+ try {
93
+ const key = createPrivateKey({
94
+ key: Buffer.from(value, "base64"),
95
+ format: "der",
96
+ type: "pkcs8",
97
+ });
98
+ return key.asymmetricKeyType === "ed25519";
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ // Pairing proofs: an Ed25519 signature binding the sender's node id to the
105
+ // token it presents. The canonical payload lives next to the signers so the
106
+ // sender and the verifier cannot drift.
107
+ export function pairingProofPayload(nodeId, token) {
108
+ return `opencode-mesh-pair:${nodeId}:${token}`;
109
+ }
110
+ export function signPairingProof(privateKey, payload) {
111
+ const key = createPrivateKey({
112
+ key: Buffer.from(privateKey, "base64"),
113
+ format: "der",
114
+ type: "pkcs8",
115
+ });
116
+ return Buffer.from(sign(null, Buffer.from(payload, "utf8"), key)).toString("base64");
117
+ }
118
+ // The wire form of a pairing proof: `<base64 spki>.<base64 signature>`.
119
+ export function pairingProof(identity, token) {
120
+ return `${identity.public_key}.${signPairingProof(identity.private_key, pairingProofPayload(identity.node_id, token))}`;
121
+ }
122
+ export function verifyPairingProof(publicKey, payload, proof) {
123
+ try {
124
+ const key = createPublicKey({
125
+ key: Buffer.from(publicKey, "base64"),
126
+ format: "der",
127
+ type: "spki",
128
+ });
129
+ return verify(null, Buffer.from(payload, "utf8"), key, Buffer.from(proof, "base64"));
130
+ }
131
+ catch {
132
+ return false;
133
+ }
134
+ }
135
+ function writeIdentity(file, identity) {
136
+ mkdirSync(dirname(file), { recursive: true, mode: 0o700 });
137
+ // Atomic write: temp file + rename, so a crash never leaves a truncated record.
39
138
  const tmp = `${file}.tmp-${process.pid}`;
40
139
  writeFileSync(tmp, JSON.stringify(identity, null, 2), { mode: 0o600 });
41
140
  renameSync(tmp, file);
42
141
  chmodSync(file, 0o600);
43
- return identity;
44
142
  }
143
+ // The fingerprint stays derived from the node_id (not from the signing key):
144
+ // keys can be replaced on migration without shuffling the operator-facing
145
+ // label for `mesh_status`.
45
146
  function withFingerprint(identity) {
46
- const { createHash } = require("node:crypto");
47
- // x is the raw public key coordinate in an Ed25519 JWK; hash it for a short
48
- // operator-verifiable fingerprint.
49
- const x = identity.public_key.x ?? "";
50
147
  return {
51
148
  ...identity,
52
- fingerprint: createHash("sha256").update(x).digest("hex").slice(0, 16),
149
+ fingerprint: createHash("sha256").update(identity.node_id).digest("hex").slice(0, 16),
53
150
  };
54
151
  }
55
- function hostName() {
56
- return osHostname();
152
+ function hostName(recorded) {
153
+ return typeof recorded === "string" && recorded.length > 0 ? recorded : osHostname();
154
+ }
155
+ function errorMessage(error) {
156
+ return error instanceof Error ? error.message : String(error);
57
157
  }
58
158
  //# sourceMappingURL=identity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/lib/identity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/lib/identity.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/lib/locks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,wBAAgB,OAAO,CACrB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9F,OAAO,CAwBT;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAM5F;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAAE,CAGpE"}
1
+ {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/lib/locks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,wBAAgB,OAAO,CACrB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9F,OAAO,CAiCT;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAM5F;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAAE,CAGpE"}
package/dist/lib/locks.js CHANGED
@@ -1,21 +1,29 @@
1
1
  export function tryLock(db, input) {
2
2
  const expiresAt = input.now + input.ttl_ms;
3
3
  db.conn.run("DELETE FROM locks WHERE expires_at <= ?", [input.now]);
4
+ // Claim atomically: a second opencode process sharing this dataDir that races
5
+ // for the same path loses the insert (ON CONFLICT DO NOTHING) instead of
6
+ // throwing a UNIQUE constraint error out of mesh_lock.
7
+ db.conn.run("INSERT INTO locks (path, owner_node, owner_session, expires_at) VALUES (?, ?, ?, ?) ON CONFLICT(path) DO NOTHING", [input.path, input.owner_node, input.owner_session, expiresAt]);
8
+ const claimed = (db.conn.get("SELECT changes() AS changes")?.changes ?? 0) > 0;
9
+ if (claimed) {
10
+ return true;
11
+ }
4
12
  const existing = db.conn.get("SELECT * FROM locks WHERE path = ?", [input.path]);
5
- if (existing) {
6
- if (existing.owner_node === input.owner_node) {
7
- // Same owner refreshes its claim.
8
- db.conn.run("UPDATE locks SET expires_at = ?, owner_session = ? WHERE path = ?", [
9
- expiresAt,
10
- input.owner_session,
11
- input.path,
12
- ]);
13
- return true;
14
- }
13
+ if (!existing) {
14
+ // Raced with a release between the insert and this read; not granted.
15
15
  return false;
16
16
  }
17
- db.conn.run("INSERT INTO locks (path, owner_node, owner_session, expires_at) VALUES (?, ?, ?, ?)", [input.path, input.owner_node, input.owner_session, expiresAt]);
18
- return true;
17
+ if (existing.owner_node === input.owner_node) {
18
+ // Same owner refreshes its claim.
19
+ db.conn.run("UPDATE locks SET expires_at = ?, owner_session = ? WHERE path = ?", [
20
+ expiresAt,
21
+ input.owner_session,
22
+ input.path,
23
+ ]);
24
+ return true;
25
+ }
26
+ return false;
19
27
  }
20
28
  export function releaseLock(db, input) {
21
29
  db.conn.run("DELETE FROM locks WHERE path = ? AND owner_node = ?", [
@@ -1 +1 @@
1
- {"version":3,"file":"locks.js","sourceRoot":"","sources":["../../src/lib/locks.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,OAAO,CACrB,EAAU,EACV,KAA+F;IAE/F,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IAE3C,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,yCAAyC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAEpE,MAAM,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAU,oCAAoC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1F,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;YAC7C,kCAAkC;YAClC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,mEAAmE,EAAE;gBAC/E,SAAS;gBACT,KAAK,CAAC,aAAa;gBACnB,KAAK,CAAC,IAAI;aACX,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,EAAE,CAAC,IAAI,CAAC,GAAG,CACT,qFAAqF,EACrF,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,aAAa,EAAE,SAAS,CAAC,CAC/D,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,KAA2C;IACjF,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,qDAAqD,EAAE;QACjE,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,UAAU;KACjB,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAsB,6BAA6B,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAU,EAAE,KAAsB;IAC1D,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,yCAAyC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAU,mCAAmC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAClF,CAAC;AASD,SAAS,SAAS,CAAC,GAAY;IAC7B,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,aAAa;QACxB,OAAO,EAAE,GAAG,CAAC,UAAU;QACvB,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"locks.js","sourceRoot":"","sources":["../../src/lib/locks.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,OAAO,CACrB,EAAU,EACV,KAA+F;IAE/F,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IAE3C,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,yCAAyC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAEpE,8EAA8E;IAC9E,yEAAyE;IACzE,uDAAuD;IACvD,EAAE,CAAC,IAAI,CAAC,GAAG,CACT,kHAAkH,EAClH,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,aAAa,EAAE,SAAS,CAAC,CAC/D,CAAC;IACF,MAAM,OAAO,GACX,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAsB,6BAA6B,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACtF,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAU,oCAAoC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1F,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,sEAAsE;QACtE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;QAC7C,kCAAkC;QAClC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,mEAAmE,EAAE;YAC/E,SAAS;YACT,KAAK,CAAC,aAAa;YACnB,KAAK,CAAC,IAAI;SACX,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,KAA2C;IACjF,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,qDAAqD,EAAE;QACjE,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,UAAU;KACjB,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAsB,6BAA6B,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAU,EAAE,KAAsB;IAC1D,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,yCAAyC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAU,mCAAmC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAClF,CAAC;AASD,SAAS,SAAS,CAAC,GAAY;IAC7B,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,aAAa;QACxB,OAAO,EAAE,GAAG,CAAC,UAAU;QACvB,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;AACJ,CAAC"}
@@ -1,10 +1,12 @@
1
1
  import type { MeshDb } from "./db.js";
2
2
  import type { Peer } from "../types/peer.js";
3
+ export declare const PAIRING_TOKEN_TTL_MS: number;
3
4
  export type PeerInput = {
4
5
  node_id: string;
5
6
  hostname: string;
6
7
  fingerprint: string;
7
8
  addresses?: string[];
9
+ public_key?: string;
8
10
  };
9
11
  export declare function pairWithToken(db: MeshDb, peer: {
10
12
  node_id: string;
@@ -12,15 +14,25 @@ export declare function pairWithToken(db: MeshDb, peer: {
12
14
  fingerprint: string;
13
15
  token: string;
14
16
  addresses?: string[];
17
+ public_key?: string;
15
18
  }): void;
16
19
  export declare function createPairingToken(db: MeshDb): string;
17
- export declare function authenticateToken(db: MeshDb, token: string, from: string | null): Peer | null;
20
+ export declare function authenticateToken(db: MeshDb, token: string, from: string | null, proof?: string): Peer | null;
21
+ export declare function prunePendingTokens(db: MeshDb): void;
18
22
  export declare function listPeers(db: MeshDb): Peer[];
23
+ export declare function findPeerById(db: MeshDb, nodeId: string): Peer | null;
19
24
  export declare function findPeerByToken(db: MeshDb, token: string): Peer | null;
25
+ export declare function findSendablePeer(db: MeshDb, query: string): {
26
+ node_id: string;
27
+ addresses: string[];
28
+ token: string | null;
29
+ } | null;
30
+ export declare function resolvePeerUrl(db: MeshDb, query: string): string | null;
20
31
  export declare function upsertDiscoveredPeer(db: MeshDb, peer: {
21
32
  node_id: string;
22
33
  hostname: string;
23
34
  fingerprint: string;
24
35
  addresses: string[];
36
+ public_key?: string;
25
37
  }): void;
26
38
  //# sourceMappingURL=peers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../src/lib/peers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAIF,wBAAgB,aAAa,CAC3B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GACA,IAAI,CAkBN;AAID,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAOrD;AAID,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CA4B7F;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,CAE5C;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGtE;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GACpF,IAAI,CASN"}
1
+ {"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../src/lib/peers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI7C,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAE/C,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAMF,wBAAgB,aAAa,CAC3B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,IAAI,CAoBN;AAID,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAOrD;AAMD,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,GAAG,IAAI,CA0Cb;AAyCD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAInD;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,CAE5C;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGpE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGtE;AAGD,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAGvE;AAKD,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQvE;AAQD,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,IAAI,CAkBN"}
package/dist/lib/peers.js CHANGED
@@ -1,17 +1,25 @@
1
1
  import { randomBytes } from "node:crypto";
2
+ import { pairingProofPayload, verifyPairingProof } from "./identity.js";
3
+ // A pairing offer is only useful while the operator can relay it out of band.
4
+ // Past this age the token is dropped instead of becoming a permanent credential.
5
+ export const PAIRING_TOKEN_TTL_MS = 5 * 60_000;
2
6
  // Complete pairing with a token the peer generated (shared out of band).
3
7
  // Only the pairing call and the peer know it; the server compares on every request.
8
+ // The pinned public_key is preserved when the call provides none (manual pairing):
9
+ // pairing must never be able to erase a proof key it did not attest.
4
10
  export function pairWithToken(db, peer) {
5
11
  const now = Date.now();
6
- db.conn.run(`INSERT INTO peers (node_id, hostname, fingerprint, addresses, paired, token, last_seen)
7
- VALUES (?, ?, ?, ?, 1, ?, ?)
12
+ db.conn.run(`INSERT INTO peers (node_id, hostname, fingerprint, addresses, paired, token, public_key, last_seen)
13
+ VALUES (?, ?, ?, ?, 1, ?, ?, ?)
8
14
  ON CONFLICT(node_id) DO UPDATE SET hostname = excluded.hostname,
9
- fingerprint = excluded.fingerprint, paired = 1, token = excluded.token, last_seen = excluded.last_seen`, [
15
+ fingerprint = excluded.fingerprint, paired = 1, token = excluded.token,
16
+ public_key = COALESCE(excluded.public_key, peers.public_key), last_seen = excluded.last_seen`, [
10
17
  peer.node_id,
11
18
  peer.hostname,
12
19
  peer.fingerprint,
13
20
  JSON.stringify(peer.addresses ?? []),
14
21
  peer.token,
22
+ peer.public_key ?? null,
15
23
  now,
16
24
  ]);
17
25
  db.conn.run("DELETE FROM pending_tokens WHERE token = ?", [peer.token]);
@@ -26,13 +34,16 @@ export function createPairingToken(db) {
26
34
  ]);
27
35
  return token;
28
36
  }
29
- // Check a bearer token: known paired peer, or a pending pairing token (which
30
- // then completes pairing for the claimed sender node).
31
- export function authenticateToken(db, token, from) {
37
+ // Check a bearer token: known paired peer, or a live pending pairing token.
38
+ // The pending path requires the claimed sender to be a node this host already
39
+ // knows, and (the rebind-window fix) a proof signed by the sender's pinned
40
+ // key: a leaked offer alone cannot rotate a paired credential.
41
+ export function authenticateToken(db, token, from, proof) {
32
42
  const paired = findPeerByToken(db, token);
33
43
  if (paired) {
34
44
  return paired;
35
45
  }
46
+ prunePendingTokens(db);
36
47
  const pending = db.conn.get("SELECT token FROM pending_tokens WHERE token = ?", [token]);
37
48
  if (!pending) {
38
49
  return null;
@@ -41,30 +52,114 @@ export function authenticateToken(db, token, from) {
41
52
  if (from === null || from.length === 0) {
42
53
  return null;
43
54
  }
44
- pairWithToken(db, { node_id: from, hostname: "pending", fingerprint: "pending", token });
45
- return {
46
- node_id: from,
47
- hostname: "pending",
48
- fingerprint: "pending",
49
- addresses: [],
50
- paired: true,
55
+ const known = findPeerById(db, from);
56
+ if (!known) {
57
+ return null;
58
+ }
59
+ // Proof of possession: the sender signs opencode-mesh-pair:<node_id>:<token>
60
+ // with the key pinned for it (first bind: the proof carries its own key).
61
+ const attested = attestedProof(known, token, proof);
62
+ if (attested === null) {
63
+ return null;
64
+ }
65
+ pairWithToken(db, {
66
+ node_id: known.node_id,
67
+ hostname: known.hostname,
68
+ fingerprint: known.fingerprint,
69
+ addresses: known.addresses,
70
+ public_key: attested,
51
71
  token,
52
- last_seen: Date.now(),
53
- };
72
+ });
73
+ return known;
74
+ }
75
+ // Returns the public key to pin for this binding, or null when the proof does
76
+ // not attest the sender. Rules:
77
+ // - wire form is exactly `<base64 spki>.<base64 sig>`: no extra dots (base64
78
+ // decoders skip '.', so a trailing dot would otherwise verify);
79
+ // - unpaired known node: the proof's key is pinned once its signature checks;
80
+ // - paired node with a pinned key: the proof key must BE the pinned key;
81
+ // - paired node without a pinned key (legacy/manual rows): no remote attested
82
+ // rebind at all. Rotation goes through the operator (re-run mesh_pair).
83
+ function attestedProof(known, token, proof) {
84
+ if (proof === undefined || proof.length === 0) {
85
+ return null;
86
+ }
87
+ const separator = proof.indexOf(".");
88
+ if (separator <= 0 || separator === proof.length - 1) {
89
+ return null;
90
+ }
91
+ const presentedKey = proof.slice(0, separator);
92
+ const signature = proof.slice(separator + 1);
93
+ if (signature.includes(".")) {
94
+ return null;
95
+ }
96
+ const payload = pairingProofPayload(known.node_id, token);
97
+ if (known.paired) {
98
+ // Rotation of a live credential: the pinned key is the only authority.
99
+ if (known.public_key === null || presentedKey !== known.public_key) {
100
+ return null;
101
+ }
102
+ }
103
+ else if (known.public_key !== null && presentedKey !== known.public_key) {
104
+ // First bind, but discovery already pinned a different key: spoof guard.
105
+ return null;
106
+ }
107
+ return verifyPairingProof(presentedKey, payload, signature) ? presentedKey : null;
108
+ }
109
+ // Pairing offers are single-shot credentials with a short life: bulk delete on
110
+ // every check keeps the table bounded without a background timer.
111
+ export function prunePendingTokens(db) {
112
+ db.conn.run("DELETE FROM pending_tokens WHERE created_at <= ?", [
113
+ Date.now() - PAIRING_TOKEN_TTL_MS,
114
+ ]);
54
115
  }
55
116
  export function listPeers(db) {
56
117
  return db.conn.all("SELECT * FROM peers ORDER BY hostname").map(rowToPeer);
57
118
  }
119
+ export function findPeerById(db, nodeId) {
120
+ const row = db.conn.get("SELECT * FROM peers WHERE node_id = ?", [nodeId]);
121
+ return row ? rowToPeer(row) : null;
122
+ }
58
123
  export function findPeerByToken(db, token) {
59
124
  const row = db.conn.get("SELECT * FROM peers WHERE token = ? AND paired = 1", [token]);
60
125
  return row ? rowToPeer(row) : null;
61
126
  }
127
+ // Paired peers only: discovery alone never makes a peer sendable.
128
+ export function findSendablePeer(db, query) {
129
+ const peers = listPeers(db).filter((p) => p.paired);
130
+ return peers.find((p) => p.node_id === query) ?? peers.find((p) => p.hostname === query) ?? null;
131
+ }
132
+ // Peer addresses must carry a port: discovery attaches the advertised port and
133
+ // mesh_pair transmits a full host:port. Returning null instead of guessing the
134
+ // local port keeps a stale address from silently reaching the wrong socket.
135
+ export function resolvePeerUrl(db, query) {
136
+ const peers = listPeers(db).filter((p) => p.paired);
137
+ const peer = peers.find((p) => p.node_id === query) ?? peers.find((p) => p.hostname === query) ?? null;
138
+ // A bare IPv6 literal also ends in ":<digits>", so the bracketed form is the
139
+ // only one that can be told apart unambiguously.
140
+ const address = peer?.addresses.find(hasPort);
141
+ return address !== undefined ? `http://${address}` : null;
142
+ }
143
+ function hasPort(address) {
144
+ // Bracketed IPv6, with an optional zone id (`[fe80::1%eth0]:4399`) and the
145
+ // v4-mapped dotted form (`[::ffff:192.168.1.10]:4399`).
146
+ return /^\[[0-9a-f:.]+(?:%[^\]]+)?\]:\d+$/i.test(address) || /^[^:]+:\d+$/.test(address);
147
+ }
62
148
  export function upsertDiscoveredPeer(db, peer) {
63
149
  const now = Date.now();
64
- db.conn.run(`INSERT INTO peers (node_id, hostname, fingerprint, addresses, paired, token, last_seen)
65
- VALUES (?, ?, ?, ?, 0, NULL, ?)
150
+ db.conn.run(`INSERT INTO peers (node_id, hostname, fingerprint, addresses, paired, token, public_key, last_seen)
151
+ VALUES (?, ?, ?, ?, 0, NULL, ?, ?)
66
152
  ON CONFLICT(node_id) DO UPDATE SET hostname = excluded.hostname,
67
- fingerprint = excluded.fingerprint, addresses = excluded.addresses, last_seen = excluded.last_seen`, [peer.node_id, peer.hostname, peer.fingerprint, JSON.stringify(peer.addresses), now]);
153
+ fingerprint = excluded.fingerprint, addresses = excluded.addresses,
154
+ public_key = CASE WHEN peers.paired = 1 THEN peers.public_key ELSE excluded.public_key END,
155
+ last_seen = excluded.last_seen`, [
156
+ peer.node_id,
157
+ peer.hostname,
158
+ peer.fingerprint,
159
+ JSON.stringify(peer.addresses),
160
+ peer.public_key ?? null,
161
+ now,
162
+ ]);
68
163
  }
69
164
  function rowToPeer(row) {
70
165
  return {
@@ -74,6 +169,7 @@ function rowToPeer(row) {
74
169
  addresses: parseAddresses(row.addresses),
75
170
  paired: row.paired === 1,
76
171
  token: row.token,
172
+ public_key: row.public_key,
77
173
  last_seen: row.last_seen,
78
174
  };
79
175
  }
@@ -1 +1 @@
1
- {"version":3,"file":"peers.js","sourceRoot":"","sources":["../../src/lib/peers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"peers.js","sourceRoot":"","sources":["../../src/lib/peers.ts"],"names":[],"mappings":"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"}
@@ -3,10 +3,12 @@ import type { MeshDb } from "./db.js";
3
3
  export type MeshServerOptions = {
4
4
  db: MeshDb;
5
5
  port: number;
6
+ bind?: "loopback" | "lan";
6
7
  };
7
8
  export type MeshServer = {
8
9
  server: Server;
9
10
  port: number;
11
+ address: string;
10
12
  };
11
13
  export declare function startMeshServer(options: MeshServerOptions): Promise<MeshServer>;
12
14
  //# sourceMappingURL=serve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/lib/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,MAAM,EAAkB,MAAM,WAAW,CAAC;AAGzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAMtC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAKF,wBAAsB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAuBrF"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/lib/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,MAAM,EAAkB,MAAM,WAAW,CAAC;AAGzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAUtC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF,wBAAsB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CA6BrF"}