@gtc6244/immediacy-daemon 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.
Files changed (57) hide show
  1. package/dist/src/crypto.d.ts +18 -39
  2. package/dist/src/crypto.js +33 -152
  3. package/dist/src/crypto.js.map +1 -1
  4. package/dist/src/devices.d.ts +46 -0
  5. package/dist/src/devices.js +114 -0
  6. package/dist/src/devices.js.map +1 -0
  7. package/dist/src/envelope.d.ts +3 -3
  8. package/dist/src/envelope.js +3 -3
  9. package/dist/src/forward.d.ts +4 -4
  10. package/dist/src/forward.js +1 -1
  11. package/dist/src/forward.js.map +1 -1
  12. package/dist/src/framing.d.ts +76 -0
  13. package/dist/src/framing.js +133 -0
  14. package/dist/src/framing.js.map +1 -0
  15. package/dist/src/hub.d.ts +21 -9
  16. package/dist/src/hub.js +6 -5
  17. package/dist/src/hub.js.map +1 -1
  18. package/dist/src/index.d.ts +3 -3
  19. package/dist/src/index.js +79 -17
  20. package/dist/src/index.js.map +1 -1
  21. package/dist/src/lan.d.ts +103 -0
  22. package/dist/src/lan.js +182 -0
  23. package/dist/src/lan.js.map +1 -0
  24. package/dist/src/pairing.d.ts +69 -0
  25. package/dist/src/pairing.js +125 -0
  26. package/dist/src/pairing.js.map +1 -0
  27. package/dist/src/protocol.d.ts +46 -21
  28. package/dist/src/relay-transport.d.ts +62 -0
  29. package/dist/src/relay-transport.js +180 -0
  30. package/dist/src/relay-transport.js.map +1 -0
  31. package/dist/src/relay-wire.d.ts +25 -0
  32. package/dist/src/relay-wire.js +37 -0
  33. package/dist/src/relay-wire.js.map +1 -0
  34. package/dist/src/rendezvous.d.ts +57 -0
  35. package/dist/src/rendezvous.js +152 -0
  36. package/dist/src/rendezvous.js.map +1 -0
  37. package/dist/src/server.d.ts +19 -7
  38. package/dist/src/server.js +150 -61
  39. package/dist/src/server.js.map +1 -1
  40. package/dist/src/session-socket.d.ts +34 -0
  41. package/dist/src/session-socket.js +84 -0
  42. package/dist/src/session-socket.js.map +1 -0
  43. package/dist/src/session.d.ts +151 -0
  44. package/dist/src/session.js +241 -0
  45. package/dist/src/session.js.map +1 -0
  46. package/dist/src/tcp-transport.d.ts +37 -0
  47. package/dist/src/tcp-transport.js +106 -0
  48. package/dist/src/tcp-transport.js.map +1 -0
  49. package/dist/src/telemetry.d.ts +69 -0
  50. package/dist/src/telemetry.js +64 -0
  51. package/dist/src/telemetry.js.map +1 -0
  52. package/dist/src/transport.d.ts +75 -0
  53. package/dist/src/transport.js +136 -0
  54. package/dist/src/transport.js.map +1 -0
  55. package/dist/src/workspace.d.ts +4 -4
  56. package/dist/src/workspace.js.map +1 -1
  57. package/package.json +16 -4
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Relay transport (daemon side) — docs/WEBRTC_MIGRATION.md Phase 4.
3
+ *
4
+ * The daemon keeps ONE persistent WebSocket to its Durable Object (`/daemon/
5
+ * <rid>`) whenever it's running. The DO multiplexes every client over that single
6
+ * socket, tagged by client id. `RelayDaemon` demuxes: each client id gets its own
7
+ * `Transport` (a `SubTransport` over the shared ws), which feeds a `Mux` + the
8
+ * §5.3 responder handshake — exactly the same session stack the loopback and LAN
9
+ * paths use. The relay only ever sees sealed frames.
10
+ */
11
+ import { WebSocket } from "ws";
12
+ import { Mux } from "./transport.js";
13
+ import { RelayType, encodeDaemon, decodeDaemon } from "./relay-wire.js";
14
+ import { respondHandshakeOverMux } from "./lan.js";
15
+ /** A per-client Transport over the daemon's shared relay WebSocket. */
16
+ class SubTransport {
17
+ clientId;
18
+ ws;
19
+ frameCb = null;
20
+ closeCb = null;
21
+ closed = false;
22
+ constructor(clientId, ws) {
23
+ this.clientId = clientId;
24
+ this.ws = ws;
25
+ }
26
+ send(bytes) {
27
+ if (this.closed || this.ws.readyState !== WebSocket.OPEN)
28
+ return;
29
+ this.ws.send(encodeDaemon(RelayType.DATA, this.clientId, bytes));
30
+ }
31
+ /** Called by RelayDaemon when a DATA frame for this client arrives. */
32
+ deliver(frame) {
33
+ this.frameCb?.(frame);
34
+ }
35
+ bufferedAmount() {
36
+ return this.ws.bufferedAmount;
37
+ }
38
+ onFrame(cb) {
39
+ this.frameCb = cb;
40
+ }
41
+ onClose(cb) {
42
+ this.closeCb = cb;
43
+ }
44
+ whenWritable(highWaterMark) {
45
+ // `ws` has no drain event; poll the buffered amount briefly. Client-bound
46
+ // volume is dominated by the daemon→client terminal stream, throttled above.
47
+ if (this.closed || this.ws.bufferedAmount < highWaterMark)
48
+ return Promise.resolve();
49
+ return new Promise((resolve) => {
50
+ const t = setInterval(() => {
51
+ if (this.closed || this.ws.bufferedAmount < highWaterMark) {
52
+ clearInterval(t);
53
+ resolve();
54
+ }
55
+ }, 10);
56
+ });
57
+ }
58
+ close() {
59
+ if (this.closed)
60
+ return;
61
+ this.closed = true;
62
+ this.closeCb?.();
63
+ }
64
+ }
65
+ /**
66
+ * Maintains the daemon's persistent relay socket and per-client sessions. Call
67
+ * `connect()` to dial; it reconnects with backoff on drop so the daemon is
68
+ * reachable over the relay whenever it's running (the Phase-4 acceptance:
69
+ * "daemon restart auto-reconnects with no user action").
70
+ */
71
+ export class RelayDaemon {
72
+ deps;
73
+ relayUrl;
74
+ opts;
75
+ ws = null;
76
+ subs = new Map();
77
+ closed = false;
78
+ attempt = 0;
79
+ reconnectTimer = null;
80
+ /** Called for each client that completes the handshake over the relay. */
81
+ onSession;
82
+ constructor(deps, relayUrl, opts) {
83
+ this.deps = deps;
84
+ this.relayUrl = relayUrl;
85
+ this.opts = opts;
86
+ }
87
+ /** The daemon endpoint for this daemon's rendezvous id. */
88
+ url() {
89
+ const rid = this.deps.identity.rendezvousId();
90
+ const base = `${this.relayUrl.replace(/\/$/, "")}/daemon/${rid}`;
91
+ return this.opts?.license ? `${base}?license=${encodeURIComponent(this.opts.license)}` : base;
92
+ }
93
+ connect() {
94
+ if (this.closed)
95
+ return;
96
+ const ws = new WebSocket(this.url());
97
+ this.ws = ws;
98
+ ws.binaryType = "nodebuffer";
99
+ ws.on("open", () => {
100
+ this.attempt = 0;
101
+ });
102
+ ws.on("message", (data) => this.onMessage(Buffer.isBuffer(data) ? data : Buffer.from(data)));
103
+ ws.on("close", () => this.onClose());
104
+ ws.on("error", () => {
105
+ /* surfaced via close */
106
+ });
107
+ }
108
+ onMessage(bytes) {
109
+ let env;
110
+ try {
111
+ env = decodeDaemon(bytes);
112
+ }
113
+ catch {
114
+ return;
115
+ }
116
+ if (env.type === RelayType.CLIENT_OPEN) {
117
+ this.openClient(env.clientId);
118
+ }
119
+ else if (env.type === RelayType.CLIENT_CLOSE) {
120
+ this.subs.get(env.clientId)?.close();
121
+ this.subs.delete(env.clientId);
122
+ }
123
+ else if (env.type === RelayType.DATA) {
124
+ this.subs.get(env.clientId)?.deliver(env.payload);
125
+ }
126
+ }
127
+ openClient(clientId) {
128
+ if (!this.ws)
129
+ return;
130
+ const sub = new SubTransport(clientId, this.ws);
131
+ this.subs.set(clientId, sub);
132
+ const mux = new Mux(sub);
133
+ const pairing = this.deps.pairing;
134
+ respondHandshakeOverMux(mux, {
135
+ daemonSignSecret: this.deps.identity.signSecretKey(),
136
+ rendezvousId: this.deps.identity.rendezvousId(),
137
+ isPaired: (dev) => this.deps.devices.has(dev),
138
+ redeemCode: pairing ? (_dev, code) => pairing.consume(code) : undefined,
139
+ })
140
+ .then(async ({ channel, devicePub, paired, label }) => {
141
+ if (paired)
142
+ await this.deps.devices.add(devicePub, label ?? "device");
143
+ void this.deps.devices.touch(devicePub).catch(() => { });
144
+ this.onSession?.({
145
+ clientId,
146
+ mux,
147
+ channel,
148
+ devicePub,
149
+ close: () => sub.close(),
150
+ onClose: (cb) => sub.onClose(cb),
151
+ });
152
+ })
153
+ .catch(() => {
154
+ sub.close();
155
+ this.subs.delete(clientId);
156
+ });
157
+ }
158
+ onClose() {
159
+ for (const sub of this.subs.values())
160
+ sub.close();
161
+ this.subs.clear();
162
+ this.ws = null;
163
+ if (this.closed)
164
+ return;
165
+ // Reconnect with capped backoff so the daemon re-registers automatically.
166
+ const delay = Math.min(30_000, 1000 * 2 ** this.attempt++);
167
+ this.reconnectTimer = setTimeout(() => this.connect(), delay);
168
+ }
169
+ close() {
170
+ this.closed = true;
171
+ if (this.reconnectTimer)
172
+ clearTimeout(this.reconnectTimer);
173
+ for (const sub of this.subs.values())
174
+ sub.close();
175
+ this.subs.clear();
176
+ this.ws?.close();
177
+ this.ws = null;
178
+ }
179
+ }
180
+ //# sourceMappingURL=relay-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-transport.js","sourceRoot":"","sources":["../../src/relay-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Relay envelope codec (daemon copy) — mirrors `relay/src/wire.ts`.
3
+ *
4
+ * The daemon holds ONE WebSocket to its Durable Object and multiplexes every
5
+ * client over it, tagged by the DO-assigned client id:
6
+ *
7
+ * daemon ↔ DO: [u8 type][u32 clientId (BE)][payload]
8
+ * DATA (0) payload = one frame for/from that client
9
+ * CLIENT_OPEN (1) DO→daemon: a client connected (start a session)
10
+ * CLIENT_CLOSE (2) DO→daemon: a client disconnected (tear it down)
11
+ *
12
+ * Duplicated here (not imported) because daemon/ and relay/ are separate
13
+ * packages; kept in sync by a shared shape + the relay's own wire tests.
14
+ */
15
+ export declare const RelayType: {
16
+ readonly DATA: 0;
17
+ readonly CLIENT_OPEN: 1;
18
+ readonly CLIENT_CLOSE: 2;
19
+ };
20
+ export declare function encodeDaemon(type: number, clientId: number, payload?: Buffer): Buffer;
21
+ export declare function decodeDaemon(bytes: Buffer): {
22
+ type: number;
23
+ clientId: number;
24
+ payload: Buffer;
25
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Relay envelope codec (daemon copy) — mirrors `relay/src/wire.ts`.
3
+ *
4
+ * The daemon holds ONE WebSocket to its Durable Object and multiplexes every
5
+ * client over it, tagged by the DO-assigned client id:
6
+ *
7
+ * daemon ↔ DO: [u8 type][u32 clientId (BE)][payload]
8
+ * DATA (0) payload = one frame for/from that client
9
+ * CLIENT_OPEN (1) DO→daemon: a client connected (start a session)
10
+ * CLIENT_CLOSE (2) DO→daemon: a client disconnected (tear it down)
11
+ *
12
+ * Duplicated here (not imported) because daemon/ and relay/ are separate
13
+ * packages; kept in sync by a shared shape + the relay's own wire tests.
14
+ */
15
+ export const RelayType = {
16
+ DATA: 0,
17
+ CLIENT_OPEN: 1,
18
+ CLIENT_CLOSE: 2,
19
+ };
20
+ export function encodeDaemon(type, clientId, payload) {
21
+ const out = Buffer.allocUnsafe(5 + (payload?.length ?? 0));
22
+ out.writeUInt8(type & 0xff, 0);
23
+ out.writeUInt32BE(clientId >>> 0, 1);
24
+ if (payload)
25
+ payload.copy(out, 5);
26
+ return out;
27
+ }
28
+ export function decodeDaemon(bytes) {
29
+ if (bytes.length < 5)
30
+ throw new RangeError("short daemon envelope");
31
+ return {
32
+ type: bytes.readUInt8(0),
33
+ clientId: bytes.readUInt32BE(1),
34
+ payload: bytes.subarray(5),
35
+ };
36
+ }
37
+ //# sourceMappingURL=relay-wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-wire.js","sourceRoot":"","sources":["../../src/relay-wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,IAAI,EAAE,CAAC;IACP,WAAW,EAAE,CAAC;IACd,YAAY,EAAE,CAAC;CACP,CAAC;AAEX,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,QAAgB,EAAE,OAAgB;IAC3E,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3D,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/B,GAAG,CAAC,aAAa,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,IAAI,OAAO;QAAE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAClC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,CAAC;IACpE,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;QACxB,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;QAC/B,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * RendezvousIdentity — the daemon's Ed25519 *signing* identity (§3.1).
3
+ *
4
+ * This is the load-bearing trust root that replaces Cloudflare Access. It is
5
+ * SEPARATE from:
6
+ * - `keys.json` (X25519 crypto_box, `crypto.ts`) — legacy E2E envelope box,
7
+ * to be retired in Phase 5.
8
+ * - `daemon.json` (`daemon-identity.ts`) — display id + human name.
9
+ *
10
+ * Persisted at `~/.immediacy/identity.key` (mode 0600) as the base64 of the
11
+ * 32-byte Ed25519 seed — the single source of truth. The keypair and the
12
+ * rendezvous id are re-derived from the seed on every load, never stored.
13
+ *
14
+ * Rendezvous id = base32( HMAC-SHA256(seed, "immediacy-rendezvous-v1")[:16] )
15
+ * — 128 bits, used as the Durable Object name and the mDNS instance id.
16
+ *
17
+ * No backup (decision #1): there is no export/import path. Wiping `~/.immediacy`
18
+ * mints a fresh seed ⇒ a new rendezvous id ⇒ every client must re-pair.
19
+ */
20
+ /** base32-encode `buf` (RFC 4648, lowercase, unpadded). */
21
+ export declare function base32Encode(buf: Buffer): string;
22
+ /** True iff `b64` is canonical base64 of a full-length Ed25519 public key. */
23
+ export declare function isValidSignPublicKeyB64(b64: string): boolean;
24
+ /** Sign `message` with a 64-byte Ed25519 secret key. Returns the 64-byte sig. */
25
+ export declare function signDetached(message: Buffer, secretKey: Buffer): Buffer;
26
+ /**
27
+ * Verify a detached Ed25519 signature. Returns false (never throws) on any
28
+ * malformed input so callers can treat a bad signature as a plain auth failure.
29
+ */
30
+ export declare function verifyDetached(signature: Buffer, message: Buffer, publicKey: Buffer): boolean;
31
+ export declare class RendezvousIdentity {
32
+ private seed;
33
+ private publicKey;
34
+ private secretKey;
35
+ private rendezvous;
36
+ /** Serializes writes so two concurrent rename() calls can't interleave. */
37
+ private writeChain;
38
+ /**
39
+ * Load the persisted seed, minting + persisting a fresh one on first run,
40
+ * then derive the keypair and rendezvous id. Idempotent across restarts:
41
+ * the same seed always yields the same keypair and id.
42
+ */
43
+ load(): Promise<void>;
44
+ /** The rendezvous id (base32, 128-bit): the DO name + mDNS instance id. */
45
+ rendezvousId(): string;
46
+ /** The Ed25519 public key, base64 — this is what clients pin at pair time. */
47
+ publicKeyB64(): string;
48
+ /** The raw Ed25519 public key (32 bytes). */
49
+ signPublicKey(): Buffer;
50
+ /** The raw Ed25519 secret key (64 bytes) — never leaves the daemon. */
51
+ signSecretKey(): Buffer;
52
+ /** Sign a message with the daemon identity key. */
53
+ sign(message: Buffer): Buffer;
54
+ private persist;
55
+ }
56
+ /** Derive the base32 rendezvous id from a 32-byte seed. Pure + deterministic. */
57
+ export declare function deriveRendezvousId(seed: Buffer): string;
@@ -0,0 +1,152 @@
1
+ /**
2
+ * RendezvousIdentity — the daemon's Ed25519 *signing* identity (§3.1).
3
+ *
4
+ * This is the load-bearing trust root that replaces Cloudflare Access. It is
5
+ * SEPARATE from:
6
+ * - `keys.json` (X25519 crypto_box, `crypto.ts`) — legacy E2E envelope box,
7
+ * to be retired in Phase 5.
8
+ * - `daemon.json` (`daemon-identity.ts`) — display id + human name.
9
+ *
10
+ * Persisted at `~/.immediacy/identity.key` (mode 0600) as the base64 of the
11
+ * 32-byte Ed25519 seed — the single source of truth. The keypair and the
12
+ * rendezvous id are re-derived from the seed on every load, never stored.
13
+ *
14
+ * Rendezvous id = base32( HMAC-SHA256(seed, "immediacy-rendezvous-v1")[:16] )
15
+ * — 128 bits, used as the Durable Object name and the mDNS instance id.
16
+ *
17
+ * No backup (decision #1): there is no export/import path. Wiping `~/.immediacy`
18
+ * mints a fresh seed ⇒ a new rendezvous id ⇒ every client must re-pair.
19
+ */
20
+ import sodium from "sodium-native";
21
+ import { createHmac } from "node:crypto";
22
+ import { existsSync } from "node:fs";
23
+ import { mkdir, readFile, rename, writeFile, chmod } from "node:fs/promises";
24
+ import { homedir } from "node:os";
25
+ import { dirname, join } from "node:path";
26
+ const IDENTITY_FILE = process.env.IDENTITY_FILE ?? join(homedir(), ".immediacy", "identity.key");
27
+ /** Domain-separation label for the rendezvous-id HMAC. Bump = new id space. */
28
+ const RENDEZVOUS_LABEL = "immediacy-rendezvous-v1";
29
+ /** RFC 4648 base32 alphabet, lowercase, no padding (mDNS/DO-name friendly). */
30
+ const BASE32_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
31
+ /** base32-encode `buf` (RFC 4648, lowercase, unpadded). */
32
+ export function base32Encode(buf) {
33
+ let out = "";
34
+ let bits = 0;
35
+ let value = 0;
36
+ for (const byte of buf) {
37
+ value = (value << 8) | byte;
38
+ bits += 8;
39
+ while (bits >= 5) {
40
+ bits -= 5;
41
+ out += BASE32_ALPHABET[(value >>> bits) & 31];
42
+ }
43
+ }
44
+ if (bits > 0)
45
+ out += BASE32_ALPHABET[(value << (5 - bits)) & 31];
46
+ return out;
47
+ }
48
+ /** True iff `b64` is canonical base64 of a full-length Ed25519 public key. */
49
+ export function isValidSignPublicKeyB64(b64) {
50
+ // Node's base64 decoder silently drops invalid characters, so a length check
51
+ // alone lets malformed input through. Require a strict alphabet, then confirm
52
+ // the value round-trips to the same canonical string at the exact length.
53
+ if (typeof b64 !== "string" || !/^[A-Za-z0-9+/]+={0,2}$/.test(b64))
54
+ return false;
55
+ const buf = Buffer.from(b64, "base64");
56
+ return (buf.length === sodium.crypto_sign_PUBLICKEYBYTES &&
57
+ buf.toString("base64") === b64);
58
+ }
59
+ /** Sign `message` with a 64-byte Ed25519 secret key. Returns the 64-byte sig. */
60
+ export function signDetached(message, secretKey) {
61
+ const sig = Buffer.alloc(sodium.crypto_sign_BYTES);
62
+ sodium.crypto_sign_detached(sig, message, secretKey);
63
+ return sig;
64
+ }
65
+ /**
66
+ * Verify a detached Ed25519 signature. Returns false (never throws) on any
67
+ * malformed input so callers can treat a bad signature as a plain auth failure.
68
+ */
69
+ export function verifyDetached(signature, message, publicKey) {
70
+ if (signature.length !== sodium.crypto_sign_BYTES ||
71
+ publicKey.length !== sodium.crypto_sign_PUBLICKEYBYTES) {
72
+ return false;
73
+ }
74
+ return sodium.crypto_sign_verify_detached(signature, message, publicKey);
75
+ }
76
+ export class RendezvousIdentity {
77
+ seed = Buffer.alloc(0);
78
+ publicKey = Buffer.alloc(0);
79
+ secretKey = Buffer.alloc(0);
80
+ rendezvous = "";
81
+ /** Serializes writes so two concurrent rename() calls can't interleave. */
82
+ writeChain = Promise.resolve();
83
+ /**
84
+ * Load the persisted seed, minting + persisting a fresh one on first run,
85
+ * then derive the keypair and rendezvous id. Idempotent across restarts:
86
+ * the same seed always yields the same keypair and id.
87
+ */
88
+ async load() {
89
+ let seed = null;
90
+ if (existsSync(IDENTITY_FILE)) {
91
+ try {
92
+ const raw = (await readFile(IDENTITY_FILE, "utf8")).trim();
93
+ const buf = Buffer.from(raw, "base64");
94
+ if (buf.length === sodium.crypto_sign_SEEDBYTES)
95
+ seed = buf;
96
+ }
97
+ catch {
98
+ /* unreadable — fall through to mint a fresh seed */
99
+ }
100
+ }
101
+ if (!seed) {
102
+ seed = Buffer.alloc(sodium.crypto_sign_SEEDBYTES);
103
+ sodium.randombytes_buf(seed);
104
+ await this.persist(seed);
105
+ }
106
+ this.seed = seed;
107
+ this.publicKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES);
108
+ this.secretKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES);
109
+ sodium.crypto_sign_seed_keypair(this.publicKey, this.secretKey, this.seed);
110
+ this.rendezvous = deriveRendezvousId(this.seed);
111
+ console.log(`[rendezvous] identity ${this.rendezvous}`);
112
+ }
113
+ /** The rendezvous id (base32, 128-bit): the DO name + mDNS instance id. */
114
+ rendezvousId() {
115
+ return this.rendezvous;
116
+ }
117
+ /** The Ed25519 public key, base64 — this is what clients pin at pair time. */
118
+ publicKeyB64() {
119
+ return this.publicKey.toString("base64");
120
+ }
121
+ /** The raw Ed25519 public key (32 bytes). */
122
+ signPublicKey() {
123
+ return this.publicKey;
124
+ }
125
+ /** The raw Ed25519 secret key (64 bytes) — never leaves the daemon. */
126
+ signSecretKey() {
127
+ return this.secretKey;
128
+ }
129
+ /** Sign a message with the daemon identity key. */
130
+ sign(message) {
131
+ return signDetached(message, this.secretKey);
132
+ }
133
+ persist(seed) {
134
+ const snapshot = seed.toString("base64");
135
+ this.writeChain = this.writeChain
136
+ .catch(() => { })
137
+ .then(async () => {
138
+ await mkdir(dirname(IDENTITY_FILE), { recursive: true });
139
+ const tmp = `${IDENTITY_FILE}.${process.pid}.tmp`;
140
+ await writeFile(tmp, snapshot, "utf8");
141
+ await chmod(tmp, 0o600); // secret seed — never world-readable
142
+ await rename(tmp, IDENTITY_FILE);
143
+ });
144
+ return this.writeChain;
145
+ }
146
+ }
147
+ /** Derive the base32 rendezvous id from a 32-byte seed. Pure + deterministic. */
148
+ export function deriveRendezvousId(seed) {
149
+ const mac = createHmac("sha256", seed).update(RENDEZVOUS_LABEL).digest();
150
+ return base32Encode(mac.subarray(0, 16)); // 128 bits
151
+ }
152
+ //# sourceMappingURL=rendezvous.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendezvous.js","sourceRoot":"","sources":["../../src/rendezvous.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,MAAM,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;AAE7E,+EAA+E;AAC/E,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AAEnD,+EAA+E;AAC/E,MAAM,eAAe,GAAG,kCAAkC,CAAC;AAE3D,2DAA2D;AAC3D,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAC5B,IAAI,IAAI,CAAC,CAAC;QACV,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC;YACjB,IAAI,IAAI,CAAC,CAAC;YACV,GAAG,IAAI,eAAe,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IACD,IAAI,IAAI,GAAG,CAAC;QAAE,GAAG,IAAI,eAAe,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACjE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,uBAAuB,CAAC,GAAW;IACjD,6EAA6E;IAC7E,8EAA8E;IAC9E,0EAA0E;IAC1E,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACjF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACvC,OAAO,CACL,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,0BAA0B;QAChD,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,GAAG,CAC/B,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,SAAiB;IAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACnD,MAAM,CAAC,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAiB,EACjB,OAAe,EACf,SAAiB;IAEjB,IACE,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,iBAAiB;QAC7C,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,0BAA0B,EACtD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,2BAA2B,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,OAAO,kBAAkB;IACrB,IAAI,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,SAAS,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpC,SAAS,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpC,UAAU,GAAG,EAAE,CAAC;IACxB,2EAA2E;IACnE,UAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEtD;;;;OAIG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,GAAkB,IAAI,CAAC;QAC/B,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,CAAC,MAAM,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBACvC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,qBAAqB;oBAAE,IAAI,GAAG,GAAG,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,oDAAoD;YACtD,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;YAClD,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;QACjE,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;QACjE,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,IAAI,CAAC,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,2EAA2E;IAC3E,YAAY;QACV,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,8EAA8E;IAC9E,YAAY;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED,6CAA6C;IAC7C,aAAa;QACX,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,uEAAuE;IACvE,aAAa;QACX,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,mDAAmD;IACnD,IAAI,CAAC,OAAe;QAClB,OAAO,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/C,CAAC;IAEO,OAAO,CAAC,IAAY;QAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU;aAC9B,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;aACf,IAAI,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,MAAM,GAAG,GAAG,GAAG,aAAa,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;YAClD,MAAM,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YACvC,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,qCAAqC;YAC9D,MAAM,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QACL,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF;AAED,iFAAiF;AACjF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC;IACzE,OAAO,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW;AACvD,CAAC"}
@@ -7,7 +7,9 @@
7
7
  */
