@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
@@ -1,49 +1,28 @@
1
1
  /**
2
- * E2E crypto (slice 5) — keep the Cloudflare edge blind.
2
+ * Wire codecs — the (de)serialization seam for the envelope transport.
3
3
  *
4
- * Cloudflare terminates TLS and can see plaintext. We additionally seal the
5
- * envelope `payload` with crypto_box (X25519 + XSalsa20-Poly1305 — NaCl box),
6
- * wire-compatible with libsodium's `crypto_box_easy` on the Flutter side
7
- * (sodium_libs). The outer fields (v, id, ts, ws, type) stay clear for routing.
8
- *
9
- * On the wire an encrypted payload is: { n: base64(nonce), c: base64(cipher) }.
10
- *
11
- * This is IN ADDITION to Cloudflare Access — Access does authn/authz at the
12
- * edge; the box keeps payload contents private from the edge.
4
+ * Since the Cloudflare-tunnel cutover (docs/WEBRTC_MIGRATION.md Phase 5) the only
5
+ * E2E path is the §5.3 session handshake, whose `SecureChannel` seals each frame
6
+ * with AES-256-GCM. The legacy NaCl `crypto_box` layer (paired to a Cloudflare
7
+ * Access gate) has been removed. Two codecs remain:
8
+ * - clearCodec: cleartext JSON (local dev, and a socket before it seals).
9
+ * - sessionCodec: seals the envelope payload with a handshake SecureChannel.
13
10
  */
14
11
  import type { Envelope } from "./protocol.js";
15
- export interface Keypair {
16
- publicKey: Buffer;
17
- secretKey: Buffer;
18
- }
19
- /** Load the daemon keypair, generating + persisting one on first run. */
20
- export declare function loadOrCreateKeypair(): Promise<Keypair>;
21
- /** One paired client. `pub` is base64 of its X25519 public key. */
22
- export interface Peer {
23
- pub: string;
24
- addedAt: number;
25
- label?: string;
26
- }
27
- /** True iff `b64` is canonical base64 of a full-length crypto_box public key. */
28
- export declare function isValidPublicKeyB64(b64: string): boolean;
29
- /**
30
- * Every paired client. Reads `peers.json`, migrating a legacy single-peer
31
- * `peer.json` (and honoring the PEER_PUBLIC_KEY dev override) on first read so
32
- * an existing pairing keeps working after the multi-device upgrade.
33
- */
34
- export declare function loadPeers(): Promise<Peer[]>;
35
- /**
36
- * Add a client public key to the paired set (idempotent) and persist. Returns
37
- * the updated list. Trust-on-first-use: the caller has already cleared the
38
- * Cloudflare Access gate, which is the shared pairing secret.
39
- */
40
- export declare function addPeer(pub: string, label?: string): Promise<Peer[]>;
12
+ import type { SecureChannel } from "./session.js";
41
13
  /** Codec abstraction so the server/hub don't care whether crypto is on. */
42
14
  export interface Codec {
43
15
  encode(env: Envelope): string;
44
16
  decode(raw: string): Envelope;
45
17
  }
46
- /** Cleartext codec (slices 1–4 default, and local dev). */
18
+ /** Cleartext codec (local dev, and any socket before it completes a handshake). */
47
19
  export declare const clearCodec: Codec;
