@claxedo/workspace-relay 0.1.0 → 0.5.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 +143 -9
- package/dist/auth.d.ts +16 -8
- package/dist/bun.d.ts +40 -8
- package/dist/cloudflare.d.ts +116 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +2153 -212
- package/dist/main.d.ts +35 -18
- package/dist/main.mjs +984 -215
- package/dist/server.d.ts +83 -15
- package/dist/worker.d.ts +50 -0
- package/package.json +18 -8
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import type { TokenVerifier } from "@claxedo/workspace-relay-protocol";
|
|
3
|
-
import { type
|
|
2
|
+
import type { RuntimeAccessVerifierClaims, TokenVerifier } from "@claxedo/workspace-relay-protocol";
|
|
3
|
+
import { type RelayClaimPair, type RelayJwtAlgorithm, type RelayKey, type RelayRole, type RuntimeAccessTokenClaims } from "./auth";
|
|
4
4
|
import type { WorkspaceRelayDirectory } from "./directory";
|
|
5
5
|
export type RelayHostPublicKey = {
|
|
6
6
|
publicKey: CryptoKey | Uint8Array;
|
|
@@ -10,9 +10,8 @@ export type WorkspaceRelayTarget = {
|
|
|
10
10
|
workspaceId: string;
|
|
11
11
|
hostId: string;
|
|
12
12
|
baseUrl: string;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
13
|
+
upstreamHeaders?: Record<string, string>;
|
|
14
|
+
} & RelayClaimPair;
|
|
16
15
|
export type RuntimeAccessTokenActiveResult = {
|
|
17
16
|
active: true;
|
|
18
17
|
} | {
|
|
@@ -20,6 +19,29 @@ export type RuntimeAccessTokenActiveResult = {
|
|
|
20
19
|
code: string;
|
|
21
20
|
reason: string;
|
|
22
21
|
};
|
|
22
|
+
export type RevocationLookupArgs = {
|
|
23
|
+
jti: string;
|
|
24
|
+
workspaceId: string;
|
|
25
|
+
hostId: string;
|
|
26
|
+
};
|
|
27
|
+
export type RevocationLookup = (args: RevocationLookupArgs) => Promise<RuntimeAccessTokenActiveResult>;
|
|
28
|
+
export type CachedRevocationOptions = {
|
|
29
|
+
/** TTL in milliseconds for cached revocation responses. Defaults to 10_000. */
|
|
30
|
+
ttlMs?: number;
|
|
31
|
+
/** Clock injection for tests. Defaults to `Date.now`. */
|
|
32
|
+
now?: () => number;
|
|
33
|
+
};
|
|
34
|
+
export type TargetLookupArgs = {
|
|
35
|
+
workspaceId: string;
|
|
36
|
+
hostId: string;
|
|
37
|
+
};
|
|
38
|
+
export type TargetLookup = (args: TargetLookupArgs) => Promise<WorkspaceRelayTarget | undefined>;
|
|
39
|
+
export type CachedTargetOptions = {
|
|
40
|
+
/** TTL in milliseconds for positive target responses. Defaults to 5_000. */
|
|
41
|
+
ttlMs?: number;
|
|
42
|
+
/** Clock injection for tests. Defaults to `Date.now`. */
|
|
43
|
+
now?: () => number;
|
|
44
|
+
};
|
|
23
45
|
export type WorkspaceRelayAuditEvent = {
|
|
24
46
|
action: "relay.request.accepted" | "relay.request.denied" | "relay.request.suppressed_summary" | "host_tunnel.connected" | "host_tunnel.disconnected";
|
|
25
47
|
result: "allow" | "deny";
|
|
@@ -39,6 +61,13 @@ export type WorkspaceRelayAuditEvent = {
|
|
|
39
61
|
};
|
|
40
62
|
export type WorkspaceRelayAuthOptions = {
|
|
41
63
|
runtimeAccessKey: RelayKey;
|
|
64
|
+
/**
|
|
65
|
+
* Cache signature/introspection-verified Runtime Access Token claims by the
|
|
66
|
+
* full token string. Revocation/active checks, role enforcement, and target
|
|
67
|
+
* resolution still run for every request after this cache hit. Defaults to
|
|
68
|
+
* 10s and is always capped by the token's exp claim minus skew.
|
|
69
|
+
*/
|
|
70
|
+
runtimeAccessTokenCacheTtlMs?: number;
|
|
42
71
|
/**
|
|
43
72
|
* P-shared: optional override for runtime-access-token verification.
|
|
44
73
|
*
|
|
@@ -54,7 +83,7 @@ export type WorkspaceRelayAuthOptions = {
|
|
|
54
83
|
* See `@claxedo/workspace-relay-protocol/token-verifier` for the
|
|
55
84
|
* `HttpTokenVerifier` and `StaticTokenVerifier` reference impls.
|
|
56
85
|
*/
|
|
57
|
-
tokenVerifier?: TokenVerifier
|
|
86
|
+
tokenVerifier?: TokenVerifier<RuntimeAccessVerifierClaims>;
|
|
58
87
|
isRuntimeAccessTokenActive?: (claims: RuntimeAccessTokenClaims) => RuntimeAccessTokenActiveResult | Promise<RuntimeAccessTokenActiveResult>;
|
|
59
88
|
};
|
|
60
89
|
export type WorkspaceRelayHostTokenOptions = {
|
|
@@ -75,10 +104,18 @@ export type WorkspaceRelayHostTokenOptions = {
|
|
|
75
104
|
* `relayHostPublicKeys` so JWKS and mint agree by construction.
|
|
76
105
|
*/
|
|
77
106
|
relayHostMintKid?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Cache Relay Host Tokens minted from an already-verified Runtime Access
|
|
109
|
+
* Token. The relay still verifies the RAT, checks revocation, enforces role,
|
|
110
|
+
* and resolves the target on every request; this only avoids repeated signing
|
|
111
|
+
* work for the same jti/target after those gates pass. Defaults to 30s.
|
|
112
|
+
*/
|
|
113
|
+
relayHostTokenCacheTtlMs?: number;
|
|
78
114
|
};
|
|
79
115
|
export type WorkspaceRelayRoutingOptions = {
|
|
80
116
|
resolveTarget: (claims: RuntimeAccessTokenClaims) => WorkspaceRelayTarget | Promise<WorkspaceRelayTarget | undefined>;
|
|
81
117
|
fetch?: typeof fetch;
|
|
118
|
+
forwardTimeoutMs?: number;
|
|
82
119
|
directory?: WorkspaceRelayDirectory;
|
|
83
120
|
};
|
|
84
121
|
export type WorkspaceRelayDrainOptions = {
|
|
@@ -125,18 +162,17 @@ export type WorkspaceRelayMetricsOptions = {
|
|
|
125
162
|
/**
|
|
126
163
|
* T31: returns the remote IP of the inbound `/metrics` request (used to
|
|
127
164
|
* gate access to the loopback when `metricsToken` is unset). When this is
|
|
128
|
-
* also unset,
|
|
129
|
-
*
|
|
130
|
-
*
|
|
165
|
+
* also unset, `/metrics` fails closed. The Bun adapter wires this resolver
|
|
166
|
+
* through `Bun.Server.requestIP`; other callers should set either this or
|
|
167
|
+
* `metricsToken`.
|
|
131
168
|
*/
|
|
132
169
|
metricsRemoteAddress?: (request: Request) => string | undefined;
|
|
133
170
|
/**
|
|
134
171
|
* T31: optional providers used to assemble the `/metrics` response body. The
|
|
135
|
-
* fragmentation/slow-consumer counters live in the bun adapter
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* dependency on the bun-only state.
|
|
172
|
+
* fragmentation/slow-consumer counters live in the bun adapter's per-handler
|
|
173
|
+
* telemetry, and the in-flight pending count lives on the drain controller.
|
|
174
|
+
* `createWorkspaceRelayBun` wires these to make ops counters visible on
|
|
175
|
+
* `/metrics` without giving `server.ts` a hard dependency on bun-only state.
|
|
140
176
|
*/
|
|
141
177
|
metricsSources?: WorkspaceRelayMetricsSources;
|
|
142
178
|
};
|
|
@@ -183,6 +219,28 @@ export type AuthorizedWorkspaceRelayRequest = {
|
|
|
183
219
|
relayHostToken: string;
|
|
184
220
|
path: string;
|
|
185
221
|
};
|
|
222
|
+
export type WorkspaceRelayAuthorizeTrace = {
|
|
223
|
+
span<T>(name: string, run: () => Promise<T>): Promise<T>;
|
|
224
|
+
};
|
|
225
|
+
export type WorkspaceRelayTracePhase = {
|
|
226
|
+
name: string;
|
|
227
|
+
ms: number;
|
|
228
|
+
};
|
|
229
|
+
export type WorkspaceRelayTrace = WorkspaceRelayAuthorizeTrace & {
|
|
230
|
+
phases: WorkspaceRelayTracePhase[];
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Wraps a revocation lookup with a small in-memory cache keyed by `jti`.
|
|
234
|
+
* Both positive and negative results are cached for the full TTL; revocation
|
|
235
|
+
* lag is bounded by the same TTL by design. Concurrent misses share one lookup.
|
|
236
|
+
*/
|
|
237
|
+
export declare function createCachedRevocationClient(inner: RevocationLookup, options?: CachedRevocationOptions): RevocationLookup;
|
|
238
|
+
/**
|
|
239
|
+
* Caches positive workspace target lookups by workspace+host. Missing targets
|
|
240
|
+
* are only coalesced while in flight, not retained, so cold-start polling can
|
|
241
|
+
* see readiness as soon as the control plane records it.
|
|
242
|
+
*/
|
|
243
|
+
export declare function createCachedTargetClient(inner: TargetLookup, options?: CachedTargetOptions): TargetLookup;
|
|
186
244
|
export type WorkspaceRelayForwardHeadersOptions = {
|
|
187
245
|
/**
|
|
188
246
|
* When true, also strip the `Cookie` request header. User-hosted workspaces
|
|
@@ -194,21 +252,31 @@ export type WorkspaceRelayForwardHeadersOptions = {
|
|
|
194
252
|
* is to NOT strip cookies. T26.
|
|
195
253
|
*/
|
|
196
254
|
userHosted?: boolean;
|
|
255
|
+
upstreamHeaders?: Record<string, string>;
|
|
256
|
+
};
|
|
257
|
+
export type WorkspaceRelayForwardRequestInitOptions = WorkspaceRelayForwardHeadersOptions & {
|
|
258
|
+
signal?: AbortSignal;
|
|
197
259
|
};
|
|
198
260
|
export declare function workspaceRelayTargetUrl(target: WorkspaceRelayTarget, path: string, search: string): URL;
|
|
199
261
|
export declare function workspaceRelayForwardHeaders(input: Headers, relayHostToken: string, workspaceId: string, options?: WorkspaceRelayForwardHeadersOptions): Headers;
|
|
262
|
+
export declare function workspaceRelayForwardRequestInit(request: Request, relayHostToken: string, workspaceId: string, options?: WorkspaceRelayForwardRequestInitOptions): RequestInit;
|
|
200
263
|
/**
|
|
201
264
|
* T16: dispose the sampler's periodic flush timer for a given options object.
|
|
202
265
|
* Tests call this in cleanup so the bun process can exit.
|
|
203
266
|
*/
|
|
204
267
|
export declare function disposeAuditSampler(options: WorkspaceRelayOptions): void;
|
|
205
|
-
export declare function
|
|
268
|
+
export declare function createWorkspaceRelayTrace(): WorkspaceRelayTrace;
|
|
269
|
+
export declare function workspaceRelayServerTiming(trace: WorkspaceRelayTrace): string;
|
|
270
|
+
export declare function workspaceRelayTimingResponse(response: Response, trace: WorkspaceRelayTrace): Response;
|
|
271
|
+
export declare function authorizeWorkspaceRelayRequest(options: WorkspaceRelayOptions, request: Request, workspaceId: string, trace?: WorkspaceRelayAuthorizeTrace): Promise<{
|
|
206
272
|
ok: true;
|
|
207
273
|
request: AuthorizedWorkspaceRelayRequest;
|
|
208
274
|
} | {
|
|
209
275
|
ok: false;
|
|
210
276
|
response: Response;
|
|
277
|
+
code: string;
|
|
211
278
|
}>;
|
|
279
|
+
export declare function forwardWorkspaceRelayRequest(request: Request, target: WorkspaceRelayTarget, relayHostToken: string, path: string, requestFetch: typeof fetch, timeoutMs: number, trace?: WorkspaceRelayAuthorizeTrace): Promise<Response>;
|
|
212
280
|
export type WorkspaceRelayApp = Hono & {
|
|
213
281
|
/**
|
|
214
282
|
* T16: clear the sampler's periodic flush timer. Tests must call this so
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type WorkspaceRelayDurableObjectNamespace, type WorkspaceRelayDurableObjectRoomOptions, type WorkspaceRelayDurableObjectSocket } from "./cloudflare";
|
|
2
|
+
import type { RuntimeAccessTokenActiveResult, WorkspaceRelayTarget } from "./server";
|
|
3
|
+
export type WorkspaceRelayWorkerEnv = Record<string, unknown> & {
|
|
4
|
+
WORKSPACE_RELAY_ROOM?: WorkspaceRelayDurableObjectNamespace;
|
|
5
|
+
CLAXEDO_RELAY_RESOLVER_URL?: string;
|
|
6
|
+
CLAXEDO_CENTRAL_URL?: string;
|
|
7
|
+
CLAXEDO_RELAY_RESOLVER_TOKEN?: string;
|
|
8
|
+
CLAXEDO_CONTROL_PLANE_JWKS_URL?: string;
|
|
9
|
+
CLAXEDO_RUNTIME_ACCESS_TOKEN_PUBLIC_KEY_PEM?: string;
|
|
10
|
+
CLAXEDO_RELAY_HOST_SIGNING_KEY_PEM?: string;
|
|
11
|
+
CLAXEDO_RELAY_HOST_PUBLIC_KEY_PEM?: string;
|
|
12
|
+
CLAXEDO_RELAY_HOST_NEXT_PUBLIC_KEY_PEM?: string;
|
|
13
|
+
CLAXEDO_RELAY_HOST_KID?: string;
|
|
14
|
+
CLAXEDO_RELAY_HOST_NEXT_KID?: string;
|
|
15
|
+
CLAXEDO_RELAY_AUDIT_ACCEPT_SAMPLE_RATE?: string;
|
|
16
|
+
CLAXEDO_RELAY_TUNNEL_CHANNEL_CAP?: string;
|
|
17
|
+
CLAXEDO_RELAY_TRACE_SAMPLE_RATE?: string;
|
|
18
|
+
CLAXEDO_RELAY_REVOCATION_CACHE_TTL_MS?: string;
|
|
19
|
+
CLAXEDO_RELAY_TARGET_CACHE_TTL_MS?: string;
|
|
20
|
+
CLAXEDO_APP_ORIGINS?: string;
|
|
21
|
+
};
|
|
22
|
+
type ResolverClient = {
|
|
23
|
+
target(workspaceId: string, hostId: string): Promise<WorkspaceRelayTarget | undefined>;
|
|
24
|
+
revocation(args: {
|
|
25
|
+
jti: string;
|
|
26
|
+
workspaceId: string;
|
|
27
|
+
hostId: string;
|
|
28
|
+
}): Promise<RuntimeAccessTokenActiveResult>;
|
|
29
|
+
};
|
|
30
|
+
type ResolverFetch = (url: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
31
|
+
export declare function workspaceRelayWorkerResolverUrl(env: WorkspaceRelayWorkerEnv): string;
|
|
32
|
+
export declare function workspaceRelayWorkerResolverClient(env: WorkspaceRelayWorkerEnv, fetcher?: ResolverFetch): ResolverClient;
|
|
33
|
+
export declare function workspaceRelayDurableObjectOptions(env: WorkspaceRelayWorkerEnv): Promise<WorkspaceRelayDurableObjectRoomOptions>;
|
|
34
|
+
export declare class WorkspaceRelayRoom {
|
|
35
|
+
private state;
|
|
36
|
+
private env;
|
|
37
|
+
private room?;
|
|
38
|
+
private loading?;
|
|
39
|
+
constructor(state: unknown, env: WorkspaceRelayWorkerEnv);
|
|
40
|
+
private hibernation;
|
|
41
|
+
private loadRoom;
|
|
42
|
+
fetch(request: Request): Promise<Response>;
|
|
43
|
+
webSocketMessage(socket: WorkspaceRelayDurableObjectSocket, message: string | ArrayBuffer): Promise<void>;
|
|
44
|
+
webSocketClose(socket: WorkspaceRelayDurableObjectSocket, code: number, reason: string, wasClean: boolean): Promise<void>;
|
|
45
|
+
webSocketError(socket: WorkspaceRelayDurableObjectSocket, error: unknown): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
declare const _default: {
|
|
48
|
+
fetch(request: Request, env: WorkspaceRelayWorkerEnv): Promise<Response>;
|
|
49
|
+
};
|
|
50
|
+
export default _default;
|
package/package.json
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claxedo/workspace-relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"bun": "./dist/index.mjs",
|
|
11
|
+
"development": "./dist/index.mjs",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
7
16
|
"bin": {
|
|
8
|
-
"workspace-relay": "
|
|
17
|
+
"workspace-relay": "dist/main.mjs"
|
|
9
18
|
},
|
|
10
19
|
"files": [
|
|
20
|
+
"README.md",
|
|
11
21
|
"dist"
|
|
12
22
|
],
|
|
13
23
|
"publishConfig": {
|
|
@@ -22,15 +32,15 @@
|
|
|
22
32
|
"test": "bun test src"
|
|
23
33
|
},
|
|
24
34
|
"dependencies": {
|
|
25
|
-
"@claxedo/workspace-relay-protocol": "
|
|
35
|
+
"@claxedo/workspace-relay-protocol": "0.5.0",
|
|
26
36
|
"hono": "4.10.7",
|
|
27
37
|
"jose": "6.0.11"
|
|
28
38
|
},
|
|
29
39
|
"devDependencies": {
|
|
30
|
-
"@tsconfig/node-lts": "
|
|
31
|
-
"@types/bun": "
|
|
32
|
-
"esbuild": "
|
|
33
|
-
"tsx": "
|
|
34
|
-
"typescript": "
|
|
40
|
+
"@tsconfig/node-lts": "22.0.4",
|
|
41
|
+
"@types/bun": "1.3.13",
|
|
42
|
+
"esbuild": "0.25.12",
|
|
43
|
+
"tsx": "4.22.3",
|
|
44
|
+
"typescript": "5.8.2"
|
|
35
45
|
}
|
|
36
46
|
}
|