@claxedo/workspace-relay 0.8.0 → 0.10.1

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/auth.d.ts CHANGED
@@ -5,23 +5,47 @@ export declare const runtimeAccessTokenAudience = "workspace-relay";
5
5
  export declare const hostTunnelTokenAudience = "workspace-relay-host-tunnel";
6
6
  export declare const relayHostTokenIssuer = "workspace-relay";
7
7
  export declare const relayHostTokenAudience = "workspace-host-service";
8
- export type RelayClaimPair = {
9
- access: "cloud";
10
- backing: "cloud-vm";
11
- } | {
12
- access: "user-hosted";
13
- backing: "local-worktree";
14
- };
15
- export type RelayAccess = RelayClaimPair["access"];
16
- export type RelayBacking = RelayClaimPair["backing"];
8
+ /**
9
+ * Where the host behind a relayed request runs: the provisioner's machine
10
+ * (`cloud-vm`), an enrolled one (`local-worktree`), or one session's Durable
11
+ * Object (`durable-object`). The only placement word on the relay wire; the
12
+ * control plane stores the first two as a workspace's backing.
13
+ */
14
+ export type RelayBacking = "cloud-vm" | "local-worktree" | "durable-object";
17
15
  export type RelayJwtAlgorithm = (typeof algorithms)[number];
18
16
  export type RelayRole = "viewer" | "editor" | "admin" | "owner";
19
17
  export type ActorKind = "human" | "agent";