48
- /** NaCl-box codec: seals/opens only the `payload` field. */
49
- export declare function boxCodec(self: Keypair, peerPublicKey: Buffer): Codec;
20
+ /**
21
+ * Session codec (docs/WEBRTC_MIGRATION.md §5.3). Seals/opens the envelope
22
+ * `payload` with a `SecureChannel` established by the signed handshake, on a
23
+ * single ordered socket. On the wire the payload is `{ s: base64(cipher) }`;
24
+ * the nonce is the channel's per-direction counter (not transmitted), so this
25
+ * REQUIRES ordered, non-lossy delivery — true for the WS/loopback socket it runs
26
+ * on.
27
+ */
28
+ export declare function sessionCodec(channel: SecureChannel): Codec;
@@ -1,169 +1,53 @@
1
1
  /**
2
- * E2E crypto (slice 5) — keep the Cloudflare edge blind.
2
+ * Wire codecs — the (de)serialization seam for the envelope transport.
3
3
  *
4
- * Cloudflare terminates TLS and can see plaintext. We additionally seal the
5
- * envelope `payload` with crypto_box (X25519 + XSalsa20-Poly1305 — NaCl box),
6
- * wire-compatible with libsodium's `crypto_box_easy` on the Flutter side
7
- * (sodium_libs). The outer fields (v, id, ts, ws, type) stay clear for routing.
8
- *
9
- * On the wire an encrypted payload is: { n: base64(nonce), c: base64(cipher) }.
10
- *
11
- * This is IN ADDITION to Cloudflare Access — Access does authn/authz at the
12
- * edge; the box keeps payload contents private from the edge.
4
+ * Since the Cloudflare-tunnel cutover (docs/WEBRTC_MIGRATION.md Phase 5) the only
5
+ * E2E path is the §5.3 session handshake, whose `SecureChannel` seals each frame
6
+ * with AES-256-GCM. The legacy NaCl `crypto_box` layer (paired to a Cloudflare
7
+ * Access gate) has been removed. Two codecs remain:
8
+ * - clearCodec: cleartext JSON (local dev, and a socket before it seals).
9
+ * - sessionCodec: seals the envelope payload with a handshake SecureChannel.
13
10
  */
14
- import sodium from "sodium-native";
15
- import { existsSync } from "node:fs";
16
- import { mkdir, readFile, writeFile, chmod } from "node:fs/promises";
17
- import { homedir } from "node:os";
18
- import { dirname, join } from "node:path";
19
11
  import { ProtocolError } from "./envelope.js";
20
- const KEYS_FILE = process.env.KEYS_FILE ?? join(homedir(), ".immediacy", "keys.json");
21
- const PEER_FILE = process.env.PEER_FILE ?? join(homedir(), ".immediacy", "peer.json");
22
- // Multi-device pairing: the set of every client public key we've paired with,
23
- // so encryption isn't pinned to one device (the old single PEER_FILE). Each
24
- // entry is bound to its own socket at connect time (per-socket sealing).
25
- const PEERS_FILE = process.env.PEERS_FILE ?? join(homedir(), ".immediacy", "peers.json");
26
- /** Load the daemon keypair, generating + persisting one on first run. */
27
- export async function loadOrCreateKeypair() {
28
- if (existsSync(KEYS_FILE)) {
29
- const j = JSON.parse(await readFile(KEYS_FILE, "utf8"));
30
- return {
31
- publicKey: Buffer.from(j.publicKey, "base64"),
32
- secretKey: Buffer.from(j.secretKey, "base64"),
33
- };
34
- }
35
- const publicKey = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES);
36
- const secretKey = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES);
37
- sodium.crypto_box_keypair(publicKey, secretKey);
38
- await mkdir(dirname(KEYS_FILE), { recursive: true });
39
- await writeFile(KEYS_FILE, JSON.stringify({
40
- publicKey: publicKey.toString("base64"),
41
- secretKey: secretKey.toString("base64"),
42
- }), "utf8");
43
- await chmod(KEYS_FILE, 0o600); // secret key — never world-readable
44
- return { publicKey, secretKey };
45
- }
46
- /** True iff `b64` is canonical base64 of a full-length crypto_box public key. */
47
- export function isValidPublicKeyB64(b64) {
48
- // Node's Buffer.from(x, "base64") never throws and silently drops invalid
49
- // characters, so a decoded-length check alone lets malformed input through.
50
- // Require a strict base64 alphabet up front, then confirm the value
51
- // round-trips to the exact same canonical string at the expected length.
52
- if (typeof b64 !== "string" || !/^[A-Za-z0-9+/]+={0,2}$/.test(b64))
53
- return false;
54
- const buf = Buffer.from(b64, "base64");
55
- return (buf.length === sodium.crypto_box_PUBLICKEYBYTES && buf.toString("base64") === b64);
56
- }
57
- /**
58
- * Every paired client. Reads `peers.json`, migrating a legacy single-peer
59
- * `peer.json` (and honoring the PEER_PUBLIC_KEY dev override) on first read so
60
- * an existing pairing keeps working after the multi-device upgrade.
61
- */
62
- export async function loadPeers() {
63
- const peers = [];
64
- const seen = new Set();
65
- const push = (pub, label) => {
66
- if (!pub || seen.has(pub) || !isValidPublicKeyB64(pub))
67
- return;
68
- seen.add(pub);
69
- peers.push({ pub, addedAt: Date.now(), label });
70
- };
71
- if (existsSync(PEERS_FILE)) {
72
- try {
73
- const j = JSON.parse(await readFile(PEERS_FILE, "utf8"));
74
- for (const p of j.peers ?? []) {
75
- // Validate on the way in too: a corrupted or hand-edited peers.json must
76
- // not seed invalid keys into the paired set (matches the push() helper).
77
- if (p && typeof p.pub === "string" && !seen.has(p.pub) && isValidPublicKeyB64(p.pub)) {
78
- seen.add(p.pub);
79
- peers.push({ pub: p.pub, addedAt: p.addedAt ?? Date.now(), label: p.label });
80
- }
81
- }
82
- }
83
- catch {
84
- /* corrupt file — fall through to migration/env seeds */
85
- }
86
- }
87
- else if (existsSync(PEER_FILE)) {
88
- // Migrate the legacy single-peer file.
89
- try {
90
- const j = JSON.parse(await readFile(PEER_FILE, "utf8"));
91
- if (j.publicKey)
92
- push(j.publicKey, "migrated");
93
- }
94
- catch {
95
- /* ignore */
96
- }
97
- }
98
- if (process.env.PEER_PUBLIC_KEY)
99
- push(process.env.PEER_PUBLIC_KEY, "env");
100
- return peers;
101
- }
102
- // Serializes the peers.json read-modify-write so two devices pairing at the
103
- // same time can't each read the old file and clobber the other's entry.
104
- let addChain = Promise.resolve([]);
105
- /**
106
- * Add a client public key to the paired set (idempotent) and persist. Returns
107
- * the updated list. Trust-on-first-use: the caller has already cleared the
108
- * Cloudflare Access gate, which is the shared pairing secret.
109
- */
110
- export function addPeer(pub, label) {
111
- addChain = addChain.then(async () => {
112
- const peers = await loadPeers();
113
- if (!peers.some((p) => p.pub === pub)) {
114
- peers.push({ pub, addedAt: Date.now(), label });
115
- await mkdir(dirname(PEERS_FILE), { recursive: true });
116
- await writeFile(PEERS_FILE, JSON.stringify({ peers }, null, 2), "utf8");
117
- await chmod(PEERS_FILE, 0o600);
118
- }
119
- return peers;
120
- });
121
- return addChain;
122
- }
123
- /** Upper bound for one sealed payload — matches the largest legitimate frame
124
- * (file contents are truncated daemon-side well below this). */
12
+ /** Upper bound for one sealed payload — matches the largest legitimate frame. */
125
13
  const MAX_SEALED_BYTES = 16 * 1024 * 1024;
