@usine/gateway 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +200 -2
  3. package/dist/admin/api.d.ts +146 -0
  4. package/dist/admin/api.d.ts.map +1 -0
  5. package/dist/admin/api.js +105 -0
  6. package/dist/admin/api.js.map +1 -0
  7. package/dist/admin/server.d.ts +10 -0
  8. package/dist/admin/server.d.ts.map +1 -0
  9. package/dist/admin/server.js +79 -0
  10. package/dist/admin/server.js.map +1 -0
  11. package/dist/attachment/attachment.d.ts +84 -0
  12. package/dist/attachment/attachment.d.ts.map +1 -0
  13. package/dist/attachment/attachment.js +67 -0
  14. package/dist/attachment/attachment.js.map +1 -0
  15. package/dist/attachment/store.d.ts +34 -0
  16. package/dist/attachment/store.d.ts.map +1 -0
  17. package/dist/attachment/store.js +133 -0
  18. package/dist/attachment/store.js.map +1 -0
  19. package/dist/caller/caller.d.ts +60 -0
  20. package/dist/caller/caller.d.ts.map +1 -0
  21. package/dist/caller/caller.js +131 -0
  22. package/dist/caller/caller.js.map +1 -0
  23. package/dist/chatgpt/auth-file.d.ts +47 -0
  24. package/dist/chatgpt/auth-file.d.ts.map +1 -0
  25. package/dist/chatgpt/auth-file.js +132 -0
  26. package/dist/chatgpt/auth-file.js.map +1 -0
  27. package/dist/chatgpt/logins.d.ts +50 -0
  28. package/dist/chatgpt/logins.d.ts.map +1 -0
  29. package/dist/chatgpt/logins.js +303 -0
  30. package/dist/chatgpt/logins.js.map +1 -0
  31. package/dist/egress/client-hello.d.ts +120 -0
  32. package/dist/egress/client-hello.d.ts.map +1 -0
  33. package/dist/egress/client-hello.js +155 -0
  34. package/dist/egress/client-hello.js.map +1 -0
  35. package/dist/egress/egress.d.ts +66 -0
  36. package/dist/egress/egress.d.ts.map +1 -0
  37. package/dist/egress/egress.js +114 -0
  38. package/dist/egress/egress.js.map +1 -0
  39. package/dist/gateway.d.ts +27 -0
  40. package/dist/gateway.d.ts.map +1 -0
  41. package/dist/gateway.js +27 -0
  42. package/dist/gateway.js.map +1 -0
  43. package/dist/github/github.d.ts +54 -0
  44. package/dist/github/github.d.ts.map +1 -0
  45. package/dist/github/github.js +112 -0
  46. package/dist/github/github.js.map +1 -0
  47. package/dist/github/rename.d.ts +30 -0
  48. package/dist/github/rename.d.ts.map +1 -0
  49. package/dist/github/rename.js +83 -0
  50. package/dist/github/rename.js.map +1 -0
  51. package/dist/llm/llm.d.ts +113 -0
  52. package/dist/llm/llm.d.ts.map +1 -0
  53. package/dist/llm/llm.js +144 -0
  54. package/dist/llm/llm.js.map +1 -0
  55. package/dist/node/egress-proxy.d.ts +30 -0
  56. package/dist/node/egress-proxy.d.ts.map +1 -0
  57. package/dist/node/egress-proxy.js +491 -0
  58. package/dist/node/egress-proxy.js.map +1 -0
  59. package/dist/node/environment.d.ts +113 -0
  60. package/dist/node/environment.d.ts.map +1 -0
  61. package/dist/node/environment.js +140 -0
  62. package/dist/node/environment.js.map +1 -0
  63. package/dist/node/main.d.ts +3 -0
  64. package/dist/node/main.d.ts.map +1 -0
  65. package/dist/node/main.js +13 -0
  66. package/dist/node/main.js.map +1 -0
  67. package/dist/node/node.d.ts +28 -0
  68. package/dist/node/node.d.ts.map +1 -0
  69. package/dist/node/node.js +81 -0
  70. package/dist/node/node.js.map +1 -0
  71. package/dist/node/reload.d.ts +27 -0
  72. package/dist/node/reload.d.ts.map +1 -0
  73. package/dist/node/reload.js +170 -0
  74. package/dist/node/reload.js.map +1 -0
  75. package/dist/proxy/forward.d.ts +12 -0
  76. package/dist/proxy/forward.d.ts.map +1 -0
  77. package/dist/proxy/forward.js +44 -0
  78. package/dist/proxy/forward.js.map +1 -0
  79. package/dist/proxy/proxy.d.ts +29 -0
  80. package/dist/proxy/proxy.d.ts.map +1 -0
  81. package/dist/proxy/proxy.js +297 -0
  82. package/dist/proxy/proxy.js.map +1 -0
  83. package/dist/settings.d.ts +52 -0
  84. package/dist/settings.d.ts.map +1 -0
  85. package/dist/settings.js +27 -0
  86. package/dist/settings.js.map +1 -0
  87. package/dist/upstream.d.ts +5 -0
  88. package/dist/upstream.d.ts.map +1 -0
  89. package/dist/upstream.js +6 -0
  90. package/dist/upstream.js.map +1 -0
  91. package/package.json +58 -3