18
+ /**
19
+ * Earliest `iat` a Runtime Access Token may carry (2026-09-21T00:00:00Z).
20
+ *
21
+ * Tokens minted before channel provenance existed carry no `channel_identity`,
22
+ * so a channel-issued one among them is indistinguishable from an app token
23
+ * and there is no binding generation to check it against. The whole vintage is
24
+ * refused rather than trusted until it expires.
25
+ */
26
+ export declare const RUNTIME_ACCESS_TOKEN_ISSUED_AT_FLOOR_SECONDS = 1789948800;
27
+ /** The channel binding that authorized a token, as the token carries it. */
28
+ export type ChannelIdentityClaim = {
29
+ channel: string;
30
+ external_user_id: string;
31
+ identity_version: number;
32
+ };
33
+ /** The same binding as a mint argument. */
34
+ export type ChannelIdentityInput = {
35
+ channel: string;
36
+ externalUserId: string;
37
+ identityVersion: number;
38
+ };
39
+ export declare function toChannelIdentityClaim(input: ChannelIdentityInput): ChannelIdentityClaim;
40
+ export type TokenScope = "workspace" | "session";
41
+ /** A token minted for one job; only the routes serving that job accept it. */
42
+ export type TokenPurpose = "turn-execution";
20
43
  export type RuntimeAccessTokenClaims = {
21
44
  iss: typeof runtimeAccessTokenIssuer;
22
45
  aud: typeof runtimeAccessTokenAudience;
23
46
  principal_kind: "user" | "service";
24
47
  actor_id: string;
48
+ user_id?: string;
25
49
  actor_kind: "human" | "agent";
26
50
  actor_public_id?: string;
27
51
  actor_name?: string;
@@ -30,6 +54,17 @@ export type RuntimeAccessTokenClaims = {
30
54
  workspace_id: string;
31
55
  host_id: string;
32
56
  role: RelayRole;
57
+ /**
58
+ * What the token reaches: the whole workspace (its owner's token) or the one
59
+ * session `session_id` names (a share holder's). A token naming neither is
60
+ * refused, so a token minted before reach was named reaches nothing.
61
+ */
62
+ scope: TokenScope;
63
+ session_id?: string;
64
+ purpose?: TokenPurpose;
65
+ channel_identity?: ChannelIdentityClaim;
66
+ /** Present for cloud workspaces; assigned atomically with the sandbox address. */
67
+ routing_id?: string;
33
68
  exp: number;
34
69
  iat: number;
35
70
  jti: string;
@@ -39,6 +74,7 @@ export type RelayHostTokenClaims = {
39
74
  aud: typeof relayHostTokenAudience;
40
75
  principal_kind: "user" | "service";
41
76
  actor_id: string;
77
+ user_id?: string;
42
78
  actor_kind: "human" | "agent";
43
79
  actor_public_id?: string;
44
80
  actor_name?: string;
@@ -47,38 +83,37 @@ export type RelayHostTokenClaims = {
47
83
  workspace_id: string;
48
84
  host_id: string;
49
85
  role: RelayRole;
86
+ scope: TokenScope;
87
+ session_id?: string;
88
+ purpose?: TokenPurpose;
89
+ channel_identity?: ChannelIdentityClaim;
50
90
  exp: number;
51
91
  iat: number;
52
92
  jti: string;
53
93
  /** Durable parent Runtime Access Token id used for revocation checks. */
54
94
  parent_jti: string;
55
- } & RelayClaimPair;
95
+ backing: RelayBacking;
96
+ };
56
97
  export type HostTunnelTokenClaims = {
57
98
  iss: typeof runtimeAccessTokenIssuer;
58
99
  aud: typeof hostTunnelTokenAudience;
59
100
  sub: string;
60
101
  host_id: string;
61
102
  workspace_ids: string[];
62
- /**
63
- * Serving-generation fence. `generation` is only valid together with
64
- * `enrollment_id`; a relay with a host-generation resolver refuses a token
65
- * whose generation is below the enrollment's current one. A token without a
66
- * generation is admitted exactly as before the fence existed, which is what
67
- * desktop and self-hosted mints still produce.
68
- */
69
- enrollment_id?: string;
70
- generation?: number;
103
+ enrollment_id: string;
104
+ generation: number;
71
105
  exp: number;
72
106
  iat: number;
73
107
  jti: string;
74
108
  };
75
109
  export declare class WorkspaceRelayAuthError extends Error {
76
- readonly code: "invalid_relay_token" | "relay_token_workspace_mismatch" | "relay_token_host_mismatch" | "relay_token_claims_invalid";
77
- constructor(code: "invalid_relay_token" | "relay_token_workspace_mismatch" | "relay_token_host_mismatch" | "relay_token_claims_invalid", message: string);
110
+ readonly code: "runtime_access_token_invalid" | "invalid_relay_token" | "relay_token_workspace_mismatch" | "relay_token_host_mismatch" | "relay_token_claims_invalid";
111
+ constructor(code: "runtime_access_token_invalid" | "invalid_relay_token" | "relay_token_workspace_mismatch" | "relay_token_host_mismatch" | "relay_token_claims_invalid", message: string);
78
112
  }
79
113
  type RuntimeInput = {
80
114
  principalKind: "user" | "service";
81
115
  actorId: string;
116
+ userId?: string;
82
117
  actorKind: ActorKind;
83
118
  actorPublicId?: string;
84
119
  actorName?: string;
@@ -87,11 +122,16 @@ type RuntimeInput = {
87
122
  workspaceId: string;
88
123
  hostId: string;
89
124
  role: RelayRole;
125
+ sessionId?: string;
126
+ purpose?: TokenPurpose;
127
+ channelIdentity?: ChannelIdentityInput;
128
+ routingId?: string;
90
129
  ttlSeconds?: number;
91
130
  jti?: string;
92
131
  now?: number;
93
132
  };
94
- type RelayHostInput = RuntimeInput & RelayClaimPair & {
133
+ type RelayHostInput = RuntimeInput & {
134
+ backing: RelayBacking;
95
135
  /** The Runtime Access Token jti from which this one-request RHT is derived. */
96
136
  parentJti: string;
97
137
  /**
@@ -107,8 +147,8 @@ type HostTunnelInput = {
107
147
  subject: string;
108
148
  hostId: string;
109
149
  workspaceIds: string[];
110
- enrollmentId?: string;
111
- generation?: number;
150
+ enrollmentId: string;
151
+ generation: number;
112
152
  ttlSeconds?: number;
113
153
  jti?: string;
114
154
  now?: number;
@@ -122,10 +162,7 @@ type ExpectedHostTunnel = {
122
162
  workspaceIds: string[];
123
163
  };
124
164
  export declare function isHostGeneration(value: unknown): value is number;
125
- export declare function isRelayClaimPair(input: {
126
- access?: unknown;
127
- backing?: unknown;
128
- }): input is RelayClaimPair;
165
+ export declare function isRelayBacking(input: unknown): input is RelayBacking;
129
166
  /**
130
167
  * Resolver function returned by `createRemoteJWKSet` / `createLocalJWKSet`.
131
168
  * Accepted by `jwtVerify` directly — jose dispatches based on whether the
@@ -138,18 +175,15 @@ export type RelayKey = CryptoKey | Uint8Array | RelayKeyResolver;
138
175
  *
139
176
  * `importJWK` is declared `Promise<CryptoKey | Uint8Array>` because a symmetric
140
177
  * (`oct`) JWK imports as raw bytes. An EdDSA public JWK never does, so the byte
141
- * branch is a configuration error worth failing loudly on — the two callers
142
- * (`main.ts` for Bun, `worker.ts` for Cloudflare) previously asserted it away
143
- * with their own copies of this round-trip.
178
+ * branch is a configuration error worth failing loudly on.
144
179
  */
145
180
  export declare function deriveRelayHostPublicKey(privateKey: CryptoKey): Promise<CryptoKey>;
146
181
  /**
147
182
  * The relay-host key id, derived from the key's public component.
148
183
  *
149
- * ONE implementation on purpose: a Bun relay and a Cloudflare relay signing
150
- * with the same key must publish the same `kid`, or a token minted by one fails
151
- * key lookup at the other. It is written against WebCrypto rather than
152
- * `node:crypto` so the workerd bundle can use it too.
184
+ * Signing with the same key must always publish the same `kid`, or a token
185
+ * minted under one build fails key lookup at the next. Written against
186
+ * WebCrypto rather than `node:crypto` so the workerd bundle can use it.
153
187
  */
154
188
  export declare function deriveRelayHostKid(publicKey: CryptoKey): Promise<string>;
155
189
  /**
@@ -162,6 +196,31 @@ export declare function verifyRuntimeAccessToken(token: string, key: RelayKey, e
162
196
  export declare function validateRuntimeAccessTokenClaims(input: Record<string, unknown>, expected: ExpectedTarget): RuntimeAccessTokenClaims;
163
197
  export declare function mintHostTunnelToken(input: HostTunnelInput, key: RelaySigningKey, alg: RelayJwtAlgorithm): Promise<string>;
164
198
  export declare function verifyHostTunnelToken(token: string, key: RelayKey, expected: ExpectedHostTunnel): Promise<HostTunnelTokenClaims>;
199
+ /**
200
+ * Validates a claims result a custom `authorizeHostTunnel` policy returns.
201
+ * The policy decides WHO the tunnel is; the binding is the relay's: the
202
+ * claims' `host_id` must equal the requested host and its `workspace_ids`
203
+ * must cover every requested workspace, so a permissive policy cannot widen
204
+ * the identity its own claims assert.
205
+ */
206
+ /** What a Relay Host Token minted from a verified Runtime Access Token carries over from it. */
207
+ export declare function relayHostInputFromRuntimeClaims(claims: RuntimeAccessTokenClaims): Omit<RelayHostInput, "backing" | "hostId" | "workspaceId">;
165
208
  export declare function mintRelayHostToken(input: RelayHostInput, key: RelaySigningKey, alg: RelayJwtAlgorithm): Promise<string>;
166
209
  export declare function verifyRelayHostToken(token: string, key: RelayKey, expected: ExpectedTarget): Promise<RelayHostTokenClaims>;
210
+ export declare function tokenScopePayload(sessionId: string | undefined): {
211
+ scope: "workspace";
212
+ session_id?: undefined;
213
+ } | {
214
+ scope: "session";
215
+ session_id: string;
216
+ };
217
+ /** The token's reach, or nothing when it names neither the workspace nor exactly one session. */
218
+ /** The purpose claim, or undefined when it names a purpose this build does not know. */
219
+ export declare function tokenPurposeClaims(payload: Record<string, unknown>): {
220
+ purpose?: TokenPurpose;
221
+ } | undefined;
222
+ export declare function tokenScopeClaims(payload: Record<string, unknown>): {
223
+ scope: TokenScope;
224
+ session_id?: string;
225
+ } | undefined;
167
226
  export {};
@@ -1,54 +1,19 @@
1
1
  import { type AuthorizedWorkspaceRelayRequest, type WorkspaceRelayOptions } from "./server";
2
+ import { type SessionHostNamespace } from "./session-host-forwarding";
3
+ import { type WorkspaceRelayTraceLogEvent } from "./relay-trace";
4
+ export type { WorkspaceRelayTraceLogEvent } from "./relay-trace";
2
5
  export type WorkspaceRelayDurableObjectId = unknown;
3
6
  export type WorkspaceRelayDurableObjectStub = {
4
7
  fetch: (request: Request) => Promise<Response>;
5
8
  };
6
9
  export type WorkspaceRelayDurableObjectGetOptions = {
10
+ /** Cloudflare's placement hint, honoured on the room's first creation only: beside the workspace's runtime (`workspaceRelayRoomPlacement`). */
7
11
  locationHint?: string;
8
12
  };
9
13
  export type WorkspaceRelayDurableObjectNamespace = {
10
14
  idFromName: (name: string) => WorkspaceRelayDurableObjectId;
11
15
  get: (id: WorkspaceRelayDurableObjectId, options?: WorkspaceRelayDurableObjectGetOptions) => WorkspaceRelayDurableObjectStub;
12
16
  };
13
- export declare const DEFAULT_RELAY_LOCATION_HINT = "apac";
14
- /**
15
- * The complete set of Cloudflare Durable Object location hints.
16
- *
17
- * Cloudflare rejects an unknown hint, and a rejected `get()` fails the request —
18
- * so an unrecognised region must fall back rather than be passed through. This
19
- * list is the validator for that.
20
- */
21
- export declare const RELAY_LOCATION_HINTS: readonly ["wnam", "enam", "sam", "weur", "eeur", "apac", "oc", "afr", "me"];
22
- export type RelayLocationHint = typeof RELAY_LOCATION_HINTS[number];
23
- /** A workspace region carried on the request, if the caller supplied one. */
24
- export declare const RELAY_REGION_HEADER = "x-claxedo-workspace-region";
25
- /**
26
- * Chooses the location hint for a workspace's Durable Object.
27
- *
28
- * This decision is PERMANENT: a DO's location is fixed at first creation and
29
- * never migrates, so a workspace created with the wrong hint pays the extra
30
- * ocean crossing on every frame for its entire life. That is why an
31
- * unrecognised value falls back to the configured default instead of being
32
- * passed through or guessed at.
33
- *
34
- * Precedence, most authoritative first:
35
- * 1. the workspace's own region (header or `?region=`), which is what the
36
- * control plane knows and the only signal tied to the WORKSPACE rather than
37
- * to whoever happens to be connecting first;
38
- * 2. the requesting user's Cloudflare edge country, a decent proxy when the
39
- * workspace has no region yet;
40
- * 3. the deployment-wide configured hint (`CLAXEDO_RELAY_LOCATION_HINT`), then
41
- * `DEFAULT_RELAY_LOCATION_HINT`.
42
- */
43
- export declare function relayLocationHint(input: {
44
- configured?: string;
45
- region?: string;
46
- country?: string;
47
- }): string;
48
- /** The workspace region a request declares, if any. */
49
- export declare function relayRequestRegion(request: Request): string | undefined;
50
- /** The Cloudflare edge country for the request, when running on Cloudflare. */
51
- export declare function relayRequestCountry(request: Request): string | undefined;
52
17
  export type WorkspaceRelayDurableObjectEnv = Record<string, unknown>;
53
18
  export type WorkspaceRelayDurableObjectGatewayOptions = {
54
19
  bindingName?: string;
@@ -77,6 +42,7 @@ export type WorkspaceRelayDurableObjectConnectWebSocket = (url: string, init: {
77
42
  headers: Record<string, string>;
78
43
  }) => WorkspaceRelayDurableObjectSocket | Promise<WorkspaceRelayDurableObjectSocket>;
79
44
  export type WorkspaceRelayDurableObjectRoomOptions = WorkspaceRelayOptions & {
45
+ sessionHost?: SessionHostNamespace;
80
46
  createWebSocketPair?: () => WorkspaceRelayDurableObjectSocketPair;
81
47
  connectWebSocket?: WorkspaceRelayDurableObjectConnectWebSocket;
82
48
  hibernation?: WorkspaceRelayDurableObjectHibernation;
@@ -89,6 +55,15 @@ export type WorkspaceRelayDurableObjectRoomOptions = WorkspaceRelayOptions & {
89
55
  tunnelResponseBodyMaxBytes?: number;
90
56
  slowConsumerHighWaterMarkBytes?: number;
91
57
  slowConsumerTimeoutMs?: number;
58
+ /**
59
+ * How often established client WebSockets are re-checked for revocation.
60
+ * Defaults to 30s; 0 disables the re-check. Because the check may answer
61
+ * from the revocation lookup's cache, a revoked token closes the socket
62
+ * within this interval plus the lookup's cache TTL — see
63
+ * `runtimeAccessTokenRevocationDelayMs` in `./server`. A resolver outage
64
+ * gets `resolverOutageGraceAttempts` and never extends the socket past the
65
+ * token's `exp`, which the watcher enforces locally.
66
+ */
92
67
  runtimeAccessTokenActiveCheckIntervalMs?: number;
93
68
  workspaceTargetActiveCheckIntervalMs?: number;
94
69
  /**
@@ -121,17 +96,6 @@ export type WorkspaceRelayDurableObjectRoomOptions = WorkspaceRelayOptions & {
121
96
  traceLog?: (event: WorkspaceRelayTraceLogEvent) => void;
122
97
  now?: () => number;
123
98
  };
124
- export type WorkspaceRelayTraceLogEvent = {
125
- event: "workspace_relay_request";
126
- traceId: string;
127
- workspaceId?: string;
128
- routeKind: string;
129
- method: string;
130
- path: string;
131
- status: number;
132
- totalMs: number;
133
- phases: Record<string, number>;
134
- };
135
99
  export type WorkspaceRelayDurableObjectHibernation = {
136
100
  acceptWebSocket: (socket: WorkspaceRelayDurableObjectSocket) => void;
137
101
  getWebSockets: () => WorkspaceRelayDurableObjectSocket[];
@@ -154,13 +118,12 @@ export type WorkspaceRelaySocketAttachment = {
154
118
  hostId: string;
155
119
  workspaceIds: string[];
156
120
  connectedAt: number;
157
- /** Fence pair from the admitting Host Tunnel Token; absent on sockets attached before the fence existed. */
158
- enrollmentId?: string;
159
- generation?: number;
121
+ enrollmentId: string;
122
+ generation: number;
160
123
  /** Consecutive unavailable generation lookups, persisted so hibernation cannot reset the outage grace. */
161
124
  generationCheckFailures?: number;
162
125
  } | {
163
- kind: "user-hosted-client";
126
+ kind: "host-tunnel-client";
164
127
  channelId: string;
165
128
  claims: RuntimeAccessAttachmentClaims;
166
129
  target: WorkspaceRelayTargetAttachment;
@@ -204,8 +167,6 @@ export declare function relayFrameDiagnostics(data: unknown): Promise<{
204
167
  }>;
205
168
  export declare function workspaceRelayDurableObjectRoomName(workspaceId: string): string;
206
169
  export declare function workspaceRelayDurableObjectWorkspaceId(request: Request): string | undefined;
207
- export declare function setWorkspaceRelayAppOrigins(raw: string | undefined): void;
208
- export declare function setWorkspaceRelayAllowedOrigins(raw: string | undefined): void;
209
170
  export declare function createWorkspaceRelayDurableObjectGateway(options?: WorkspaceRelayDurableObjectGatewayOptions): {
210
171
  fetch(request: Request, env?: WorkspaceRelayDurableObjectEnv): Promise<Response>;
211
172
  };
@@ -229,4 +190,3 @@ export declare function createWorkspaceRelayDurableObjectRoom(options: Workspace
229
190
  };
230
191
  drain: WorkspaceRelayDurableObjectDrainController;
231
192
  };
232
- export {};
@@ -1,3 +1,5 @@
1
1
  export declare const DEFAULT_RELAY_APP_ORIGINS: string[];
2
+ /** Request headers a browser may send on relayed workspace requests, as the `access-control-allow-headers` value. */
3
+ export declare const RELAY_ALLOWED_REQUEST_HEADERS: string;
2
4
  export declare function parseAllowedOrigins(raw: string | undefined): string[] | undefined;
3
5
  export declare function createOriginMatcher(patterns: string[]): (origin: string) => boolean;
@@ -11,56 +11,23 @@ export type HostTunnelPresence = {
11
11
  expiresAt: number;
12
12
  };
13
13
  /**
14
- * Presence is keyed by (host, workspace): one host may hold a separate tunnel
15
- * per workspace on a Bun relay, and a later registration for a workspace takes
16
- * that workspace over without touching the host's other entries. `recordPong`
17
- * and `disconnectHost` take the optional workspace set for the same reason —
18
- * without it they act on every entry of the host, which is what a
19
- * single-workspace Cloudflare room wants.
14
+ * Presence is keyed by (host, workspace), so a later registration for a
15
+ * workspace takes that workspace over without touching the host's other
16
+ * entries. `recordPong` and `disconnectHost` act on every entry of the host.
20
17
  */
21
18
  export type WorkspaceRelayDirectory = {
22
19
  registerHostTunnel(input: {
23
20
  hostId: string;
24
21
  workspaceIds: string[];
25
22
  }): HostTunnelPresence;
26
- recordPong(hostId: string, workspaceIds?: string[]): HostTunnelPresence | undefined;
27
- disconnectHost(hostId: string, workspaceIds?: string[]): void;
23
+ recordPong(hostId: string): HostTunnelPresence | undefined;
24
+ disconnectHost(hostId: string): void;
28
25
  activeHost(input: {
29
26
  hostId: string;
30
27
  workspaceId: string;
31
28
  }): HostTunnelPresence | undefined;
32
- /**
33
- * Manually run an expiry sweep over the presence map. Removes any entry
34
- * whose `expiresAt <= now()`. Safe to call on a fresh/empty directory.
35
- * Useful in tests; the production path runs this on a timer (see
36
- * `sweepIntervalMs`).
37
- */
38
- sweep(): void;
39
- /**
40
- * Stop the active sweep timer (if one was started). Safe to call multiple
41
- * times and on directories that were created with `sweepIntervalMs: 0`.
42
- */
43
- dispose(): void;
44
- /**
45
- * Count of hosts with at least one non-expired presence entry, taken as a
46
- * snapshot without evicting. Surfaced via `/metrics` as
47
- * `directory.activeHostCount`.
48
- */
49
- size(): number;
50
29
  };
51
30
  export declare function createWorkspaceRelayDirectory(options?: {
52
31
  ttlMs?: number;
53
32
  now?: () => number;
54
- /**
55
- * Interval in milliseconds at which a background sweep runs to evict
56
- * expired host presence entries. Defaults to 30_000. Set to `0` to disable
57
- * the timer entirely (useful for tests; callers can still drive cleanup
58
- * manually via `sweep()`).
59
- */
60
- sweepIntervalMs?: number;
61
33
  }): WorkspaceRelayDirectory;
62
- /**
63
- * Convenience companion to `createWorkspaceRelayDirectory` for callers that
64
- * prefer a free function over a method. Equivalent to `directory.dispose()`.
65
- */
66
- export declare function disposeWorkspaceRelayDirectory(directory: WorkspaceRelayDirectory): void;
@@ -0,0 +1,7 @@
1
+ import type { WorkspaceRelayDurableObjectEnv } from "./cloudflare";
2
+ /**
3
+ * Where the gateway creates the room of the request's workspace. The room's
4
+ * location is fixed at its first creation and never migrates, so this
5
+ * decision is permanent for the workspace.
6
+ */
7
+ export declare function workspaceRelayRoomPlacement(request: Request, env: WorkspaceRelayDurableObjectEnv, workspaceId: string): Promise<import("./relay-location").RelayRoomPlacement>;
@@ -0,0 +1,7 @@
1
+ export type HostTunnelFailureCode = "host_tunnel_timeout" | "host_tunnel_response_body_too_large" | "host_tunnel_unavailable";
2
+ export declare function hostTunnelFailure(code: HostTunnelFailureCode): Error & {
3
+ code: HostTunnelFailureCode;
4
+ status: number;
5
+ retryable: boolean;
6
+ };
7
+ export declare function hostTunnelFailureResponse(error: unknown): Response;
@@ -0,0 +1,29 @@
1
+ import type { RelayBacking } from "./auth";
2
+ /**
3
+ * Whether a relay target is reached through a host tunnel.
4
+ *
5
+ * A `local-worktree` placement runs on an enrolled machine that dials the
6
+ * relay and multiplexes its traffic over that one socket; a `cloud-vm`
7
+ * placement runs on the provisioner's machine, which the relay reaches by a
8
+ * direct upstream connection.
9
+ */
10
+ export declare function isHostTunnelTarget(target: {
11
+ backing: RelayBacking;
12
+ }): boolean;
13
+ export type WorkspaceRelaySocketKind = "host-tunnel-client" | "client";
14
+ export declare function socketKindFor(target: {
15
+ backing: RelayBacking;
16
+ }): WorkspaceRelaySocketKind;
17
+ /**
18
+ * Privacy rule: never forward the browser's `Cookie` header through a host
19
+ * tunnel. The host process runs on a machine somebody uses, whose cookie jar
20
+ * the browser may share (localhost dev tooling), so passing cookies verbatim
21
+ * risks leaking session data to it. A cloud-vm sits behind a dedicated network
22
+ * boundary where a session cookie may legitimately be needed, so it passes
23
+ * `Cookie` through unchanged.
24
+ *
25
+ * Returns a new `Headers` instance; `headers` is not mutated.
26
+ */
27
+ export declare function forwardHeadersFor(target: {
28
+ backing: RelayBacking;
29
+ }, headers: Headers): Headers;
package/dist/http.d.ts CHANGED
@@ -6,3 +6,7 @@ export declare function errorBody(code: string, message: string): {
6
6
  message: string;
7
7
  };
8
8
  };
9
+ /** The host a tunnel request names, `/host-tunnels/<hostId>`. */
10
+ export declare function hostIdFromTunnelPath(pathname: string): string | undefined;
11
+ /** The workspaces a tunnel request registers for, under either spelling of the query name. */
12
+ export declare function workspaceIdsFromSearch(url: URL): string[];
package/dist/index.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export * from "./auth";
2
2
  export * from "./cloudflare";
3
+ export * from "./relay-cors";
3
4
  export * from "./cors-origins";
4
5
  export * from "./directory";
6
+ export * from "./gateway-placement";
7
+ export * from "./relay-location";
8
+ export * from "./relay-target";
5
9
  export * from "./server";
10
+ export * from "./resolver-clients";