diff --git a/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts b/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts index d229c9d3a9..729379fb23 100644 --- a/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts +++ b/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts @@ -135,6 +135,10 @@ export class CloudflareActorsManagerDriver implements ManagerDriver { return webSocket as unknown as UniversalWebSocket; } + async buildGatewayUrl(actorId: string): Promise { + return `http://actor/gateway/${encodeURIComponent(actorId)}`; + } + async proxyRequest( c: HonoContext<{ Bindings: Bindings }>, actorRequest: Request, diff --git a/rivetkit-typescript/packages/rivetkit/src/client/actor-handle.ts b/rivetkit-typescript/packages/rivetkit/src/client/actor-handle.ts index 8041008f34..7fe7cdb0ca 100644 --- a/rivetkit-typescript/packages/rivetkit/src/client/actor-handle.ts +++ b/rivetkit-typescript/packages/rivetkit/src/client/actor-handle.ts @@ -239,9 +239,7 @@ export class ActorHandleRaw { } /** - * Resolves the actor to get its unique actor ID - * - * @returns {Promise} - A promise that resolves to the actor's ID + * Resolves the actor to get its unique actor ID. */ async resolve({ signal }: { signal?: AbortSignal } = {}): Promise { if ( @@ -277,6 +275,18 @@ export class ActorHandleRaw { assertUnreachable(this.#actorQuery); } } + + /** + * Returns the raw URL for routing traffic to the actor. + */ + async getGatewayUrl(): Promise { + const { actorId } = await queryActor( + undefined, + this.#actorQuery, + this.#driver, + ); + return await this.#driver.buildGatewayUrl(actorId); + } } /** diff --git a/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts b/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts index baa27e1b8d..d005cf5c14 100644 --- a/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts +++ b/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts @@ -147,6 +147,11 @@ export class FileSystemManagerDriver implements ManagerDriver { return upgradeWebSocket(() => wsHandler)(c, noopNext()); } + async buildGatewayUrl(actorId: string): Promise { + const port = this.#config.managerPort ?? 6420; + return `http://127.0.0.1:${port}/gateway/${encodeURIComponent(actorId)}`; + } + async getForId({ actorId, }: GetForIdInput): Promise { diff --git a/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts b/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts index ae01fc63d0..4be926c891 100644 --- a/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts +++ b/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts @@ -32,6 +32,13 @@ export interface ManagerDriver { params: unknown, ): Promise; + /** + * Build a public gateway URL for a specific actor. + * + * This lives on the driver because the base endpoint varies by runtime. + */ + buildGatewayUrl(actorId: string): Promise; + displayInformation(): ManagerDisplayInformation; extraStartupLog?: () => Record; diff --git a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/actor-http-client.ts b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/actor-http-client.ts index 7beb4dbae4..2a931c3b87 100644 --- a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/actor-http-client.ts +++ b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/actor-http-client.ts @@ -1,6 +1,6 @@ import type { ClientConfig } from "@/client/config"; import { HEADER_RIVET_TOKEN } from "@/common/actor-router-consts"; -import { combineUrlPath } from "@/utils"; +import { buildActorGatewayUrl } from "./actor-websocket-client"; import { getEndpoint } from "./api-utils"; export async function sendHttpRequestToActor( @@ -11,9 +11,11 @@ export async function sendHttpRequestToActor( // Route through guard port const url = new URL(actorRequest.url); const endpoint = getEndpoint(runConfig); - const guardUrl = combineUrlPath( + const guardUrl = buildActorGatewayUrl( endpoint, - `/gateway/${actorId}${url.pathname}${url.search}`, + actorId, + runConfig.token, + `${url.pathname}${url.search}`, ); // Handle body properly based on method and presence diff --git a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/actor-websocket-client.ts b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/actor-websocket-client.ts index 445a04d0db..53a454ad00 100644 --- a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/actor-websocket-client.ts +++ b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/actor-websocket-client.ts @@ -13,6 +13,18 @@ import { combineUrlPath } from "@/utils"; import { getEndpoint } from "./api-utils"; import { logger } from "./log"; +export function buildActorGatewayUrl( + endpoint: string, + actorId: string, + token: string | undefined, + path = "", +): string { + const tokenSegment = + token !== undefined ? `@${encodeURIComponent(token)}` : ""; + const gatewayPath = `/gateway/${encodeURIComponent(actorId)}${tokenSegment}${path}`; + return combineUrlPath(endpoint, gatewayPath); +} + export async function openWebSocketToActor( runConfig: ClientConfig, path: string, @@ -24,13 +36,12 @@ export async function openWebSocketToActor( // WebSocket connections go through guard const endpoint = getEndpoint(runConfig); - let gatewayPath; - if (runConfig.token !== undefined) { - gatewayPath = `/gateway/${encodeURIComponent(actorId)}@${encodeURIComponent(runConfig.token)}${path}`; - } else { - gatewayPath = `/gateway/${encodeURIComponent(actorId)}${path}`; - } - const guardUrl = combineUrlPath(endpoint, gatewayPath); + const guardUrl = buildActorGatewayUrl( + endpoint, + actorId, + runConfig.token, + path, + ); logger().debug({ msg: "opening websocket to actor via guard", diff --git a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts index 56e1dfef35..7ca17d2f3d 100644 --- a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts +++ b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts @@ -21,6 +21,7 @@ import { combineUrlPath, type GetUpgradeWebSocket } from "@/utils"; import { getNextPhase } from "@/utils/env-vars"; import { sendHttpRequestToActor } from "./actor-http-client"; import { + buildActorGatewayUrl, buildWebSocketProtocols, openWebSocketToActor, } from "./actor-websocket-client"; @@ -309,6 +310,15 @@ export class RemoteManagerDriver implements ManagerDriver { ); } + async buildGatewayUrl(actorId: string): Promise { + if (this.#metadataPromise) { + await this.#metadataPromise; + } + + const endpoint = getEndpoint(this.#config); + return buildActorGatewayUrl(endpoint, actorId, this.#config.token); + } + async proxyRequest( _c: HonoContext, actorRequest: Request,