@dsh-remote/relay 3.1.0 → 3.1.2

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 (2) hide show
  1. package/dist/bundle/main.js +57 -16
  2. package/package.json +1 -1
@@ -2279,7 +2279,7 @@ var require_websocket = __commonJS({
2279
2279
  var http = __require("http");
2280
2280
  var net = __require("net");
2281
2281
  var tls = __require("tls");
2282
- var { randomBytes: randomBytes2, createHash } = __require("crypto");
2282
+ var { randomBytes: randomBytes3, createHash } = __require("crypto");
2283
2283
  var { Duplex, Readable } = __require("stream");
2284
2284
  var { URL: URL2 } = __require("url");
2285
2285
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -2830,7 +2830,7 @@ var require_websocket = __commonJS({
2830
2830
  }
2831
2831
  }
2832
2832
  const defaultPort = isSecure ? 443 : 80;
2833
- const key = randomBytes2(16).toString("base64");
2833
+ const key = randomBytes3(16).toString("base64");
2834
2834
  const request = isSecure ? https.request : http.request;
2835
2835
  const protocolSet = /* @__PURE__ */ new Set();
2836
2836
  let perMessageDeflate;
@@ -23394,7 +23394,7 @@ function date4(params) {
23394
23394
  return _coercedDate(ZodDate, params);
23395
23395
  }
23396
23396
 
23397
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/wire/limits.js
23397
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/wire/limits.js
23398
23398
  var MAX_RELAY_MESSAGE_BYTES = 1024 * 1024;
23399
23399
  var MAX_CIPHERTEXT_BYTES = MAX_RELAY_MESSAGE_BYTES - 8 * 1024;
23400
23400
  var MAX_IMAGE_ATTACHMENTS = 4;
@@ -23403,7 +23403,7 @@ var MAX_ATTACHMENT_BYTES = 512 * 1024;
23403
23403
  var MAX_ATTACHMENT_TOTAL_BYTES = 512 * 1024;
23404
23404
  var IDEMPOTENCY_WINDOW_MS = 5 * 60 * 1e3;
23405
23405
 
23406
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/wire/frames.js
23406
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/wire/frames.js
23407
23407
  var PROTOCOL_VERSION = 1;
23408
23408
  var MIN_SUPPORTED_PROTOCOL = 1;
23409
23409
  var PAIRING_TOKEN_RE = /^\d{6}$/;
@@ -23855,7 +23855,7 @@ function loadConfig(env, version2) {
23855
23855
 
23856
23856
  // src/server.ts
23857
23857
  import { createServer } from "node:http";
23858
- import { randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
23858
+ import { randomBytes as randomBytes2, randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
23859
23859
 
23860
23860
  // node_modules/.pnpm/ws@8.22.0/node_modules/ws/wrapper.mjs
23861
23861
  var import_stream = __toESM(require_stream(), 1);
@@ -23867,7 +23867,7 @@ var import_subprotocol = __toESM(require_subprotocol(), 1);
23867
23867
  var import_websocket = __toESM(require_websocket(), 1);
23868
23868
  var import_websocket_server = __toESM(require_websocket_server(), 1);
23869
23869
 
23870
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/identity/ids.js
23870
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/identity/ids.js
23871
23871
  import { randomBytes, randomUUID } from "node:crypto";
23872
23872
  var CONVERSATION_ID_PREFIX = "c_";
23873
23873
  function newConversationId() {
@@ -23877,10 +23877,10 @@ function newHostId() {
23877
23877
  return randomBytes(4).toString("hex");
23878
23878
  }
23879
23879
 
23880
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/platform/sleep.js
23880
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/platform/sleep.js
23881
23881
  var SLEEP_BACKENDS = ["caffeinate", "systemd-inhibit", "unsupported"];
23882
23882
 
23883
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/wire/payloads.js
23883
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/wire/payloads.js
23884
23884
  var SESSION_STATES = [
23885
23885
  "idle",
23886
23886
  "running",
@@ -24291,7 +24291,7 @@ var PAYLOAD_TYPES = {
24291
24291
  evSessionHistory: "ev.session_history"
24292
24292
  };
24293
24293
 
24294
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/wire/registry.js
24294
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/wire/registry.js
24295
24295
  var ENDPOINT_FRAME_NAMES = [
24296
24296
  "hello",
24297
24297
  "pair-begin",
@@ -24383,7 +24383,7 @@ var CAPABILITY_IDS = [
24383
24383
  ];
24384
24384
  var CAPABILITY_SET = new Set(CAPABILITY_IDS);
24385
24385
 
24386
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/wire/negotiate.js
24386
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/wire/negotiate.js
24387
24387
  function negotiateProtocol(peerProtocol, own2 = PROTOCOL_VERSION, min = MIN_SUPPORTED_PROTOCOL) {
24388
24388
  if (peerProtocol === void 0 || peerProtocol === null) {
24389
24389
  return verdictFor(1, own2, min, true);
@@ -24423,13 +24423,13 @@ function verdictFor(peer, own2, min, missing) {
24423
24423
  return { ok: true, version: peer, missing };
24424
24424
  }
24425
24425
 
24426
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/wire/errors.js
24426
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/wire/errors.js
24427
24427
  var WAIT_HINTABLE = /* @__PURE__ */ new Set(["rate_limited", "pair_table_full"]);
24428
24428
  function wantsRetryAfter(code) {
24429
24429
  return typeof code === "string" && WAIT_HINTABLE.has(code);
24430
24430
  }
24431
24431
 
24432
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/wire/outbound.js
24432
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/wire/outbound.js
24433
24433
  function helloOkForClient(clientId, protocol) {
24434
24434
  return { t: "hello-ok", role: "client", clientId, ...protocol === void 0 ? {} : { protocol } };
24435
24435
  }
@@ -24507,7 +24507,7 @@ function encBatchToRelay(sessionId, items) {
24507
24507
  return { t: "enc-batch", sessionId, items: items.map((item) => ({ ...item })) };
24508
24508
  }
24509
24509
 
24510
- // node_modules/.pnpm/@dsh-remote+protocol@3.0.4/node_modules/@dsh-remote/protocol/dist/src/wire/classify.js
24510
+ // node_modules/.pnpm/@dsh-remote+protocol@3.0.6/node_modules/@dsh-remote/protocol/dist/src/wire/classify.js
24511
24511
  function frameTypeOf(value) {
24512
24512
  if (typeof value !== "object" || value === null || Array.isArray(value))
24513
24513
  return void 0;
@@ -25147,6 +25147,8 @@ function tokenMatches(expected, got) {
25147
25147
  if (a.length !== b.length) return false;
25148
25148
  return timingSafeEqual(a, b);
25149
25149
  }
25150
+ var CLAIM_MIN_INTERVAL_MS = 4e3;
25151
+ var CLAIM_TRACK_MAX = 1e4;
25150
25152
  function createRelay(config2) {
25151
25153
  const log = new Log(config2.logLevel);
25152
25154
  const state = new RelayState({ maxPendingPairs: config2.maxPendingPairs });
@@ -25155,10 +25157,24 @@ function createRelay(config2) {
25155
25157
  const pairStatusBudget = new Budget(config2.pairStatusBudgetPerSec);
25156
25158
  const peers = /* @__PURE__ */ new WeakMap();
25157
25159
  let shuttingDown = false;
25160
+ let currentHostToken = config2.hostToken;
25161
+ const lastClaimAt = /* @__PURE__ */ new Map();
25158
25162
  let sweepTimer;
25159
25163
  let pingTimer;
25160
25164
  let lastPingAt = 0;
25161
25165
  let lastCountersAt = 0;
25166
+ function rotateHostToken() {
25167
+ currentHostToken = randomBytes2(24).toString("hex");
25168
+ if (lastClaimAt.size >= CLAIM_TRACK_MAX) {
25169
+ const drop = lastClaimAt.size - CLAIM_TRACK_MAX + 1;
25170
+ let i = 0;
25171
+ for (const key of lastClaimAt.keys()) {
25172
+ lastClaimAt.delete(key);
25173
+ if (++i >= drop) break;
25174
+ }
25175
+ }
25176
+ return currentHostToken;
25177
+ }
25162
25178
  const persistence = { enabled: config2.stateFile !== "", restored: 0, savedAt: 0, writes: 0, failures: 0 };
25163
25179
  if (persistence.enabled) {
25164
25180
  persistence.restored = restoreState(config2.stateFile, state, log, startedAt);
@@ -25284,12 +25300,12 @@ function createRelay(config2) {
25284
25300
  }
25285
25301
  peer.protocol = frame.protocol;
25286
25302
  if (frame.role === "host") {
25287
- if (!config2.hostToken) {
25303
+ if (!currentHostToken) {
25288
25304
  sendError(peer, "internal", "relay has no host token configured");
25289
25305
  peer.ws.close(1011);
25290
25306
  return;
25291
25307
  }
25292
- if (!frame.token || !tokenMatches(config2.hostToken, frame.token)) {
25308
+ if (!frame.token || !tokenMatches(currentHostToken, frame.token)) {
25293
25309
  peer.authAttempts += 1;
25294
25310
  log.warn("host auth failed", { attempts: peer.authAttempts });
25295
25311
  sendError(peer, "bad_token");
@@ -25765,6 +25781,31 @@ function createRelay(config2) {
25765
25781
  res.end(JSON.stringify(health()));
25766
25782
  return;
25767
25783
  }
25784
+ if (url2.pathname === "/api/claim-token") {
25785
+ if (req.method !== "POST") {
25786
+ res.statusCode = 405;
25787
+ res.end(JSON.stringify({ error: "use POST" }));
25788
+ return;
25789
+ }
25790
+ if (shuttingDown) {
25791
+ res.statusCode = 503;
25792
+ res.end(JSON.stringify({ error: "shutting_down" }));
25793
+ return;
25794
+ }
25795
+ const now = Date.now();
25796
+ const ip = req.socket.remoteAddress ?? "unknown";
25797
+ const last = lastClaimAt.get(ip) ?? 0;
25798
+ if (now - last < CLAIM_MIN_INTERVAL_MS) {
25799
+ res.statusCode = 429;
25800
+ res.end(JSON.stringify({ error: "too_soon", retryAfterMs: CLAIM_MIN_INTERVAL_MS - (now - last) }));
25801
+ return;
25802
+ }
25803
+ lastClaimAt.set(ip, now);
25804
+ const token = rotateHostToken();
25805
+ log.info("host token claimed", { ip });
25806
+ res.end(JSON.stringify({ token, rotatedAt: new Date(now).toISOString() }));
25807
+ return;
25808
+ }
25768
25809
  if (url2.pathname === "/api/info") {
25769
25810
  res.end(JSON.stringify({ publicUrl: config2.publicUrl, protocol: PROTOCOL_VERSION }));
25770
25811
  return;
@@ -25951,7 +25992,7 @@ function registerSignalHandlers(shutdown) {
25951
25992
 
25952
25993
  // src/main.ts
25953
25994
  function readVersion() {
25954
- if ("3.1.0") return "3.1.0";
25995
+ if ("3.1.2") return "3.1.2";
25955
25996
  try {
25956
25997
  const pkg = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf8"));
25957
25998
  if (typeof pkg.version === "string") return pkg.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-remote/relay",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "DSH Remote Control 的零知识 WebSocket 中继:只做路由,不碰载荷明文。可自托管的单文件产物。",
5
5
  "type": "module",
6
6
  "license": "MIT",