@walkhi/code-relax 0.1.0-beta.24 → 0.1.0-beta.25

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.
@@ -56,7 +56,7 @@ const relay = await startSelfRelay({ port, hostKey, stateFile, mysqlConfigFile,
56
56
  usageDiagnosticKeyFile: process.env.RELAY_USAGE_DIAGNOSTIC_KEY_FILE,
57
57
  publicRelayUrl: process.env.RELAY_PUBLIC_URL || serviceEndpoints.relayUrl, appDownloadUrl: process.env.RELAY_APP_DOWNLOAD_URL || serviceEndpoints.installUrl,
58
58
  log: entry => console.log(JSON.stringify({ time: new Date().toISOString(), ...entry })) });
59
- console.log(JSON.stringify({ service: 'codex-self-relay', listen: relay.url, maxConnections: 32, maxRelaySessions,
59
+ console.log(JSON.stringify({ service: 'codex-self-relay', listen: relay.url, maxConnections: 128, maxRelaySessions,
60
60
  maxFrameMiB: 8, encryption: 'TLS must be supplied by reverse proxy; no end-to-end encryption' }));
61
61
  await new Promise(resolve => { process.once('SIGTERM', resolve); process.once('SIGINT', resolve); });
62
62
  await relay.close();
@@ -16,7 +16,7 @@ import { relayDiagnostics } from './diagnostics.mjs';
16
16
 
17
17
  // Always loopback; a TLS reverse proxy supplies the private-trial Internet endpoint.
18
18
  export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlMs = 120000,
19
- maxConnections = 32, maxClientsPerHost = 16, maxPayload = 8 * 1024 * 1024, stateFile,
19
+ maxConnections = 128, maxClientsPerHost = 16, maxPayload = 8 * 1024 * 1024, stateFile,
20
20
  maxRelaySessions = 8, adminStateFile, mysqlConfigFile, accountConfigFile, usageDiagnosticKeyFile,
21
21
  publicRelayUrl = '', appDownloadUrl = '', debugAllowlistFile, log = () => {} } = {}) {
22
22
  if (!validSecret(hostKey)) throw new Error('Invalid host credential');
@@ -46,18 +46,34 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
46
46
  let activeRelaySessions = 0;
47
47
  for (const record of store.entries()) rooms.set(record.id, { ...record, durable: true, connections: new Map(), observers: new Map(), uploadClients: new Map() });
48
48
  let commitTail = Promise.resolve();
49
+ function queueStore(operation) {
50
+ const pending = commitTail.then(operation);
51
+ commitTail = pending.catch(() => {});
52
+ return pending;
53
+ }
49
54
  function commit(room, changes) {
50
- const operation = commitTail.then(async () => {
51
- changes = typeof changes === 'function' ? changes(room) : changes;
52
- const next = { ...room, ...changes, updatedAt: new Date().toISOString() };
53
- if (room.durable) {
54
- const { id, hostHash, clients, revision, status, createdAt, updatedAt, officialComputer } = next;
55
- await store.save({ id, hostHash, clients, revision, status, createdAt, updatedAt, ...(officialComputer ? { officialComputer } : {}) });
56
- }
57
- Object.assign(room, changes, { updatedAt: next.updatedAt });
55
+ return queueStore(async () => {
56
+ changes = typeof changes === 'function' ? changes(room) : changes;
57
+ const next = { ...room, ...changes, updatedAt: new Date().toISOString() };
58
+ if (room.durable) {
59
+ const { id, hostHash, clients, revision, status, createdAt, updatedAt, officialComputer } = next;
60
+ await store.save({ id, hostHash, clients, revision, status, createdAt, updatedAt, ...(officialComputer ? { officialComputer } : {}) });
61
+ }
62
+ Object.assign(room, changes, { updatedAt: next.updatedAt });
63
+ });
64
+ }
65
+ function registerRoom(id, durable, credential) {
66
+ return queueStore(async () => {
67
+ // Resolve concurrent first connections before writing the device identity.
68
+ if (durable && rooms.has(id)) return rooms.get(id);
69
+ const now = new Date().toISOString();
70
+ const record = { id, revision: 0, status: 'active', clients: [], createdAt: now, updatedAt: now,
71
+ ...(durable ? { hostHash: digest(credential) } : {}) };
72
+ if (durable) await store.save(record);
73
+ const room = { ...record, durable, connections: new Map(), observers: new Map(), uploadClients: new Map() };
74
+ rooms.set(id, room);
75
+ return room;
58
76
  });
59
- commitTail = operation.catch(() => {});
60
- return operation;
61
77
  }
62
78
  function invitation(room) {
63
79
  const code = secret(); room.pairingHash = digest(code); room.expires = Date.now() + pairingTtlMs;
@@ -298,7 +314,7 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
298
314
  socket.end('HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n'); return;
299
315
  }
300
316
  if (Date.now() - admissionStart >= 60000) { admissionStart = Date.now(); admissions = 0; }
301
- if (wss.clients.size >= maxConnections || ++admissions > 60) {
317
+ if (wss.clients.size >= maxConnections || ++admissions > 240) {
302
318
  socket.end('HTTP/1.1 429 Too Many Requests\r\nConnection: close\r\n\r\n'); return;
303
319
  }
304
320
  wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws));
@@ -435,18 +451,12 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
435
451
  }
436
452
  catch { return fail(1008, 'Invalid LAN candidates'); }
437
453
  room = durable ? rooms.get(message.deviceId) : undefined;
438
- if (room && (room.status !== 'active' || !matches(message.deviceSecret, room.hostHash))) return fail();
439
454
  if (!room) {
440
- // Durable computers prove future ownership with their generated device secret.
441
- // The legacy deployment key remains accepted, but is no longer required for first registration.
442
455
  if (!durable && !matches(message.key, hostHash)) return fail();
443
- if (rooms.size >= 256) return fail(4429, 'Device limit reached');
444
- const now = new Date().toISOString();
445
- room = { id: durable ? message.deviceId : randomUUID(), durable,
446
- revision: 0, status: 'active', clients: [], connections: new Map(), observers: new Map(), uploadClients: new Map(), createdAt: now, updatedAt: now,
447
- ...(durable ? { hostHash: digest(message.deviceSecret) } : {}) };
448
- await commit(room, {}); rooms.set(room.id, room);
456
+ room = await registerRoom(durable ? message.deviceId : randomUUID(), durable, message.deviceSecret);
457
+ if (ws.readyState !== ws.OPEN) return;
449
458
  }
459
+ if (durable && (room.status !== 'active' || !matches(message.deviceSecret, room.hostHash))) return fail();
450
460
  const oldHost = room.host;
451
461
  room.observers ||= new Map();
452
462
  if (oldHost && oldHost !== ws) {
@@ -490,6 +500,7 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
490
500
  await commit(room, current => ({ clients: [...current.clients.filter(item => item.id !== clientId), client], revision: current.revision + 1 }));
491
501
  room.pairingHash = undefined;
492
502
  } finally { room.pairingInProgress = false; }
503
+ if (!room.host) return fail(4410, 'Computer offline; authorization retained');
493
504
  send(room.host, { type: 'authorization-changed', clients: room.clients, revision: room.revision });
494
505
  } else {
495
506
  clientId = message.clientId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walkhi/code-relax",
3
- "version": "0.1.0-beta.24",
3
+ "version": "0.1.0-beta.25",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "scripts": {