8
8
  import { type Server } from "node:http";
9
9
  import { WebSocketServer } from "ws";
10
- import { type Keypair } from "./crypto.js";
10
+ import { RendezvousIdentity } from "./rendezvous.js";
11
+ import { DeviceTable } from "./devices.js";
12
+ import { PairingCodes } from "./pairing.js";
11
13
  import { Hub } from "./hub.js";
12
14
  import { WorkspaceManager } from "./workspace.js";
13
15
  import { JobScheduler } from "./jobs/scheduler.js";
@@ -15,13 +17,21 @@ export interface DaemonOptions {
15
17
  port: number;
16
18
  host: string;
17
19
  /**
18
- * Present when E2E is on. The daemon seals each socket's frames to that
19
- * device's key after its `hello` handshake; without this, every socket stays
20
- * cleartext (local dev / slices 1–4).
20
+ * The multi-transport session handshake services (docs/WEBRTC_MIGRATION.md):
21
+ * the daemon's Ed25519 identity, its paired-device table, and the in-memory
22
+ * pairing-code issuer. This is the only E2E path since the Phase-5 cutover.
21
23
  */
22
- crypto?: {
23
- self: Keypair;
24
- };
24
+ rendezvous?: RendezvousServices;
25
+ /** LAN direct server (Phase 3); its sessions attach to the same dispatch. */
26
+ lan?: import("./lan.js").LanServer;
27
+ /** Relay client (Phase 4); its per-client sessions attach to the dispatch. */
28
+ relay?: import("./relay-transport.js").RelayDaemon;
29
+ }
30
+ /** The Phase-1 session-handshake services, constructed once at startup. */
31
+ export interface RendezvousServices {
32
+ identity: RendezvousIdentity;
33
+ devices: DeviceTable;
34
+ pairing: PairingCodes;
25
35
  }
26
36
  export interface Daemon {
27
37
  http: Server;
@@ -29,5 +39,7 @@ export interface Daemon {
29
39
  hub: Hub;
30
40
  workspaces: WorkspaceManager;
31
41
  jobs: JobScheduler;
42
+ /** The session-handshake services, when enabled (see DaemonOptions). */
43
+ rendezvous?: RendezvousServices;
32
44
  }
33
45
  export declare function startServer(opts: DaemonOptions): Promise<Daemon>;