@seedprotocol/gateway-hyper 0.7.12 → 0.7.13-next.1527.g318ec68

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.
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { F as e, _ as t, c as n, g as r, m as i, p as a, r as o, s, v as c } from "./src-C_1bcYLA.js";
2
+ import { F as e, _ as t, c as n, g as r, m as i, p as a, r as o, s, v as c } from "./src-CJc2Ap3J.js";
3
3
  import l from "node:path";
4
4
  import u from "node:process";
5
5
  import { parseArgs as d } from "node:util";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { l as DEFAULT_CONNECT_TIMEOUT_MS, o as DEFAULT_MAX_RAW_CONNECTIONS, u as DEFAULT_MAX_STREAMS, p as DEFAULT_RAW_OPERATOR_KEY_FILE, s as DEFAULT_RAW_PORT, d as DEFAULT_REQUEST_TIMEOUT_MS, t as DEFAULT_SEED_GATEWAY_HYPER_KEY, y as DEFAULT_UPSTREAM_TIMEOUT_MS, w as MAX_FRAME_BYTES, h as RAW_CLOSE_GRACE_MS, x as TunnelConnectTimeoutError, S as TunnelTimeoutError, C as UpstreamTimeoutError, c as connectRawTunnel, r as connectTunnel, v as createAllowlist, n as createGatewayProxy, f as createTunnelSession, N as decodePublicKey, P as encodePublicKeyZ32, T as filterResponseHeaders, E as flattenRequestHeaders, _ as formatTunnelEvent, F as loadOrCreateOperatorKeypair, i as localGatewayUrl, D as readFrame, O as readTunnelRequest, k as readTunnelResponse, m as serveRawTunnel, g as serveTunnel, a as stripMountPath, b as upstreamRequestHeaders, A as writeFrame, j as writeTunnelRequest, M as writeTunnelResponse } from "./src-C_1bcYLA.js";
1
+ export { l as DEFAULT_CONNECT_TIMEOUT_MS, o as DEFAULT_MAX_RAW_CONNECTIONS, u as DEFAULT_MAX_STREAMS, p as DEFAULT_RAW_OPERATOR_KEY_FILE, s as DEFAULT_RAW_PORT, d as DEFAULT_REQUEST_TIMEOUT_MS, t as DEFAULT_SEED_GATEWAY_HYPER_KEY, y as DEFAULT_UPSTREAM_TIMEOUT_MS, w as MAX_FRAME_BYTES, h as RAW_CLOSE_GRACE_MS, x as TunnelConnectTimeoutError, S as TunnelTimeoutError, C as UpstreamTimeoutError, c as connectRawTunnel, r as connectTunnel, v as createAllowlist, n as createGatewayProxy, f as createTunnelSession, N as decodePublicKey, P as encodePublicKeyZ32, T as filterResponseHeaders, E as flattenRequestHeaders, _ as formatTunnelEvent, F as loadOrCreateOperatorKeypair, i as localGatewayUrl, D as readFrame, O as readTunnelRequest, k as readTunnelResponse, m as serveRawTunnel, g as serveTunnel, a as stripMountPath, b as upstreamRequestHeaders, A as writeFrame, j as writeTunnelRequest, M as writeTunnelResponse } from "./src-CJc2Ap3J.js";
@@ -233,8 +233,8 @@ var j = class extends Error {
233
233
  constructor(e) {
234
234
  super(`No response from upstream for ${e} ms`), this.timeoutMs = e, this.name = "UpstreamTimeoutError";
235
235
  }
236
- }, P = 13e4;
237
- function re(e, t) {
236
+ }, re = 13e4;
237
+ function ie(e, t) {
238
238
  if (!t) return e;
239
239
  let n = new Set(Object.keys(t).map((e) => e.toLowerCase())), r = {};
240
240
  for (let [t, i] of Object.entries(e)) n.has(t.toLowerCase()) || (r[t] = i);
@@ -243,7 +243,7 @@ function re(e, t) {
243
243
  ...t
244
244
  };
245
245
  }
