@claxedo/workspace-relay 0.8.0 → 0.10.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.
- package/README.md +92 -223
- package/dist/auth.d.ts +94 -35
- package/dist/cloudflare.d.ts +15 -45
- package/dist/cors-origins.d.ts +2 -0
- package/dist/directory.d.ts +5 -38
- package/dist/gateway-placement.d.ts +7 -0
- package/dist/host-tunnel-failure.d.ts +7 -0
- package/dist/host-tunnel-forwarding.d.ts +29 -0
- package/dist/http.d.ts +4 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +901 -835
- package/dist/relay-audit.d.ts +4 -0
- package/dist/relay-cors.d.ts +12 -0
- package/dist/relay-location.d.ts +93 -0
- package/dist/relay-target.d.ts +25 -0
- package/dist/resolver-clients.d.ts +116 -0
- package/dist/server.d.ts +27 -286
- package/dist/session-host-forwarding.d.ts +22 -0
- package/dist/test-support/unfenced-host-token.d.ts +6 -0
- package/dist/worker.d.ts +3 -3
- package/package.json +6 -25
- package/dist/bun.d.ts +0 -225
- package/dist/bun.mjs +0 -2492
- package/dist/main.d.ts +0 -255
- package/dist/main.mjs +0 -23713
- package/dist/synthetic.d.ts +0 -60
- package/dist/upstream-websocket.d.ts +0 -34
- package/dist/user-hosted-forwarding.d.ts +0 -40
package/dist/worker.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type WorkspaceRelayDurableObjectNamespace, type WorkspaceRelayDurableObjectRoomOptions, type WorkspaceRelayDurableObjectSocket } from "./cloudflare";
|
|
2
|
-
import
|
|
3
|
-
import { type HostGenerationLookup } from "./
|
|
2
|
+
import { type WorkspaceRelayTarget } from "./relay-target";
|
|
3
|
+
import { type HostGenerationLookup, type RuntimeAccessTokenActiveResult } from "./resolver-clients";
|
|
4
4
|
type WorkspaceRelayWorkerBindings = {
|
|
5
5
|
WORKSPACE_RELAY_ROOM?: WorkspaceRelayDurableObjectNamespace;
|
|
6
6
|
CLAXEDO_RELAY_RESOLVER_URL?: string;
|
|
@@ -31,7 +31,7 @@ type WorkspaceRelayWorkerBindings = {
|
|
|
31
31
|
};
|
|
32
32
|
export type WorkspaceRelayWorkerEnv = Record<string, unknown> & WorkspaceRelayWorkerBindings;
|
|
33
33
|
type ResolverClient = {
|
|
34
|
-
target(workspaceId: string, hostId: string): Promise<WorkspaceRelayTarget | undefined>;
|
|
34
|
+
target(workspaceId: string, hostId: string, routingId?: string): Promise<WorkspaceRelayTarget | undefined>;
|
|
35
35
|
revocation(args: {
|
|
36
36
|
jti: string;
|
|
37
37
|
workspaceId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
|
-
"description": "Relay/tunnel
|
|
3
|
+
"description": "Relay/tunnel that routes authenticated traffic to workspace runtimes on Cloudflare: JWT workspace tokens and a header-stripping trust boundary",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -22,18 +22,8 @@
|
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"import": "./dist/index.mjs",
|
|
24
24
|
"default": "./dist/index.mjs"
|
|
25
|
-
},
|
|
26
|
-
"./bun": {
|
|
27
|
-
"development": "./src/bun.ts",
|
|
28
|
-
"bun": "./src/bun.ts",
|
|
29
|
-
"types": "./dist/bun.d.ts",
|
|
30
|
-
"import": "./dist/bun.mjs",
|
|
31
|
-
"default": "./dist/bun.mjs"
|
|
32
25
|
}
|
|
33
26
|
},
|
|
34
|
-
"bin": {
|
|
35
|
-
"workspace-relay": "dist/main.mjs"
|
|
36
|
-
},
|
|
37
27
|
"files": [
|
|
38
28
|
"README.md",
|
|
39
29
|
"LICENSE",
|
|
@@ -43,28 +33,19 @@
|
|
|
43
33
|
"access": "public"
|
|
44
34
|
},
|
|
45
35
|
"name": "@claxedo/workspace-relay",
|
|
46
|
-
"version": "0.
|
|
36
|
+
"version": "0.10.0",
|
|
47
37
|
"scripts": {
|
|
48
|
-
"start": "bun run src/main.ts",
|
|
49
|
-
"dev": "bun run --hot src/main.ts",
|
|
50
38
|
"build": "tsx scripts/build.ts",
|
|
51
39
|
"prepublishOnly": "npm run build",
|
|
52
40
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
53
|
-
"
|
|
54
|
-
"test": "mkdir -p .artifacts/unit && node ../../scripts/test-deadline.mjs bun test src bench --path-ignore-patterns='**/relay-workerd-*' --path-ignore-patterns='**/bun.test.ts' --timeout 30000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml && node ../../scripts/test-deadline.mjs bun test src/bun.test.ts --timeout 120000 --reporter=junit --reporter-outfile=.artifacts/unit/junit-bun-adapter.xml && node ../../scripts/test-deadline.mjs bun test src/relay-workerd-backpressure.test.ts src/relay-workerd-binary.test.ts --timeout 240000 --reporter=junit --reporter-outfile=.artifacts/unit/junit-workerd.xml",
|
|
55
|
-
"bench:gate": "bun bench/local-dry-run.ts",
|
|
56
|
-
"bench:gate:cf": "bun bench/cf-dev-smoke.ts"
|
|
41
|
+
"test": "mkdir -p .artifacts/unit && node ../../script/test-deadline.mjs bun test src scripts --path-ignore-patterns='**/relay-workerd-*' --timeout 30000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml && node ../../script/test-deadline.mjs bun test src/relay-workerd-backpressure.test.ts src/relay-workerd-binary.test.ts src/relay-workerd-session-host.test.ts --timeout 240000 --reporter=junit --reporter-outfile=.artifacts/unit/junit-workerd.xml"
|
|
57
42
|
},
|
|
58
43
|
"dependencies": {
|
|
59
|
-
"@claxedo/helpers": "0.
|
|
60
|
-
"@claxedo/workspace-relay-protocol": "0.
|
|
61
|
-
"
|
|
62
|
-
"jose": "6.0.11",
|
|
63
|
-
"posthog-node": "4.18.0"
|
|
44
|
+
"@claxedo/helpers": "0.2.0",
|
|
45
|
+
"@claxedo/workspace-relay-protocol": "0.10.0",
|
|
46
|
+
"jose": "6.0.11"
|
|
64
47
|
},
|
|
65
48
|
"devDependencies": {
|
|
66
|
-
"@claxedo/sandbox-manager": "0.9.0",
|
|
67
|
-
"@daytona/sdk": "0.192.0",
|
|
68
49
|
"@tsconfig/node-lts": "22.0.4",
|
|
69
50
|
"@types/bun": "1.3.13",
|
|
70
51
|
"esbuild": "0.25.12",
|
package/dist/bun.d.ts
DELETED
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { type WorkspaceRelayOptions } from "./server";
|
|
2
|
-
import { type RuntimeAccessTokenClaims } from "./auth";
|
|
3
|
-
import { type UpstreamWebSocketConstructor } from "./upstream-websocket";
|
|
4
|
-
/**
|
|
5
|
-
* Bun expresses HTTP idle timeout in seconds. Runtime SSE heartbeats arrive
|
|
6
|
-
* every 30 seconds, so this exceeds that interval while still bounding idle
|
|
7
|
-
* unauthenticated and incomplete HTTP connections.
|
|
8
|
-
*/
|
|
9
|
-
export declare const WORKSPACE_RELAY_IDLE_TIMEOUT_SECONDS = 45;
|
|
10
|
-
type RelayClientWebSocketData = {
|
|
11
|
-
kind: "client";
|
|
12
|
-
claims: RuntimeAccessTokenClaims;
|
|
13
|
-
upstreamUrl: string;
|
|
14
|
-
headers: Record<string, string>;
|
|
15
|
-
queue: Array<{
|
|
16
|
-
payload: string | Buffer<ArrayBuffer>;
|
|
17
|
-
queuedAt: number;
|
|
18
|
-
}>;
|
|
19
|
-
/** Running byte size of `queue`; reset when the queue is flushed upstream. */
|
|
20
|
-
queuedBytes?: number;
|
|
21
|
-
upstream?: WebSocket;
|
|
22
|
-
upstreamOpenTimer?: ReturnType<typeof setTimeout>;
|
|
23
|
-
trace?: RelayClientWebSocketTrace;
|
|
24
|
-
accessCheckTimer?: ReturnType<typeof setInterval>;
|
|
25
|
-
expiryTimer?: ReturnType<typeof setTimeout>;
|
|
26
|
-
};
|
|
27
|
-
type RelayClientWebSocketTrace = {
|
|
28
|
-
acceptedAt: number;
|
|
29
|
-
upstreamStartedAt?: number;
|
|
30
|
-
upstreamOpenMs?: number;
|
|
31
|
-
queuedFrames: number;
|
|
32
|
-
maxQueuedDelayMs: number;
|
|
33
|
-
emitted: boolean;
|
|
34
|
-
};
|
|
35
|
-
type RelayHostTunnelWebSocketData = {
|
|
36
|
-
kind: "host-tunnel";
|
|
37
|
-
hostId: string;
|
|
38
|
-
workspaceIds: string[];
|
|
39
|
-
/** Fence pair from the last verified Host Tunnel Token (connect or registration update); absent for tokens without one. */
|
|
40
|
-
enrollmentId?: string;
|
|
41
|
-
generation?: number;
|
|
42
|
-
pending: Map<string, PendingTunnelHttpResponse>;
|
|
43
|
-
channels: Map<string, RelayUserHostedClientWebSocket>;
|
|
44
|
-
heartbeat?: ReturnType<typeof setInterval>;
|
|
45
|
-
missedPongs: number;
|
|
46
|
-
generationCheckTimer?: ReturnType<typeof setInterval>;
|
|
47
|
-
generationCheckFailures: number;
|
|
48
|
-
messageBuffer: string;
|
|
49
|
-
};
|
|
50
|
-
type RelayUserHostedClientWebSocketData = {
|
|
51
|
-
kind: "user-hosted-client";
|
|
52
|
-
claims: RuntimeAccessTokenClaims;
|
|
53
|
-
hostId: string;
|
|
54
|
-
workspaceId: string;
|
|
55
|
-
channelId: string;
|
|
56
|
-
path: string;
|
|
57
|
-
relayHostToken: string;
|
|
58
|
-
accessCheckTimer?: ReturnType<typeof setInterval>;
|
|
59
|
-
expiryTimer?: ReturnType<typeof setTimeout>;
|
|
60
|
-
};
|
|
61
|
-
type RelayWebSocketData = RelayClientWebSocketData | RelayHostTunnelWebSocketData | RelayUserHostedClientWebSocketData;
|
|
62
|
-
/** The socket type Bun hands every handler: one type carrying the data union. */
|
|
63
|
-
type RelayWebSocket = Bun.ServerWebSocket<RelayWebSocketData>;
|
|
64
|
-
type RelayHostTunnelWebSocket = Bun.ServerWebSocket<RelayHostTunnelWebSocketData>;
|
|
65
|
-
type RelayUserHostedClientWebSocket = Bun.ServerWebSocket<RelayUserHostedClientWebSocketData>;
|
|
66
|
-
type PendingTunnelHttpResponse = {
|
|
67
|
-
controller: ReadableStreamDefaultController<Uint8Array>;
|
|
68
|
-
stream: ReadableStream<Uint8Array>;
|
|
69
|
-
resolve: (response: Response) => void;
|
|
70
|
-
reject: (error: Error) => void;
|
|
71
|
-
timeout: ReturnType<typeof setTimeout>;
|
|
72
|
-
pendingChunks: Uint8Array[];
|
|
73
|
-
bytesQueued: number;
|
|
74
|
-
slowConsumerTimeout?: ReturnType<typeof setTimeout>;
|
|
75
|
-
responseStarted: boolean;
|
|
76
|
-
/**
|
|
77
|
-
* The relay's own CORS headers for this request, applied to whatever the
|
|
78
|
-
* tunnelled host answers with. The browser talks to the RELAY, so the
|
|
79
|
-
* relay's allowlist is the one that decides what it may read — the host
|
|
80
|
-
* behind the tunnel runs its own, unrelated policy, and passing its headers
|
|
81
|
-
* through made a relay's configured app origin readable only when the host
|
|
82
|
-
* happened to allow it too.
|
|
83
|
-
*/
|
|
84
|
-
corsHeaders: (upstream: Headers) => Headers;
|
|
85
|
-
};
|
|
86
|
-
export type WorkspaceRelayHostTunnelOptions = {
|
|
87
|
-
/**
|
|
88
|
-
* Test seam replacing Host Tunnel Token verification. A boolean carries no
|
|
89
|
-
* claims, so a tunnel admitted through it has no generation and is never
|
|
90
|
-
* fenced.
|
|
91
|
-
*/
|
|
92
|
-
authorizeHostTunnel?: (request: Request, input: {
|
|
93
|
-
hostId: string;
|
|
94
|
-
workspaceIds: string[];
|
|
95
|
-
}) => boolean | Promise<boolean>;
|
|
96
|
-
hostTunnelPingIntervalMs?: number;
|
|
97
|
-
hostTunnelMaxMissedPongs?: number;
|
|
98
|
-
/**
|
|
99
|
-
* How often an established host tunnel's generation is re-checked against
|
|
100
|
-
* `resolveHostGeneration`. Defaults to 30s; 0 disables the check. Because
|
|
101
|
-
* the lookup may answer from its cache, a superseded tunnel closes within
|
|
102
|
-
* this interval plus the lookup's cache TTL.
|
|
103
|
-
*/
|
|
104
|
-
hostGenerationCheckIntervalMs?: number;
|
|
105
|
-
/**
|
|
106
|
-
* Consecutive unavailable lookups an established tunnel survives before it
|
|
107
|
-
* is closed 1012 for the host to reconnect. Defaults to 3.
|
|
108
|
-
*/
|
|
109
|
-
hostGenerationOutageGraceAttempts?: number;
|
|
110
|
-
hostTunnelStateDebounceMs?: number;
|
|
111
|
-
};
|
|
112
|
-
export type WorkspaceRelayBackpressureOptions = {
|
|
113
|
-
slowConsumerHighWaterMarkBytes?: number;
|
|
114
|
-
slowConsumerTimeoutMs?: number;
|
|
115
|
-
tunnelRequestBodyMaxBytes?: number;
|
|
116
|
-
tunnelHttpResponseTimeoutMs?: number;
|
|
117
|
-
directHttpTimeoutMs?: number;
|
|
118
|
-
directHttpConcurrency?: number;
|
|
119
|
-
upstreamWebSocketOpenTimeoutMs?: number;
|
|
120
|
-
upstreamWebSocketPreOpenQueueMaxFrames?: number;
|
|
121
|
-
upstreamWebSocketPreOpenQueueMaxBytes?: number;
|
|
122
|
-
upstreamWebSocket?: UpstreamWebSocketConstructor;
|
|
123
|
-
webSocketBufferedAmountMaxBytes?: number;
|
|
124
|
-
};
|
|
125
|
-
export type WorkspaceRelayBunOptions = WorkspaceRelayHostTunnelOptions & WorkspaceRelayBackpressureOptions & {
|
|
126
|
-
/** How often established user WebSockets are re-checked for revocation. Defaults to 30s. */
|
|
127
|
-
runtimeAccessTokenActiveCheckIntervalMs?: number;
|
|
128
|
-
/** Clock injection used to calculate the local token-expiry deadline. */
|
|
129
|
-
now?: () => number;
|
|
130
|
-
};
|
|
131
|
-
/**
|
|
132
|
-
* Declared as function-valued properties, not methods, because every member is
|
|
133
|
-
* a closure over the adapter's counters and is routinely handed around
|
|
134
|
-
* detached (`metricsSources.fragmentation ?? telemetry.getFragmentationStats`).
|
|
135
|
-
* None of them reads `this`, and the same shape is used by
|
|
136
|
-
* `WorkspaceRelayMetricsSources` in `./server`.
|
|
137
|
-
*/
|
|
138
|
-
export type WorkspaceRelayBunTelemetry = {
|
|
139
|
-
getFragmentationStats: () => FragmentationStats;
|
|
140
|
-
resetFragmentationStats: () => void;
|
|
141
|
-
getSlowConsumerStats: () => SlowConsumerStats;
|
|
142
|
-
resetSlowConsumerStats: () => void;
|
|
143
|
-
};
|
|
144
|
-
/**
|
|
145
|
-
* Drain controller exposed by `createWorkspaceRelayBun`.
|
|
146
|
-
*
|
|
147
|
-
* On SIGTERM the operator (see `installShutdownDrainHandler` in main.ts)
|
|
148
|
-
* flips `setDraining(true)`; from that point onward the relay
|
|
149
|
-
* - returns 503 from `/health` so Fly removes the instance from routing
|
|
150
|
-
* - rejects new HTTP requests with 503 (`relay_draining`) before doing auth
|
|
151
|
-
* - rejects new host-tunnel WebSocket upgrades with 503
|
|
152
|
-
* - closes active host tunnels with 1012 so host clients reconnect promptly
|
|
153
|
-
*
|
|
154
|
-
* `pendingCount()` reports the total number of in-flight tunnel HTTP
|
|
155
|
-
* responses across every connected host tunnel — this is what the operator
|
|
156
|
-
* polls via `waitForDrain(timeoutMs)` before hard-closing remaining sockets.
|
|
157
|
-
*
|
|
158
|
-
* Function-valued properties rather than methods, for the same reason as
|
|
159
|
-
* `WorkspaceRelayBunTelemetry`: `isDraining` and `pendingCount` are passed
|
|
160
|
-
* detached into `createWorkspaceRelay`, and none of them reads `this`.
|
|
161
|
-
*/
|
|
162
|
-
export type WorkspaceRelayBunDrainController = {
|
|
163
|
-
isDraining: () => boolean;
|
|
164
|
-
setDraining: (value: boolean) => void;
|
|
165
|
-
pendingCount: () => number;
|
|
166
|
-
waitForDrain: (timeoutMs: number) => Promise<{
|
|
167
|
-
drained: boolean;
|
|
168
|
-
remaining: number;
|
|
169
|
-
}>;
|
|
170
|
-
};
|
|
171
|
-
/**
|
|
172
|
-
* Bytes queued in a socket's send buffer, or `undefined` if it cannot report.
|
|
173
|
-
*
|
|
174
|
-
* Bun spells this `getBufferedAmount()` — a method, not the browser's
|
|
175
|
-
* `bufferedAmount` property. Reading the property on a Bun socket yields
|
|
176
|
-
* `undefined`, and `undefined > limit` is always false, so a guard built on it
|
|
177
|
-
* never fires.
|
|
178
|
-
*
|
|
179
|
-
* Fails open on a socket that cannot report, deliberately: an unguarded healthy
|
|
180
|
-
* connection beats a guard that kills healthy connections. This is also why the
|
|
181
|
-
* guard is not portable to `cloudflare.ts` — workerd's WebSocket exposes no
|
|
182
|
-
* buffer depth, so bounding that path needs a protocol-level credit/ack window.
|
|
183
|
-
*/
|
|
184
|
-
export declare const relayBufferedBytes: (socket: unknown) => number | undefined;
|
|
185
|
-
/**
|
|
186
|
-
* True only when the socket reports a depth over the limit. Unknown ⇒ false.
|
|
187
|
-
*
|
|
188
|
-
* Exported for `bun.test.ts`: a loopback peer drains faster than a test can
|
|
189
|
-
* fill it, so the decision is asserted directly.
|
|
190
|
-
*/
|
|
191
|
-
export declare const relayOverBackpressureLimit: (socket: unknown, limitBytes: number) => boolean;
|
|
192
|
-
export type FragmentationStats = {
|
|
193
|
-
fragmentsBuffered: number;
|
|
194
|
-
oversizedClosed: number;
|
|
195
|
-
};
|
|
196
|
-
export type SlowConsumerStats = {
|
|
197
|
-
overflowEvents: number;
|
|
198
|
-
timerFired: number;
|
|
199
|
-
droppedRequests: number;
|
|
200
|
-
};
|
|
201
|
-
declare function createSlowConsumerStats(): SlowConsumerStats;
|
|
202
|
-
declare function enqueueChunkWithBackpressure(input: {
|
|
203
|
-
ws: RelayHostTunnelWebSocket;
|
|
204
|
-
requestId: string;
|
|
205
|
-
entry: PendingTunnelHttpResponse;
|
|
206
|
-
chunk: Uint8Array;
|
|
207
|
-
slowConsumerTimeoutMs: number;
|
|
208
|
-
slowConsumerStats: SlowConsumerStats;
|
|
209
|
-
}): void;
|
|
210
|
-
export declare const __slowConsumerInternalsForTest: {
|
|
211
|
-
enqueueChunkWithBackpressure: typeof enqueueChunkWithBackpressure;
|
|
212
|
-
createSlowConsumerStats: typeof createSlowConsumerStats;
|
|
213
|
-
};
|
|
214
|
-
export declare function createWorkspaceRelayBun(options: WorkspaceRelayOptions, bunOptions?: WorkspaceRelayBunOptions): {
|
|
215
|
-
fetch: (request: Request, server: Bun.Server<RelayWebSocketData>) => Promise<Response | undefined>;
|
|
216
|
-
websocket: {
|
|
217
|
-
maxPayloadLength: number;
|
|
218
|
-
message(ws: RelayWebSocket, message: string | Buffer<ArrayBuffer>): void;
|
|
219
|
-
open(ws: RelayWebSocket): void;
|
|
220
|
-
close(ws: RelayWebSocket, code: number, reason: string): void;
|
|
221
|
-
};
|
|
222
|
-
drain: WorkspaceRelayBunDrainController;
|
|
223
|
-
telemetry: WorkspaceRelayBunTelemetry;
|
|
224
|
-
};
|
|
225
|
-
export {};
|