@@ -0,0 +1,491 @@
1
+ import { lookup } from "node:dns/promises";
2
+ import { createServer, request as httpRequest, } from "node:http";
3
+ import { BlockList, connect } from "node:net";
4
+ import { Data, Effect, Fiber, Match, Option, Predicate, Schema } from "effect";
5
+ import { Headers } from "effect/http";
6
+ import { tokenFromAuthorization, CallerTokens } from "../caller/caller.js";
7
+ import { ClientHelloReader, comparableHost } from "../egress/client-hello.js";
8
+ import { EgressRefused } from "../egress/egress.js";
9
+ import { endToEndHeaders } from "../proxy/forward.js";
10
+ import { REFUSAL_HEADER } from "../proxy/proxy.js";
11
+ import { GatewaySettings, gatewayHosts, SettingsInForce } from "../settings.js";
12
+ /**
13
+ * Returns the ranges a public name must never resolve to: private, loopback, link-local,
14
+ * shared, multicast, and reserved. Node checks IPv4-mapped IPv6 addresses against the IPv4
15
+ * ranges, so there's no `::ffff:0:0/96` rule, which would match every IPv4 address.
16
+ */
17
+ const privateAddresses = () => {
18
+ const list = new BlockList();
19
+ for (const [network, prefix] of [
20
+ ["0.0.0.0", 8],
21
+ ["10.0.0.0", 8],
22
+ ["100.64.0.0", 10],
23
+ ["127.0.0.0", 8],
24
+ ["169.254.0.0", 16],
25
+ ["172.16.0.0", 12],
26
+ ["192.0.0.0", 24],
27
+ ["192.168.0.0", 16],
28
+ ["198.18.0.0", 15],
29
+ ["224.0.0.0", 3],
30
+ ])
31
+ list.addSubnet(network, prefix, "ipv4");
32
+ for (const [network, prefix] of [
33
+ ["::", 127],
34
+ ["fc00::", 7],
35
+ ["fe80::", 10],
36
+ ["ff00::", 8],
37
+ ])
38
+ list.addSubnet(network, prefix, "ipv6");
39
+ return list;
40
+ };
41
+ /** The error when the DNS lookup of an allowed host fails or returns nothing. */
42
+ class Unresolvable extends Schema.TaggedError()("Unresolvable", {
43
+ host: Schema.String,
44
+ }) {
45
+ }
46
+ /** The error when the forward proxy can't listen on its address. */
47
+ export class EgressProxyListenError extends Schema.TaggedError()("EgressProxyListenError", { host: Schema.String, port: Schema.Int, message: Schema.String }) {
48
+ }
49
+ /** The error when the upstream connection can't be opened or its response doesn't finish. */
50
+ class Unreachable extends Schema.TaggedError()("Unreachable", {
51
+ host: Schema.String,
52
+ port: Schema.Int,
53
+ message: Schema.String,
54
+ }) {
55
+ }
56
+ /**
57
+ * The error for a tunnel refused after `200 Connection Established`: the first bytes weren't a
58
+ * TLS ClientHello; the ClientHello had no server name, hid it with ECH, or had a different one
59
+ * than the CONNECT host; or the host resolved to a private address. `serverName` is the
60
+ * ClientHello's server name, if any.
61
+ */
62
+ class TunnelRefused extends Data.TaggedError("TunnelRefused") {
63
+ }
64
+ /** The error when the client closes a tunnel before sending any bytes. */
65
+ class TunnelAbandoned extends Data.TaggedError("TunnelAbandoned") {
66
+ }
67
+ /** A fatal TLS `unrecognized_name` alert (RFC 8446, section 6), sent so the client can report why the handshake ended. */
68
+ const UNRECOGNIZED_NAME_ALERT = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x70]);
69
+ const CLIENT_HELLO_TIMEOUT = "5 seconds";
70
+ const notTls = (detail) => new TunnelRefused({ reason: "not-tls", detail, serverName: Option.none() });
71
+ /**
72
+ * Buffers a tunnel's first bytes, including `head`, until they contain a complete ClientHello
73
+ * (at most 16 KiB and five seconds), then pauses the client so nothing more is read before the
74
+ * upstream connection exists. Fails with `TunnelAbandoned` if the client closes before sending
75
+ * any bytes.
76
+ */
77
+ const clientHello = (client, head) => Effect.callback((resume) => {
78
+ const reader = new ClientHelloReader();
79
+ const stop = () => {
80
+ client.off("data", onData);
81
+ client.off("end", onEnd);
82
+ client.off("close", onEnd);
83
+ };
84
+ function onData(chunk) {
85
+ const read = reader.push(chunk);
86
+ if (read._tag === "Incomplete")
87
+ return;
88
+ stop();
89
+ client.pause();
90
+ if (read._tag === "NotTls")
91
+ return resume(Effect.fail(notTls(read.detail)));
92
+ resume(Effect.succeed({
93
+ buffered: Buffer.from(reader.bytes),
94
+ serverName: read.serverName,
95
+ encryptedClientHello: read.encryptedClientHello,
96
+ }));
97
+ }
98
+ function onEnd() {
99
+ stop();
100
+ resume(Effect.fail(reader.bytes.length === 0
101
+ ? new TunnelAbandoned()
102
+ : notTls("the client closed the tunnel before its ClientHello was complete")));
103
+ }
104
+ client.on("data", onData);
105
+ client.once("end", onEnd);
106
+ client.once("close", onEnd);
107
+ if (head.length > 0)
108
+ onData(head);
109
+ return Effect.sync(stop);
110
+ }).pipe(Effect.timeoutOrElse({
111
+ duration: CLIENT_HELLO_TIMEOUT,
112
+ orElse: () => Effect.fail(notTls("no ClientHello arrived within five seconds")),
113
+ }));
114
+ /**
115
+ * Checks that the ClientHello's server name is the CONNECT host, so a tunnel to a shared CDN
116
+ * address can't connect to another tenant of that address.
117
+ */
118
+ const pinnedServerName = (host, hello) => {
119
+ if (hello.encryptedClientHello)
120
+ return Effect.fail(new TunnelRefused({
121
+ reason: "server-name-missing",
122
+ detail: "the ClientHello encrypts its server name (ECH), so it cannot be checked",
123
+ serverName: hello.serverName,
124
+ }));
125
+ if (Option.isNone(hello.serverName))
126
+ return Effect.fail(new TunnelRefused({
127
+ reason: "server-name-missing",
128
+ detail: "the ClientHello names no server",
129
+ serverName: Option.none(),
130
+ }));
131
+ const serverName = hello.serverName.value;
132
+ return comparableHost(serverName) === comparableHost(host)
133
+ ? Effect.succeed({ buffered: hello.buffered, serverName })
134
+ : Effect.fail(new TunnelRefused({
135
+ reason: "server-name-mismatch",
136
+ detail: `the ClientHello asks for ${serverName}, not the CONNECT host`,
137
+ serverName: hello.serverName,
138
+ }));
139
+ };
140
+ const RelayEnd = Data.taggedEnum();
141
+ const AUTHORITY = /^(?:\[([0-9a-fA-F:.]+)\]|([^:[\]]+)):(\d{1,5})$/u;
142
+ const parseAuthority = (authority) => {
143
+ const match = AUTHORITY.exec(authority);
144
+ const host = match?.[1] ?? match?.[2];
145
+ if (match === null || host === undefined)
146
+ return Option.none();
147
+ return Option.some({ host, port: Number(match[3]) });
148
+ };
149
+ const statusLine = (status, reason, body, refusal) => [
150
+ `HTTP/1.1 ${status} ${reason}`,
151
+ "Content-Type: text/plain; charset=utf-8",
152
+ `Content-Length: ${Buffer.byteLength(body)}`,
153
+ ...Option.match(refusal, {
154
+ onNone: () => [],
155
+ onSome: (value) => [`${REFUSAL_HEADER}: ${value}`],
156
+ }),
157
+ "Connection: close",
158
+ "",
159
+ body,
160
+ ].join("\r\n");
161
+ /**
162
+ * Serves the forward proxy that Workers use for package registries and other allowlisted
163
+ * hosts: `CONNECT host:443` for HTTPS, and absolute-form requests (`GET http://host/...`) for
164
+ * plain HTTP mirrors such as apt.
165
+ *
166
+ * - A tunnel opens only after the ClientHello's server name matches the CONNECT host.
167
+ * - The proxy resolves names itself and refuses one that resolves to a private address.
168
+ * - Requests for `git.<domain>` and `llm.<domain>` on port 80 go to the reverse proxy at
169
+ * `gateway`, so a client that proxies everything (Claude Code with `HTTPS_PROXY`) still
170
+ * connects to them.
171
+ * - A caller token in `Proxy-Authorization` is optional and only identifies the Worker in logs.
172
+ * - Each refusal is logged with the host, reason, and client address.
173
+ * - Each request is checked against the allowlist current at arrival, so an open tunnel stays
174
+ * open after a reload removes its host.
175
+ */
176
+ export const serveEgressProxy = Effect.fn("EgressProxy.serve")(function* (listener, gateway) {
177
+ const settings = yield* GatewaySettings;
178
+ const ownHosts = new Set(Object.values(gatewayHosts(settings.domain)));
179
+ const inForce = yield* SettingsInForce;
180
+ const callers = yield* CallerTokens;
181
+ const blocked = privateAddresses();
182
+ const scope = yield* Effect.scope;
183
+ const context = yield* Effect.context();
184
+ /** Returns the log fields for a request's caller: the Worker from a valid caller token, if any. */
185
+ const caller = (value) => Option.fromNullishOr(value).pipe(Option.flatMap(tokenFromAuthorization), Option.match({
186
+ onNone: () => Effect.succeed({ caller: "none" }),
187
+ onSome: (token) => callers.verify(token).pipe(Effect.map(({ worker }) => ({ worker })), Effect.orElseSucceed(() => ({ caller: "invalid-token" }))),
188
+ }));
189
+ const logRefusal = (refusal, client, who) => Effect.logWarning("Gateway refused a request", {
190
+ reason: refusal.reason,
191
+ host: refusal.host,
192
+ port: refusal.port,
193
+ client,
194
+ ...who,
195
+ });
196
+ /** Resolves an allowed host and returns its first address. Fails if any address is private. */
197
+ const resolve = Effect.fn("EgressProxy.resolve")(function* (permitted) {
198
+ const addresses = yield* Effect.tryPromise({
199
+ try: () => lookup(permitted.host, { all: true, verbatim: true }),
200
+ catch: () => new Unresolvable({ host: permitted.host }),
201
+ });
202
+ const first = addresses[0];
203
+ if (first === undefined)
204
+ return yield* new Unresolvable({ host: permitted.host });
205
+ if (addresses.some(({ address, family }) => blocked.check(address, family === 6 ? "ipv6" : "ipv4")))
206
+ return yield* Effect.fail(new EgressRefused({
207
+ host: permitted.host,
208
+ port: permitted.port,
209
+ reason: "private-address",
210
+ }));
211
+ return first.address;
212
+ });
213
+ const decide = (host, port) => Effect.flatMap(inForce.current, ({ egress }) => {
214
+ const decision = egress.decide(host, port);
215
+ return decision._tag === "EgressRefused" ? Effect.fail(decision) : Effect.succeed(decision);
216
+ });
217
+ /** Returns the target for `host` on `port`: the reverse proxy for the Gateway's own names, otherwise the resolved allowed host. */
218
+ const destination = (host, port) => ownHosts.has(host.toLowerCase()) && port === 80
219
+ ? Effect.succeed({ address: gateway.host, port: gateway.port, kind: "gateway" })
220
+ : decide(host, port).pipe(Effect.flatMap(resolve), Effect.map((address) => ({ address, port, kind: "egress" })));
221
+ const openTunnel = (address, port, host) => Effect.callback((resume) => {
222
+ const socket = connect({ host: address, port });
223
+ const onError = (error) => resume(Effect.fail(new Unreachable({ host, port, message: error.message })));
224
+ socket.once("error", onError);
225
+ socket.once("connect", () => {
226
+ socket.off("error", onError);
227
+ resume(Effect.succeed(socket));
228
+ });
229
+ return Effect.sync(() => socket.destroy());
230
+ });
231
+ /** Copies bytes both ways until either side closes, then closes both. */
232
+ const splice = (client, upstream) => Effect.callback((resume) => {
233
+ const close = () => {
234
+ client.destroy();
235
+ upstream.destroy();
236
+ resume(Effect.void);
237
+ };
238
+ client.on("error", close).on("close", close);
239
+ upstream.on("error", close).on("close", close);
240
+ client.pipe(upstream);
241
+ upstream.pipe(client);
242
+ return Effect.sync(close);
243
+ });
244
+ /** Closes a tunnel refused after its `200 Connection Established`. */
245
+ const closeRefused = (client, refusal) => {
246
+ if (refusal.reason === "not-tls")
247
+ client.destroy();
248
+ else
249
+ client.end(UNRECOGNIZED_NAME_ALERT);
250
+ };
251
+ const tunnel = Effect.fn("EgressProxy.tunnel")(function* (request, client, head) {
252
+ const from = request.socket.remoteAddress ?? "unknown";
253
+ const who = yield* caller(request.headers["proxy-authorization"]);
254
+ const authority = parseAuthority(request.url ?? "");
255
+ if (Option.isNone(authority)) {
256
+ client.end(statusLine(400, "Bad Request", "Expected CONNECT host:port\n", Option.none()));
257
+ return yield* Effect.logWarning("Gateway refused a request", {
258
+ reason: "malformed-connect",
259
+ client: from,
260
+ ...who,
261
+ });
262
+ }
263
+ const { host, port } = authority.value;
264
+ yield* Effect.annotateCurrentSpan({ host, port });
265
+ if (ownHosts.has(comparableHost(host)) && port === 80) {
266
+ const opened = yield* openTunnel(gateway.host, gateway.port, host).pipe(Effect.result);
267
+ if (opened._tag === "Failure") {
268
+ client.end(statusLine(502, "Bad Gateway", "usine gateway: the reverse proxy is unreachable.\n", Option.none()));
269
+ return yield* Effect.logError("The gateway's reverse proxy could not be reached", {
270
+ host,
271
+ port,
272
+ client: from,
273
+ ...who,
274
+ error: opened.failure._tag,
275
+ });
276
+ }
277
+ client.write("HTTP/1.1 200 Connection Established\r\n\r\n");
278
+ if (head.length > 0)
279
+ opened.success.write(head);
280
+ yield* Effect.logInfo("Opened an egress tunnel", {
281
+ host,
282
+ port,
283
+ destination: "gateway",
284
+ client: from,
285
+ ...who,
286
+ });
287
+ return yield* splice(client, opened.success);
288
+ }
289
+ const decision = (yield* inForce.current).egress.decide(host, port);
290
+ // Only port 443 tunnels are allowed; plain HTTP goes through the absolute-form relay, which checks the host.
291
+ if (decision._tag === "EgressRefused" || port !== 443) {
292
+ const refused = decision._tag === "EgressRefused"
293
+ ? decision
294
+ : new EgressRefused({ host, port, reason: "port-not-allowed" });
295
+ client.end(statusLine(403, "Forbidden", `usine gateway: ${host}:${port} is refused (${refused.reason}).\n`, Option.some(refused.reason)));
296
+ return yield* logRefusal(refused, from, who);
297
+ }
298
+ // The client sends its ClientHello after this line; no upstream connection is opened until its server name is checked.
299
+ client.write("HTTP/1.1 200 Connection Established\r\n\r\n");
300
+ const opened = yield* clientHello(client, head).pipe(Effect.flatMap((hello) => pinnedServerName(host, hello)), Effect.flatMap(({ buffered, serverName }) => resolve(decision).pipe(Effect.catchTag("EgressRefused", (refused) => Effect.fail(new TunnelRefused({
301
+ reason: "private-address",
302
+ detail: `${refused.host} resolves to a private address`,
303
+ serverName: Option.some(serverName),
304
+ }))), Effect.flatMap((address) => openTunnel(address, port, decision.host)), Effect.map((upstream) => ({ upstream, buffered, serverName })))), Effect.result);
305
+ if (opened._tag === "Failure") {
306
+ const failure = opened.failure;
307
+ if (failure._tag === "TunnelAbandoned") {
308
+ client.destroy();
309
+ return yield* Effect.logInfo("The client abandoned a tunnel before sending anything", {
310
+ host,
311
+ port,
312
+ client: from,
313
+ ...who,
314
+ });
315
+ }
316
+ if (failure._tag === "TunnelRefused") {
317
+ closeRefused(client, failure);
318
+ return yield* Effect.logWarning("Gateway refused a request", {
319
+ reason: failure.reason,
320
+ host,
321
+ port,
322
+ serverName: Option.getOrElse(failure.serverName, () => "none"),
323
+ detail: failure.detail,
324
+ client: from,
325
+ ...who,
326
+ });
327
+ }
328
+ client.destroy();
329
+ return yield* Effect.logError("An allowed egress host could not be reached", {
330
+ host,
331
+ port,
332
+ client: from,
333
+ ...who,
334
+ error: failure._tag,
335
+ });
336
+ }
337
+ const { upstream, buffered, serverName } = opened.success;
338
+ upstream.write(buffered);
339
+ yield* Effect.logInfo("Opened an egress tunnel", {
340
+ host,
341
+ port,
342
+ serverName,
343
+ destination: "egress",
344
+ client: from,
345
+ ...who,
346
+ });
347
+ yield* splice(client, upstream);
348
+ });
349
+ const relay = Effect.fn("EgressProxy.relay")(function* (request, response) {
350
+ const from = request.socket.remoteAddress ?? "unknown";
351
+ const who = yield* caller(request.headers["proxy-authorization"]);
352
+ const url = Option.liftThrowable(() => new URL(request.url ?? ""))().pipe(Option.filter((parsed) => parsed.protocol === "http:"));
353
+ if (Option.isNone(url)) {
354
+ response.writeHead(400, { "content-type": "text/plain; charset=utf-8" });
355
+ response.end("usine gateway: the forward proxy accepts only CONNECT or an absolute http:// URL.\n");
356
+ return yield* Effect.logWarning("Gateway refused a request", {
357
+ reason: "not-a-proxy-request",
358
+ client: from,
359
+ ...who,
360
+ });
361
+ }
362
+ const port = url.value.port === "" ? 80 : Number(url.value.port);
363
+ const host = url.value.hostname.replace(/^\[(.*)\]$/u, "$1");
364
+ yield* Effect.annotateCurrentSpan({ host, port });
365
+ const forwarded = endToEndHeaders(Headers.fromInput(request.headers));
366
+ /**
367
+ * Sends the request to `target` and copies the response back until it ends or the client
368
+ * disconnects. If the client disconnects mid-transfer, destroys the upstream connection
369
+ * immediately. Fails with `Unreachable` if the upstream connection fails or ends early, after
370
+ * the client's response is destroyed or set to 502.
371
+ */
372
+ const copy = (target, headers) => Effect.callback((resume) => {
373
+ let settled = false;
374
+ const settle = (end) => {
375
+ settled = true;
376
+ resume(end);
377
+ };
378
+ const fail = (error) => {
379
+ if (settled)
380
+ return;
381
+ if (response.headersSent)
382
+ response.destroy();
383
+ else {
384
+ response.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
385
+ response.end();
386
+ }
387
+ settle(Effect.fail(new Unreachable({ host, port, message: error.message })));
388
+ };
389
+ const outgoing = httpRequest({
390
+ host: target.address,
391
+ port: target.port,
392
+ method: request.method,
393
+ path: `${url.value.pathname}${url.value.search}`,
394
+ headers: { ...headers, host: url.value.host },
395
+ setHost: false,
396
+ }, (incoming) => {
397
+ const status = incoming.statusCode ?? 502;
398
+ response.writeHead(status, {
399
+ ...endToEndHeaders(Headers.fromInput(incoming.headers)),
400
+ });
401
+ incoming.once("error", fail);
402
+ incoming.once("close", () => {
403
+ if (!incoming.complete)
404
+ fail(new Error("The upstream response ended prematurely"));
405
+ });
406
+ incoming.once("end", () => settle(Effect.succeed(RelayEnd.Relayed({ status }))));
407
+ incoming.pipe(response);
408
+ });
409
+ // `close` also fires after a finished response, which already completed the relay; before that, it means the client disconnected.
410
+ response.once("close", () => {
411
+ if (settled)
412
+ return;
413
+ outgoing.destroy();
414
+ settle(Effect.succeed(RelayEnd.Abandoned({
415
+ status: response.headersSent ? Option.some(response.statusCode) : Option.none(),
416
+ })));
417
+ });
418
+ outgoing.once("error", fail);
419
+ request.pipe(outgoing);
420
+ return Effect.sync(() => outgoing.destroy());
421
+ });
422
+ const relayed = yield* destination(host, port).pipe(Effect.flatMap((target) =>
423
+ // Only the reverse proxy receives the caller token; hosts outside the Gateway must never receive it.
424
+ (target.kind === "gateway"
425
+ ? Effect.succeed(forwarded)
426
+ : callers.withoutCallerTokens(forwarded)).pipe(Effect.map((headers) => ({ target, headers })))), Effect.flatMap(({ target, headers }) => copy(target, headers).pipe(Effect.map((end) => ({ end, kind: target.kind })))), Effect.result);
427
+ if (relayed._tag === "Success") {
428
+ const fields = {
429
+ host,
430
+ port,
431
+ destination: relayed.success.kind,
432
+ method: request.method ?? "GET",
433
+ client: from,
434
+ ...who,
435
+ };
436
+ return yield* Match.valueTags(relayed.success.end, {
437
+ Relayed: ({ status }) => Effect.logInfo("Relayed a plain HTTP request", { ...fields, status }),
438
+ Abandoned: ({ status }) => Effect.logInfo("The client abandoned a relayed request", {
439
+ ...fields,
440
+ status: Option.getOrElse(status, () => "none"),
441
+ }),
442
+ });
443
+ }
444
+ const failure = relayed.failure;
445
+ if (failure._tag === "EgressRefused") {
446
+ response.writeHead(403, {
447
+ "content-type": "text/plain; charset=utf-8",
448
+ [REFUSAL_HEADER]: failure.reason,
449
+ });
450
+ response.end(`usine gateway: ${host}:${port} is refused (${failure.reason}).\n`);
451
+ return yield* logRefusal(failure, from, who);
452
+ }
453
+ if (!response.headersSent) {
454
+ response.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
455
+ response.end(`usine gateway: ${host} is unreachable.\n`);
456
+ }
457
+ yield* Effect.logError("An allowed egress host could not be reached", {
458
+ host,
459
+ port,
460
+ client: from,
461
+ ...who,
462
+ error: failure._tag,
463
+ });
464
+ });
465
+ const server = createServer({ requestTimeout: 0 });
466
+ const run = (effect) => Fiber.runIn(Effect.runForkWith(context)(effect), scope);
467
+ server.on("connect", (request, client, head) => {
468
+ client.on("error", () => client.destroy());
469
+ // The server's sockets are half-open, so `server.close` waits for an interrupted tunnel until its client socket is destroyed.
470
+ run(tunnel(request, client, head).pipe(Effect.onInterrupt(() => Effect.sync(() => client.destroy()))));
471
+ });
472
+ server.on("request", (request, response) => run(relay(request, response)));
473
+ yield* Effect.acquireRelease(Effect.callback((resume) => {
474
+ server.once("error", (error) => resume(Effect.fail(new EgressProxyListenError({
475
+ host: listener.host,
476
+ port: listener.port,
477
+ message: error.message,
478
+ }))));
479
+ server.listen(listener.port, listener.host, () => resume(Effect.void));
480
+ }), () => Effect.callback((resume) => {
481
+ server.close(() => resume(Effect.void));
482
+ server.closeAllConnections();
483
+ }));
484
+ const address = server.address();
485
+ const bound = address === null || Predicate.isString(address)
486
+ ? listener
487
+ : { host: address.address, port: address.port };
488
+ yield* Effect.logInfo("The egress proxy is listening", bound);
489
+ return bound;
490
+ });
491
+ //# sourceMappingURL=egress-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egress-proxy.js","sourceRoot":"","sources":["../../src/node/egress-proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import { GatewayDomain } from "@usine/core/worker";
2
+ import { Config, ConfigProvider, Effect, FileSystem, Layer, Option, Schema } from "effect";
3
+ import { AnthropicProvider, OpenAiProvider } from "../llm/llm.ts";
4
+ import { AdminToken, CallerKey, GatewaySettings } from "../settings.ts";
5
+ /** The allowlist path if `USINE_GATEWAY_EGRESS_ALLOWLIST` isn't set. */
6
+ export declare const DEFAULT_EGRESS_ALLOWLIST_PATH = "/etc/usine-gateway/egress-allowlist";
7
+ declare const ConflictingProviderCredentials_base: Schema.Class<ConflictingProviderCredentials, Schema.TaggedStruct<"ConflictingProviderCredentials", {
8
+ readonly provider: Schema.Literals<readonly ["anthropic", "openai"]>;
9
+ readonly message: Schema.String;
10
+ }>, import("effect/Cause").YieldableError>;
11
+ /** The error when two credentials are set for one provider, such as an API key and a subscription token. */
12
+ export declare class ConflictingProviderCredentials extends ConflictingProviderCredentials_base {
13
+ }
14
+ declare const EnvironmentFileError_base: Schema.Class<EnvironmentFileError, Schema.TaggedStruct<"EnvironmentFileError", {
15
+ readonly path: Schema.String;
16
+ readonly message: Schema.String;
17
+ }>, import("effect/Cause").YieldableError>;
18
+ /** The error when a file listed in `USINE_GATEWAY_ENVIRONMENT_FILES` can't be read. */
19
+ export declare class EnvironmentFileError extends EnvironmentFileError_base {
20
+ }
21
+ /** The Gateway's settings source: its environment files layered over the process environment. */
22
+ export interface SettingsSource {
23
+ /** The environment files in read order; empty if none is listed. */
24
+ readonly files: ReadonlyArray<string>;
25
+ readonly provider: ConfigProvider.ConfigProvider;
26
+ }
27
+ /**
28
+ * Reads the `:`-separated environment files listed in `USINE_GATEWAY_ENVIRONMENT_FILES` in
29
+ * `base`, one `KEY=value` per line, and layers them over `base`. Later files override earlier
30
+ * ones, as with systemd's `EnvironmentFile=`. Without that variable, returns `base` alone.
31
+ */
32
+ export declare const readSettingsSource: (base: ConfigProvider.ConfigProvider) => Effect.Effect<{
33
+ files: string[] | readonly string[];
34
+ provider: ConfigProvider.ConfigProvider;
35
+ }, Config.ConfigError | EnvironmentFileError, FileSystem.FileSystem>;
36
+ /**
37
+ * The LLM providers and credentials from the settings. A ChatGPT login's auth file is read and
38
+ * parsed, and its directory checked for writing, so an unusable file fails here, before the
39
+ * Gateway listens or a reload applies it.
40
+ */
41
+ export declare const llmProviders: Effect.Effect<{
42
+ anthropic: Option.Option<AnthropicProvider>;
43
+ openai: Option.Option<OpenAiProvider>;
44
+ }, import("../chatgpt/auth-file.ts").ChatGptAuthFileError | Config.ConfigError | ConflictingProviderCredentials, FileSystem.FileSystem>;
45
+ export declare const egressAllowlistPath: Config.Config<string>;
46
+ /** Reads and parses the allowlist file at `path`. */
47
+ export declare const readEgressAllowlist: (path: string) => Effect.Effect<import("../egress/egress.ts").EgressAllowlist, import("../egress/egress.ts").EgressAllowlistError | import("effect/PlatformError").PlatformError, FileSystem.FileSystem>;
48
+ export interface Listener {
49
+ readonly host: string;
50
+ readonly port: number;
51
+ }
52
+ /** The listeners for the reverse proxy (GitHub and LLMs), the forward proxy (allowlisted hosts), and the admin API (executor). */
53
+ export interface GatewayListeners {
54
+ readonly proxy: Listener;
55
+ readonly egress: Listener;
56
+ readonly admin: Listener;
57
+ }
58
+ /**
59
+ * Listeners from the environment. The proxies default to loopback, so an unconfigured Gateway
60
+ * is unreachable; the VM's unit sets its private address.
61
+ */
62
+ export declare const listenersFromEnvironment: Config.Config<{
63
+ proxy: {
64
+ host: string;
65
+ port: number;
66
+ };
67
+ egress: {
68
+ host: string;
69
+ port: number;
70
+ };
71
+ admin: {
72
+ host: string;
73
+ port: number;
74
+ };
75
+ }>;
76
+ /** The settings that only a restart changes: the domain, the listeners, the admin token, and the caller key that signs all caller tokens. */
77
+ export interface RestartSettings {
78
+ readonly domain: GatewayDomain;
79
+ readonly adminToken: AdminToken;
80
+ readonly callerKey: CallerKey;
81
+ readonly listeners: GatewayListeners;
82
+ }
83
+ export declare const restartSettings: Config.Config<RestartSettings>;
84
+ export interface StartupSettings {
85
+ readonly settings: GatewaySettings["Service"];
86
+ readonly listeners: GatewayListeners;
87
+ }
88
+ /**
89
+ * Reads the Gateway's settings from `source`. Fails on a missing secret, an invalid value, an
90
+ * unreadable allowlist, or an unusable ChatGPT auth file, so the Gateway stops before it listens.
91
+ */
92
+ export declare const readStartupSettings: (source: SettingsSource) => Effect.Effect<{
93
+ settings: {
94
+ domain: string & import("effect/Brand").Brand<"GatewayDomain">;
95
+ github: import("../github/github.ts").GitHubOrigins;
96
+ providers: {
97
+ anthropic: Option.Option<AnthropicProvider>;
98
+ openai: Option.Option<OpenAiProvider>;
99
+ };
100
+ egress: import("../egress/egress.ts").EgressAllowlist;
101
+ adminToken: import("effect/Redacted").Redacted<string>;
102
+ callerKey: import("effect/Redacted").Redacted<string>;
103
+ };
104
+ listeners: GatewayListeners;
105
+ }, import("../chatgpt/auth-file.ts").ChatGptAuthFileError | Config.ConfigError | ConflictingProviderCredentials | import("../egress/egress.ts").EgressAllowlistError | import("effect/PlatformError").PlatformError, FileSystem.FileSystem>;
106
+ /**
107
+ * The Gateway's settings, read once at startup from the files in
108
+ * `USINE_GATEWAY_ENVIRONMENT_FILES` over the process environment, plus the egress allowlist
109
+ * file. Fails on a missing secret, an invalid value, or an unreadable or unusable file.
110
+ */
111
+ export declare const settingsFromEnvironment: Layer.Layer<GatewaySettings, import("../chatgpt/auth-file.ts").ChatGptAuthFileError | Config.ConfigError | ConflictingProviderCredentials | import("../egress/egress.ts").EgressAllowlistError | EnvironmentFileError | import("effect/PlatformError").PlatformError, FileSystem.FileSystem>;
112
+ export {};
113
+ //# sourceMappingURL=environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/node/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAK3F,OAAO,EAEL,iBAAiB,EAOjB,cAAc,EACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGxE,wEAAwE;AACxE,eAAO,MAAM,6BAA6B,wCAAwC,CAAC;;;;;AAOnF,4GAA4G;AAC5G,qBAAa,8BAA+B,SAAQ,mCAGnD;CAAG;;;;;AAEJ,uFAAuF;AACvF,qBAAa,oBAAqB,SAAQ,yBAGzC;CAAG;AAEJ,iGAAiG;AACjG,MAAM,WAAW,cAAc;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,cAAc,CAAC;CAClD;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;oEAmB7B,CAAC;AAiBH;;;;GAIG;AACH,eAAO,MAAM,YAAY;;;uIAwDvB,CAAC;AAWH,eAAO,MAAM,mBAAmB,uBAE/B,CAAC;AAEF,qDAAqD;AACrD,eAAO,MAAM,mBAAmB,0MAM9B,CAAC;AAEH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,kIAAkI;AAClI,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;CAC1B;AAQD;;;GAGG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;EAInC,CAAC;AAEH,6IAA6I;AAC7I,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC;CACtC;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,eAAe,CAKzD,CAAC;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC;CACtC;AAaD;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;2OAO9B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,8RAOnC,CAAC"}