246
- var ie = /* @__PURE__ */ new Set([
246
+ var ae = /* @__PURE__ */ new Set([
247
247
  "connection",
248
248
  "keep-alive",
249
249
  "proxy-connection",
@@ -252,21 +252,21 @@ var ie = /* @__PURE__ */ new Set([
252
252
  "transfer-encoding",
253
253
  "upgrade"
254
254
  ]);
255
- function F(e) {
255
+ function P(e) {
256
256
  let t = new Set((Object.entries(e).find(([e]) => e.toLowerCase() === "connection")?.[1] ?? "").split(",").map((e) => e.trim().toLowerCase()).filter(Boolean)), n = {}, r;
257
257
  for (let [i, a] of Object.entries(e)) {
258
258
  let e = i.toLowerCase();
259
- e === "host" ? r = a : e === "content-length" || ie.has(e) || t.has(e) || (n[i] = a);
259
+ e === "host" ? r = a : e === "content-length" || ae.has(e) || t.has(e) || (n[i] = a);
260
260
  }
261
261
  return r && !Object.keys(n).some((e) => e.toLowerCase() === "x-forwarded-host") && (n["x-forwarded-host"] = r), n;
262
262
  }
263
- async function ae(e, t, n, r = {}) {
263
+ async function oe(e, t, n, r = {}) {
264
264
  let o = e.replace(/\/$/, ""), s = new a(t.path, `${o}/`), c = r.timeoutMs ?? 13e4, { observer: l } = r;
265
265
  return new Promise((e, a) => {
266
266
  l?.onPhase?.("send");
267
267
  let o = i.request(s, {
268
268
  method: t.method,
269
- headers: re(F(t.headers), r.extraHeaders),
269
+ headers: ie(P(t.headers), r.extraHeaders),
270
270
  signal: r.signal
271
271
  }, (t) => {
272
272
  l && (l.onPhase?.("body"), l.onResponse?.(t.statusCode ?? 502));
@@ -286,13 +286,13 @@ async function ae(e, t, n, r = {}) {
286
286
  }
287
287
  //#endregion
288
288
  //#region src/tunnel/allowlist.ts
289
- function I(e) {
289
+ function F(e) {
290
290
  return o.isIPv4(e) ? "ipv4" : o.isIPv6(e) ? "ipv6" : null;
291
291
  }
292
- function oe(e) {
292
+ function se(e) {
293
293
  let t = new o.BlockList();
294
294
  for (let n of e) {
295
- let [e = "", r, i] = n.split("/"), a = I(e);
295
+ let [e = "", r, i] = n.split("/"), a = F(e);
296
296
  if (!a || i !== void 0) throw Error(`Invalid allowlist IP (expected an address or CIDR): ${n}`);
297
297
  if (r === void 0) {
298
298
  t.addAddress(e, a);
@@ -304,23 +304,23 @@ function oe(e) {
304
304
  }
305
305
  return t;
306
306
  }
307
- function se(e) {
307
+ function ce(e) {
308
308
  let t = /* @__PURE__ */ new Set();
309
309
  for (let n of e) t.add(u(n).toString("hex"));
310
310
  return t;
311
311
  }
312
- function ce(e) {
312
+ function le(e) {
313
313
  if (!e) return null;
314
314
  let t = e.toLowerCase().startsWith("::ffff:") ? e.slice(7) : null;
315
315
  return t && o.isIPv4(t) ? t : e;
316
316
  }
317
- function L(e) {
317
+ function I(e) {
318
318
  return (e ?? []).map((e) => e.trim()).filter(Boolean);
319
319
  }
320
- function R(e = {}) {
321
- let t = L(e.keys), n = L(e.ips), r = se(t), i = oe(n), a = r.size > 0 || n.length > 0, o = (e) => e != null && r.has(Buffer.from(e).toString("hex")), s = (e) => {
320
+ function L(e = {}) {
321
+ let t = I(e.keys), n = I(e.ips), r = ce(t), i = se(n), a = r.size > 0 || n.length > 0, o = (e) => e != null && r.has(Buffer.from(e).toString("hex")), s = (e) => {
322
322
  if (!e) return !1;
323
- let t = I(e);
323
+ let t = F(e);
324
324
  return t !== null && i.check(e, t);
325
325
  };
326
326
  return {
@@ -328,7 +328,7 @@ function R(e = {}) {
328
328
  summary: a ? `keys=${r.size} ips=${n.join(",") || "none"}` : "off",
329
329
  firewall: (e) => !a || n.length > 0 ? !1 : !o(e),
330
330
  check: (e) => {
331
- let t = ce(e.rawStream?.remoteHost);
331
+ let t = le(e.rawStream?.remoteHost);
332
332
  return a ? o(e.remotePublicKey) ? {
333
333
  ok: !0,
334
334
  host: t,
@@ -351,28 +351,28 @@ function R(e = {}) {
351
351
  }
352
352
  //#endregion
353
353
  //#region src/tunnel/events.ts
354
- function z(e) {
354
+ function R(e) {
355
355
  if (e) return (t) => {
356
356
  try {
357
357
  e(t);
358
358
  } catch {}
359
359
  };
360
360
  }
361
- function B(e) {
361
+ function z(e) {
362
362
  let t = e instanceof Error ? e.message : String(e), n = e?.code;
363
363
  return typeof n == "string" ? {
364
364
  error: t,
365
365
  code: n
366
366
  } : { error: t };
367
367
  }
368
- function V() {
368
+ function B() {
369
369
  let e = Date.now().toString(36), t = 0, n = 0;
370
370
  return {
371
371
  stream: () => `s${++t}`,
372
372
  request: () => `${e}-r${++n}`
373
373
  };
374
374
  }
375
- function le(e, t = /* @__PURE__ */ new Date()) {
375
+ function ue(e, t = /* @__PURE__ */ new Date()) {
376
376
  let n = [
377
377
  t.toISOString(),
378
378
  "tunnel-trace",
@@ -387,15 +387,15 @@ function le(e, t = /* @__PURE__ */ new Date()) {
387
387
  }
388
388
  //#endregion
389
389
  //#region src/tunnel/serve.ts
390
- function H(e, t) {
390
+ function V(e, t) {
391
391
  return {
392
392
  status: e,
393
393
  headers: { "content-type": "text/plain; charset=utf-8" },
394
394
  body: Buffer.from(t, "utf8")
395
395
  };
396
396
  }
397
- async function ue(e, t, n = {}) {
398
- let r = z(n.onEvent), { requestIdHeader: i } = n, a = n.streamId ?? "s0", o = r || i ? n.nextRequestId ?? V().request : null, s = new AbortController(), c = () => s.abort();
397
+ async function de(e, t, n = {}) {
398
+ let r = R(n.onEvent), { requestIdHeader: i } = n, a = n.streamId ?? "s0", o = r || i ? n.nextRequestId ?? B().request : null, s = new AbortController(), c = () => s.abort();
399
399
  e.once("end", c), e.once("close", c), e.on("error", c);
400
400
  let l = performance.now(), u = 0, d = 0, f = null, p = null;
401
401
  r && (e.on("data", (e) => {
@@ -403,7 +403,7 @@ async function ue(e, t, n = {}) {
403
403
  }), e.on("error", (e) => r({
404
404
  name: "stream.error",
405
405
  stream: a,
406
- ...B(e)
406
+ ...z(e)
407
407
  })), e.once("close", () => r({
408
408
  name: "stream.close",
409
409
  stream: a,
@@ -429,7 +429,7 @@ async function ue(e, t, n = {}) {
429
429
  }), g = performance.now());
430
430
  let v = () => Math.round(performance.now() - g), y;
431
431
  try {
432
- y = await ae(t, c, l, {
432
+ y = await oe(t, c, l, {
433
433
  timeoutMs: n.upstreamTimeoutMs,
434
434
  signal: s.signal,
435
435
  extraHeaders: i && d ? { [i]: d } : void 0,
@@ -462,11 +462,11 @@ async function ue(e, t, n = {}) {
462
462
  name: "up.error",
463
463
  request: d,
464
464
  phase: _,
465
- ...B(e),
465
+ ...z(e),
466
466
  aborted: s.signal.aborted
467
467
  }), s.signal.aborted) return;
468
468
  let t = e instanceof Error ? e.message : String(e);
469
- y = e instanceof N ? H(504, `Gateway Hyper upstream timeout: ${t}`) : H(502, `Gateway Hyper upstream error: ${t}`);
469
+ y = e instanceof N ? V(504, `Gateway Hyper upstream timeout: ${t}`) : V(502, `Gateway Hyper upstream error: ${t}`);
470
470
  }
471
471
  await D(e, {
472
472
  status: y.status,
@@ -483,10 +483,10 @@ async function ue(e, t, n = {}) {
483
483
  e.destroy();
484
484
  }
485
485
  }
486
- async function de(e) {
486
+ async function fe(e) {
487
487
  let t = e.upstream.replace(/\/$/, "");
488
488
  if (/^https:\/\//i.test(t)) throw Error(`serveTunnel speaks plain HTTP to its upstream; got ${t}. Point it at an http:// origin, or use serveRawTunnel (seed-gateway serve --raw) to carry TLS end to end.`);
489
- let r = R(e.allowlist), i = m(e, ".seed/gateway-tunnel/operator.key.json"), a = z(e.onEvent), o = V(), s = new n(e.bootstrap ? { bootstrap: e.bootstrap } : {}), c = s.createServer({ firewall: (e) => {
489
+ let r = L(e.allowlist), i = m(e, ".seed/gateway-tunnel/operator.key.json"), a = R(e.onEvent), o = B(), s = new n(e.bootstrap ? { bootstrap: e.bootstrap } : {}), c = s.createServer({ firewall: (e) => {
490
490
  let t = r.firewall(e);
491
491
  return t && a && a({
492
492
  name: "stream.refused",
@@ -503,10 +503,13 @@ async function de(e) {
503
503
  stage: "connection",
504
504
  host: i.host
505
505
  }), n.on("error", () => {});
506
- let e = H(403, `Gateway Hyper: this operator's allowlist doesn't include this client (address ${i.host ?? "unknown"}).`);
506
+ let e = V(403, `Gateway Hyper: this operator's allowlist doesn't include this client (address ${i.host ?? "unknown"}).`);
507
507
  D(n, {
508
508
  status: e.status,
509
- headers: e.headers
509
+ headers: {
510
+ ...e.headers,
511
+ connection: "close"
512
+ }
510
513
  }, e.body).then(() => n.end()).catch(() => n.destroy());
511
514
  return;
512
515
  }
@@ -517,7 +520,7 @@ async function de(e) {
517
520
  remoteKey: l(n.remotePublicKey),
518
521
  host: i.host,
519
522
  by: i.by
520
- }), ue(n, t, {
523
+ }), de(n, t, {
521
524
  upstreamTimeoutMs: e.upstreamTimeoutMs,
522
525
  onEvent: e.onEvent,
523
526
  requestIdHeader: e.requestIdHeader,
@@ -535,19 +538,19 @@ async function de(e) {
535
538
  }
536
539
  //#endregion
537
540
  //#region src/tunnel/rawPipe.ts
538
- var fe = 3e4;
539
- function U(e, t) {
541
+ var pe = 3e4;
542
+ function H(e, t) {
540
543
  let n = t?.code, r = t instanceof Error ? t.message : String(t);
541
544
  return `${e}:${typeof n == "string" ? n : r}`;
542
545
  }
543
- function W(e, t, n = {}) {
546
+ function U(e, t, n = {}) {
544
547
  let r = n.graceMs ?? 3e4, i = performance.now(), a = null, o = 0;
545
548
  e.on("data", (n) => {
546
549
  t.write(n) || e.pause();
547
550
  }), t.on("drain", () => e.resume()), e.on("end", () => t.end()), t.pipe(e), e.on("error", (e) => {
548
- a ??= U("hyper", e);
551
+ a ??= H("hyper", e);
549
552
  }), t.on("error", (e) => {
550
- a ??= U("tcp", e);
553
+ a ??= H("tcp", e);
551
554
  });
552
555
  let s = (e) => () => {
553
556
  e.destroyed || (a ? e.destroy() : (e.end(), setTimeout(() => e.destroy(), r).unref()));
@@ -565,8 +568,8 @@ function W(e, t, n = {}) {
565
568
  }
566
569
  //#endregion
567
570
  //#region src/tunnel/rawServe.ts
568
- var G = ".seed/gateway-tunnel/raw-operator.key.json";
569
- function pe(e) {
571
+ var W = ".seed/gateway-tunnel/raw-operator.key.json";
572
+ function me(e) {
570
573
  let t = e.trim(), n = new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(t) ? t : `tcp://${t}`), r = n.protocol === "http:" ? 80 : n.protocol === "https:" ? 443 : NaN, i = n.port ? Number(n.port) : r, a = n.hostname.replace(/^\[(.*)\]$/, "$1");
571
574
  if (!a || !Number.isInteger(i)) throw Error(`Invalid raw upstream (expected http://host:port or host:port): ${e}`);
572
575
  return {
@@ -574,8 +577,8 @@ function pe(e) {
574
577
  port: i
575
578
  };
576
579
  }
577
- async function me(e) {
578
- let t = pe(e.upstream), r = R(e.allowlist), i = m(e, G), a = z(e.onEvent), s = /* @__PURE__ */ new Set(), c = 0, u = new n(e.bootstrap ? { bootstrap: e.bootstrap } : {}), d = u.createServer({ firewall: (e) => {
580
+ async function he(e) {
581
+ let t = me(e.upstream), r = L(e.allowlist), i = m(e, W), a = R(e.onEvent), s = /* @__PURE__ */ new Set(), c = 0, u = new n(e.bootstrap ? { bootstrap: e.bootstrap } : {}), d = u.createServer({ firewall: (e) => {
579
582
  let t = r.firewall(e);
580
583
  return t && a && a({
581
584
  name: "stream.refused",
@@ -607,7 +610,7 @@ async function me(e) {
607
610
  port: t.port,
608
611
  allowHalfOpen: !0
609
612
  });
610
- d.setNoDelay(!0), s.add(n), s.add(d), W(n, d, {
613
+ d.setNoDelay(!0), s.add(n), s.add(d), U(n, d, {
611
614
  graceMs: e.closeGraceMs,
612
615
  onClose: (e) => {
613
616
  s.delete(n), s.delete(d), a?.({
@@ -632,7 +635,7 @@ async function me(e) {
632
635
  }
633
636
  //#endregion
634
637
  //#region src/tunnel/session.ts
635
- var K = 15e4, he = 3e4, ge = 4;
638
+ var G = 15e4, K = 3e4, ge = 4;
636
639
  function q(e) {
637
640
  let t = e.reason;
638
641
  if (t instanceof Error) return t;
@@ -649,7 +652,7 @@ function _e(e, t) {
649
652
  });
650
653
  });
651
654
  }
652
- function ve(e, t = K) {
655
+ function ve(e, t = G) {
653
656
  let n = async (n, r) => {
654
657
  let { signal: i } = r, a = r.timeoutMs ?? t;
655
658
  if (i?.aborted) throw q(i);
@@ -677,7 +680,7 @@ function J() {
677
680
  return /* @__PURE__ */ Error("Tunnel session is closed");
678
681
  }
679
682
  function ye(e, t = {}) {
680
- let n = Math.max(1, Math.floor(t.maxStreams ?? 4)), r = t.requestTimeoutMs ?? 15e4, i = [], a = [], o = !1, s = () => {
683
+ let n = Math.max(1, Math.floor(t.maxStreams ?? 4)), r = t.requestTimeoutMs ?? 15e4, i = [], a = [], o = !1, s = (e) => e !== null && !e.destroyed && !e.readableEnded, c = () => {
681
684
  let t = {
682
685
  run: null,
683
686
  socket: null,
@@ -685,19 +688,19 @@ function ye(e, t = {}) {
685
688
  };
686
689
  return t.run = ve(async () => {
687
690
  if (o) throw J();
688
- if (t.socket && !t.socket.destroyed) return t.socket;
691
+ if (s(t.socket)) return t.socket;
689
692
  let n = await e();
690
693
  if (o) throw n.destroy(), J();
691
694
  return t.socket = n, n;
692
695
  }, r), i.push(t), t;
693
- }, c = () => {
694
- let e = i.filter((e) => !e.busy), t = e.find((e) => e.socket !== null && !e.socket.destroyed) ?? e[0] ?? (i.length < n ? s() : void 0);
696
+ }, l = () => {
697
+ let e = i.filter((e) => !e.busy), t = e.find((e) => s(e.socket)) ?? e[0] ?? (i.length < n ? c() : void 0);
695
698
  return t ? (t.busy = !0, t) : null;
696
- }, l = (e) => {
699
+ }, u = (e) => {
697
700
  let t = a.shift();
698
701
  t ? t.resolve(e) : e.busy = !1;
699
- }, u = (e) => {
700
- let t = c();
702
+ }, d = (e) => {
703
+ let t = l();
701
704
  return t ? Promise.resolve(t) : new Promise((t, n) => {
702
705
  let r = () => {
703
706
  let t = a.indexOf(i);
@@ -717,11 +720,11 @@ function ye(e, t = {}) {
717
720
  runOnTunnel: async (e, t = {}) => {
718
721
  if (t.signal?.aborted) throw q(t.signal);
719
722
  if (o) throw J();
720
- let n = await u(t.signal);
723
+ let n = await d(t.signal);
721
724
  try {
722
725
  return await n.run(e, t);
723
726
  } finally {
724
- l(n);
727
+ u(n);
725
728
  }
726
729
  },
727
730
  close: () => {
@@ -731,6 +734,9 @@ function ye(e, t = {}) {
731
734
  }
732
735
  };
733
736
  }
737
+ function be(e) {
738
+ return Object.entries(e.headers).some(([e, t]) => e.toLowerCase() === "connection" && /(^|,)\s*close\s*(,|$)/i.test(String(t)));
739
+ }
734
740
  async function Y(e) {
735
741
  let t = u(e.key), r = p(e), i = e.connectTimeoutMs ?? 3e4, a = new n(e.bootstrap ? { bootstrap: e.bootstrap } : {}), o = ye(() => new Promise((e, n) => {
736
742
  let o = a.connect(t, { keyPair: {
@@ -741,7 +747,7 @@ async function Y(e) {
741
747
  }, u = () => {
742
748
  s || (s = !0, c && clearTimeout(c), e(o));
743
749
  };
744
- o.on("error", (e) => l(e)), o.once("close", () => l(/* @__PURE__ */ Error("Tunnel connection closed before it opened"))), o.once("open", u), o.once("connect", u);
750
+ o.on("error", (e) => l(e)), o.once("close", () => l(/* @__PURE__ */ Error("Tunnel connection closed before it opened"))), o.once("end", () => o.destroy()), o.once("open", u), o.once("connect", u);
745
751
  }), {
746
752
  maxStreams: e.maxStreams,
747
753
  requestTimeoutMs: e.requestTimeoutMs
@@ -750,7 +756,11 @@ async function Y(e) {
750
756
  key: e.key,
751
757
  clientKey: r.z32,
752
758
  runOnTunnel: s,
753
- forwardHttp: async (e, t, n) => s(async (n) => (await T(n, e, t), O(n)), n),
759
+ forwardHttp: async (e, t, n) => s(async (n) => {
760
+ await T(n, e, t);
761
+ let r = await O(n);
762
+ return be(r.meta) && n.destroy(), r;
763
+ }, n),
754
764
  close: async () => {
755
765
  o.close(), await a.destroy();
756
766
  }
@@ -758,9 +768,9 @@ async function Y(e) {
758
768
  }
759
769
  //#endregion
760
770
  //#region src/tunnel/rawConnect.ts
761
- var be = 1985, xe = 64;
762
- async function Se(e) {
763
- let t = u(e.key), r = p(e), i = e.host ?? "127.0.0.1", a = e.maxConnections ?? 64, s = e.connectTimeoutMs ?? 3e4, c = z(e.onEvent), l = /* @__PURE__ */ new Set(), d = 0, f = new n(e.bootstrap ? { bootstrap: e.bootstrap } : {}), m = o.createServer({ allowHalfOpen: !0 }, (n) => {
771
+ var xe = 1985, Se = 64;
772
+ async function Ce(e) {
773
+ let t = u(e.key), r = p(e), i = e.host ?? "127.0.0.1", a = e.maxConnections ?? 64, s = e.connectTimeoutMs ?? 3e4, c = R(e.onEvent), l = /* @__PURE__ */ new Set(), d = 0, f = new n(e.bootstrap ? { bootstrap: e.bootstrap } : {}), m = o.createServer({ allowHalfOpen: !0 }, (n) => {
764
774
  if (l.size >= a) {
765
775
  c?.({
766
776
  name: "conn.refused",
@@ -774,7 +784,7 @@ async function Se(e) {
774
784
  let u = f.connect(t, { keyPair: {
775
785
  publicKey: r.publicKey,
776
786
  secretKey: r.secretKey
777
- } }), p = null, m = W(u, n, {
787
+ } }), p = null, m = U(u, n, {
778
788
  graceMs: e.closeGraceMs,
779
789
  onClose: (e) => {
780
790
  p && clearTimeout(p), l.delete(n), c?.({
@@ -825,7 +835,7 @@ function X(e, t) {
825
835
  }
826
836
  //#endregion
827
837
  //#region src/tunnel/forward.ts
828
- async function Ce(e) {
838
+ async function we(e) {
829
839
  let t = [];
830
840
  for await (let n of e) t.push(Buffer.isBuffer(n) ? n : Buffer.from(n));
831
841
  return Buffer.concat(t);
@@ -842,12 +852,12 @@ async function $(e, t, n, r) {
842
852
  };
843
853
  n.once("close", a);
844
854
  try {
845
- let a = await Ce(t), o = X(t.url ?? "/", r), s = {
855
+ let a = await we(t), o = X(t.url ?? "/", r), s = {
846
856
  method: t.method ?? "GET",
847
857
  path: o,
848
858
  headers: k(t.headers)
849
859
  }, { meta: c, body: l } = await e.forwardHttp(s, a, { signal: i.signal });
850
- we(n, c, l);
860
+ Te(n, c, l);
851
861
  } catch (e) {
852
862
  if (i.signal.aborted) return;
853
863
  n.headersSent || n.writeHead(Z(e), { "Content-Type": "text/plain; charset=utf-8" }), n.end(Q(e));
@@ -855,10 +865,10 @@ async function $(e, t, n, r) {
855
865
  n.off("close", a);
856
866
  }
857
867
  }
858
- function we(e, t, n) {
868
+ function Te(e, t, n) {
859
869
  e.writeHead(t.status, A(t.headers)), e.end(n);
860
870
  }
861
- async function Te(e, t, n) {
871
+ async function Ee(e, t, n) {
862
872
  try {
863
873
  let r = new URL(t.url), i = X(`${r.pathname}${r.search}`, n), a = Buffer.from(await t.arrayBuffer()), o = {};
864
874
  t.headers.forEach((e, t) => {
@@ -882,7 +892,7 @@ async function Te(e, t, n) {
882
892
  }
883
893
  //#endregion
884
894
  //#region src/tunnel/connect.ts
885
- async function Ee(e) {
895
+ async function De(e) {
886
896
  let t = e.host ?? "127.0.0.1", n = e.port ?? 1984, r = await Y({
887
897
  key: e.key,
888
898
  keyFile: e.keyFile,
@@ -909,19 +919,19 @@ async function Ee(e) {
909
919
  }
910
920
  };
911
921
  }
912
- function De(e, t = "") {
922
+ function Oe(e, t = "") {
913
923
  let n = e.replace(/\/$/, "");
914
924
  return t ? t.startsWith("/") ? `${n}${t}` : `${n}/${t}` : n;
915
925
  }
916
926
  //#endregion
917
927
  //#region src/tunnel/proxyApp.ts
918
- function Oe(e) {
928
+ function ke(e) {
919
929
  let t = e.key?.trim() || (typeof process < "u" ? process.env.SEED_GATEWAY_HYPER_KEY?.trim() : "") || "";
920
930
  if (!t) throw Error("createGatewayProxy requires options.key or SEED_GATEWAY_HYPER_KEY environment variable");
921
931
  return t;
922
932
  }
923
- function ke(e = {}) {
924
- let t = e.mountPath?.trim() || "/api/seed-gateway", n = Oe(e), r = null, i = null, a = async () => {
933
+ function Ae(e = {}) {
934
+ let t = e.mountPath?.trim() || "/api/seed-gateway", n = ke(e), r = null, i = null, a = async () => {
925
935
  if (r) return r;
926
936
  if (i) return i;
927
937
  i = Y({
@@ -961,7 +971,7 @@ function ke(e = {}) {
961
971
  },
962
972
  handleFetch: async (e) => {
963
973
  try {
964
- return await Te(await a(), e, t);
974
+ return await Ee(await a(), e, t);
965
975
  } catch (e) {
966
976
  let t = e instanceof Error ? e.message : String(e);
967
977
  return new Response(`Gateway Hyper tunnel error: ${t}`, {
@@ -975,8 +985,8 @@ function ke(e = {}) {
975
985
  }
976
986
  //#endregion
977
987
  //#region src/constants.ts
978
- var Ae = "";
988
+ var je = "";
979
989
  //#endregion
980
- export { C as A, N as C, w as D, k as E, d as F, D as M, u as N, E as O, l as P, j as S, A as T, le as _, X as a, F as b, Se as c, K as d, Y as f, de as g, fe as h, De as i, T as j, O as k, he as l, me as m, ke as n, xe as o, G as p, Ee as r, be as s, Ae as t, ge as u, R as v, g as w, M as x, P as y };
990
+ export { C as A, N as C, w as D, k as E, d as F, D as M, u as N, E as O, l as P, j as S, A as T, ue as _, X as a, P as b, Ce as c, G as d, Y as f, fe as g, pe as h, Oe as i, T as j, O as k, K as l, he as m, Ae as n, Se as o, W as p, De as r, xe as s, je as t, ge as u, L as v, g as w, M as x, re as y };
981
991
 
982
- //# sourceMappingURL=src-C_1bcYLA.js.map
992
+ //# sourceMappingURL=src-CJc2Ap3J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-CJc2Ap3J.js","names":["URL","fs","path","HyperDHT","ID","http","URL$1","net","toHex","buf","fromHex","hex","encodePublicKeyZ32","publicKey","decodePublicKey","trimmed","key","loadOrCreateOperatorKeypair","resolved","keyFile","raw","parsed","dir","kp","persisted","toOperatorKeypair","keyPair","secretKey","resolveClientKeypair","options","createEphemeralClientKeypair","resolveOperatorKeypair","defaultKeyFile","MAX_FRAME_BYTES","WRITE_CHUNK_BYTES","StreamByteReader","stream","chunk","err","length","resolve","waiters","w","next","out","readers","getReader","reader","readExact","writeBuffer","cleanup","onDrain","onError","onClose","reject","HTTP_PREFIXES","text","assertNotRawHttp","header","assertFrameSize","writeFrame","payload","first","offset","readFrame","writeTunnelRequest","body","meta","readTunnelRequest","metaBuf","writeTunnelResponse","readTunnelResponse","flattenRequestHeaders","value","headers","filterResponseHeaders","skip","TunnelTimeoutError","timeoutMs","TunnelConnectTimeoutError","UpstreamTimeoutError","DEFAULT_UPSTREAM_TIMEOUT_MS","withHeaders","extra","names","HOP_BY_HOP_HEADERS","upstreamRequestHeaders","named","token","host","lower","proxyTunnelRequest","base","upstreamBase","targetUrl","req","observer","res","chunks","received","ipFamily","addr","parseIps","list","entry","entries","bits","family","prefix","max","parseKeys","keys","normalizeHost","mapped","cleanEntries","createAllowlist","keyEntries","ipEntries","ips","enabled","keyAllowed","ipAllowed","remotePublicKey","safeEmitter","onEvent","event","errorFields","error","code","createIdSource","run","streams","requests","formatTunnelEvent","parts","now","errorResponse","message","serveTunnelConnection","emit","streamId","nextRequestId","requestIdHeader","closed","onGone","socket","opened","bytesIn","firstByteAt","inflight","pendingRequest","requestId","t0","upstreamStart","phase","upstreamMs","result","upstream","serveTunnel","allowlist","keypair","ids","dht","server","refuse","verdict","refusal","RAW_CLOSE_GRACE_MS","errorLabel","side","pipeRaw","graceMs","started","hyper","tcp","closeOther","other","label","DEFAULT_RAW_OPERATOR_KEY_FILE","parseTcpUpstream","url","defaultPort","port","serveRawTunnel","open","connections","conn","DEFAULT_REQUEST_TIMEOUT_MS","DEFAULT_CONNECT_TIMEOUT_MS","DEFAULT_MAX_STREAMS","abortError","reason","signal","raceAbort","onAbort","promise","createTunnelQueue","runGuarded","opts","requestTimeoutMs","getSocket","settled","timer","armTimer","abandon","fn","tunnelQueue","closedError","createTunnelPool","maxStreams","slots","waiting","isLive","addSlot","slot","openStream","takeSlot","idle","s","release","acquire","index","waiter","closesStream","createTunnelSession","clientKey","connectTimeoutMs","pool","fail","onReady","runOnTunnel","response","DEFAULT_RAW_PORT","DEFAULT_MAX_RAW_CONNECTIONS","connectRawTunnel","maxConnections","pipe","address","stripMountPath","urlPath","qIndex","pathname","query","mount","mountPath","rest","readNodeBody","tunnelErrorStatus","tunnelErrorMessage","forwardNodeRequest","controller","responseMeta","responseBody","session","writeNodeResponse","forwardFetchRequest","request","bodyBuf","headerRecord","connectTunnel","boundPort","baseUrl","localGatewayUrl","resolveProxyKey","createGatewayProxy","starting","ensureSession","proxy","DEFAULT_SEED_GATEWAY_HYPER_KEY"],"sources":["../src/keys.ts","../src/tunnel/protocol.ts","../src/tunnel/errors.ts","../src/tunnel/proxy.ts","../src/tunnel/allowlist.ts","../src/tunnel/events.ts","../src/tunnel/serve.ts","../src/tunnel/rawPipe.ts","../src/tunnel/rawServe.ts","../src/tunnel/session.ts","../src/tunnel/rawConnect.ts","../src/tunnel/mountPath.ts","../src/tunnel/forward.ts","../src/tunnel/connect.ts","../src/tunnel/proxyApp.ts","../src/constants.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport HyperDHT from 'hyperdht'\nimport ID from 'hypercore-id-encoding'\nimport type { OperatorKeyPair } from './types'\n\nexport interface PersistedKeyFile {\n publicKey: string\n secretKey: string\n encoding: 'hex'\n}\n\nfunction toHex(buf: Buffer): string {\n return buf.toString('hex')\n}\n\nfunction fromHex(hex: string): Buffer {\n return Buffer.from(hex, 'hex')\n}\n\nexport function encodePublicKeyZ32(publicKey: Buffer): string {\n return ID.encode(publicKey)\n}\n\nexport function decodePublicKey(key: string): Buffer {\n const trimmed = key.trim()\n if (!trimmed) {\n throw new Error('Gateway Hyper key is empty')\n }\n try {\n return Buffer.from(ID.decode(trimmed))\n } catch {\n if (/^[a-fA-F0-9]{64}$/.test(trimmed)) {\n return fromHex(trimmed)\n }\n throw new Error(`Invalid Gateway Hyper key (expected z32 or 64-char hex): ${trimmed.slice(0, 12)}…`)\n }\n}\n\nexport function loadOrCreateOperatorKeypair(keyFile: string): OperatorKeyPair {\n const resolved = path.resolve(keyFile)\n if (fs.existsSync(resolved)) {\n const raw = fs.readFileSync(resolved, 'utf8')\n const parsed = JSON.parse(raw) as PersistedKeyFile\n if (!parsed.publicKey || !parsed.secretKey) {\n throw new Error(`Invalid key file at ${resolved}: missing publicKey or secretKey`)\n }\n const publicKey = fromHex(parsed.publicKey)\n const secretKey = fromHex(parsed.secretKey)\n return {\n publicKey,\n secretKey,\n z32: encodePublicKeyZ32(publicKey),\n }\n }\n\n const dir = path.dirname(resolved)\n fs.mkdirSync(dir, { recursive: true })\n const kp = HyperDHT.keyPair()\n const persisted: PersistedKeyFile = {\n publicKey: toHex(kp.publicKey),\n secretKey: toHex(kp.secretKey),\n encoding: 'hex',\n }\n fs.writeFileSync(resolved, `${JSON.stringify(persisted, null, 2)}\\n`, { mode: 0o600 })\n return {\n publicKey: kp.publicKey,\n secretKey: kp.secretKey,\n z32: encodePublicKeyZ32(kp.publicKey),\n }\n}\n\n/** Check a keypair passed in code and add its z32 key. */\nexport function toOperatorKeypair(keyPair: { publicKey: Buffer; secretKey: Buffer }): OperatorKeyPair {\n const { publicKey, secretKey } = keyPair\n if (publicKey?.length !== 32 || secretKey?.length !== 64) {\n throw new Error('Invalid keyPair: expected a 32-byte publicKey and a 64-byte secretKey')\n }\n // An Ed25519 secret key in libsodium form is the seed followed by the public key.\n if (!Buffer.from(secretKey.subarray(32)).equals(Buffer.from(publicKey))) {\n throw new Error('Invalid keyPair: publicKey does not belong to secretKey')\n }\n return { publicKey, secretKey, z32: encodePublicKeyZ32(publicKey) }\n}\n\n/**\n * The keypair a client presents to the operator: `keyPair` if given, else the one in\n * `keyFile` (created on first use), else a new one for this run only.\n */\nexport function resolveClientKeypair(options: {\n keyFile?: string\n keyPair?: { publicKey: Buffer; secretKey: Buffer }\n}): OperatorKeyPair {\n if (options.keyFile && options.keyPair) {\n throw new Error('Pass keyFile or keyPair, not both')\n }\n if (options.keyPair) return toOperatorKeypair(options.keyPair)\n if (options.keyFile) return loadOrCreateOperatorKeypair(options.keyFile)\n return createEphemeralClientKeypair()\n}\n\n/**\n * The operator's keypair: `keyPair` if given, else the one in `keyFile` (`defaultKeyFile` when\n * unset), created on first use. Passing both throws, as for a client.\n */\nexport function resolveOperatorKeypair(\n options: { keyFile?: string; keyPair?: { publicKey: Buffer; secretKey: Buffer } },\n defaultKeyFile: string,\n): OperatorKeyPair {\n if (options.keyFile && options.keyPair) {\n throw new Error('Pass keyFile or keyPair, not both')\n }\n if (options.keyPair) return toOperatorKeypair(options.keyPair)\n return loadOrCreateOperatorKeypair(options.keyFile ?? defaultKeyFile)\n}\n\nexport function createEphemeralClientKeypair(): OperatorKeyPair {\n const kp = HyperDHT.keyPair()\n return {\n publicKey: kp.publicKey,\n secretKey: kp.secretKey,\n z32: encodePublicKeyZ32(kp.publicKey),\n }\n}\n","import type { Readable, Writable } from 'node:stream'\nimport type { TunnelMeta, TunnelResponseMeta } from '../types'\n\n/** Maximum single frame payload (64 MiB). */\nexport const MAX_FRAME_BYTES = 64 * 1024 * 1024\n\n/** Largest single stream write. Secret-stream's limit is 16 MiB - 1. */\nexport const WRITE_CHUNK_BYTES = 1024 * 1024\n\ntype ByteWaiter = {\n length: number\n resolve: (buf: Buffer) => void\n reject: (err: Error) => void\n}\n\n/**\n * Per-stream byte buffer. HyperDHT duplexes do not reliably support `unshift`,\n * so we accumulate and slice locally instead of pushing leftovers back.\n *\n * Once the stream ends, errors or closes, every pending and later read that the\n * buffer cannot satisfy rejects. A destroyed stream emits `close` without `end`,\n * so listening for `end` alone would leave reads waiting forever.\n */\nclass StreamByteReader {\n private buffer = Buffer.alloc(0)\n private waiters: ByteWaiter[] = []\n private failure: Error | null = null\n\n constructor(stream: Readable) {\n stream.on('data', (chunk: Buffer) => {\n this.buffer = Buffer.concat([this.buffer, chunk])\n this.drain()\n })\n stream.on('error', (err: Error) => this.fail(err))\n stream.on('end', () => this.fail(new Error('Unexpected end of stream')))\n stream.on('close', () => this.fail(new Error('Stream closed')))\n }\n\n readExact(length: number): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n if (length === 0) {\n resolve(Buffer.alloc(0))\n return\n }\n this.waiters.push({ length, resolve, reject })\n this.drain()\n this.rejectWaiters()\n })\n }\n\n private fail(err: Error): void {\n this.failure ??= err\n this.drain()\n this.rejectWaiters()\n }\n\n private rejectWaiters(): void {\n if (!this.failure || this.waiters.length === 0) return\n const waiters = this.waiters.splice(0)\n for (const w of waiters) {\n w.reject(\n new Error(\n `${this.failure.message} (expected ${w.length} bytes, got ${this.buffer.length})`,\n ),\n )\n }\n }\n\n private drain(): void {\n while (this.waiters.length > 0) {\n const next = this.waiters[0]\n if (!next || this.buffer.length < next.length) break\n this.waiters.shift()\n const out = this.buffer.subarray(0, next.length)\n this.buffer = this.buffer.subarray(next.length)\n next.resolve(out)\n }\n }\n}\n\nconst readers = new WeakMap<Readable, StreamByteReader>()\n\nfunction getReader(stream: Readable): StreamByteReader {\n let reader = readers.get(stream)\n if (!reader) {\n reader = new StreamByteReader(stream)\n readers.set(stream, reader)\n }\n return reader\n}\n\nfunction readExact(stream: Readable, length: number): Promise<Buffer> {\n return getReader(stream).readExact(length)\n}\n\nfunction writeBuffer(stream: Writable, buf: Buffer): Promise<void> {\n return new Promise((resolve, reject) => {\n if (stream.destroyed) {\n reject(new Error('Stream closed'))\n return\n }\n // HyperDHT duplexes often never invoke write(callback). Use return value + drain.\n try {\n const ok = stream.write(buf)\n if (ok) {\n resolve()\n return\n }\n const cleanup = () => {\n stream.off('drain', onDrain)\n stream.off('error', onError)\n stream.off('close', onClose)\n }\n const onDrain = () => {\n cleanup()\n resolve()\n }\n const onError = (err: Error) => {\n cleanup()\n reject(err)\n }\n const onClose = () => {\n cleanup()\n reject(new Error('Stream closed'))\n }\n stream.once('drain', onDrain)\n stream.once('error', onError)\n stream.once('close', onClose)\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)))\n }\n })\n}\n\n/** First four bytes of an HTTP request or response, as a frame length would read them. */\nconst HTTP_PREFIXES = ['HTTP', 'GET ', 'POST', 'PUT ', 'HEAD', 'DELE', 'PATC', 'OPTI', 'CONN', 'TRAC'].map((text) =>\n Buffer.from(text, 'latin1').readUInt32BE(0),\n)\n\n/** A frame header that is really the start of plain HTTP: the other side runs in raw mode. */\nfunction assertNotRawHttp(header: Buffer): void {\n if (HTTP_PREFIXES.includes(header.readUInt32BE(0))) {\n throw new Error(\n `The other side sent plain HTTP (\"${header.toString('latin1')}…\"), not a framed tunnel message: it runs in raw mode. Use seed-gateway serve --raw / connect --raw (serveRawTunnel, connectRawTunnel) on both sides, with the raw operator's key.`,\n )\n }\n}\n\nfunction assertFrameSize(length: number): void {\n if (length > MAX_FRAME_BYTES) {\n throw new Error(`Frame too large: ${length} bytes (max ${MAX_FRAME_BYTES})`)\n }\n}\n\nexport async function writeFrame(stream: Writable, payload: Buffer): Promise<void> {\n assertFrameSize(payload.length)\n const header = Buffer.alloc(4)\n header.writeUInt32BE(payload.length, 0)\n // The header goes out with the first chunk. Secret-stream rejects any single write over\n // 16 MiB, so larger payloads are split, waiting for drain between chunks.\n const first = payload.subarray(0, WRITE_CHUNK_BYTES)\n await writeBuffer(stream, Buffer.concat([header, first]))\n for (let offset = first.length; offset < payload.length; offset += WRITE_CHUNK_BYTES) {\n await writeBuffer(stream, payload.subarray(offset, offset + WRITE_CHUNK_BYTES))\n }\n}\n\nexport async function readFrame(stream: Readable): Promise<Buffer> {\n const header = await readExact(stream, 4)\n assertNotRawHttp(header)\n const length = header.readUInt32BE(0)\n if (length === 0) {\n return Buffer.alloc(0)\n }\n assertFrameSize(length)\n return readExact(stream, length)\n}\n\nexport async function writeTunnelRequest(\n stream: Writable,\n meta: TunnelMeta,\n body: Buffer,\n): Promise<void> {\n // Check the body before writing meta so an oversized body leaves the stream in sync.\n assertFrameSize(body.length)\n const metaFrame = Buffer.from(JSON.stringify(meta), 'utf8')\n await writeFrame(stream, metaFrame)\n await writeFrame(stream, body)\n}\n\nexport async function readTunnelRequest(\n stream: Readable,\n): Promise<{ meta: TunnelMeta; body: Buffer }> {\n const metaBuf = await readFrame(stream)\n const meta = JSON.parse(metaBuf.toString('utf8')) as TunnelMeta\n if (!meta.method || !meta.path) {\n throw new Error('Invalid tunnel request meta: missing method or path')\n }\n meta.headers = meta.headers ?? {}\n const body = await readFrame(stream)\n return { meta, body }\n}\n\nexport async function writeTunnelResponse(\n stream: Writable,\n meta: TunnelResponseMeta,\n body: Buffer,\n): Promise<void> {\n // Check the body before writing meta so an oversized body leaves the stream in sync.\n assertFrameSize(body.length)\n const metaFrame = Buffer.from(JSON.stringify(meta), 'utf8')\n await writeFrame(stream, metaFrame)\n await writeFrame(stream, body)\n}\n\nexport async function readTunnelResponse(\n stream: Readable,\n): Promise<{ meta: TunnelResponseMeta; body: Buffer }> {\n const metaBuf = await readFrame(stream)\n const meta = JSON.parse(metaBuf.toString('utf8')) as TunnelResponseMeta\n if (typeof meta.status !== 'number') {\n throw new Error('Invalid tunnel response meta: missing status')\n }\n meta.headers = meta.headers ?? {}\n const body = await readFrame(stream)\n return { meta, body }\n}\n\n/** Normalize header values to strings for tunnel transport. */\nexport function flattenRequestHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (value == null) continue\n out[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return out\n}\n\n/** Pick response headers safe to forward to the local HTTP client. */\nexport function filterResponseHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const skip = new Set([\n 'connection',\n 'keep-alive',\n 'transfer-encoding',\n 'upgrade',\n ])\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (skip.has(key.toLowerCase())) continue\n if (value == null) continue\n out[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return out\n}\n","/** Client side: the tunnel stream was idle longer than `requestTimeoutMs`, so the request was abandoned. */\nexport class TunnelTimeoutError extends Error {\n readonly code = 'TUNNEL_TIMEOUT'\n\n constructor(readonly timeoutMs: number) {\n super(`No bytes sent or received on the tunnel for ${timeoutMs} ms`)\n this.name = 'TunnelTimeoutError'\n }\n}\n\n/** Client side: the HyperDHT connection to the operator did not open within `connectTimeoutMs`. */\nexport class TunnelConnectTimeoutError extends Error {\n readonly code = 'TUNNEL_CONNECT_TIMEOUT'\n\n constructor(readonly timeoutMs: number) {\n super(`Could not connect to the operator within ${timeoutMs} ms`)\n this.name = 'TunnelConnectTimeoutError'\n }\n}\n\n/** Operator side: the upstream socket was idle longer than `upstreamTimeoutMs`. */\nexport class UpstreamTimeoutError extends Error {\n readonly code = 'UPSTREAM_TIMEOUT'\n\n constructor(readonly timeoutMs: number) {\n super(`No response from upstream for ${timeoutMs} ms`)\n this.name = 'UpstreamTimeoutError'\n }\n}\n","import http from 'node:http'\nimport { URL } from 'node:url'\nimport { UpstreamTimeoutError } from './errors'\nimport { filterResponseHeaders, MAX_FRAME_BYTES } from './protocol'\nimport type { UpstreamPhase } from './events'\nimport type { TunnelMeta } from '../types'\n\n/** Default idle limit on the operator's upstream socket. */\n/**\n * Above @seedprotocol/publish's 120 s batch upload, so an upload API that stays quiet while it\n * stores a large batch answers before the operator gives up on it.\n */\nexport const DEFAULT_UPSTREAM_TIMEOUT_MS = 130_000\n\nexport type ProxyTunnelRequestOptions = {\n /** Fail with `UpstreamTimeoutError` once the upstream socket is idle this long. 0 disables. */\n timeoutMs?: number\n /** Abort the upstream request, e.g. when the tunnel stream closes. */\n signal?: AbortSignal\n /** Headers set on the upstream request, replacing any the client sent under the same name. */\n extraHeaders?: Record<string, string>\n /** Progress callbacks, for tracing. */\n observer?: ProxyObserver\n}\n\nexport type ProxyObserver = {\n /** The request moved to `phase`: `send` from the start, `wait` once the body is sent, `body` on headers. */\n onPhase?: (phase: UpstreamPhase) => void\n onResponse?: (status: number) => void\n onDone?: (status: number, bytes: number) => void\n}\n\nfunction withHeaders(\n headers: Record<string, string>,\n extra: Record<string, string> | undefined,\n): Record<string, string> {\n if (!extra) return headers\n const names = new Set(Object.keys(extra).map((key) => key.toLowerCase()))\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (!names.has(key.toLowerCase())) out[key] = value\n }\n return { ...out, ...extra }\n}\n\n/** Headers that describe one hop and must not be forwarded (RFC 9110 section 7.6.1). */\nconst HOP_BY_HOP_HEADERS = new Set([\n 'connection',\n 'keep-alive',\n 'proxy-connection',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n])\n\n/**\n * Drop hop-by-hop headers and the client's `host`, which names the app server rather than\n * the upstream; it is passed on as `x-forwarded-host`. `content-length` is dropped because\n * Node sets it from the buffered body.\n */\nexport function upstreamRequestHeaders(headers: Record<string, string>): Record<string, string> {\n const named = new Set(\n (Object.entries(headers).find(([key]) => key.toLowerCase() === 'connection')?.[1] ?? '')\n .split(',')\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean),\n )\n const out: Record<string, string> = {}\n let host: string | undefined\n for (const [key, value] of Object.entries(headers)) {\n const lower = key.toLowerCase()\n if (lower === 'host') {\n host = value\n continue\n }\n if (lower === 'content-length' || HOP_BY_HOP_HEADERS.has(lower) || named.has(lower)) continue\n out[key] = value\n }\n if (host && !Object.keys(out).some((key) => key.toLowerCase() === 'x-forwarded-host')) {\n out['x-forwarded-host'] = host\n }\n return out\n}\n\nexport async function proxyTunnelRequest(\n upstreamBase: string,\n meta: TunnelMeta,\n body: Buffer,\n options: ProxyTunnelRequestOptions = {},\n): Promise<{ status: number; headers: Record<string, string>; body: Buffer }> {\n const base = upstreamBase.replace(/\\/$/, '')\n const targetUrl = new URL(meta.path, `${base}/`)\n const timeoutMs = options.timeoutMs ?? DEFAULT_UPSTREAM_TIMEOUT_MS\n const { observer } = options\n\n return new Promise((resolve, reject) => {\n observer?.onPhase?.('send')\n const req = http.request(\n targetUrl,\n {\n method: meta.method,\n headers: withHeaders(upstreamRequestHeaders(meta.headers), options.extraHeaders),\n signal: options.signal,\n },\n (res) => {\n if (observer) {\n observer.onPhase?.('body')\n observer.onResponse?.(res.statusCode ?? 502)\n }\n const chunks: Buffer[] = []\n let received = 0\n res.on('data', (chunk: Buffer) => {\n received += chunk.length\n if (received > MAX_FRAME_BYTES) {\n req.destroy(\n new Error(`Upstream response exceeds ${MAX_FRAME_BYTES} bytes, the tunnel's limit`),\n )\n return\n }\n chunks.push(chunk)\n })\n res.on('end', () => {\n observer?.onDone?.(res.statusCode ?? 502, received)\n resolve({\n status: res.statusCode ?? 502,\n headers: filterResponseHeaders(res.headers as Record<string, string | string[] | undefined>),\n body: Buffer.concat(chunks),\n })\n })\n res.on('error', reject)\n },\n )\n if (timeoutMs > 0) {\n req.setTimeout(timeoutMs, () => req.destroy(new UpstreamTimeoutError(timeoutMs)))\n }\n req.on('error', reject)\n if (observer?.onPhase) req.once('finish', () => observer.onPhase?.('wait'))\n if (body.length > 0) {\n req.end(body)\n } else {\n req.end()\n }\n })\n}\n","import net from 'node:net'\nimport { decodePublicKey } from '../keys'\n\nexport type AllowlistOptions = {\n /** Client public keys (z32 or hex) */\n keys?: string[]\n /** Client IPs or CIDR ranges, IPv4 or IPv6 */\n ips?: string[]\n}\n\n/** How a stream was let in, or `none` when it was refused. */\nexport type AllowedBy = 'open' | 'key' | 'ip' | 'none'\n\nexport type AllowlistVerdict = {\n ok: boolean\n /** The UDP address the connection uses, IPv4-mapped prefix removed */\n host: string | null\n by: AllowedBy\n}\n\n/** The parts of a HyperDHT stream the allowlist reads. */\nexport type AllowlistStream = {\n remotePublicKey?: Uint8Array | null\n rawStream?: { remoteHost?: string | null } | null\n}\n\nexport type Allowlist = {\n /** False when neither list has entries, so every client is let in. */\n enabled: boolean\n /** One line for logs, e.g. `keys=2 ips=192.168.1.0/24` or `off`. */\n summary: string\n /**\n * For HyperDHT's `firewall` server option: true refuses the client before it gets a stream.\n * Refuses only on the key, and only when no IP list is set, since the client's real address\n * is not known until the connection is up.\n */\n firewall: (remotePublicKey: Uint8Array) => boolean\n /** Check an established stream by key, then by address. */\n check: (stream: AllowlistStream) => AllowlistVerdict\n}\n\nfunction ipFamily(addr: string): 'ipv4' | 'ipv6' | null {\n if (net.isIPv4(addr)) return 'ipv4'\n if (net.isIPv6(addr)) return 'ipv6'\n return null\n}\n\nfunction parseIps(entries: string[]): net.BlockList {\n const list = new net.BlockList()\n for (const entry of entries) {\n const [addr = '', bits, extra] = entry.split('/')\n const family = ipFamily(addr)\n if (!family || extra !== undefined) {\n throw new Error(`Invalid allowlist IP (expected an address or CIDR): ${entry}`)\n }\n if (bits === undefined) {\n list.addAddress(addr, family)\n continue\n }\n const prefix = Number(bits)\n const max = family === 'ipv4' ? 32 : 128\n if (!/^\\d+$/.test(bits) || prefix > max) {\n throw new Error(`Invalid allowlist CIDR prefix (expected 0–${max}): ${entry}`)\n }\n list.addSubnet(addr, prefix, family)\n }\n return list\n}\n\nfunction parseKeys(entries: string[]): Set<string> {\n const keys = new Set<string>()\n for (const entry of entries) {\n keys.add(decodePublicKey(entry).toString('hex'))\n }\n return keys\n}\n\n/** Strip the IPv4-mapped IPv6 prefix so `::ffff:1.2.3.4` matches an IPv4 entry. */\nfunction normalizeHost(host: string | null | undefined): string | null {\n if (!host) return null\n const mapped = host.toLowerCase().startsWith('::ffff:') ? host.slice(7) : null\n return mapped && net.isIPv4(mapped) ? mapped : host\n}\n\nfunction cleanEntries(entries: string[] | undefined): string[] {\n return (entries ?? []).map((entry) => entry.trim()).filter(Boolean)\n}\n\n/**\n * Decide which HyperDHT clients may use a tunnel. A client is let in when its public key or\n * its address is listed; with both lists empty, every client is. Invalid entries throw.\n *\n * The address is the UDP address the connection uses. A client on the operator's LAN\n * connects over the LAN and shows a private address, and a connection relayed through\n * another HyperDHT node shows the relay's address, so it is refused unless its key is listed.\n */\nexport function createAllowlist(options: AllowlistOptions = {}): Allowlist {\n const keyEntries = cleanEntries(options.keys)\n const ipEntries = cleanEntries(options.ips)\n const keys = parseKeys(keyEntries)\n const ips = parseIps(ipEntries)\n const enabled = keys.size > 0 || ipEntries.length > 0\n\n const keyAllowed = (publicKey: Uint8Array | null | undefined): boolean =>\n publicKey != null && keys.has(Buffer.from(publicKey).toString('hex'))\n\n const ipAllowed = (host: string | null): boolean => {\n if (!host) return false\n const family = ipFamily(host)\n return family !== null && ips.check(host, family)\n }\n\n return {\n enabled,\n summary: enabled ? `keys=${keys.size} ips=${ipEntries.join(',') || 'none'}` : 'off',\n firewall: (remotePublicKey) => {\n if (!enabled || ipEntries.length > 0) return false\n return !keyAllowed(remotePublicKey)\n },\n check: (stream) => {\n const host = normalizeHost(stream.rawStream?.remoteHost)\n if (!enabled) return { ok: true, host, by: 'open' }\n if (keyAllowed(stream.remotePublicKey)) return { ok: true, host, by: 'key' }\n if (ipAllowed(host)) return { ok: true, host, by: 'ip' }\n return { ok: false, host, by: 'none' }\n },\n }\n}\n","import type { AllowedBy } from './allowlist'\n\n/** Where an upstream request was when it failed. */\nexport type UpstreamPhase = 'send' | 'wait' | 'body'\n\n/**\n * Events from `serveTunnel`'s `onEvent`. Streams are `s<N>`; requests carry the id that\n * `requestIdHeader` sends upstream. Times are milliseconds; `totalMs` and `receiveMs` count\n * from the request's first byte on the stream.\n */\nexport type TunnelEvent =\n | {\n name: 'stream.open'\n stream: string\n /** Client public key (z32) */\n remoteKey: string\n /** UDP address the connection uses */\n host: string | null\n by: AllowedBy\n }\n | {\n name: 'stream.refused'\n remoteKey: string\n /**\n * `handshake`: refused by key in HyperDHT's firewall, before a stream exists, so the\n * client's address is unknown and `host` is null. `connection`: refused once connected.\n */\n stage: 'handshake' | 'connection'\n host: string | null\n }\n | {\n name: 'stream.close'\n stream: string\n requests: number\n bytesIn: number\n durationMs: number\n /** The request in flight when the stream closed, if any */\n inflight: string | null\n }\n | { name: 'stream.error'; stream: string; error: string; code?: string }\n | {\n name: 'req.recv'\n request: string\n stream: string\n method: string\n path: string\n bodyBytes: number\n /** From the request's first byte to its last */\n receiveMs: number\n }\n | { name: 'up.response'; request: string; status: number; upstreamMs: number }\n | { name: 'up.done'; request: string; status: number; bytes: number; upstreamMs: number }\n | {\n name: 'up.error'\n request: string\n phase: UpstreamPhase\n error: string\n code?: string\n /** True when the tunnel stream closed and the operator aborted the upstream request */\n aborted: boolean\n }\n | { name: 'up.timeout'; request: string; phase: UpstreamPhase; timeoutMs: number }\n | { name: 'res.sent'; request: string; status: number; bytes: number; totalMs: number }\n\nexport type TunnelEventName = TunnelEvent['name']\n\n/** Called synchronously; a throw is caught and dropped so tracing can't break the tunnel. */\nexport type TunnelEventListener = (event: TunnelEvent) => void\n\n/** Bytes are counted from the HTTP client's side: up is toward the upstream. */\ntype RawCloseFields = {\n conn: string\n bytesUp: number\n bytesDown: number\n durationMs: number\n /** First error on either side, as `hyper:<code>`, `tcp:<code>` or `connect-timeout` */\n error: string | null\n}\n\n/** Events from `serveRawTunnel`'s `onEvent`. Connections are `c<N>`. */\nexport type RawTunnelEvent =\n | {\n name: 'conn.open'\n conn: string\n remoteKey: string\n host: string | null\n by: AllowedBy\n }\n | Extract<TunnelEvent, { name: 'stream.refused' }>\n | ({ name: 'conn.close' } & RawCloseFields)\n\nexport type RawTunnelEventListener = (event: RawTunnelEvent) => void\n\n/** Events from `connectRawTunnel`'s `onEvent`, one TCP connection each. */\nexport type RawClientEvent =\n | { name: 'conn.open'; conn: string; connectMs: number }\n /** A TCP connection refused because `maxConnections` were already open */\n | { name: 'conn.refused'; open: number; max: number }\n | ({ name: 'conn.close' } & RawCloseFields)\n\nexport type RawClientEventListener = (event: RawClientEvent) => void\n\n/** Wrap `onEvent` so a throw is dropped; undefined when there is no listener. */\nexport function safeEmitter<E>(onEvent: ((event: E) => void) | undefined): ((event: E) => void) | undefined {\n if (!onEvent) return undefined\n return (event) => {\n try {\n onEvent(event)\n } catch {\n // Tracing must not break the tunnel.\n }\n }\n}\n\nexport function errorFields(err: unknown): { error: string; code?: string } {\n const error = err instanceof Error ? err.message : String(err)\n const code = (err as { code?: unknown } | null)?.code\n return typeof code === 'string' ? { error, code } : { error }\n}\n\n/** A run-unique prefix, so request ids from different operator runs never collide. */\nexport function createIdSource(): { stream: () => string; request: () => string } {\n const run = Date.now().toString(36)\n let streams = 0\n let requests = 0\n return {\n stream: () => `s${++streams}`,\n request: () => `${run}-r${++requests}`,\n }\n}\n\n/** One `tunnel-trace` line per event: ISO time, event name, then `key=value` fields. */\nexport function formatTunnelEvent(\n event: TunnelEvent | RawTunnelEvent | RawClientEvent,\n now = new Date(),\n): string {\n const parts = [now.toISOString(), 'tunnel-trace', event.name]\n for (const [key, value] of Object.entries(event)) {\n if (key === 'name' || value === undefined || value === null) continue\n const text = String(value)\n parts.push(`${key}=${/[\\s\"=]/.test(text) ? JSON.stringify(text) : text}`)\n }\n return parts.join(' ')\n}\n","import type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { readTunnelRequest, writeTunnelResponse } from './protocol'\nimport { UpstreamTimeoutError } from './errors'\nimport { proxyTunnelRequest } from './proxy'\nimport { createAllowlist } from './allowlist'\nimport {\n createIdSource,\n errorFields,\n safeEmitter,\n type TunnelEventListener,\n type UpstreamPhase,\n} from './events'\nimport { encodePublicKeyZ32, resolveOperatorKeypair } from '../keys'\nimport type { ServeTunnelOptions, ServeTunnelResult } from '../types'\n\nexport type TunnelConnectionOptions = {\n /** Idle limit on each upstream request. Default 130 000; 0 disables. */\n upstreamTimeoutMs?: number\n /** Receives this stream's events; see `TunnelEvent`. */\n onEvent?: TunnelEventListener\n /** Send each request's id upstream under this header. */\n requestIdHeader?: string\n /** Id for this stream in events. Default `s0`. */\n streamId?: string\n /** Makes request ids. Default: run-unique ids from `createIdSource`. */\n nextRequestId?: () => string\n}\n\nfunction errorResponse(\n status: number,\n message: string,\n): { status: number; headers: Record<string, string>; body: Buffer } {\n return {\n status,\n headers: { 'content-type': 'text/plain; charset=utf-8' },\n body: Buffer.from(message, 'utf8'),\n }\n}\n\n/**\n * Answer framed requests on one tunnel stream, in order, until it closes. Upstream timeouts\n * become 504 and other upstream failures 502, so the stream stays usable; only a broken\n * stream ends the loop. The stream ending or closing aborts the upstream request in flight.\n *\n * Without `onEvent` or `requestIdHeader`, no tracing state is kept and no listeners are added.\n */\nexport async function serveTunnelConnection(\n socket: Duplex,\n upstream: string,\n options: TunnelConnectionOptions = {},\n): Promise<void> {\n const emit = safeEmitter(options.onEvent)\n const { requestIdHeader } = options\n const streamId = options.streamId ?? 's0'\n const nextRequestId =\n emit || requestIdHeader ? (options.nextRequestId ?? createIdSource().request) : null\n\n // Clients never half-close, so `end` means the peer is gone, same as `close` or `error`.\n const closed = new AbortController()\n const onGone = () => closed.abort()\n socket.once('end', onGone)\n socket.once('close', onGone)\n socket.on('error', onGone)\n\n // Tracing state, kept only when someone listens.\n const opened = performance.now()\n let requests = 0\n let bytesIn = 0\n let firstByteAt: number | null = null\n let inflight: string | null = null\n if (emit) {\n // A second data listener beside the frame reader's: byte counts, and when each request's\n // first byte arrived, so receive time leaves out idle time between requests.\n socket.on('data', (chunk: Buffer) => {\n bytesIn += chunk.length\n firstByteAt ??= performance.now()\n })\n socket.on('error', (err: unknown) => emit({ name: 'stream.error', stream: streamId, ...errorFields(err) }))\n socket.once('close', () =>\n emit({\n name: 'stream.close',\n stream: streamId,\n requests,\n bytesIn,\n durationMs: Math.round(performance.now() - opened),\n inflight,\n }),\n )\n }\n\n // Attach framed readers synchronously so HyperDHT early bytes are not lost.\n let pendingRequest = readTunnelRequest(socket)\n try {\n while (true) {\n const { meta, body } = await pendingRequest\n const requestId = nextRequestId?.() ?? null\n const t0 = firstByteAt ?? performance.now()\n firstByteAt = null\n let upstreamStart = 0\n let phase: UpstreamPhase = 'send'\n if (emit && requestId) {\n requests++\n inflight = requestId\n emit({\n name: 'req.recv',\n request: requestId,\n stream: streamId,\n method: meta.method,\n path: meta.path,\n bodyBytes: body.length,\n receiveMs: Math.round(performance.now() - t0),\n })\n upstreamStart = performance.now()\n }\n const upstreamMs = () => Math.round(performance.now() - upstreamStart)\n\n let result: { status: number; headers: Record<string, string>; body: Buffer }\n try {\n result = await proxyTunnelRequest(upstream, meta, body, {\n timeoutMs: options.upstreamTimeoutMs,\n signal: closed.signal,\n extraHeaders:\n requestIdHeader && requestId ? { [requestIdHeader]: requestId } : undefined,\n observer:\n emit && requestId\n ? {\n onPhase: (next) => {\n // `finish` can land after the response started; keep the later phase.\n if (!(next === 'wait' && phase === 'body')) phase = next\n },\n onResponse: (status) =>\n emit({ name: 'up.response', request: requestId, status, upstreamMs: upstreamMs() }),\n onDone: (status, bytes) =>\n emit({ name: 'up.done', request: requestId, status, bytes, upstreamMs: upstreamMs() }),\n }\n : undefined,\n })\n } catch (err) {\n if (emit && requestId) {\n emit(\n err instanceof UpstreamTimeoutError && !closed.signal.aborted\n ? { name: 'up.timeout', request: requestId, phase, timeoutMs: err.timeoutMs }\n : {\n name: 'up.error',\n request: requestId,\n phase,\n ...errorFields(err),\n aborted: closed.signal.aborted,\n },\n )\n }\n if (closed.signal.aborted) return\n const message = err instanceof Error ? err.message : String(err)\n result =\n err instanceof UpstreamTimeoutError\n ? errorResponse(504, `Gateway Hyper upstream timeout: ${message}`)\n : errorResponse(502, `Gateway Hyper upstream error: ${message}`)\n }\n await writeTunnelResponse(\n socket,\n { status: result.status, headers: result.headers },\n result.body,\n )\n if (emit && requestId) {\n inflight = null\n emit({\n name: 'res.sent',\n request: requestId,\n status: result.status,\n bytes: result.body.length,\n totalMs: Math.round(performance.now() - t0),\n })\n }\n pendingRequest = readTunnelRequest(socket)\n }\n } catch {\n socket.destroy()\n }\n}\n\n/**\n * Operator: accept Hyperswarm/DHT connections and proxy framed HTTP to the upstream.\n */\nexport async function serveTunnel(options: ServeTunnelOptions): Promise<ServeTunnelResult> {\n const upstream = options.upstream.replace(/\\/$/, '')\n if (/^https:\\/\\//i.test(upstream)) {\n throw new Error(\n `serveTunnel speaks plain HTTP to its upstream; got ${upstream}. Point it at an http:// origin, or use serveRawTunnel (seed-gateway serve --raw) to carry TLS end to end.`,\n )\n }\n const allowlist = createAllowlist(options.allowlist)\n const keypair = resolveOperatorKeypair(options, '.seed/gateway-tunnel/operator.key.json')\n const emit = safeEmitter(options.onEvent)\n const ids = createIdSource()\n\n const dht = new HyperDHT(options.bootstrap ? { bootstrap: options.bootstrap } : {})\n const server = dht.createServer(\n {\n firewall: (remotePublicKey) => {\n const refuse = allowlist.firewall(remotePublicKey)\n if (refuse && emit) {\n emit({\n name: 'stream.refused',\n remoteKey: encodePublicKeyZ32(remotePublicKey),\n stage: 'handshake',\n host: null,\n })\n }\n return refuse\n },\n },\n (socket) => {\n const verdict = allowlist.check(socket)\n // Refuse before serveTunnelConnection attaches a reader, so nothing is read. The client\n // gets a 403 for the request it sends, rather than a reset it can't tell from a network\n // failure.\n if (!verdict.ok) {\n emit?.({\n name: 'stream.refused',\n remoteKey: encodePublicKeyZ32(socket.remotePublicKey),\n stage: 'connection',\n host: verdict.host,\n })\n socket.on('error', () => {})\n const refusal = errorResponse(\n 403,\n `Gateway Hyper: this operator's allowlist doesn't include this client (address ${verdict.host ?? 'unknown'}).`,\n )\n // `connection: close` tells the client not to send another request on this stream; the\n // stream's end can reach it after it has.\n writeTunnelResponse(\n socket,\n { status: refusal.status, headers: { ...refusal.headers, connection: 'close' } },\n refusal.body,\n )\n .then(() => socket.end())\n .catch(() => socket.destroy())\n return\n }\n const streamId = ids.stream()\n emit?.({\n name: 'stream.open',\n stream: streamId,\n remoteKey: encodePublicKeyZ32(socket.remotePublicKey),\n host: verdict.host,\n by: verdict.by,\n })\n void serveTunnelConnection(socket, upstream, {\n upstreamTimeoutMs: options.upstreamTimeoutMs,\n onEvent: options.onEvent,\n requestIdHeader: options.requestIdHeader,\n streamId,\n nextRequestId: ids.request,\n })\n },\n )\n\n await server.listen(keypair)\n\n return {\n key: keypair.z32,\n upstream,\n close: async () => {\n await server.close()\n await dht.destroy()\n },\n }\n}\n","import type net from 'node:net'\nimport type { Duplex } from 'node:stream'\n\n/** How long a side may stay open after the other closed cleanly before it is destroyed. */\nexport const RAW_CLOSE_GRACE_MS = 30_000\n\nexport type RawPipeResult = {\n /** Bytes read from the TCP socket and sent over the HyperDHT stream */\n tcpBytesRead: number\n /** Bytes from the HyperDHT stream written to the TCP socket */\n tcpBytesWritten: number\n durationMs: number\n /** First error on either side, as `hyper:<code>` or `tcp:<code>`, or null */\n error: string | null\n}\n\nexport type RawPipe = {\n /** Record an error that is about to destroy one side, e.g. a connect timeout. */\n fail: (label: string) => void\n}\n\nfunction errorLabel(side: 'hyper' | 'tcp', err: unknown): string {\n const code = (err as { code?: unknown } | null)?.code\n const message = err instanceof Error ? err.message : String(err)\n return `${side}:${typeof code === 'string' ? code : message}`\n}\n\n/**\n * Copy bytes both ways between a HyperDHT stream and a TCP socket until both close.\n *\n * HyperDHT → TCP is forwarded by hand with backpressure, not with `stream.pipe(socket)`:\n * streamx's pipe destroys its source with PREMATURE_CLOSE when the destination closes first,\n * so the far end would see a reset even though every byte arrived. TCP → HyperDHT uses Node's\n * `pipe`, which has no such problem. Half-closes pass through, so an HTTP connection ends the\n * way its client or server ended it; `tcp` must be created with `allowHalfOpen: true` for that.\n *\n * When one side closes cleanly, the other is ended so data already written still arrives,\n * and destroyed only if it is still open `graceMs` later. After an error on either side, the\n * other is destroyed at once.\n */\nexport function pipeRaw(\n hyper: Duplex,\n tcp: net.Socket,\n options: { graceMs?: number; onClose?: (result: RawPipeResult) => void } = {},\n): RawPipe {\n const graceMs = options.graceMs ?? RAW_CLOSE_GRACE_MS\n const started = performance.now()\n let error: string | null = null\n let closed = 0\n\n hyper.on('data', (chunk: Buffer) => {\n if (!tcp.write(chunk)) hyper.pause()\n })\n tcp.on('drain', () => hyper.resume())\n hyper.on('end', () => tcp.end())\n tcp.pipe(hyper)\n\n hyper.on('error', (err: unknown) => {\n error ??= errorLabel('hyper', err)\n })\n tcp.on('error', (err: unknown) => {\n error ??= errorLabel('tcp', err)\n })\n\n const closeOther = (other: Duplex) => () => {\n if (other.destroyed) return\n if (error) {\n other.destroy()\n return\n }\n other.end()\n setTimeout(() => other.destroy(), graceMs).unref()\n }\n const onClose = () => {\n if (++closed < 2) return\n options.onClose?.({\n tcpBytesRead: tcp.bytesRead,\n tcpBytesWritten: tcp.bytesWritten,\n durationMs: Math.round(performance.now() - started),\n error,\n })\n }\n hyper.on('close', closeOther(tcp))\n tcp.on('close', closeOther(hyper))\n hyper.on('close', onClose)\n tcp.on('close', onClose)\n\n return {\n fail: (label) => {\n error ??= label\n },\n }\n}\n","import net from 'node:net'\nimport type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { createAllowlist } from './allowlist'\nimport { safeEmitter } from './events'\nimport { pipeRaw } from './rawPipe'\nimport { encodePublicKeyZ32, resolveOperatorKeypair } from '../keys'\nimport type { ServeRawTunnelOptions, ServeRawTunnelResult } from '../types'\n\n/** Default operator key file for raw mode, apart from the framed tunnel's key. */\nexport const DEFAULT_RAW_OPERATOR_KEY_FILE = '.seed/gateway-tunnel/raw-operator.key.json'\n\n/** `http://host:port`, `https://host`, or `host:port`, as a TCP address. */\nexport function parseTcpUpstream(upstream: string): { host: string; port: number } {\n const text = upstream.trim()\n const url = new URL(/^[a-z][a-z0-9+.-]*:\\/\\//i.test(text) ? text : `tcp://${text}`)\n const defaultPort = url.protocol === 'http:' ? 80 : url.protocol === 'https:' ? 443 : NaN\n const port = url.port ? Number(url.port) : defaultPort\n const host = url.hostname.replace(/^\\[(.*)\\]$/, '$1')\n if (!host || !Number.isInteger(port)) {\n throw new Error(`Invalid raw upstream (expected http://host:port or host:port): ${upstream}`)\n }\n return { host, port }\n}\n\n/**\n * Operator, raw mode: pipe each HyperDHT stream byte for byte to a new TCP connection to the\n * upstream. There is no framing, so HTTP/1.1 runs end to end between the client's HTTP client\n * and the upstream: keep-alive, streamed bodies of any size, `Range` and WebSockets all work.\n *\n * Clients must use `connectRawTunnel`; a framed `connectTunnel` client can't talk to this\n * server, so it listens on its own key.\n */\nexport async function serveRawTunnel(options: ServeRawTunnelOptions): Promise<ServeRawTunnelResult> {\n const upstream = parseTcpUpstream(options.upstream)\n const allowlist = createAllowlist(options.allowlist)\n const keypair = resolveOperatorKeypair(options, DEFAULT_RAW_OPERATOR_KEY_FILE)\n const emit = safeEmitter(options.onEvent)\n const open = new Set<Duplex>()\n let connections = 0\n\n const dht = new HyperDHT(options.bootstrap ? { bootstrap: options.bootstrap } : {})\n const server = dht.createServer(\n {\n firewall: (remotePublicKey) => {\n const refuse = allowlist.firewall(remotePublicKey)\n if (refuse && emit) {\n emit({\n name: 'stream.refused',\n remoteKey: encodePublicKeyZ32(remotePublicKey),\n stage: 'handshake',\n host: null,\n })\n }\n return refuse\n },\n },\n (stream) => {\n const verdict = allowlist.check(stream)\n if (!verdict.ok) {\n emit?.({\n name: 'stream.refused',\n remoteKey: encodePublicKeyZ32(stream.remotePublicKey),\n stage: 'connection',\n host: verdict.host,\n })\n stream.on('error', () => {})\n stream.destroy()\n return\n }\n const conn = `c${++connections}`\n emit?.({\n name: 'conn.open',\n conn,\n remoteKey: encodePublicKeyZ32(stream.remotePublicKey),\n host: verdict.host,\n by: verdict.by,\n })\n // allowHalfOpen, so an upstream that half-closes can still read what the client sends.\n const socket = net.connect({ host: upstream.host, port: upstream.port, allowHalfOpen: true })\n socket.setNoDelay(true)\n open.add(stream)\n open.add(socket)\n pipeRaw(stream, socket, {\n graceMs: options.closeGraceMs,\n onClose: (result) => {\n open.delete(stream)\n open.delete(socket)\n emit?.({\n name: 'conn.close',\n conn,\n bytesUp: result.tcpBytesWritten,\n bytesDown: result.tcpBytesRead,\n durationMs: result.durationMs,\n error: result.error,\n })\n },\n })\n },\n )\n\n await server.listen(keypair)\n\n return {\n key: keypair.z32,\n upstream: `${upstream.host}:${upstream.port}`,\n close: async () => {\n for (const side of open) side.destroy()\n await server.close()\n await dht.destroy()\n },\n }\n}\n","import type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { decodePublicKey, resolveClientKeypair } from '../keys'\nimport type { DhtBootstrapNode, TunnelMeta, TunnelResponseMeta } from '../types'\nimport { TunnelConnectTimeoutError, TunnelTimeoutError } from './errors'\nimport { readTunnelResponse, writeTunnelRequest } from './protocol'\n\n/** Default idle limit for one request on the tunnel stream. */\n/** Above the operator's default upstream timeout (130 s), so the operator's own 504 arrives first. */\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 150_000\n\n/** Default limit for opening the HyperDHT connection. */\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 30_000\n\n/**\n * Default number of streams a session opens to the operator. Streams open only when every\n * open one is busy, and each costs a DHT lookup and handshake plus an upstream request on\n * the operator, so a small pool keeps that cost down. Four leaves room for a page's burst of\n * gateway reads after one or two requests hang until their timeout.\n */\nexport const DEFAULT_MAX_STREAMS = 4\n\nexport type TunnelSessionOptions = {\n /** Operator public key (z32 or hex) */\n key: string\n /**\n * Keypair file (created on first run if missing) for the key this client presents to the\n * operator. Without `keyFile` or `keyPair`, each session uses a new key.\n */\n keyFile?: string\n /** The client keypair itself. Exclusive with `keyFile`. */\n keyPair?: { publicKey: Buffer; secretKey: Buffer }\n /** HyperDHT bootstrap nodes. Defaults to the public network. */\n bootstrap?: DhtBootstrapNode[]\n /**\n * Abandon a request once the stream has sent and received nothing for this long, then\n * reconnect for the next request. Counts from when the request gets a stream.\n * Default 150 000; 0 disables.\n */\n requestTimeoutMs?: number\n /** Give up opening the HyperDHT connection after this long. Default 30 000; 0 disables. */\n connectTimeoutMs?: number\n /**\n * Most HyperDHT streams open to the operator at once; each carries one request at a time.\n * Default 4.\n */\n maxStreams?: number\n}\n\nexport type TunnelRequestOptions = {\n /** Aborting rejects the request; if it is already on the stream, the stream is destroyed. */\n signal?: AbortSignal\n /** Overrides the session's `requestTimeoutMs` for this request. */\n timeoutMs?: number\n}\n\nexport type TunnelSession = {\n key: string\n /** The public key (z32) this client presents to the operator */\n clientKey: string\n runOnTunnel: <T>(\n fn: (socket: Duplex) => Promise<T>,\n options?: TunnelRequestOptions,\n ) => Promise<T>\n forwardHttp: (\n meta: TunnelMeta,\n body: Buffer,\n options?: TunnelRequestOptions,\n ) => Promise<{ meta: TunnelResponseMeta; body: Buffer }>\n close: () => Promise<void>\n}\n\nfunction abortError(signal: AbortSignal): Error {\n const reason: unknown = signal.reason\n if (reason instanceof Error) return reason\n const err = new Error('The request was aborted')\n err.name = 'AbortError'\n return err\n}\n\n/** Reject as soon as `signal` aborts, even while `promise` is still queued. */\nfunction raceAbort<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(abortError(signal))\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortError(signal))\n signal.addEventListener('abort', onAbort, { once: true })\n promise.then(\n (value) => {\n signal.removeEventListener('abort', onAbort)\n resolve(value)\n },\n (err: unknown) => {\n signal.removeEventListener('abort', onAbort)\n reject(err)\n },\n )\n })\n}\n\n/**\n * Serialize transactions on the socket `getSocket` returns. Each runs under an idle timeout\n * and an optional abort signal; one that is abandoned or fails destroys the socket, so\n * `getSocket` must open a new one once the current one is destroyed.\n */\nexport function createTunnelQueue(\n getSocket: () => Promise<Duplex>,\n requestTimeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,\n): TunnelSession['runOnTunnel'] {\n const runGuarded = async <T>(\n fn: (socket: Duplex) => Promise<T>,\n opts: TunnelRequestOptions,\n ): Promise<T> => {\n const { signal } = opts\n const timeoutMs = opts.timeoutMs ?? requestTimeoutMs\n if (signal?.aborted) throw abortError(signal)\n const socket = await getSocket()\n if (signal?.aborted) throw abortError(signal)\n\n return new Promise<T>((resolve, reject) => {\n let settled = false\n let timer: ReturnType<typeof setTimeout> | null = null\n const armTimer = () => {\n if (timeoutMs <= 0) return\n if (timer) clearTimeout(timer)\n timer = setTimeout(() => abandon(new TunnelTimeoutError(timeoutMs)), timeoutMs)\n }\n const cleanup = () => {\n if (timer) clearTimeout(timer)\n socket.off('data', armTimer)\n socket.off('drain', armTimer)\n signal?.removeEventListener('abort', onAbort)\n }\n // A late response would be read as the next request's reply, so the stream goes too.\n const abandon = (err: unknown) => {\n if (settled) return\n settled = true\n cleanup()\n socket.destroy()\n reject(err)\n }\n const onAbort = () => abandon(abortError(signal as AbortSignal))\n\n armTimer()\n // Bytes moving either way keep the request alive, so large bodies are not cut off.\n socket.on('data', armTimer)\n socket.on('drain', armTimer)\n signal?.addEventListener('abort', onAbort, { once: true })\n\n fn(socket).then((value) => {\n if (settled) return\n settled = true\n cleanup()\n resolve(value)\n }, abandon)\n })\n }\n\n let tunnelQueue: Promise<unknown> = Promise.resolve()\n return <T>(\n fn: (socket: Duplex) => Promise<T>,\n opts: TunnelRequestOptions = {},\n ): Promise<T> => {\n const run = tunnelQueue.then(() => runGuarded(fn, opts))\n tunnelQueue = run.catch(() => {})\n return opts.signal ? raceAbort(run, opts.signal) : run\n }\n}\n\ntype PoolSlot = {\n run: TunnelSession['runOnTunnel']\n socket: Duplex | null\n busy: boolean\n}\n\ntype PoolWaiter = {\n resolve: (slot: PoolSlot) => void\n reject: (err: Error) => void\n}\n\nexport type TunnelPoolOptions = {\n /** Most streams open at once. Default `DEFAULT_MAX_STREAMS`. */\n maxStreams?: number\n /** Idle limit for each request, as in `createTunnelQueue`. */\n requestTimeoutMs?: number\n}\n\nexport type TunnelPool = {\n runOnTunnel: TunnelSession['runOnTunnel']\n /** Reject queued requests and destroy every stream. */\n close: () => void\n}\n\nfunction closedError(): Error {\n return new Error('Tunnel session is closed')\n}\n\n/**\n * Run each transaction alone on one of up to `maxStreams` sockets from `openStream`. A request\n * takes an idle open stream if there is one, else opens a new stream while fewer than\n * `maxStreams` exist, else waits for the first stream to come free. Each stream is a\n * `createTunnelQueue`, so a request that is abandoned destroys only its own stream, and that\n * stream reopens for the next request it takes.\n */\nexport function createTunnelPool(\n openStream: () => Promise<Duplex>,\n options: TunnelPoolOptions = {},\n): TunnelPool {\n const maxStreams = Math.max(1, Math.floor(options.maxStreams ?? DEFAULT_MAX_STREAMS))\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS\n const slots: PoolSlot[] = []\n const waiting: PoolWaiter[] = []\n let closed = false\n\n // A stream the operator ended (after a 403, say) can't carry another response.\n const isLive = (socket: Duplex | null): socket is Duplex =>\n socket !== null && !socket.destroyed && !socket.readableEnded\n\n const addSlot = (): PoolSlot => {\n const slot: PoolSlot = { run: null as never, socket: null, busy: false }\n // The slot runs one request at a time, so there is never a second open in progress.\n slot.run = createTunnelQueue(async () => {\n if (closed) throw closedError()\n if (isLive(slot.socket)) return slot.socket\n const socket = await openStream()\n if (closed) {\n socket.destroy()\n throw closedError()\n }\n slot.socket = socket\n return socket\n }, requestTimeoutMs)\n slots.push(slot)\n return slot\n }\n\n const takeSlot = (): PoolSlot | null => {\n const idle = slots.filter((slot) => !slot.busy)\n // An idle slot whose stream died reopens it, same as a new slot would.\n const slot =\n idle.find((s) => isLive(s.socket)) ??\n idle[0] ??\n (slots.length < maxStreams ? addSlot() : undefined)\n if (!slot) return null\n slot.busy = true\n return slot\n }\n\n const release = (slot: PoolSlot) => {\n const next = waiting.shift()\n if (next) next.resolve(slot)\n else slot.busy = false\n }\n\n const acquire = (signal: AbortSignal | undefined): Promise<PoolSlot> => {\n const slot = takeSlot()\n if (slot) return Promise.resolve(slot)\n return new Promise<PoolSlot>((resolve, reject) => {\n const onAbort = () => {\n const index = waiting.indexOf(waiter)\n if (index >= 0) waiting.splice(index, 1)\n reject(abortError(signal as AbortSignal))\n }\n const waiter: PoolWaiter = {\n resolve: (s) => {\n signal?.removeEventListener('abort', onAbort)\n resolve(s)\n },\n reject: (err) => {\n signal?.removeEventListener('abort', onAbort)\n reject(err)\n },\n }\n waiting.push(waiter)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n }\n\n const runOnTunnel = async <T>(\n fn: (socket: Duplex) => Promise<T>,\n opts: TunnelRequestOptions = {},\n ): Promise<T> => {\n if (opts.signal?.aborted) throw abortError(opts.signal)\n if (closed) throw closedError()\n const slot = await acquire(opts.signal)\n try {\n return await slot.run(fn, opts)\n } finally {\n release(slot)\n }\n }\n\n return {\n runOnTunnel,\n close: () => {\n closed = true\n for (const waiter of waiting.splice(0)) waiter.reject(closedError())\n for (const slot of slots) slot.socket?.destroy()\n },\n }\n}\n\n/** Whether the operator sent `connection: close`. Upstream `connection` headers never reach the client. */\nfunction closesStream(meta: TunnelResponseMeta): boolean {\n return Object.entries(meta.headers).some(\n ([key, value]) => key.toLowerCase() === 'connection' && /(^|,)\\s*close\\s*(,|$)/i.test(String(value)),\n )\n}\n\n/**\n * Dial the operator HyperDHT key and expose a request/response forwarder.\n *\n * Requests run on a pool of up to `maxStreams` streams to the operator, one request per\n * stream at a time (see `createTunnelPool`). The framing has no request ids, so a request\n * that is abandoned mid-flight (timeout, abort, or error) destroys its stream; the next\n * request on that stream opens a new one.\n */\nexport async function createTunnelSession(options: TunnelSessionOptions): Promise<TunnelSession> {\n const remotePublicKey = decodePublicKey(options.key)\n const clientKey = resolveClientKeypair(options)\n const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS\n\n const dht = new HyperDHT(options.bootstrap ? { bootstrap: options.bootstrap } : {})\n\n // Without a `pool` option, each connect is its own handshake and encrypted stream.\n const openStream = () =>\n new Promise<Duplex>((resolve, reject) => {\n const socket = dht.connect(remotePublicKey, {\n keyPair: { publicKey: clientKey.publicKey, secretKey: clientKey.secretKey },\n }) as Duplex\n let settled = false\n const timer =\n connectTimeoutMs > 0\n ? setTimeout(() => fail(new TunnelConnectTimeoutError(connectTimeoutMs)), connectTimeoutMs)\n : null\n const fail = (err: Error) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n socket.destroy()\n reject(err)\n }\n const onReady = () => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n resolve(socket)\n }\n // Keep a listener for the socket's whole life so a late error never goes unhandled;\n // the frame reader sees errors through its own listener.\n socket.on('error', (err: Error) => fail(err))\n socket.once('close', () => fail(new Error('Tunnel connection closed before it opened')))\n // The operator ends a stream after answering a request it refused; the pool opens a new\n // one for the next request.\n socket.once('end', () => socket.destroy())\n socket.once('open', onReady)\n socket.once('connect', onReady)\n })\n\n const pool = createTunnelPool(openStream, {\n maxStreams: options.maxStreams,\n requestTimeoutMs: options.requestTimeoutMs,\n })\n const { runOnTunnel } = pool\n\n const forwardHttp = async (\n meta: TunnelMeta,\n body: Buffer,\n opts?: TunnelRequestOptions,\n ): Promise<{ meta: TunnelResponseMeta; body: Buffer }> => {\n return runOnTunnel(async (socket) => {\n await writeTunnelRequest(socket, meta, body)\n const response = await readTunnelResponse(socket)\n // The operator is ending this stream, so the next request opens a new one.\n if (closesStream(response.meta)) socket.destroy()\n return response\n }, opts)\n }\n\n return {\n key: options.key,\n clientKey: clientKey.z32,\n runOnTunnel,\n forwardHttp,\n close: async () => {\n pool.close()\n await dht.destroy()\n },\n }\n}\n","import net from 'node:net'\nimport HyperDHT from 'hyperdht'\nimport { decodePublicKey, resolveClientKeypair } from '../keys'\nimport { safeEmitter } from './events'\nimport { TunnelConnectTimeoutError } from './errors'\nimport { pipeRaw } from './rawPipe'\nimport { DEFAULT_CONNECT_TIMEOUT_MS } from './session'\nimport type { ConnectRawTunnelOptions, ConnectRawTunnelResult } from '../types'\n\n/** Default local port for `connectRawTunnel`, next to the framed sidecar's 1984. */\nexport const DEFAULT_RAW_PORT = 1985\n\n/** Default limit on TCP connections open through one raw client at once. */\nexport const DEFAULT_MAX_RAW_CONNECTIONS = 64\n\n/**\n * Client, raw mode: listen on a local TCP port and carry each connection over its own\n * HyperDHT stream to a `serveRawTunnel` operator, which pipes it to its upstream.\n *\n * Each connection costs a DHT lookup and handshake (about 100–400 ms), so put an HTTP client\n * that keeps connections alive in front, such as nginx with `keepalive` on its upstream.\n */\nexport async function connectRawTunnel(options: ConnectRawTunnelOptions): Promise<ConnectRawTunnelResult> {\n const remotePublicKey = decodePublicKey(options.key)\n const clientKey = resolveClientKeypair(options)\n const host = options.host ?? '127.0.0.1'\n const maxConnections = options.maxConnections ?? DEFAULT_MAX_RAW_CONNECTIONS\n const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS\n const emit = safeEmitter(options.onEvent)\n const open = new Set<net.Socket>()\n let connections = 0\n\n const dht = new HyperDHT(options.bootstrap ? { bootstrap: options.bootstrap } : {})\n\n // allowHalfOpen: a client that half-closes after its request still gets the response.\n // Node's default would end our side as soon as the client's side ended.\n const server = net.createServer({ allowHalfOpen: true }, (socket) => {\n if (open.size >= maxConnections) {\n emit?.({ name: 'conn.refused', open: open.size, max: maxConnections })\n socket.destroy()\n return\n }\n const conn = `c${++connections}`\n const started = performance.now()\n open.add(socket)\n socket.setNoDelay(true)\n\n const stream = dht.connect(remotePublicKey, {\n keyPair: { publicKey: clientKey.publicKey, secretKey: clientKey.secretKey },\n })\n let timer: ReturnType<typeof setTimeout> | null = null\n const pipe = pipeRaw(stream, socket, {\n graceMs: options.closeGraceMs,\n onClose: (result) => {\n if (timer) clearTimeout(timer)\n open.delete(socket)\n emit?.({\n name: 'conn.close',\n conn,\n bytesUp: result.tcpBytesRead,\n bytesDown: result.tcpBytesWritten,\n durationMs: result.durationMs,\n error: result.error,\n })\n },\n })\n // A refused or unreachable operator can leave the stream opening forever.\n if (connectTimeoutMs > 0) {\n timer = setTimeout(() => {\n pipe.fail('connect-timeout')\n stream.destroy(new TunnelConnectTimeoutError(connectTimeoutMs))\n }, connectTimeoutMs)\n }\n stream.once('open', () => {\n if (timer) clearTimeout(timer)\n emit?.({ name: 'conn.open', conn, connectMs: Math.round(performance.now() - started) })\n })\n })\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(options.port ?? DEFAULT_RAW_PORT, host, () => resolve())\n })\n const address = server.address()\n const port = address && typeof address === 'object' ? address.port : (options.port ?? DEFAULT_RAW_PORT)\n\n return {\n key: options.key,\n clientKey: clientKey.z32,\n host,\n port,\n close: async () => {\n for (const socket of open) socket.destroy()\n await new Promise<void>((resolve) => server.close(() => resolve()))\n await dht.destroy()\n },\n }\n}\n","/**\n * Strip a mount path prefix from an incoming request URL path (including query string).\n * If the path is already unmounted (framework stripped it), returns it unchanged (with leading `/`).\n */\nexport function stripMountPath(urlPath: string, mountPath: string): string {\n const raw = urlPath || '/'\n const qIndex = raw.indexOf('?')\n const pathname = qIndex === -1 ? raw : raw.slice(0, qIndex)\n const query = qIndex === -1 ? '' : raw.slice(qIndex)\n\n const mount = (mountPath || '').replace(/\\/$/, '')\n if (!mount || mount === '/') {\n const path = pathname.startsWith('/') ? pathname : `/${pathname}`\n return `${path}${query}` || '/'\n }\n\n if (pathname === mount || pathname.startsWith(`${mount}/`)) {\n const rest = pathname.slice(mount.length)\n const path = rest.startsWith('/') ? rest : rest ? `/${rest}` : '/'\n return `${path}${query}`\n }\n\n // Already stripped by the framework (e.g. Express mount)\n const path = pathname.startsWith('/') ? pathname : `/${pathname}`\n return `${path}${query}`\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n filterResponseHeaders,\n flattenRequestHeaders,\n} from './protocol'\nimport { stripMountPath } from './mountPath'\nimport { TunnelTimeoutError } from './errors'\nimport type { TunnelSession } from './session'\nimport type { TunnelMeta, TunnelResponseMeta } from '../types'\n\nasync function readNodeBody(req: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n return Buffer.concat(chunks)\n}\n\n/** 504 when the tunnel timed out, 502 for any other tunnel failure. */\nfunction tunnelErrorStatus(err: unknown): number {\n return err instanceof TunnelTimeoutError ? 504 : 502\n}\n\nfunction tunnelErrorMessage(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err)\n return `Gateway Hyper tunnel error: ${message}`\n}\n\nexport async function forwardNodeRequest(\n session: TunnelSession,\n req: IncomingMessage,\n res: ServerResponse,\n mountPath: string,\n): Promise<void> {\n // The caller hanging up abandons the request instead of leaving it on the tunnel.\n const controller = new AbortController()\n const onClose = () => {\n if (!res.writableEnded) controller.abort()\n }\n res.once('close', onClose)\n try {\n const body = await readNodeBody(req)\n const path = stripMountPath(req.url ?? '/', mountPath)\n const meta: TunnelMeta = {\n method: req.method ?? 'GET',\n path,\n headers: flattenRequestHeaders(req.headers as Record<string, string | string[] | undefined>),\n }\n const { meta: responseMeta, body: responseBody } = await session.forwardHttp(meta, body, {\n signal: controller.signal,\n })\n writeNodeResponse(res, responseMeta, responseBody)\n } catch (err) {\n if (controller.signal.aborted) return\n if (!res.headersSent) {\n res.writeHead(tunnelErrorStatus(err), { 'Content-Type': 'text/plain; charset=utf-8' })\n }\n res.end(tunnelErrorMessage(err))\n } finally {\n res.off('close', onClose)\n }\n}\n\nfunction writeNodeResponse(\n res: ServerResponse,\n meta: TunnelResponseMeta,\n body: Buffer,\n): void {\n res.writeHead(meta.status, filterResponseHeaders(meta.headers))\n res.end(body)\n}\n\nexport async function forwardFetchRequest(\n session: TunnelSession,\n request: Request,\n mountPath: string,\n): Promise<Response> {\n try {\n const url = new URL(request.url)\n const path = stripMountPath(`${url.pathname}${url.search}`, mountPath)\n const bodyBuf = Buffer.from(await request.arrayBuffer())\n const headerRecord: Record<string, string> = {}\n request.headers.forEach((value, key) => {\n headerRecord[key] = value\n })\n const meta: TunnelMeta = {\n method: request.method || 'GET',\n path,\n headers: flattenRequestHeaders(headerRecord),\n }\n const { meta: responseMeta, body: responseBody } = await session.forwardHttp(meta, bodyBuf, {\n signal: request.signal,\n })\n return new Response(responseBody, {\n status: responseMeta.status,\n headers: filterResponseHeaders(responseMeta.headers),\n })\n } catch (err) {\n return new Response(tunnelErrorMessage(err), {\n status: tunnelErrorStatus(err),\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n })\n }\n}\n","import http from 'node:http'\nimport { forwardNodeRequest } from './forward'\nimport { createTunnelSession } from './session'\nimport type { ConnectTunnelOptions, ConnectTunnelResult } from '../types'\n\n/**\n * Client: dial operator key and expose a localhost HTTP server that forwards through the tunnel.\n */\nexport async function connectTunnel(options: ConnectTunnelOptions): Promise<ConnectTunnelResult> {\n const host = options.host ?? '127.0.0.1'\n const port = options.port ?? 1984\n\n const session = await createTunnelSession({\n key: options.key,\n keyFile: options.keyFile,\n keyPair: options.keyPair,\n bootstrap: options.bootstrap,\n requestTimeoutMs: options.requestTimeoutMs,\n connectTimeoutMs: options.connectTimeoutMs,\n maxStreams: options.maxStreams,\n })\n\n const server = http.createServer((req, res) => {\n void forwardNodeRequest(session, req, res, '')\n })\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, host, () => resolve())\n })\n\n const address = server.address()\n const boundPort =\n address && typeof address === 'object' ? address.port : port\n\n const baseUrl = `http://${host}:${boundPort}`\n\n return {\n key: options.key,\n clientKey: session.clientKey,\n host,\n port: boundPort,\n baseUrl,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()))\n await session.close()\n },\n }\n}\n\nexport function localGatewayUrl(baseUrl: string, path = ''): string {\n const base = baseUrl.replace(/\\/$/, '')\n if (!path) return base\n return path.startsWith('/') ? `${base}${path}` : `${base}/${path}`\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createTunnelSession, type TunnelSession } from './session'\nimport { forwardFetchRequest, forwardNodeRequest } from './forward'\nimport type { CreateGatewayProxyOptions, GatewayProxy } from '../types'\n\nfunction resolveProxyKey(options: CreateGatewayProxyOptions): string {\n const key =\n options.key?.trim() ||\n (typeof process !== 'undefined' ? process.env.SEED_GATEWAY_HYPER_KEY?.trim() : '') ||\n ''\n if (!key) {\n throw new Error(\n 'createGatewayProxy requires options.key or SEED_GATEWAY_HYPER_KEY environment variable',\n )\n }\n return key\n}\n\n/**\n * App-server Hyper gateway proxy for hosted web apps (Path B).\n *\n * Mount on a Node route (Next.js Node runtime, Express, etc.). Keep the operator\n * Hyper key on the server only — browsers talk HTTP to `proxyBaseUrl`.\n *\n * Prefer Node.js over Bun for Holepunch natives.\n */\nexport function createGatewayProxy(options: CreateGatewayProxyOptions = {}): GatewayProxy {\n const mountPath = options.mountPath?.trim() || '/api/seed-gateway'\n const key = resolveProxyKey(options)\n\n let session: TunnelSession | null = null\n let starting: Promise<TunnelSession> | null = null\n\n const ensureSession = async (): Promise<TunnelSession> => {\n if (session) return session\n if (starting) return starting\n starting = createTunnelSession({\n key,\n keyFile: options.keyFile,\n keyPair: options.keyPair,\n bootstrap: options.bootstrap,\n requestTimeoutMs: options.requestTimeoutMs,\n connectTimeoutMs: options.connectTimeoutMs,\n maxStreams: options.maxStreams,\n }).then((s) => {\n session = s\n starting = null\n return s\n })\n try {\n return await starting\n } catch (err) {\n starting = null\n throw err\n }\n }\n\n const proxy: GatewayProxy = {\n mountPath,\n start: async () => {\n await ensureSession()\n },\n close: async () => {\n if (starting) {\n try {\n await starting\n } catch {\n /* ignore start failure during close */\n }\n }\n if (session) {\n await session.close()\n session = null\n }\n },\n handleNode: (req: IncomingMessage, res: ServerResponse) => {\n void (async () => {\n try {\n const s = await ensureSession()\n await forwardNodeRequest(s, req, res, mountPath)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' })\n }\n res.end(`Gateway Hyper tunnel error: ${message}`)\n }\n })()\n },\n handleFetch: async (request: Request): Promise<Response> => {\n try {\n const s = await ensureSession()\n return await forwardFetchRequest(s, request, mountPath)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n return new Response(`Gateway Hyper tunnel error: ${message}`, {\n status: 502,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n })\n }\n },\n }\n\n if (options.eager) {\n void proxy.start().catch(() => {\n /* first request will surface the error */\n })\n }\n\n return proxy\n}\n","/**\n * Official Seed Protocol gateway Hyper tunnel operator key (z32).\n * Empty until ops generate and back up the first operator keypair.\n * Also defined in packages/arweave/src/constants.ts; keep both equal (gateway-hyper __tests__/constants.test.ts checks).\n */\nexport const DEFAULT_SEED_GATEWAY_HYPER_KEY = ''\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/tunnel/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAMzC,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,UAAU,CAAA;AAEjB,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAErE,MAAM,MAAM,uBAAuB,GAAG;IACpC,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,wDAAwD;IACxD,OAAO,CAAC,EAAE,mBAAmB,CAAA;IAC7B,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,MAAM,CAAA;CAC7B,CAAA;AAaD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAgIf;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA8EzF"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/tunnel/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAMzC,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,UAAU,CAAA;AAEjB,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAErE,MAAM,MAAM,uBAAuB,GAAG;IACpC,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,wDAAwD;IACxD,OAAO,CAAC,EAAE,mBAAmB,CAAA;IAC7B,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,MAAM,CAAA;CAC7B,CAAA;AAaD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAgIf;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoFzF"}
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/tunnel/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAIhF,+DAA+D;AAC/D,sGAAsG;AACtG,eAAO,MAAM,0BAA0B,SAAU,CAAA;AAEjD,yDAAyD;AACzD,eAAO,MAAM,0BAA0B,QAAS,CAAA;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAEpC,MAAM,MAAM,oBAAoB,GAAG;IACjC,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2DAA2D;IAC3D,OAAO,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClD,gEAAgE;IAChE,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC9B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,2FAA2F;IAC3F,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,6FAA6F;IAC7F,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,CAAC,CAAC,EACb,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC,CAAC,CAAC,CAAA;IACf,WAAW,EAAE,CACX,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,kBAAkB,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACxD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AA6BD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAChC,gBAAgB,GAAE,MAAmC,GACpD,aAAa,CAAC,aAAa,CAAC,CA2D9B;AAaD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IACzC,uDAAuD;IACvD,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAAA;AAMD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EACjC,OAAO,GAAE,iBAAsB,GAC9B,UAAU,CAyFZ;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAkE/F"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/tunnel/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAIhF,+DAA+D;AAC/D,sGAAsG;AACtG,eAAO,MAAM,0BAA0B,SAAU,CAAA;AAEjD,yDAAyD;AACzD,eAAO,MAAM,0BAA0B,QAAS,CAAA;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAEpC,MAAM,MAAM,oBAAoB,GAAG;IACjC,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2DAA2D;IAC3D,OAAO,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClD,gEAAgE;IAChE,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC9B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,2FAA2F;IAC3F,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,6FAA6F;IAC7F,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,CAAC,CAAC,EACb,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC,CAAC,CAAC,CAAA;IACf,WAAW,EAAE,CACX,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,kBAAkB,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACxD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AA6BD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAChC,gBAAgB,GAAE,MAAmC,GACpD,aAAa,CAAC,aAAa,CAAC,CA2D9B;AAaD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IACzC,uDAAuD;IACvD,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAAA;AAMD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EACjC,OAAO,GAAE,iBAAsB,GAC9B,UAAU,CA6FZ;AASD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAwE/F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedprotocol/gateway-hyper",
3
- "version": "0.7.12",
3
+ "version": "0.7.13-next.1527.g318ec68",
4
4
  "description": "HyperDHT HTTP tunnel that exposes a Seed Protocol gateway and upload API through a local sidecar or app-server proxy",
5
5
  "keywords": [
6
6
  "seed-protocol",
@@ -1 +0,0 @@
1
- {"version":3,"file":"src-C_1bcYLA.js","names":["URL","fs","path","HyperDHT","ID","http","URL$1","net","toHex","buf","fromHex","hex","encodePublicKeyZ32","publicKey","decodePublicKey","trimmed","key","loadOrCreateOperatorKeypair","resolved","keyFile","raw","parsed","dir","kp","persisted","toOperatorKeypair","keyPair","secretKey","resolveClientKeypair","options","createEphemeralClientKeypair","resolveOperatorKeypair","defaultKeyFile","MAX_FRAME_BYTES","WRITE_CHUNK_BYTES","StreamByteReader","stream","chunk","err","length","resolve","waiters","w","next","out","readers","getReader","reader","readExact","writeBuffer","cleanup","onDrain","onError","onClose","reject","HTTP_PREFIXES","text","assertNotRawHttp","header","assertFrameSize","writeFrame","payload","first","offset","readFrame","writeTunnelRequest","body","meta","readTunnelRequest","metaBuf","writeTunnelResponse","readTunnelResponse","flattenRequestHeaders","value","headers","filterResponseHeaders","skip","TunnelTimeoutError","timeoutMs","TunnelConnectTimeoutError","UpstreamTimeoutError","DEFAULT_UPSTREAM_TIMEOUT_MS","withHeaders","extra","names","HOP_BY_HOP_HEADERS","upstreamRequestHeaders","named","token","host","lower","proxyTunnelRequest","base","upstreamBase","targetUrl","req","observer","res","chunks","received","ipFamily","addr","parseIps","list","entry","entries","bits","family","prefix","max","parseKeys","keys","normalizeHost","mapped","cleanEntries","createAllowlist","keyEntries","ipEntries","ips","enabled","keyAllowed","ipAllowed","remotePublicKey","safeEmitter","onEvent","event","errorFields","error","code","createIdSource","run","streams","requests","formatTunnelEvent","parts","now","errorResponse","message","serveTunnelConnection","emit","streamId","nextRequestId","requestIdHeader","closed","onGone","socket","opened","bytesIn","firstByteAt","inflight","pendingRequest","requestId","t0","upstreamStart","phase","upstreamMs","result","upstream","serveTunnel","allowlist","keypair","ids","dht","server","refuse","verdict","refusal","RAW_CLOSE_GRACE_MS","errorLabel","side","pipeRaw","graceMs","started","hyper","tcp","closeOther","other","label","DEFAULT_RAW_OPERATOR_KEY_FILE","parseTcpUpstream","url","defaultPort","port","serveRawTunnel","open","connections","conn","DEFAULT_REQUEST_TIMEOUT_MS","DEFAULT_CONNECT_TIMEOUT_MS","DEFAULT_MAX_STREAMS","abortError","reason","signal","raceAbort","onAbort","promise","createTunnelQueue","runGuarded","opts","requestTimeoutMs","getSocket","settled","timer","armTimer","abandon","fn","tunnelQueue","closedError","createTunnelPool","maxStreams","slots","waiting","addSlot","slot","openStream","takeSlot","idle","s","release","acquire","index","waiter","createTunnelSession","clientKey","connectTimeoutMs","pool","fail","onReady","runOnTunnel","DEFAULT_RAW_PORT","DEFAULT_MAX_RAW_CONNECTIONS","connectRawTunnel","maxConnections","pipe","address","stripMountPath","urlPath","qIndex","pathname","query","mount","mountPath","rest","readNodeBody","tunnelErrorStatus","tunnelErrorMessage","forwardNodeRequest","controller","responseMeta","responseBody","session","writeNodeResponse","forwardFetchRequest","request","bodyBuf","headerRecord","connectTunnel","boundPort","baseUrl","localGatewayUrl","resolveProxyKey","createGatewayProxy","starting","ensureSession","proxy","DEFAULT_SEED_GATEWAY_HYPER_KEY"],"sources":["../src/keys.ts","../src/tunnel/protocol.ts","../src/tunnel/errors.ts","../src/tunnel/proxy.ts","../src/tunnel/allowlist.ts","../src/tunnel/events.ts","../src/tunnel/serve.ts","../src/tunnel/rawPipe.ts","../src/tunnel/rawServe.ts","../src/tunnel/session.ts","../src/tunnel/rawConnect.ts","../src/tunnel/mountPath.ts","../src/tunnel/forward.ts","../src/tunnel/connect.ts","../src/tunnel/proxyApp.ts","../src/constants.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport HyperDHT from 'hyperdht'\nimport ID from 'hypercore-id-encoding'\nimport type { OperatorKeyPair } from './types'\n\nexport interface PersistedKeyFile {\n publicKey: string\n secretKey: string\n encoding: 'hex'\n}\n\nfunction toHex(buf: Buffer): string {\n return buf.toString('hex')\n}\n\nfunction fromHex(hex: string): Buffer {\n return Buffer.from(hex, 'hex')\n}\n\nexport function encodePublicKeyZ32(publicKey: Buffer): string {\n return ID.encode(publicKey)\n}\n\nexport function decodePublicKey(key: string): Buffer {\n const trimmed = key.trim()\n if (!trimmed) {\n throw new Error('Gateway Hyper key is empty')\n }\n try {\n return Buffer.from(ID.decode(trimmed))\n } catch {\n if (/^[a-fA-F0-9]{64}$/.test(trimmed)) {\n return fromHex(trimmed)\n }\n throw new Error(`Invalid Gateway Hyper key (expected z32 or 64-char hex): ${trimmed.slice(0, 12)}…`)\n }\n}\n\nexport function loadOrCreateOperatorKeypair(keyFile: string): OperatorKeyPair {\n const resolved = path.resolve(keyFile)\n if (fs.existsSync(resolved)) {\n const raw = fs.readFileSync(resolved, 'utf8')\n const parsed = JSON.parse(raw) as PersistedKeyFile\n if (!parsed.publicKey || !parsed.secretKey) {\n throw new Error(`Invalid key file at ${resolved}: missing publicKey or secretKey`)\n }\n const publicKey = fromHex(parsed.publicKey)\n const secretKey = fromHex(parsed.secretKey)\n return {\n publicKey,\n secretKey,\n z32: encodePublicKeyZ32(publicKey),\n }\n }\n\n const dir = path.dirname(resolved)\n fs.mkdirSync(dir, { recursive: true })\n const kp = HyperDHT.keyPair()\n const persisted: PersistedKeyFile = {\n publicKey: toHex(kp.publicKey),\n secretKey: toHex(kp.secretKey),\n encoding: 'hex',\n }\n fs.writeFileSync(resolved, `${JSON.stringify(persisted, null, 2)}\\n`, { mode: 0o600 })\n return {\n publicKey: kp.publicKey,\n secretKey: kp.secretKey,\n z32: encodePublicKeyZ32(kp.publicKey),\n }\n}\n\n/** Check a keypair passed in code and add its z32 key. */\nexport function toOperatorKeypair(keyPair: { publicKey: Buffer; secretKey: Buffer }): OperatorKeyPair {\n const { publicKey, secretKey } = keyPair\n if (publicKey?.length !== 32 || secretKey?.length !== 64) {\n throw new Error('Invalid keyPair: expected a 32-byte publicKey and a 64-byte secretKey')\n }\n // An Ed25519 secret key in libsodium form is the seed followed by the public key.\n if (!Buffer.from(secretKey.subarray(32)).equals(Buffer.from(publicKey))) {\n throw new Error('Invalid keyPair: publicKey does not belong to secretKey')\n }\n return { publicKey, secretKey, z32: encodePublicKeyZ32(publicKey) }\n}\n\n/**\n * The keypair a client presents to the operator: `keyPair` if given, else the one in\n * `keyFile` (created on first use), else a new one for this run only.\n */\nexport function resolveClientKeypair(options: {\n keyFile?: string\n keyPair?: { publicKey: Buffer; secretKey: Buffer }\n}): OperatorKeyPair {\n if (options.keyFile && options.keyPair) {\n throw new Error('Pass keyFile or keyPair, not both')\n }\n if (options.keyPair) return toOperatorKeypair(options.keyPair)\n if (options.keyFile) return loadOrCreateOperatorKeypair(options.keyFile)\n return createEphemeralClientKeypair()\n}\n\n/**\n * The operator's keypair: `keyPair` if given, else the one in `keyFile` (`defaultKeyFile` when\n * unset), created on first use. Passing both throws, as for a client.\n */\nexport function resolveOperatorKeypair(\n options: { keyFile?: string; keyPair?: { publicKey: Buffer; secretKey: Buffer } },\n defaultKeyFile: string,\n): OperatorKeyPair {\n if (options.keyFile && options.keyPair) {\n throw new Error('Pass keyFile or keyPair, not both')\n }\n if (options.keyPair) return toOperatorKeypair(options.keyPair)\n return loadOrCreateOperatorKeypair(options.keyFile ?? defaultKeyFile)\n}\n\nexport function createEphemeralClientKeypair(): OperatorKeyPair {\n const kp = HyperDHT.keyPair()\n return {\n publicKey: kp.publicKey,\n secretKey: kp.secretKey,\n z32: encodePublicKeyZ32(kp.publicKey),\n }\n}\n","import type { Readable, Writable } from 'node:stream'\nimport type { TunnelMeta, TunnelResponseMeta } from '../types'\n\n/** Maximum single frame payload (64 MiB). */\nexport const MAX_FRAME_BYTES = 64 * 1024 * 1024\n\n/** Largest single stream write. Secret-stream's limit is 16 MiB - 1. */\nexport const WRITE_CHUNK_BYTES = 1024 * 1024\n\ntype ByteWaiter = {\n length: number\n resolve: (buf: Buffer) => void\n reject: (err: Error) => void\n}\n\n/**\n * Per-stream byte buffer. HyperDHT duplexes do not reliably support `unshift`,\n * so we accumulate and slice locally instead of pushing leftovers back.\n *\n * Once the stream ends, errors or closes, every pending and later read that the\n * buffer cannot satisfy rejects. A destroyed stream emits `close` without `end`,\n * so listening for `end` alone would leave reads waiting forever.\n */\nclass StreamByteReader {\n private buffer = Buffer.alloc(0)\n private waiters: ByteWaiter[] = []\n private failure: Error | null = null\n\n constructor(stream: Readable) {\n stream.on('data', (chunk: Buffer) => {\n this.buffer = Buffer.concat([this.buffer, chunk])\n this.drain()\n })\n stream.on('error', (err: Error) => this.fail(err))\n stream.on('end', () => this.fail(new Error('Unexpected end of stream')))\n stream.on('close', () => this.fail(new Error('Stream closed')))\n }\n\n readExact(length: number): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n if (length === 0) {\n resolve(Buffer.alloc(0))\n return\n }\n this.waiters.push({ length, resolve, reject })\n this.drain()\n this.rejectWaiters()\n })\n }\n\n private fail(err: Error): void {\n this.failure ??= err\n this.drain()\n this.rejectWaiters()\n }\n\n private rejectWaiters(): void {\n if (!this.failure || this.waiters.length === 0) return\n const waiters = this.waiters.splice(0)\n for (const w of waiters) {\n w.reject(\n new Error(\n `${this.failure.message} (expected ${w.length} bytes, got ${this.buffer.length})`,\n ),\n )\n }\n }\n\n private drain(): void {\n while (this.waiters.length > 0) {\n const next = this.waiters[0]\n if (!next || this.buffer.length < next.length) break\n this.waiters.shift()\n const out = this.buffer.subarray(0, next.length)\n this.buffer = this.buffer.subarray(next.length)\n next.resolve(out)\n }\n }\n}\n\nconst readers = new WeakMap<Readable, StreamByteReader>()\n\nfunction getReader(stream: Readable): StreamByteReader {\n let reader = readers.get(stream)\n if (!reader) {\n reader = new StreamByteReader(stream)\n readers.set(stream, reader)\n }\n return reader\n}\n\nfunction readExact(stream: Readable, length: number): Promise<Buffer> {\n return getReader(stream).readExact(length)\n}\n\nfunction writeBuffer(stream: Writable, buf: Buffer): Promise<void> {\n return new Promise((resolve, reject) => {\n if (stream.destroyed) {\n reject(new Error('Stream closed'))\n return\n }\n // HyperDHT duplexes often never invoke write(callback). Use return value + drain.\n try {\n const ok = stream.write(buf)\n if (ok) {\n resolve()\n return\n }\n const cleanup = () => {\n stream.off('drain', onDrain)\n stream.off('error', onError)\n stream.off('close', onClose)\n }\n const onDrain = () => {\n cleanup()\n resolve()\n }\n const onError = (err: Error) => {\n cleanup()\n reject(err)\n }\n const onClose = () => {\n cleanup()\n reject(new Error('Stream closed'))\n }\n stream.once('drain', onDrain)\n stream.once('error', onError)\n stream.once('close', onClose)\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)))\n }\n })\n}\n\n/** First four bytes of an HTTP request or response, as a frame length would read them. */\nconst HTTP_PREFIXES = ['HTTP', 'GET ', 'POST', 'PUT ', 'HEAD', 'DELE', 'PATC', 'OPTI', 'CONN', 'TRAC'].map((text) =>\n Buffer.from(text, 'latin1').readUInt32BE(0),\n)\n\n/** A frame header that is really the start of plain HTTP: the other side runs in raw mode. */\nfunction assertNotRawHttp(header: Buffer): void {\n if (HTTP_PREFIXES.includes(header.readUInt32BE(0))) {\n throw new Error(\n `The other side sent plain HTTP (\"${header.toString('latin1')}…\"), not a framed tunnel message: it runs in raw mode. Use seed-gateway serve --raw / connect --raw (serveRawTunnel, connectRawTunnel) on both sides, with the raw operator's key.`,\n )\n }\n}\n\nfunction assertFrameSize(length: number): void {\n if (length > MAX_FRAME_BYTES) {\n throw new Error(`Frame too large: ${length} bytes (max ${MAX_FRAME_BYTES})`)\n }\n}\n\nexport async function writeFrame(stream: Writable, payload: Buffer): Promise<void> {\n assertFrameSize(payload.length)\n const header = Buffer.alloc(4)\n header.writeUInt32BE(payload.length, 0)\n // The header goes out with the first chunk. Secret-stream rejects any single write over\n // 16 MiB, so larger payloads are split, waiting for drain between chunks.\n const first = payload.subarray(0, WRITE_CHUNK_BYTES)\n await writeBuffer(stream, Buffer.concat([header, first]))\n for (let offset = first.length; offset < payload.length; offset += WRITE_CHUNK_BYTES) {\n await writeBuffer(stream, payload.subarray(offset, offset + WRITE_CHUNK_BYTES))\n }\n}\n\nexport async function readFrame(stream: Readable): Promise<Buffer> {\n const header = await readExact(stream, 4)\n assertNotRawHttp(header)\n const length = header.readUInt32BE(0)\n if (length === 0) {\n return Buffer.alloc(0)\n }\n assertFrameSize(length)\n return readExact(stream, length)\n}\n\nexport async function writeTunnelRequest(\n stream: Writable,\n meta: TunnelMeta,\n body: Buffer,\n): Promise<void> {\n // Check the body before writing meta so an oversized body leaves the stream in sync.\n assertFrameSize(body.length)\n const metaFrame = Buffer.from(JSON.stringify(meta), 'utf8')\n await writeFrame(stream, metaFrame)\n await writeFrame(stream, body)\n}\n\nexport async function readTunnelRequest(\n stream: Readable,\n): Promise<{ meta: TunnelMeta; body: Buffer }> {\n const metaBuf = await readFrame(stream)\n const meta = JSON.parse(metaBuf.toString('utf8')) as TunnelMeta\n if (!meta.method || !meta.path) {\n throw new Error('Invalid tunnel request meta: missing method or path')\n }\n meta.headers = meta.headers ?? {}\n const body = await readFrame(stream)\n return { meta, body }\n}\n\nexport async function writeTunnelResponse(\n stream: Writable,\n meta: TunnelResponseMeta,\n body: Buffer,\n): Promise<void> {\n // Check the body before writing meta so an oversized body leaves the stream in sync.\n assertFrameSize(body.length)\n const metaFrame = Buffer.from(JSON.stringify(meta), 'utf8')\n await writeFrame(stream, metaFrame)\n await writeFrame(stream, body)\n}\n\nexport async function readTunnelResponse(\n stream: Readable,\n): Promise<{ meta: TunnelResponseMeta; body: Buffer }> {\n const metaBuf = await readFrame(stream)\n const meta = JSON.parse(metaBuf.toString('utf8')) as TunnelResponseMeta\n if (typeof meta.status !== 'number') {\n throw new Error('Invalid tunnel response meta: missing status')\n }\n meta.headers = meta.headers ?? {}\n const body = await readFrame(stream)\n return { meta, body }\n}\n\n/** Normalize header values to strings for tunnel transport. */\nexport function flattenRequestHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (value == null) continue\n out[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return out\n}\n\n/** Pick response headers safe to forward to the local HTTP client. */\nexport function filterResponseHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const skip = new Set([\n 'connection',\n 'keep-alive',\n 'transfer-encoding',\n 'upgrade',\n ])\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (skip.has(key.toLowerCase())) continue\n if (value == null) continue\n out[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return out\n}\n","/** Client side: the tunnel stream was idle longer than `requestTimeoutMs`, so the request was abandoned. */\nexport class TunnelTimeoutError extends Error {\n readonly code = 'TUNNEL_TIMEOUT'\n\n constructor(readonly timeoutMs: number) {\n super(`No bytes sent or received on the tunnel for ${timeoutMs} ms`)\n this.name = 'TunnelTimeoutError'\n }\n}\n\n/** Client side: the HyperDHT connection to the operator did not open within `connectTimeoutMs`. */\nexport class TunnelConnectTimeoutError extends Error {\n readonly code = 'TUNNEL_CONNECT_TIMEOUT'\n\n constructor(readonly timeoutMs: number) {\n super(`Could not connect to the operator within ${timeoutMs} ms`)\n this.name = 'TunnelConnectTimeoutError'\n }\n}\n\n/** Operator side: the upstream socket was idle longer than `upstreamTimeoutMs`. */\nexport class UpstreamTimeoutError extends Error {\n readonly code = 'UPSTREAM_TIMEOUT'\n\n constructor(readonly timeoutMs: number) {\n super(`No response from upstream for ${timeoutMs} ms`)\n this.name = 'UpstreamTimeoutError'\n }\n}\n","import http from 'node:http'\nimport { URL } from 'node:url'\nimport { UpstreamTimeoutError } from './errors'\nimport { filterResponseHeaders, MAX_FRAME_BYTES } from './protocol'\nimport type { UpstreamPhase } from './events'\nimport type { TunnelMeta } from '../types'\n\n/** Default idle limit on the operator's upstream socket. */\n/**\n * Above @seedprotocol/publish's 120 s batch upload, so an upload API that stays quiet while it\n * stores a large batch answers before the operator gives up on it.\n */\nexport const DEFAULT_UPSTREAM_TIMEOUT_MS = 130_000\n\nexport type ProxyTunnelRequestOptions = {\n /** Fail with `UpstreamTimeoutError` once the upstream socket is idle this long. 0 disables. */\n timeoutMs?: number\n /** Abort the upstream request, e.g. when the tunnel stream closes. */\n signal?: AbortSignal\n /** Headers set on the upstream request, replacing any the client sent under the same name. */\n extraHeaders?: Record<string, string>\n /** Progress callbacks, for tracing. */\n observer?: ProxyObserver\n}\n\nexport type ProxyObserver = {\n /** The request moved to `phase`: `send` from the start, `wait` once the body is sent, `body` on headers. */\n onPhase?: (phase: UpstreamPhase) => void\n onResponse?: (status: number) => void\n onDone?: (status: number, bytes: number) => void\n}\n\nfunction withHeaders(\n headers: Record<string, string>,\n extra: Record<string, string> | undefined,\n): Record<string, string> {\n if (!extra) return headers\n const names = new Set(Object.keys(extra).map((key) => key.toLowerCase()))\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (!names.has(key.toLowerCase())) out[key] = value\n }\n return { ...out, ...extra }\n}\n\n/** Headers that describe one hop and must not be forwarded (RFC 9110 section 7.6.1). */\nconst HOP_BY_HOP_HEADERS = new Set([\n 'connection',\n 'keep-alive',\n 'proxy-connection',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n])\n\n/**\n * Drop hop-by-hop headers and the client's `host`, which names the app server rather than\n * the upstream; it is passed on as `x-forwarded-host`. `content-length` is dropped because\n * Node sets it from the buffered body.\n */\nexport function upstreamRequestHeaders(headers: Record<string, string>): Record<string, string> {\n const named = new Set(\n (Object.entries(headers).find(([key]) => key.toLowerCase() === 'connection')?.[1] ?? '')\n .split(',')\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean),\n )\n const out: Record<string, string> = {}\n let host: string | undefined\n for (const [key, value] of Object.entries(headers)) {\n const lower = key.toLowerCase()\n if (lower === 'host') {\n host = value\n continue\n }\n if (lower === 'content-length' || HOP_BY_HOP_HEADERS.has(lower) || named.has(lower)) continue\n out[key] = value\n }\n if (host && !Object.keys(out).some((key) => key.toLowerCase() === 'x-forwarded-host')) {\n out['x-forwarded-host'] = host\n }\n return out\n}\n\nexport async function proxyTunnelRequest(\n upstreamBase: string,\n meta: TunnelMeta,\n body: Buffer,\n options: ProxyTunnelRequestOptions = {},\n): Promise<{ status: number; headers: Record<string, string>; body: Buffer }> {\n const base = upstreamBase.replace(/\\/$/, '')\n const targetUrl = new URL(meta.path, `${base}/`)\n const timeoutMs = options.timeoutMs ?? DEFAULT_UPSTREAM_TIMEOUT_MS\n const { observer } = options\n\n return new Promise((resolve, reject) => {\n observer?.onPhase?.('send')\n const req = http.request(\n targetUrl,\n {\n method: meta.method,\n headers: withHeaders(upstreamRequestHeaders(meta.headers), options.extraHeaders),\n signal: options.signal,\n },\n (res) => {\n if (observer) {\n observer.onPhase?.('body')\n observer.onResponse?.(res.statusCode ?? 502)\n }\n const chunks: Buffer[] = []\n let received = 0\n res.on('data', (chunk: Buffer) => {\n received += chunk.length\n if (received > MAX_FRAME_BYTES) {\n req.destroy(\n new Error(`Upstream response exceeds ${MAX_FRAME_BYTES} bytes, the tunnel's limit`),\n )\n return\n }\n chunks.push(chunk)\n })\n res.on('end', () => {\n observer?.onDone?.(res.statusCode ?? 502, received)\n resolve({\n status: res.statusCode ?? 502,\n headers: filterResponseHeaders(res.headers as Record<string, string | string[] | undefined>),\n body: Buffer.concat(chunks),\n })\n })\n res.on('error', reject)\n },\n )\n if (timeoutMs > 0) {\n req.setTimeout(timeoutMs, () => req.destroy(new UpstreamTimeoutError(timeoutMs)))\n }\n req.on('error', reject)\n if (observer?.onPhase) req.once('finish', () => observer.onPhase?.('wait'))\n if (body.length > 0) {\n req.end(body)\n } else {\n req.end()\n }\n })\n}\n","import net from 'node:net'\nimport { decodePublicKey } from '../keys'\n\nexport type AllowlistOptions = {\n /** Client public keys (z32 or hex) */\n keys?: string[]\n /** Client IPs or CIDR ranges, IPv4 or IPv6 */\n ips?: string[]\n}\n\n/** How a stream was let in, or `none` when it was refused. */\nexport type AllowedBy = 'open' | 'key' | 'ip' | 'none'\n\nexport type AllowlistVerdict = {\n ok: boolean\n /** The UDP address the connection uses, IPv4-mapped prefix removed */\n host: string | null\n by: AllowedBy\n}\n\n/** The parts of a HyperDHT stream the allowlist reads. */\nexport type AllowlistStream = {\n remotePublicKey?: Uint8Array | null\n rawStream?: { remoteHost?: string | null } | null\n}\n\nexport type Allowlist = {\n /** False when neither list has entries, so every client is let in. */\n enabled: boolean\n /** One line for logs, e.g. `keys=2 ips=192.168.1.0/24` or `off`. */\n summary: string\n /**\n * For HyperDHT's `firewall` server option: true refuses the client before it gets a stream.\n * Refuses only on the key, and only when no IP list is set, since the client's real address\n * is not known until the connection is up.\n */\n firewall: (remotePublicKey: Uint8Array) => boolean\n /** Check an established stream by key, then by address. */\n check: (stream: AllowlistStream) => AllowlistVerdict\n}\n\nfunction ipFamily(addr: string): 'ipv4' | 'ipv6' | null {\n if (net.isIPv4(addr)) return 'ipv4'\n if (net.isIPv6(addr)) return 'ipv6'\n return null\n}\n\nfunction parseIps(entries: string[]): net.BlockList {\n const list = new net.BlockList()\n for (const entry of entries) {\n const [addr = '', bits, extra] = entry.split('/')\n const family = ipFamily(addr)\n if (!family || extra !== undefined) {\n throw new Error(`Invalid allowlist IP (expected an address or CIDR): ${entry}`)\n }\n if (bits === undefined) {\n list.addAddress(addr, family)\n continue\n }\n const prefix = Number(bits)\n const max = family === 'ipv4' ? 32 : 128\n if (!/^\\d+$/.test(bits) || prefix > max) {\n throw new Error(`Invalid allowlist CIDR prefix (expected 0–${max}): ${entry}`)\n }\n list.addSubnet(addr, prefix, family)\n }\n return list\n}\n\nfunction parseKeys(entries: string[]): Set<string> {\n const keys = new Set<string>()\n for (const entry of entries) {\n keys.add(decodePublicKey(entry).toString('hex'))\n }\n return keys\n}\n\n/** Strip the IPv4-mapped IPv6 prefix so `::ffff:1.2.3.4` matches an IPv4 entry. */\nfunction normalizeHost(host: string | null | undefined): string | null {\n if (!host) return null\n const mapped = host.toLowerCase().startsWith('::ffff:') ? host.slice(7) : null\n return mapped && net.isIPv4(mapped) ? mapped : host\n}\n\nfunction cleanEntries(entries: string[] | undefined): string[] {\n return (entries ?? []).map((entry) => entry.trim()).filter(Boolean)\n}\n\n/**\n * Decide which HyperDHT clients may use a tunnel. A client is let in when its public key or\n * its address is listed; with both lists empty, every client is. Invalid entries throw.\n *\n * The address is the UDP address the connection uses. A client on the operator's LAN\n * connects over the LAN and shows a private address, and a connection relayed through\n * another HyperDHT node shows the relay's address, so it is refused unless its key is listed.\n */\nexport function createAllowlist(options: AllowlistOptions = {}): Allowlist {\n const keyEntries = cleanEntries(options.keys)\n const ipEntries = cleanEntries(options.ips)\n const keys = parseKeys(keyEntries)\n const ips = parseIps(ipEntries)\n const enabled = keys.size > 0 || ipEntries.length > 0\n\n const keyAllowed = (publicKey: Uint8Array | null | undefined): boolean =>\n publicKey != null && keys.has(Buffer.from(publicKey).toString('hex'))\n\n const ipAllowed = (host: string | null): boolean => {\n if (!host) return false\n const family = ipFamily(host)\n return family !== null && ips.check(host, family)\n }\n\n return {\n enabled,\n summary: enabled ? `keys=${keys.size} ips=${ipEntries.join(',') || 'none'}` : 'off',\n firewall: (remotePublicKey) => {\n if (!enabled || ipEntries.length > 0) return false\n return !keyAllowed(remotePublicKey)\n },\n check: (stream) => {\n const host = normalizeHost(stream.rawStream?.remoteHost)\n if (!enabled) return { ok: true, host, by: 'open' }\n if (keyAllowed(stream.remotePublicKey)) return { ok: true, host, by: 'key' }\n if (ipAllowed(host)) return { ok: true, host, by: 'ip' }\n return { ok: false, host, by: 'none' }\n },\n }\n}\n","import type { AllowedBy } from './allowlist'\n\n/** Where an upstream request was when it failed. */\nexport type UpstreamPhase = 'send' | 'wait' | 'body'\n\n/**\n * Events from `serveTunnel`'s `onEvent`. Streams are `s<N>`; requests carry the id that\n * `requestIdHeader` sends upstream. Times are milliseconds; `totalMs` and `receiveMs` count\n * from the request's first byte on the stream.\n */\nexport type TunnelEvent =\n | {\n name: 'stream.open'\n stream: string\n /** Client public key (z32) */\n remoteKey: string\n /** UDP address the connection uses */\n host: string | null\n by: AllowedBy\n }\n | {\n name: 'stream.refused'\n remoteKey: string\n /**\n * `handshake`: refused by key in HyperDHT's firewall, before a stream exists, so the\n * client's address is unknown and `host` is null. `connection`: refused once connected.\n */\n stage: 'handshake' | 'connection'\n host: string | null\n }\n | {\n name: 'stream.close'\n stream: string\n requests: number\n bytesIn: number\n durationMs: number\n /** The request in flight when the stream closed, if any */\n inflight: string | null\n }\n | { name: 'stream.error'; stream: string; error: string; code?: string }\n | {\n name: 'req.recv'\n request: string\n stream: string\n method: string\n path: string\n bodyBytes: number\n /** From the request's first byte to its last */\n receiveMs: number\n }\n | { name: 'up.response'; request: string; status: number; upstreamMs: number }\n | { name: 'up.done'; request: string; status: number; bytes: number; upstreamMs: number }\n | {\n name: 'up.error'\n request: string\n phase: UpstreamPhase\n error: string\n code?: string\n /** True when the tunnel stream closed and the operator aborted the upstream request */\n aborted: boolean\n }\n | { name: 'up.timeout'; request: string; phase: UpstreamPhase; timeoutMs: number }\n | { name: 'res.sent'; request: string; status: number; bytes: number; totalMs: number }\n\nexport type TunnelEventName = TunnelEvent['name']\n\n/** Called synchronously; a throw is caught and dropped so tracing can't break the tunnel. */\nexport type TunnelEventListener = (event: TunnelEvent) => void\n\n/** Bytes are counted from the HTTP client's side: up is toward the upstream. */\ntype RawCloseFields = {\n conn: string\n bytesUp: number\n bytesDown: number\n durationMs: number\n /** First error on either side, as `hyper:<code>`, `tcp:<code>` or `connect-timeout` */\n error: string | null\n}\n\n/** Events from `serveRawTunnel`'s `onEvent`. Connections are `c<N>`. */\nexport type RawTunnelEvent =\n | {\n name: 'conn.open'\n conn: string\n remoteKey: string\n host: string | null\n by: AllowedBy\n }\n | Extract<TunnelEvent, { name: 'stream.refused' }>\n | ({ name: 'conn.close' } & RawCloseFields)\n\nexport type RawTunnelEventListener = (event: RawTunnelEvent) => void\n\n/** Events from `connectRawTunnel`'s `onEvent`, one TCP connection each. */\nexport type RawClientEvent =\n | { name: 'conn.open'; conn: string; connectMs: number }\n /** A TCP connection refused because `maxConnections` were already open */\n | { name: 'conn.refused'; open: number; max: number }\n | ({ name: 'conn.close' } & RawCloseFields)\n\nexport type RawClientEventListener = (event: RawClientEvent) => void\n\n/** Wrap `onEvent` so a throw is dropped; undefined when there is no listener. */\nexport function safeEmitter<E>(onEvent: ((event: E) => void) | undefined): ((event: E) => void) | undefined {\n if (!onEvent) return undefined\n return (event) => {\n try {\n onEvent(event)\n } catch {\n // Tracing must not break the tunnel.\n }\n }\n}\n\nexport function errorFields(err: unknown): { error: string; code?: string } {\n const error = err instanceof Error ? err.message : String(err)\n const code = (err as { code?: unknown } | null)?.code\n return typeof code === 'string' ? { error, code } : { error }\n}\n\n/** A run-unique prefix, so request ids from different operator runs never collide. */\nexport function createIdSource(): { stream: () => string; request: () => string } {\n const run = Date.now().toString(36)\n let streams = 0\n let requests = 0\n return {\n stream: () => `s${++streams}`,\n request: () => `${run}-r${++requests}`,\n }\n}\n\n/** One `tunnel-trace` line per event: ISO time, event name, then `key=value` fields. */\nexport function formatTunnelEvent(\n event: TunnelEvent | RawTunnelEvent | RawClientEvent,\n now = new Date(),\n): string {\n const parts = [now.toISOString(), 'tunnel-trace', event.name]\n for (const [key, value] of Object.entries(event)) {\n if (key === 'name' || value === undefined || value === null) continue\n const text = String(value)\n parts.push(`${key}=${/[\\s\"=]/.test(text) ? JSON.stringify(text) : text}`)\n }\n return parts.join(' ')\n}\n","import type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { readTunnelRequest, writeTunnelResponse } from './protocol'\nimport { UpstreamTimeoutError } from './errors'\nimport { proxyTunnelRequest } from './proxy'\nimport { createAllowlist } from './allowlist'\nimport {\n createIdSource,\n errorFields,\n safeEmitter,\n type TunnelEventListener,\n type UpstreamPhase,\n} from './events'\nimport { encodePublicKeyZ32, resolveOperatorKeypair } from '../keys'\nimport type { ServeTunnelOptions, ServeTunnelResult } from '../types'\n\nexport type TunnelConnectionOptions = {\n /** Idle limit on each upstream request. Default 130 000; 0 disables. */\n upstreamTimeoutMs?: number\n /** Receives this stream's events; see `TunnelEvent`. */\n onEvent?: TunnelEventListener\n /** Send each request's id upstream under this header. */\n requestIdHeader?: string\n /** Id for this stream in events. Default `s0`. */\n streamId?: string\n /** Makes request ids. Default: run-unique ids from `createIdSource`. */\n nextRequestId?: () => string\n}\n\nfunction errorResponse(\n status: number,\n message: string,\n): { status: number; headers: Record<string, string>; body: Buffer } {\n return {\n status,\n headers: { 'content-type': 'text/plain; charset=utf-8' },\n body: Buffer.from(message, 'utf8'),\n }\n}\n\n/**\n * Answer framed requests on one tunnel stream, in order, until it closes. Upstream timeouts\n * become 504 and other upstream failures 502, so the stream stays usable; only a broken\n * stream ends the loop. The stream ending or closing aborts the upstream request in flight.\n *\n * Without `onEvent` or `requestIdHeader`, no tracing state is kept and no listeners are added.\n */\nexport async function serveTunnelConnection(\n socket: Duplex,\n upstream: string,\n options: TunnelConnectionOptions = {},\n): Promise<void> {\n const emit = safeEmitter(options.onEvent)\n const { requestIdHeader } = options\n const streamId = options.streamId ?? 's0'\n const nextRequestId =\n emit || requestIdHeader ? (options.nextRequestId ?? createIdSource().request) : null\n\n // Clients never half-close, so `end` means the peer is gone, same as `close` or `error`.\n const closed = new AbortController()\n const onGone = () => closed.abort()\n socket.once('end', onGone)\n socket.once('close', onGone)\n socket.on('error', onGone)\n\n // Tracing state, kept only when someone listens.\n const opened = performance.now()\n let requests = 0\n let bytesIn = 0\n let firstByteAt: number | null = null\n let inflight: string | null = null\n if (emit) {\n // A second data listener beside the frame reader's: byte counts, and when each request's\n // first byte arrived, so receive time leaves out idle time between requests.\n socket.on('data', (chunk: Buffer) => {\n bytesIn += chunk.length\n firstByteAt ??= performance.now()\n })\n socket.on('error', (err: unknown) => emit({ name: 'stream.error', stream: streamId, ...errorFields(err) }))\n socket.once('close', () =>\n emit({\n name: 'stream.close',\n stream: streamId,\n requests,\n bytesIn,\n durationMs: Math.round(performance.now() - opened),\n inflight,\n }),\n )\n }\n\n // Attach framed readers synchronously so HyperDHT early bytes are not lost.\n let pendingRequest = readTunnelRequest(socket)\n try {\n while (true) {\n const { meta, body } = await pendingRequest\n const requestId = nextRequestId?.() ?? null\n const t0 = firstByteAt ?? performance.now()\n firstByteAt = null\n let upstreamStart = 0\n let phase: UpstreamPhase = 'send'\n if (emit && requestId) {\n requests++\n inflight = requestId\n emit({\n name: 'req.recv',\n request: requestId,\n stream: streamId,\n method: meta.method,\n path: meta.path,\n bodyBytes: body.length,\n receiveMs: Math.round(performance.now() - t0),\n })\n upstreamStart = performance.now()\n }\n const upstreamMs = () => Math.round(performance.now() - upstreamStart)\n\n let result: { status: number; headers: Record<string, string>; body: Buffer }\n try {\n result = await proxyTunnelRequest(upstream, meta, body, {\n timeoutMs: options.upstreamTimeoutMs,\n signal: closed.signal,\n extraHeaders:\n requestIdHeader && requestId ? { [requestIdHeader]: requestId } : undefined,\n observer:\n emit && requestId\n ? {\n onPhase: (next) => {\n // `finish` can land after the response started; keep the later phase.\n if (!(next === 'wait' && phase === 'body')) phase = next\n },\n onResponse: (status) =>\n emit({ name: 'up.response', request: requestId, status, upstreamMs: upstreamMs() }),\n onDone: (status, bytes) =>\n emit({ name: 'up.done', request: requestId, status, bytes, upstreamMs: upstreamMs() }),\n }\n : undefined,\n })\n } catch (err) {\n if (emit && requestId) {\n emit(\n err instanceof UpstreamTimeoutError && !closed.signal.aborted\n ? { name: 'up.timeout', request: requestId, phase, timeoutMs: err.timeoutMs }\n : {\n name: 'up.error',\n request: requestId,\n phase,\n ...errorFields(err),\n aborted: closed.signal.aborted,\n },\n )\n }\n if (closed.signal.aborted) return\n const message = err instanceof Error ? err.message : String(err)\n result =\n err instanceof UpstreamTimeoutError\n ? errorResponse(504, `Gateway Hyper upstream timeout: ${message}`)\n : errorResponse(502, `Gateway Hyper upstream error: ${message}`)\n }\n await writeTunnelResponse(\n socket,\n { status: result.status, headers: result.headers },\n result.body,\n )\n if (emit && requestId) {\n inflight = null\n emit({\n name: 'res.sent',\n request: requestId,\n status: result.status,\n bytes: result.body.length,\n totalMs: Math.round(performance.now() - t0),\n })\n }\n pendingRequest = readTunnelRequest(socket)\n }\n } catch {\n socket.destroy()\n }\n}\n\n/**\n * Operator: accept Hyperswarm/DHT connections and proxy framed HTTP to the upstream.\n */\nexport async function serveTunnel(options: ServeTunnelOptions): Promise<ServeTunnelResult> {\n const upstream = options.upstream.replace(/\\/$/, '')\n if (/^https:\\/\\//i.test(upstream)) {\n throw new Error(\n `serveTunnel speaks plain HTTP to its upstream; got ${upstream}. Point it at an http:// origin, or use serveRawTunnel (seed-gateway serve --raw) to carry TLS end to end.`,\n )\n }\n const allowlist = createAllowlist(options.allowlist)\n const keypair = resolveOperatorKeypair(options, '.seed/gateway-tunnel/operator.key.json')\n const emit = safeEmitter(options.onEvent)\n const ids = createIdSource()\n\n const dht = new HyperDHT(options.bootstrap ? { bootstrap: options.bootstrap } : {})\n const server = dht.createServer(\n {\n firewall: (remotePublicKey) => {\n const refuse = allowlist.firewall(remotePublicKey)\n if (refuse && emit) {\n emit({\n name: 'stream.refused',\n remoteKey: encodePublicKeyZ32(remotePublicKey),\n stage: 'handshake',\n host: null,\n })\n }\n return refuse\n },\n },\n (socket) => {\n const verdict = allowlist.check(socket)\n // Refuse before serveTunnelConnection attaches a reader, so nothing is read. The client\n // gets a 403 for the request it sends, rather than a reset it can't tell from a network\n // failure.\n if (!verdict.ok) {\n emit?.({\n name: 'stream.refused',\n remoteKey: encodePublicKeyZ32(socket.remotePublicKey),\n stage: 'connection',\n host: verdict.host,\n })\n socket.on('error', () => {})\n const refusal = errorResponse(\n 403,\n `Gateway Hyper: this operator's allowlist doesn't include this client (address ${verdict.host ?? 'unknown'}).`,\n )\n writeTunnelResponse(socket, { status: refusal.status, headers: refusal.headers }, refusal.body)\n .then(() => socket.end())\n .catch(() => socket.destroy())\n return\n }\n const streamId = ids.stream()\n emit?.({\n name: 'stream.open',\n stream: streamId,\n remoteKey: encodePublicKeyZ32(socket.remotePublicKey),\n host: verdict.host,\n by: verdict.by,\n })\n void serveTunnelConnection(socket, upstream, {\n upstreamTimeoutMs: options.upstreamTimeoutMs,\n onEvent: options.onEvent,\n requestIdHeader: options.requestIdHeader,\n streamId,\n nextRequestId: ids.request,\n })\n },\n )\n\n await server.listen(keypair)\n\n return {\n key: keypair.z32,\n upstream,\n close: async () => {\n await server.close()\n await dht.destroy()\n },\n }\n}\n","import type net from 'node:net'\nimport type { Duplex } from 'node:stream'\n\n/** How long a side may stay open after the other closed cleanly before it is destroyed. */\nexport const RAW_CLOSE_GRACE_MS = 30_000\n\nexport type RawPipeResult = {\n /** Bytes read from the TCP socket and sent over the HyperDHT stream */\n tcpBytesRead: number\n /** Bytes from the HyperDHT stream written to the TCP socket */\n tcpBytesWritten: number\n durationMs: number\n /** First error on either side, as `hyper:<code>` or `tcp:<code>`, or null */\n error: string | null\n}\n\nexport type RawPipe = {\n /** Record an error that is about to destroy one side, e.g. a connect timeout. */\n fail: (label: string) => void\n}\n\nfunction errorLabel(side: 'hyper' | 'tcp', err: unknown): string {\n const code = (err as { code?: unknown } | null)?.code\n const message = err instanceof Error ? err.message : String(err)\n return `${side}:${typeof code === 'string' ? code : message}`\n}\n\n/**\n * Copy bytes both ways between a HyperDHT stream and a TCP socket until both close.\n *\n * HyperDHT → TCP is forwarded by hand with backpressure, not with `stream.pipe(socket)`:\n * streamx's pipe destroys its source with PREMATURE_CLOSE when the destination closes first,\n * so the far end would see a reset even though every byte arrived. TCP → HyperDHT uses Node's\n * `pipe`, which has no such problem. Half-closes pass through, so an HTTP connection ends the\n * way its client or server ended it; `tcp` must be created with `allowHalfOpen: true` for that.\n *\n * When one side closes cleanly, the other is ended so data already written still arrives,\n * and destroyed only if it is still open `graceMs` later. After an error on either side, the\n * other is destroyed at once.\n */\nexport function pipeRaw(\n hyper: Duplex,\n tcp: net.Socket,\n options: { graceMs?: number; onClose?: (result: RawPipeResult) => void } = {},\n): RawPipe {\n const graceMs = options.graceMs ?? RAW_CLOSE_GRACE_MS\n const started = performance.now()\n let error: string | null = null\n let closed = 0\n\n hyper.on('data', (chunk: Buffer) => {\n if (!tcp.write(chunk)) hyper.pause()\n })\n tcp.on('drain', () => hyper.resume())\n hyper.on('end', () => tcp.end())\n tcp.pipe(hyper)\n\n hyper.on('error', (err: unknown) => {\n error ??= errorLabel('hyper', err)\n })\n tcp.on('error', (err: unknown) => {\n error ??= errorLabel('tcp', err)\n })\n\n const closeOther = (other: Duplex) => () => {\n if (other.destroyed) return\n if (error) {\n other.destroy()\n return\n }\n other.end()\n setTimeout(() => other.destroy(), graceMs).unref()\n }\n const onClose = () => {\n if (++closed < 2) return\n options.onClose?.({\n tcpBytesRead: tcp.bytesRead,\n tcpBytesWritten: tcp.bytesWritten,\n durationMs: Math.round(performance.now() - started),\n error,\n })\n }\n hyper.on('close', closeOther(tcp))\n tcp.on('close', closeOther(hyper))\n hyper.on('close', onClose)\n tcp.on('close', onClose)\n\n return {\n fail: (label) => {\n error ??= label\n },\n }\n}\n","import net from 'node:net'\nimport type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { createAllowlist } from './allowlist'\nimport { safeEmitter } from './events'\nimport { pipeRaw } from './rawPipe'\nimport { encodePublicKeyZ32, resolveOperatorKeypair } from '../keys'\nimport type { ServeRawTunnelOptions, ServeRawTunnelResult } from '../types'\n\n/** Default operator key file for raw mode, apart from the framed tunnel's key. */\nexport const DEFAULT_RAW_OPERATOR_KEY_FILE = '.seed/gateway-tunnel/raw-operator.key.json'\n\n/** `http://host:port`, `https://host`, or `host:port`, as a TCP address. */\nexport function parseTcpUpstream(upstream: string): { host: string; port: number } {\n const text = upstream.trim()\n const url = new URL(/^[a-z][a-z0-9+.-]*:\\/\\//i.test(text) ? text : `tcp://${text}`)\n const defaultPort = url.protocol === 'http:' ? 80 : url.protocol === 'https:' ? 443 : NaN\n const port = url.port ? Number(url.port) : defaultPort\n const host = url.hostname.replace(/^\\[(.*)\\]$/, '$1')\n if (!host || !Number.isInteger(port)) {\n throw new Error(`Invalid raw upstream (expected http://host:port or host:port): ${upstream}`)\n }\n return { host, port }\n}\n\n/**\n * Operator, raw mode: pipe each HyperDHT stream byte for byte to a new TCP connection to the\n * upstream. There is no framing, so HTTP/1.1 runs end to end between the client's HTTP client\n * and the upstream: keep-alive, streamed bodies of any size, `Range` and WebSockets all work.\n *\n * Clients must use `connectRawTunnel`; a framed `connectTunnel` client can't talk to this\n * server, so it listens on its own key.\n */\nexport async function serveRawTunnel(options: ServeRawTunnelOptions): Promise<ServeRawTunnelResult> {\n const upstream = parseTcpUpstream(options.upstream)\n const allowlist = createAllowlist(options.allowlist)\n const keypair = resolveOperatorKeypair(options, DEFAULT_RAW_OPERATOR_KEY_FILE)\n const emit = safeEmitter(options.onEvent)\n const open = new Set<Duplex>()\n let connections = 0\n\n const dht = new HyperDHT(options.bootstrap ? { bootstrap: options.bootstrap } : {})\n const server = dht.createServer(\n {\n firewall: (remotePublicKey) => {\n const refuse = allowlist.firewall(remotePublicKey)\n if (refuse && emit) {\n emit({\n name: 'stream.refused',\n remoteKey: encodePublicKeyZ32(remotePublicKey),\n stage: 'handshake',\n host: null,\n })\n }\n return refuse\n },\n },\n (stream) => {\n const verdict = allowlist.check(stream)\n if (!verdict.ok) {\n emit?.({\n name: 'stream.refused',\n remoteKey: encodePublicKeyZ32(stream.remotePublicKey),\n stage: 'connection',\n host: verdict.host,\n })\n stream.on('error', () => {})\n stream.destroy()\n return\n }\n const conn = `c${++connections}`\n emit?.({\n name: 'conn.open',\n conn,\n remoteKey: encodePublicKeyZ32(stream.remotePublicKey),\n host: verdict.host,\n by: verdict.by,\n })\n // allowHalfOpen, so an upstream that half-closes can still read what the client sends.\n const socket = net.connect({ host: upstream.host, port: upstream.port, allowHalfOpen: true })\n socket.setNoDelay(true)\n open.add(stream)\n open.add(socket)\n pipeRaw(stream, socket, {\n graceMs: options.closeGraceMs,\n onClose: (result) => {\n open.delete(stream)\n open.delete(socket)\n emit?.({\n name: 'conn.close',\n conn,\n bytesUp: result.tcpBytesWritten,\n bytesDown: result.tcpBytesRead,\n durationMs: result.durationMs,\n error: result.error,\n })\n },\n })\n },\n )\n\n await server.listen(keypair)\n\n return {\n key: keypair.z32,\n upstream: `${upstream.host}:${upstream.port}`,\n close: async () => {\n for (const side of open) side.destroy()\n await server.close()\n await dht.destroy()\n },\n }\n}\n","import type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { decodePublicKey, resolveClientKeypair } from '../keys'\nimport type { DhtBootstrapNode, TunnelMeta, TunnelResponseMeta } from '../types'\nimport { TunnelConnectTimeoutError, TunnelTimeoutError } from './errors'\nimport { readTunnelResponse, writeTunnelRequest } from './protocol'\n\n/** Default idle limit for one request on the tunnel stream. */\n/** Above the operator's default upstream timeout (130 s), so the operator's own 504 arrives first. */\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 150_000\n\n/** Default limit for opening the HyperDHT connection. */\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 30_000\n\n/**\n * Default number of streams a session opens to the operator. Streams open only when every\n * open one is busy, and each costs a DHT lookup and handshake plus an upstream request on\n * the operator, so a small pool keeps that cost down. Four leaves room for a page's burst of\n * gateway reads after one or two requests hang until their timeout.\n */\nexport const DEFAULT_MAX_STREAMS = 4\n\nexport type TunnelSessionOptions = {\n /** Operator public key (z32 or hex) */\n key: string\n /**\n * Keypair file (created on first run if missing) for the key this client presents to the\n * operator. Without `keyFile` or `keyPair`, each session uses a new key.\n */\n keyFile?: string\n /** The client keypair itself. Exclusive with `keyFile`. */\n keyPair?: { publicKey: Buffer; secretKey: Buffer }\n /** HyperDHT bootstrap nodes. Defaults to the public network. */\n bootstrap?: DhtBootstrapNode[]\n /**\n * Abandon a request once the stream has sent and received nothing for this long, then\n * reconnect for the next request. Counts from when the request gets a stream.\n * Default 150 000; 0 disables.\n */\n requestTimeoutMs?: number\n /** Give up opening the HyperDHT connection after this long. Default 30 000; 0 disables. */\n connectTimeoutMs?: number\n /**\n * Most HyperDHT streams open to the operator at once; each carries one request at a time.\n * Default 4.\n */\n maxStreams?: number\n}\n\nexport type TunnelRequestOptions = {\n /** Aborting rejects the request; if it is already on the stream, the stream is destroyed. */\n signal?: AbortSignal\n /** Overrides the session's `requestTimeoutMs` for this request. */\n timeoutMs?: number\n}\n\nexport type TunnelSession = {\n key: string\n /** The public key (z32) this client presents to the operator */\n clientKey: string\n runOnTunnel: <T>(\n fn: (socket: Duplex) => Promise<T>,\n options?: TunnelRequestOptions,\n ) => Promise<T>\n forwardHttp: (\n meta: TunnelMeta,\n body: Buffer,\n options?: TunnelRequestOptions,\n ) => Promise<{ meta: TunnelResponseMeta; body: Buffer }>\n close: () => Promise<void>\n}\n\nfunction abortError(signal: AbortSignal): Error {\n const reason: unknown = signal.reason\n if (reason instanceof Error) return reason\n const err = new Error('The request was aborted')\n err.name = 'AbortError'\n return err\n}\n\n/** Reject as soon as `signal` aborts, even while `promise` is still queued. */\nfunction raceAbort<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(abortError(signal))\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortError(signal))\n signal.addEventListener('abort', onAbort, { once: true })\n promise.then(\n (value) => {\n signal.removeEventListener('abort', onAbort)\n resolve(value)\n },\n (err: unknown) => {\n signal.removeEventListener('abort', onAbort)\n reject(err)\n },\n )\n })\n}\n\n/**\n * Serialize transactions on the socket `getSocket` returns. Each runs under an idle timeout\n * and an optional abort signal; one that is abandoned or fails destroys the socket, so\n * `getSocket` must open a new one once the current one is destroyed.\n */\nexport function createTunnelQueue(\n getSocket: () => Promise<Duplex>,\n requestTimeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,\n): TunnelSession['runOnTunnel'] {\n const runGuarded = async <T>(\n fn: (socket: Duplex) => Promise<T>,\n opts: TunnelRequestOptions,\n ): Promise<T> => {\n const { signal } = opts\n const timeoutMs = opts.timeoutMs ?? requestTimeoutMs\n if (signal?.aborted) throw abortError(signal)\n const socket = await getSocket()\n if (signal?.aborted) throw abortError(signal)\n\n return new Promise<T>((resolve, reject) => {\n let settled = false\n let timer: ReturnType<typeof setTimeout> | null = null\n const armTimer = () => {\n if (timeoutMs <= 0) return\n if (timer) clearTimeout(timer)\n timer = setTimeout(() => abandon(new TunnelTimeoutError(timeoutMs)), timeoutMs)\n }\n const cleanup = () => {\n if (timer) clearTimeout(timer)\n socket.off('data', armTimer)\n socket.off('drain', armTimer)\n signal?.removeEventListener('abort', onAbort)\n }\n // A late response would be read as the next request's reply, so the stream goes too.\n const abandon = (err: unknown) => {\n if (settled) return\n settled = true\n cleanup()\n socket.destroy()\n reject(err)\n }\n const onAbort = () => abandon(abortError(signal as AbortSignal))\n\n armTimer()\n // Bytes moving either way keep the request alive, so large bodies are not cut off.\n socket.on('data', armTimer)\n socket.on('drain', armTimer)\n signal?.addEventListener('abort', onAbort, { once: true })\n\n fn(socket).then((value) => {\n if (settled) return\n settled = true\n cleanup()\n resolve(value)\n }, abandon)\n })\n }\n\n let tunnelQueue: Promise<unknown> = Promise.resolve()\n return <T>(\n fn: (socket: Duplex) => Promise<T>,\n opts: TunnelRequestOptions = {},\n ): Promise<T> => {\n const run = tunnelQueue.then(() => runGuarded(fn, opts))\n tunnelQueue = run.catch(() => {})\n return opts.signal ? raceAbort(run, opts.signal) : run\n }\n}\n\ntype PoolSlot = {\n run: TunnelSession['runOnTunnel']\n socket: Duplex | null\n busy: boolean\n}\n\ntype PoolWaiter = {\n resolve: (slot: PoolSlot) => void\n reject: (err: Error) => void\n}\n\nexport type TunnelPoolOptions = {\n /** Most streams open at once. Default `DEFAULT_MAX_STREAMS`. */\n maxStreams?: number\n /** Idle limit for each request, as in `createTunnelQueue`. */\n requestTimeoutMs?: number\n}\n\nexport type TunnelPool = {\n runOnTunnel: TunnelSession['runOnTunnel']\n /** Reject queued requests and destroy every stream. */\n close: () => void\n}\n\nfunction closedError(): Error {\n return new Error('Tunnel session is closed')\n}\n\n/**\n * Run each transaction alone on one of up to `maxStreams` sockets from `openStream`. A request\n * takes an idle open stream if there is one, else opens a new stream while fewer than\n * `maxStreams` exist, else waits for the first stream to come free. Each stream is a\n * `createTunnelQueue`, so a request that is abandoned destroys only its own stream, and that\n * stream reopens for the next request it takes.\n */\nexport function createTunnelPool(\n openStream: () => Promise<Duplex>,\n options: TunnelPoolOptions = {},\n): TunnelPool {\n const maxStreams = Math.max(1, Math.floor(options.maxStreams ?? DEFAULT_MAX_STREAMS))\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS\n const slots: PoolSlot[] = []\n const waiting: PoolWaiter[] = []\n let closed = false\n\n const addSlot = (): PoolSlot => {\n const slot: PoolSlot = { run: null as never, socket: null, busy: false }\n // The slot runs one request at a time, so there is never a second open in progress.\n slot.run = createTunnelQueue(async () => {\n if (closed) throw closedError()\n if (slot.socket && !slot.socket.destroyed) return slot.socket\n const socket = await openStream()\n if (closed) {\n socket.destroy()\n throw closedError()\n }\n slot.socket = socket\n return socket\n }, requestTimeoutMs)\n slots.push(slot)\n return slot\n }\n\n const takeSlot = (): PoolSlot | null => {\n const idle = slots.filter((slot) => !slot.busy)\n // An idle slot whose stream died reopens it, same as a new slot would.\n const slot =\n idle.find((s) => s.socket !== null && !s.socket.destroyed) ??\n idle[0] ??\n (slots.length < maxStreams ? addSlot() : undefined)\n if (!slot) return null\n slot.busy = true\n return slot\n }\n\n const release = (slot: PoolSlot) => {\n const next = waiting.shift()\n if (next) next.resolve(slot)\n else slot.busy = false\n }\n\n const acquire = (signal: AbortSignal | undefined): Promise<PoolSlot> => {\n const slot = takeSlot()\n if (slot) return Promise.resolve(slot)\n return new Promise<PoolSlot>((resolve, reject) => {\n const onAbort = () => {\n const index = waiting.indexOf(waiter)\n if (index >= 0) waiting.splice(index, 1)\n reject(abortError(signal as AbortSignal))\n }\n const waiter: PoolWaiter = {\n resolve: (s) => {\n signal?.removeEventListener('abort', onAbort)\n resolve(s)\n },\n reject: (err) => {\n signal?.removeEventListener('abort', onAbort)\n reject(err)\n },\n }\n waiting.push(waiter)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n }\n\n const runOnTunnel = async <T>(\n fn: (socket: Duplex) => Promise<T>,\n opts: TunnelRequestOptions = {},\n ): Promise<T> => {\n if (opts.signal?.aborted) throw abortError(opts.signal)\n if (closed) throw closedError()\n const slot = await acquire(opts.signal)\n try {\n return await slot.run(fn, opts)\n } finally {\n release(slot)\n }\n }\n\n return {\n runOnTunnel,\n close: () => {\n closed = true\n for (const waiter of waiting.splice(0)) waiter.reject(closedError())\n for (const slot of slots) slot.socket?.destroy()\n },\n }\n}\n\n/**\n * Dial the operator HyperDHT key and expose a request/response forwarder.\n *\n * Requests run on a pool of up to `maxStreams` streams to the operator, one request per\n * stream at a time (see `createTunnelPool`). The framing has no request ids, so a request\n * that is abandoned mid-flight (timeout, abort, or error) destroys its stream; the next\n * request on that stream opens a new one.\n */\nexport async function createTunnelSession(options: TunnelSessionOptions): Promise<TunnelSession> {\n const remotePublicKey = decodePublicKey(options.key)\n const clientKey = resolveClientKeypair(options)\n const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS\n\n const dht = new HyperDHT(options.bootstrap ? { bootstrap: options.bootstrap } : {})\n\n // Without a `pool` option, each connect is its own handshake and encrypted stream.\n const openStream = () =>\n new Promise<Duplex>((resolve, reject) => {\n const socket = dht.connect(remotePublicKey, {\n keyPair: { publicKey: clientKey.publicKey, secretKey: clientKey.secretKey },\n }) as Duplex\n let settled = false\n const timer =\n connectTimeoutMs > 0\n ? setTimeout(() => fail(new TunnelConnectTimeoutError(connectTimeoutMs)), connectTimeoutMs)\n : null\n const fail = (err: Error) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n socket.destroy()\n reject(err)\n }\n const onReady = () => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n resolve(socket)\n }\n // Keep a listener for the socket's whole life so a late error never goes unhandled;\n // the frame reader sees errors through its own listener.\n socket.on('error', (err: Error) => fail(err))\n socket.once('close', () => fail(new Error('Tunnel connection closed before it opened')))\n socket.once('open', onReady)\n socket.once('connect', onReady)\n })\n\n const pool = createTunnelPool(openStream, {\n maxStreams: options.maxStreams,\n requestTimeoutMs: options.requestTimeoutMs,\n })\n const { runOnTunnel } = pool\n\n const forwardHttp = async (\n meta: TunnelMeta,\n body: Buffer,\n opts?: TunnelRequestOptions,\n ): Promise<{ meta: TunnelResponseMeta; body: Buffer }> => {\n return runOnTunnel(async (socket) => {\n await writeTunnelRequest(socket, meta, body)\n return readTunnelResponse(socket)\n }, opts)\n }\n\n return {\n key: options.key,\n clientKey: clientKey.z32,\n runOnTunnel,\n forwardHttp,\n close: async () => {\n pool.close()\n await dht.destroy()\n },\n }\n}\n","import net from 'node:net'\nimport HyperDHT from 'hyperdht'\nimport { decodePublicKey, resolveClientKeypair } from '../keys'\nimport { safeEmitter } from './events'\nimport { TunnelConnectTimeoutError } from './errors'\nimport { pipeRaw } from './rawPipe'\nimport { DEFAULT_CONNECT_TIMEOUT_MS } from './session'\nimport type { ConnectRawTunnelOptions, ConnectRawTunnelResult } from '../types'\n\n/** Default local port for `connectRawTunnel`, next to the framed sidecar's 1984. */\nexport const DEFAULT_RAW_PORT = 1985\n\n/** Default limit on TCP connections open through one raw client at once. */\nexport const DEFAULT_MAX_RAW_CONNECTIONS = 64\n\n/**\n * Client, raw mode: listen on a local TCP port and carry each connection over its own\n * HyperDHT stream to a `serveRawTunnel` operator, which pipes it to its upstream.\n *\n * Each connection costs a DHT lookup and handshake (about 100–400 ms), so put an HTTP client\n * that keeps connections alive in front, such as nginx with `keepalive` on its upstream.\n */\nexport async function connectRawTunnel(options: ConnectRawTunnelOptions): Promise<ConnectRawTunnelResult> {\n const remotePublicKey = decodePublicKey(options.key)\n const clientKey = resolveClientKeypair(options)\n const host = options.host ?? '127.0.0.1'\n const maxConnections = options.maxConnections ?? DEFAULT_MAX_RAW_CONNECTIONS\n const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS\n const emit = safeEmitter(options.onEvent)\n const open = new Set<net.Socket>()\n let connections = 0\n\n const dht = new HyperDHT(options.bootstrap ? { bootstrap: options.bootstrap } : {})\n\n // allowHalfOpen: a client that half-closes after its request still gets the response.\n // Node's default would end our side as soon as the client's side ended.\n const server = net.createServer({ allowHalfOpen: true }, (socket) => {\n if (open.size >= maxConnections) {\n emit?.({ name: 'conn.refused', open: open.size, max: maxConnections })\n socket.destroy()\n return\n }\n const conn = `c${++connections}`\n const started = performance.now()\n open.add(socket)\n socket.setNoDelay(true)\n\n const stream = dht.connect(remotePublicKey, {\n keyPair: { publicKey: clientKey.publicKey, secretKey: clientKey.secretKey },\n })\n let timer: ReturnType<typeof setTimeout> | null = null\n const pipe = pipeRaw(stream, socket, {\n graceMs: options.closeGraceMs,\n onClose: (result) => {\n if (timer) clearTimeout(timer)\n open.delete(socket)\n emit?.({\n name: 'conn.close',\n conn,\n bytesUp: result.tcpBytesRead,\n bytesDown: result.tcpBytesWritten,\n durationMs: result.durationMs,\n error: result.error,\n })\n },\n })\n // A refused or unreachable operator can leave the stream opening forever.\n if (connectTimeoutMs > 0) {\n timer = setTimeout(() => {\n pipe.fail('connect-timeout')\n stream.destroy(new TunnelConnectTimeoutError(connectTimeoutMs))\n }, connectTimeoutMs)\n }\n stream.once('open', () => {\n if (timer) clearTimeout(timer)\n emit?.({ name: 'conn.open', conn, connectMs: Math.round(performance.now() - started) })\n })\n })\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(options.port ?? DEFAULT_RAW_PORT, host, () => resolve())\n })\n const address = server.address()\n const port = address && typeof address === 'object' ? address.port : (options.port ?? DEFAULT_RAW_PORT)\n\n return {\n key: options.key,\n clientKey: clientKey.z32,\n host,\n port,\n close: async () => {\n for (const socket of open) socket.destroy()\n await new Promise<void>((resolve) => server.close(() => resolve()))\n await dht.destroy()\n },\n }\n}\n","/**\n * Strip a mount path prefix from an incoming request URL path (including query string).\n * If the path is already unmounted (framework stripped it), returns it unchanged (with leading `/`).\n */\nexport function stripMountPath(urlPath: string, mountPath: string): string {\n const raw = urlPath || '/'\n const qIndex = raw.indexOf('?')\n const pathname = qIndex === -1 ? raw : raw.slice(0, qIndex)\n const query = qIndex === -1 ? '' : raw.slice(qIndex)\n\n const mount = (mountPath || '').replace(/\\/$/, '')\n if (!mount || mount === '/') {\n const path = pathname.startsWith('/') ? pathname : `/${pathname}`\n return `${path}${query}` || '/'\n }\n\n if (pathname === mount || pathname.startsWith(`${mount}/`)) {\n const rest = pathname.slice(mount.length)\n const path = rest.startsWith('/') ? rest : rest ? `/${rest}` : '/'\n return `${path}${query}`\n }\n\n // Already stripped by the framework (e.g. Express mount)\n const path = pathname.startsWith('/') ? pathname : `/${pathname}`\n return `${path}${query}`\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n filterResponseHeaders,\n flattenRequestHeaders,\n} from './protocol'\nimport { stripMountPath } from './mountPath'\nimport { TunnelTimeoutError } from './errors'\nimport type { TunnelSession } from './session'\nimport type { TunnelMeta, TunnelResponseMeta } from '../types'\n\nasync function readNodeBody(req: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n return Buffer.concat(chunks)\n}\n\n/** 504 when the tunnel timed out, 502 for any other tunnel failure. */\nfunction tunnelErrorStatus(err: unknown): number {\n return err instanceof TunnelTimeoutError ? 504 : 502\n}\n\nfunction tunnelErrorMessage(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err)\n return `Gateway Hyper tunnel error: ${message}`\n}\n\nexport async function forwardNodeRequest(\n session: TunnelSession,\n req: IncomingMessage,\n res: ServerResponse,\n mountPath: string,\n): Promise<void> {\n // The caller hanging up abandons the request instead of leaving it on the tunnel.\n const controller = new AbortController()\n const onClose = () => {\n if (!res.writableEnded) controller.abort()\n }\n res.once('close', onClose)\n try {\n const body = await readNodeBody(req)\n const path = stripMountPath(req.url ?? '/', mountPath)\n const meta: TunnelMeta = {\n method: req.method ?? 'GET',\n path,\n headers: flattenRequestHeaders(req.headers as Record<string, string | string[] | undefined>),\n }\n const { meta: responseMeta, body: responseBody } = await session.forwardHttp(meta, body, {\n signal: controller.signal,\n })\n writeNodeResponse(res, responseMeta, responseBody)\n } catch (err) {\n if (controller.signal.aborted) return\n if (!res.headersSent) {\n res.writeHead(tunnelErrorStatus(err), { 'Content-Type': 'text/plain; charset=utf-8' })\n }\n res.end(tunnelErrorMessage(err))\n } finally {\n res.off('close', onClose)\n }\n}\n\nfunction writeNodeResponse(\n res: ServerResponse,\n meta: TunnelResponseMeta,\n body: Buffer,\n): void {\n res.writeHead(meta.status, filterResponseHeaders(meta.headers))\n res.end(body)\n}\n\nexport async function forwardFetchRequest(\n session: TunnelSession,\n request: Request,\n mountPath: string,\n): Promise<Response> {\n try {\n const url = new URL(request.url)\n const path = stripMountPath(`${url.pathname}${url.search}`, mountPath)\n const bodyBuf = Buffer.from(await request.arrayBuffer())\n const headerRecord: Record<string, string> = {}\n request.headers.forEach((value, key) => {\n headerRecord[key] = value\n })\n const meta: TunnelMeta = {\n method: request.method || 'GET',\n path,\n headers: flattenRequestHeaders(headerRecord),\n }\n const { meta: responseMeta, body: responseBody } = await session.forwardHttp(meta, bodyBuf, {\n signal: request.signal,\n })\n return new Response(responseBody, {\n status: responseMeta.status,\n headers: filterResponseHeaders(responseMeta.headers),\n })\n } catch (err) {\n return new Response(tunnelErrorMessage(err), {\n status: tunnelErrorStatus(err),\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n })\n }\n}\n","import http from 'node:http'\nimport { forwardNodeRequest } from './forward'\nimport { createTunnelSession } from './session'\nimport type { ConnectTunnelOptions, ConnectTunnelResult } from '../types'\n\n/**\n * Client: dial operator key and expose a localhost HTTP server that forwards through the tunnel.\n */\nexport async function connectTunnel(options: ConnectTunnelOptions): Promise<ConnectTunnelResult> {\n const host = options.host ?? '127.0.0.1'\n const port = options.port ?? 1984\n\n const session = await createTunnelSession({\n key: options.key,\n keyFile: options.keyFile,\n keyPair: options.keyPair,\n bootstrap: options.bootstrap,\n requestTimeoutMs: options.requestTimeoutMs,\n connectTimeoutMs: options.connectTimeoutMs,\n maxStreams: options.maxStreams,\n })\n\n const server = http.createServer((req, res) => {\n void forwardNodeRequest(session, req, res, '')\n })\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, host, () => resolve())\n })\n\n const address = server.address()\n const boundPort =\n address && typeof address === 'object' ? address.port : port\n\n const baseUrl = `http://${host}:${boundPort}`\n\n return {\n key: options.key,\n clientKey: session.clientKey,\n host,\n port: boundPort,\n baseUrl,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()))\n await session.close()\n },\n }\n}\n\nexport function localGatewayUrl(baseUrl: string, path = ''): string {\n const base = baseUrl.replace(/\\/$/, '')\n if (!path) return base\n return path.startsWith('/') ? `${base}${path}` : `${base}/${path}`\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createTunnelSession, type TunnelSession } from './session'\nimport { forwardFetchRequest, forwardNodeRequest } from './forward'\nimport type { CreateGatewayProxyOptions, GatewayProxy } from '../types'\n\nfunction resolveProxyKey(options: CreateGatewayProxyOptions): string {\n const key =\n options.key?.trim() ||\n (typeof process !== 'undefined' ? process.env.SEED_GATEWAY_HYPER_KEY?.trim() : '') ||\n ''\n if (!key) {\n throw new Error(\n 'createGatewayProxy requires options.key or SEED_GATEWAY_HYPER_KEY environment variable',\n )\n }\n return key\n}\n\n/**\n * App-server Hyper gateway proxy for hosted web apps (Path B).\n *\n * Mount on a Node route (Next.js Node runtime, Express, etc.). Keep the operator\n * Hyper key on the server only — browsers talk HTTP to `proxyBaseUrl`.\n *\n * Prefer Node.js over Bun for Holepunch natives.\n */\nexport function createGatewayProxy(options: CreateGatewayProxyOptions = {}): GatewayProxy {\n const mountPath = options.mountPath?.trim() || '/api/seed-gateway'\n const key = resolveProxyKey(options)\n\n let session: TunnelSession | null = null\n let starting: Promise<TunnelSession> | null = null\n\n const ensureSession = async (): Promise<TunnelSession> => {\n if (session) return session\n if (starting) return starting\n starting = createTunnelSession({\n key,\n keyFile: options.keyFile,\n keyPair: options.keyPair,\n bootstrap: options.bootstrap,\n requestTimeoutMs: options.requestTimeoutMs,\n connectTimeoutMs: options.connectTimeoutMs,\n maxStreams: options.maxStreams,\n }).then((s) => {\n session = s\n starting = null\n return s\n })\n try {\n return await starting\n } catch (err) {\n starting = null\n throw err\n }\n }\n\n const proxy: GatewayProxy = {\n mountPath,\n start: async () => {\n await ensureSession()\n },\n close: async () => {\n if (starting) {\n try {\n await starting\n } catch {\n /* ignore start failure during close */\n }\n }\n if (session) {\n await session.close()\n session = null\n }\n },\n handleNode: (req: IncomingMessage, res: ServerResponse) => {\n void (async () => {\n try {\n const s = await ensureSession()\n await forwardNodeRequest(s, req, res, mountPath)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' })\n }\n res.end(`Gateway Hyper tunnel error: ${message}`)\n }\n })()\n },\n handleFetch: async (request: Request): Promise<Response> => {\n try {\n const s = await ensureSession()\n return await forwardFetchRequest(s, request, mountPath)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n return new Response(`Gateway Hyper tunnel error: ${message}`, {\n status: 502,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n })\n }\n },\n }\n\n if (options.eager) {\n void proxy.start().catch(() => {\n /* first request will surface the error */\n })\n }\n\n return proxy\n}\n","/**\n * Official Seed Protocol gateway Hyper tunnel operator key (z32).\n * Empty until ops generate and back up the first operator keypair.\n * Also defined in packages/arweave/src/constants.ts; keep both equal (gateway-hyper __tests__/constants.test.ts checks).\n */\nexport const DEFAULT_SEED_GATEWAY_HYPER_KEY = ''\n"],"mappings":"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"}