126
- /** Cleartext codec (slices 1–4 default, and local dev). */
14
+ /** Cleartext codec (local dev, and any socket before it completes a handshake). */
127
15
  export const clearCodec = {
128
16
  encode: (env) => JSON.stringify(env),
129
17
  decode: (raw) => parseEnvelope(raw),
130
18
  };
131
- /** NaCl-box codec: seals/opens only the `payload` field. */
132
- export function boxCodec(self, peerPublicKey) {
19
+ /**
20
+ * Session codec (docs/WEBRTC_MIGRATION.md §5.3). Seals/opens the envelope
21
+ * `payload` with a `SecureChannel` established by the signed handshake, on a
22
+ * single ordered socket. On the wire the payload is `{ s: base64(cipher) }`;
23
+ * the nonce is the channel's per-direction counter (not transmitted), so this
24
+ * REQUIRES ordered, non-lossy delivery — true for the WS/loopback socket it runs
25
+ * on.
26
+ */
27
+ export function sessionCodec(channel) {
133
28
  return {
134
29
  encode(env) {
135
30
  const plaintext = Buffer.from(JSON.stringify(env.payload), "utf8");
136
- const nonce = Buffer.alloc(sodium.crypto_box_NONCEBYTES);
137
- sodium.randombytes_buf(nonce);
138
- const cipher = Buffer.alloc(plaintext.length + sodium.crypto_box_MACBYTES);
139
- sodium.crypto_box_easy(cipher, plaintext, nonce, peerPublicKey, self.secretKey);
140
- const sealed = {
141
- ...env,
142
- payload: { n: nonce.toString("base64"), c: cipher.toString("base64") },
143
- };
144
- return JSON.stringify(sealed);
31
+ const cipher = channel.seal(plaintext);
32
+ return JSON.stringify({ ...env, payload: { s: cipher.toString("base64") } });
145
33
  },
146
34
  decode(raw) {
147
35
  const env = parseEnvelope(raw, /*allowSealed*/ true);
148
36
  const sealed = env.payload;
149
- if (typeof sealed.n !== "string" || typeof sealed.c !== "string") {
150
- throw new ProtocolError("not_sealed", "payload is not a sealed box");
151
- }
152
- const nonce = Buffer.from(sealed.n, "base64");
153
- const cipher = Buffer.from(sealed.c, "base64");
154
- // Validate sizes before allocating: a short ciphertext would make the
155
- // alloc below negative (RangeError), and an oversized frame would
156
- // allocate unbounded memory — both should fail as protocol errors.
157
- if (nonce.length !== sodium.crypto_box_NONCEBYTES) {
158
- throw new ProtocolError("bad_sealed_box", "nonce has the wrong length");
37
+ if (typeof sealed.s !== "string") {
38
+ throw new ProtocolError("not_sealed", "payload is not a sealed session frame");
159
39
  }
160
- if (cipher.length < sodium.crypto_box_MACBYTES || cipher.length > MAX_SEALED_BYTES) {
40
+ const cipher = Buffer.from(sealed.s, "base64");
41
+ if (cipher.length > MAX_SEALED_BYTES) {
161
42
  throw new ProtocolError("bad_sealed_box", "ciphertext length out of range");
162
43
  }
163
- const plaintext = Buffer.alloc(cipher.length - sodium.crypto_box_MACBYTES);
164
- const ok = sodium.crypto_box_open_easy(plaintext, cipher, nonce, peerPublicKey, self.secretKey);
165
- if (!ok)
166
- throw new ProtocolError("decrypt_failed", "box authentication failed");
44
+ let plaintext;
45
+ try {
46
+ plaintext = channel.open(cipher);
47
+ }
48
+ catch {
49
+ throw new ProtocolError("decrypt_failed", "session frame authentication failed");
50
+ }
167
51
  env.payload = JSON.parse(plaintext.toString("utf8"));
168
52
  return env;
169
53
  },
@@ -183,11 +67,8 @@ function parseEnvelope(raw, allowSealed = false) {
183
67
  }
184
68
  const e = parsed;
185
69
  // `v` is informational (routing + skew detection), NOT a hard compatibility
186
- // gate. Version skew is surfaced as a non-fatal heads-up by the client.hello
187
- // handshake (server.ts) and the client-side banners, and the field stays clear
188
- // for routing (see this module's header). Additive-only bumps like v1→v2 stay
189
- // wire-compatible, so decoding must tolerate any positive integer version. A
190
- // missing, non-numeric, or non-positive-integer `v` is a malformed frame.
70
+ // gate. A missing, non-numeric, or non-positive-integer `v` is a malformed
71
+ // frame; any positive integer decodes (additive-only bumps stay compatible).
191
72
  if (!Number.isSafeInteger(e.v) || e.v <= 0) {
192
73
  throw new ProtocolError("bad_version", `missing/invalid version ${String(e.v)}`);
193
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * DeviceTable — the daemon's set of paired devices (§3.2).
3
+ *
4
+ * Replaces the legacy `peers.json` (X25519 crypto_box TOFU record). Persisted at
5
+ * `~/.immediacy/devices.json` (mode 0600). Each entry pins a device's *Ed25519*
6
+ * public key — the long-term identity the §5.3 session handshake verifies
7
+ * against. Membership in this table IS authorization: a device whose key is not
8
+ * present (never paired, or revoked) must fail its next handshake.
9
+ *
10
+ * Writes are serialized and land via atomic rename (mirrors
11
+ * `RendezvousIdentity.persist` / `DaemonIdentity.persist`).
12
+ */
13
+ /** One paired device. `publicKey` is base64 of its Ed25519 public key. */
14
+ export interface PairedDevice {
15
+ publicKey: string;
16
+ label: string;
17
+ pairedAt: string;
18
+ lastSeenAt: string;
19
+ }
20
+ export declare class DeviceTable {
21
+ private devices;
22
+ /** Serializes read-modify-write so concurrent pairings can't clobber. */
23
+ private writeChain;
24
+ /** Load persisted devices, dropping any entry with a non-canonical key. */
25
+ load(): Promise<void>;
26
+ /** Every paired device, newest-paired last (insertion order). */
27
+ list(): PairedDevice[];
28
+ /** True iff `publicKey` is a currently-paired (non-revoked) device. */
29
+ has(publicKey: string): boolean;
30
+ /** The paired device for `publicKey`, or undefined. */
31
+ get(publicKey: string): PairedDevice | undefined;
32
+ /**
33
+ * Pair a device (idempotent). A fresh key is appended; an existing key keeps
34
+ * its `pairedAt` and only updates the label. Rejects non-canonical Ed25519
35
+ * keys. Returns the stored entry.
36
+ */
37
+ add(publicKey: string, label: string): Promise<PairedDevice>;
38
+ /**
39
+ * Revoke a device. Returns true if it was present. The revoked key is gone
40
+ * from the authorized set, so its next handshake fails (Phase-1 acceptance).
41
+ */
42
+ revoke(publicKey: string): Promise<boolean>;
43
+ /** Update a device's `lastSeenAt` (no-op for unknown keys). */
44
+ touch(publicKey: string): Promise<void>;
45
+ private persist;
46
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * DeviceTable — the daemon's set of paired devices (§3.2).
3
+ *
4
+ * Replaces the legacy `peers.json` (X25519 crypto_box TOFU record). Persisted at
5
+ * `~/.immediacy/devices.json` (mode 0600). Each entry pins a device's *Ed25519*
6
+ * public key — the long-term identity the §5.3 session handshake verifies
7
+ * against. Membership in this table IS authorization: a device whose key is not
8
+ * present (never paired, or revoked) must fail its next handshake.
9
+ *
10
+ * Writes are serialized and land via atomic rename (mirrors
11
+ * `RendezvousIdentity.persist` / `DaemonIdentity.persist`).
12
+ */
13
+ import { existsSync } from "node:fs";
14
+ import { mkdir, readFile, rename, writeFile, chmod } from "node:fs/promises";
15
+ import { homedir } from "node:os";
16
+ import { dirname, join } from "node:path";
17
+ import { isValidSignPublicKeyB64 } from "./rendezvous.js";
18
+ const DEVICES_FILE = process.env.DEVICES_FILE ?? join(homedir(), ".immediacy", "devices.json");
19
+ /** A label a malicious/confused client can't blow up to a multi-KB blob. */
20
+ const MAX_LABEL_LEN = 200;
21
+ export class DeviceTable {
22
+ devices = new Map();
23
+ /** Serializes read-modify-write so concurrent pairings can't clobber. */
24
+ writeChain = Promise.resolve();
25
+ /** Load persisted devices, dropping any entry with a non-canonical key. */
26
+ async load() {
27
+ this.devices.clear();
28
+ if (!existsSync(DEVICES_FILE))
29
+ return;
30
+ let stored = {};
31
+ try {
32
+ stored = JSON.parse(await readFile(DEVICES_FILE, "utf8"));
33
+ }
34
+ catch {
35
+ return; // corrupt file — start empty rather than crash
36
+ }
37
+ for (const d of stored.devices ?? []) {
38
+ // Validate on the way in: a hand-edited/corrupt file must not seed an
39
+ // invalid key into the authorized set.
40
+ if (!d || !isValidSignPublicKeyB64(d.publicKey))
41
+ continue;
42
+ const now = new Date().toISOString();
43
+ this.devices.set(d.publicKey, {
44
+ publicKey: d.publicKey,
45
+ label: typeof d.label === "string" ? d.label.slice(0, MAX_LABEL_LEN) : "",
46
+ pairedAt: typeof d.pairedAt === "string" ? d.pairedAt : now,
47
+ lastSeenAt: typeof d.lastSeenAt === "string" ? d.lastSeenAt : now,
48
+ });
49
+ }
50
+ }
51
+ /** Every paired device, newest-paired last (insertion order). */
52
+ list() {
53
+ return [...this.devices.values()];
54
+ }
55
+ /** True iff `publicKey` is a currently-paired (non-revoked) device. */
56
+ has(publicKey) {
57
+ return this.devices.has(publicKey);
58
+ }
59
+ /** The paired device for `publicKey`, or undefined. */
60
+ get(publicKey) {
61
+ return this.devices.get(publicKey);
62
+ }
63
+ /**
64
+ * Pair a device (idempotent). A fresh key is appended; an existing key keeps
65
+ * its `pairedAt` and only updates the label. Rejects non-canonical Ed25519
66
+ * keys. Returns the stored entry.
67
+ */
68
+ async add(publicKey, label) {
69
+ if (!isValidSignPublicKeyB64(publicKey)) {
70
+ throw new Error("device public key is not a canonical Ed25519 key");
71
+ }
72
+ const now = new Date().toISOString();
73
+ const cleanLabel = (label ?? "").slice(0, MAX_LABEL_LEN);
74
+ const existing = this.devices.get(publicKey);
75
+ const entry = existing
76
+ ? { ...existing, label: cleanLabel, lastSeenAt: now }
77
+ : { publicKey, label: cleanLabel, pairedAt: now, lastSeenAt: now };
78
+ this.devices.set(publicKey, entry);
79
+ await this.persist();
80
+ return entry;
81
+ }
82
+ /**
83
+ * Revoke a device. Returns true if it was present. The revoked key is gone
84
+ * from the authorized set, so its next handshake fails (Phase-1 acceptance).
85
+ */
86
+ async revoke(publicKey) {
87
+ const had = this.devices.delete(publicKey);
88
+ if (had)
89
+ await this.persist();
90
+ return had;
91
+ }
92
+ /** Update a device's `lastSeenAt` (no-op for unknown keys). */
93
+ async touch(publicKey) {
94
+ const entry = this.devices.get(publicKey);
95
+ if (!entry)
96
+ return;
97
+ entry.lastSeenAt = new Date().toISOString();
98
+ await this.persist();
99
+ }
100
+ persist() {
101
+ const snapshot = JSON.stringify({ devices: [...this.devices.values()] }, null, 2);
102
+ this.writeChain = this.writeChain
103
+ .catch(() => { })
104
+ .then(async () => {
105
+ await mkdir(dirname(DEVICES_FILE), { recursive: true });
106
+ const tmp = `${DEVICES_FILE}.${process.pid}.tmp`;
107
+ await writeFile(tmp, snapshot, "utf8");
108
+ await chmod(tmp, 0o600); // pins authorization — keep private
109
+ await rename(tmp, DEVICES_FILE);
110
+ });
111
+ return this.writeChain;
112
+ }
113
+ }
114
+ //# sourceMappingURL=devices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices.js","sourceRoot":"","sources":["../../src/devices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,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;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,MAAM,YAAY,GAChB,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;AAE5E,4EAA4E;AAC5E,MAAM,aAAa,GAAG,GAAG,CAAC;AAc1B,MAAM,OAAO,WAAW;IACd,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;IAClD,yEAAyE;IACjE,UAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEtD,2EAA2E;IAC3E,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;YAAE,OAAO;QACtC,IAAI,MAAM,GAAoB,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAoB,CAAC;QAC/E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,+CAA+C;QACzD,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;YACrC,sEAAsE;YACtE,uCAAuC;YACvC,IAAI,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC,SAAS,CAAC;gBAAE,SAAS;YAC1D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE;gBAC5B,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE;gBACzE,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG;gBAC3D,UAAU,EAAE,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG;aAClE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,iEAAiE;IACjE,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,uEAAuE;IACvE,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,uDAAuD;IACvD,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,CAAC,SAAiB,EAAE,KAAa;QACxC,IAAI,CAAC,uBAAuB,CAAC,SAAS,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAiB,QAAQ;YAClC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE;YACrD,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;QACrE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,SAAiB;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,GAAG;YAAE,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,+DAA+D;IAC/D,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,KAAK,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;IAEO,OAAO;QACb,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAC7B,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAmB,EACxD,IAAI,EACJ,CAAC,CACF,CAAC;QACF,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,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,MAAM,GAAG,GAAG,GAAG,YAAY,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;YACjD,MAAM,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YACvC,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,oCAAoC;YAC7D,MAAM,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QACL,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF"}
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Envelope construction + the shared ProtocolError.
3
3
  *
4
- * The (de)serialization seam lives in `crypto.ts` (`clearCodec` / `boxCodec` →
5
- * `parseEnvelope`), because whether `payload` is cleartext or a sealed NaCl box
6
- * is a per-socket codec decision. This module only stamps outbound envelopes.
4
+ * The (de)serialization seam lives in `crypto.ts` (`clearCodec` / `sessionCodec`
5
+ * → `parseEnvelope`), because whether `payload` is cleartext or sealed by the
6
+ * session handshake is a per-socket codec decision. This only stamps envelopes.
7
7
  */
8
8
  import { Envelope, MessageType, PayloadFor } from "./protocol.js";
9
9
  /** Build a fully-stamped envelope ready to send. */
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Envelope construction + the shared ProtocolError.
3
3
  *
4
- * The (de)serialization seam lives in `crypto.ts` (`clearCodec` / `boxCodec` →
5
- * `parseEnvelope`), because whether `payload` is cleartext or a sealed NaCl box
6
- * is a per-socket codec decision. This module only stamps outbound envelopes.
4
+ * The (de)serialization seam lives in `crypto.ts` (`clearCodec` / `sessionCodec`
5
+ * → `parseEnvelope`), because whether `payload` is cleartext or sealed by the
6
+ * session handshake is a per-socket codec decision. This only stamps envelopes.
7
7
  */
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { PROTOCOL_VERSION, } from "./protocol.js";
@@ -5,7 +5,7 @@
5
5
  * accepts becomes a `forward.open` here. We dial `127.0.0.1:<port>` on the
6
6
  * daemon box and pipe bytes in both directions as base64 `forward.data` frames,
7
7
  * multiplexed over the one WSS connection by `streamId`. This is a raw TCP
8
- * bridge (not HTTP-aware), so HTTP, WebSocket HMR, and SSE all pass through
8
+ * bridge (not HTTP-aware), so HTTP, SocketLike HMR, and SSE all pass through
9
9
  * unchanged — the dev server sees an ordinary local client.
10
10
  *
11
11
  * Streams are per-socket, like the terminal PTY: the reply bytes go back to the
@@ -16,7 +16,7 @@
16
16
  * only ports the `isOwned` check confirms belong to the named workspace's
17
17
  * worktree (see `ports.ts`). An `open` for any other port is refused immediately.
18
18
  */
19
- import type { WebSocket } from "ws";
19
+ import type { SocketLike } from "./hub.js";
20
20
  import type { Hub } from "./hub.js";
21
21
  export declare class ForwardManager {
22
22
  private hub;
@@ -31,7 +31,7 @@ export declare class ForwardManager {
31
31
  * port against the worktree-owned set, dials loopback, and wires both
32
32
  * directions. Any failure closes the stream back to the client with a reason.
33
33
  */
34
- open(client: WebSocket, ws: string, streamId: string, port: number): void;
34
+ open(client: SocketLike, ws: string, streamId: string, port: number): void;
35
35
  /** Pause a stream's source read until its client-bound WS queue drains. */
36
36
  private pauseUntilDrained;
37
37
  /** A chunk of browser→server bytes for an open stream. */
@@ -39,7 +39,7 @@ export declare class ForwardManager {
39
39
  /** The client closed its end; drop the daemon-side socket (no echo back). */
40
40
  close(streamId: string): void;
41
41
  /** Tear down every stream owned by a socket that just disconnected. */
42
- reapClient(client: WebSocket): void;
42
+ reapClient(client: SocketLike): void;
43
43
  /** Refuse before a socket ever existed: just tell the client it's closed. */
44
44
  private refuse;
45
45
  /**
@@ -5,7 +5,7 @@
5
5
  * accepts becomes a `forward.open` here. We dial `127.0.0.1:<port>` on the
6
6
  * daemon box and pipe bytes in both directions as base64 `forward.data` frames,
7
7
  * multiplexed over the one WSS connection by `streamId`. This is a raw TCP
8
- * bridge (not HTTP-aware), so HTTP, WebSocket HMR, and SSE all pass through
8
+ * bridge (not HTTP-aware), so HTTP, SocketLike HMR, and SSE all pass through
9
9
  * unchanged — the dev server sees an ordinary local client.
10
10
  *
11
11
  * Streams are per-socket, like the terminal PTY: the reply bytes go back to the
@@ -1 +1 @@
1
- {"version":3,"file":"forward.js","sourceRoot":"","sources":["../../src/forward.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"forward.js","sourceRoot":"","sources":["../../src/forward.ts"],"names":[],"mappings":"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"}