From 940e49fcfafb424df4c97fdb539e0b91ae8a50b9 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Thu, 12 Mar 2026 18:45:14 -0700 Subject: [PATCH] Use vanilla Rivet routing in Foundry backend --- foundry/CLAUDE.md | 43 +++++--------------- foundry/compose.dev.yaml | 3 -- foundry/compose.preview.yaml | 2 - foundry/packages/backend/src/actors/index.ts | 20 --------- foundry/packages/backend/src/index.ts | 41 +++++++------------ 5 files changed, 26 insertions(+), 83 deletions(-) diff --git a/foundry/CLAUDE.md b/foundry/CLAUDE.md index d25fbfa..5e7c3f6 100644 --- a/foundry/CLAUDE.md +++ b/foundry/CLAUDE.md @@ -45,6 +45,13 @@ Use `pnpm` workspaces and Turborepo. - Stop the preview stack: `just foundry-preview-down` - Tail preview logs: `just foundry-preview-logs` +## Railway Logs + +- Production Foundry Railway logs can be read from a linked workspace with `railway logs --deployment --lines 200` or `railway logs --deployment --lines 200`. +- If Railway logs fail because the workspace is not linked to the correct project/service/environment, run: + `railway link --project 33e3e2df-32c5-41c5-a4af-dca8654acb1d --environment cf387142-61fd-4668-8cf7-b3559e0983cb --service 91c7e450-d6d2-481a-b2a4-0a916f4160fc` +- That links this directory to the `sandbox-agent` project, `production` environment, and `foundry-api` service. + ## Frontend + Client Boundary - Keep a browser-friendly GUI implementation aligned with the TUI interaction model wherever possible. @@ -96,39 +103,11 @@ For all Rivet/RivetKit implementation: pnpm build -F rivetkit ``` -## Inspector HTTP API (Workflow Debugging) +## Rivet Routing -- The Inspector HTTP routes come from RivetKit `feat: inspector http api (#4144)` and are served from the RivetKit manager endpoint (not `/api/rivet`). -- Resolve manager endpoint from backend metadata: - ```bash - curl -sS http://127.0.0.1:7741/api/rivet/metadata | jq -r '.clientEndpoint' - ``` -- List actors: - - `GET {manager}/actors?name=task` -- Inspector endpoints (path prefix: `/gateway/{actorId}/inspector`): - - `GET /state` - - `PATCH /state` - - `GET /connections` - - `GET /rpcs` - - `POST /action/{name}` - - `GET /queue?limit=50` - - `GET /traces?startMs=0&endMs=&limit=1000` - - `GET /workflow-history` - - `GET /summary` -- Auth: - - Production: send `Authorization: Bearer $RIVET_INSPECTOR_TOKEN`. - - Development: auth can be skipped when no inspector token is configured. -- Task workflow quick inspect: - ```bash - MGR="$(curl -sS http://127.0.0.1:7741/api/rivet/metadata | jq -r '.clientEndpoint')" - HID="7df7656e-bbd2-4b8c-bf0f-30d4df2f619a" - AID="$(curl -sS "$MGR/actors?name=task" \ - | jq -r --arg hid "$HID" '.actors[] | select(.key | endswith("/task/\($hid)")) | .actor_id' \ - | head -n1)" - curl -sS "$MGR/gateway/$AID/inspector/workflow-history" | jq . - curl -sS "$MGR/gateway/$AID/inspector/summary" | jq . - ``` -- If inspector routes return `404 Not Found (RivetKit)`, the running backend is on a RivetKit build that predates `#4144`; rebuild linked RivetKit and restart backend. +- Mount RivetKit directly on `/api/rivet` via `registry.handler(c.req.raw)`. +- Do not add an extra proxy or manager-specific route layer in the backend. +- Let RivetKit own metadata/public endpoint behavior for `/api/rivet`. ## Workspace + Actor Rules diff --git a/foundry/compose.dev.yaml b/foundry/compose.dev.yaml index d2b604e..01c6934 100644 --- a/foundry/compose.dev.yaml +++ b/foundry/compose.dev.yaml @@ -10,7 +10,6 @@ services: environment: HF_BACKEND_HOST: "0.0.0.0" HF_BACKEND_PORT: "7741" - HF_RIVET_MANAGER_PORT: "8750" RIVETKIT_STORAGE_PATH: "/root/.local/share/foundry/rivetkit" # Pass through credentials needed for agent execution + PR creation in dev/e2e. # Do not hardcode secrets; set these in your environment when starting compose. @@ -43,8 +42,6 @@ services: HF_DAYTONA_API_KEY: "${HF_DAYTONA_API_KEY:-}" ports: - "7741:7741" - # RivetKit manager (used by browser clients after /api/rivet metadata redirect in dev) - - "8750:8750" volumes: - "..:/app" # The linked RivetKit checkout resolves from Foundry packages to /task/rivet-checkout in-container. diff --git a/foundry/compose.preview.yaml b/foundry/compose.preview.yaml index e13f5e1..6213885 100644 --- a/foundry/compose.preview.yaml +++ b/foundry/compose.preview.yaml @@ -9,7 +9,6 @@ services: environment: HF_BACKEND_HOST: "0.0.0.0" HF_BACKEND_PORT: "7841" - HF_RIVET_MANAGER_PORT: "8850" RIVETKIT_STORAGE_PATH: "/root/.local/share/foundry/rivetkit" ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}" CLAUDE_API_KEY: "${CLAUDE_API_KEY:-${ANTHROPIC_API_KEY:-}}" @@ -23,7 +22,6 @@ services: HF_DAYTONA_API_KEY: "${HF_DAYTONA_API_KEY:-}" ports: - "7841:7841" - - "8850:8850" volumes: - "${HOME}/.codex:/root/.codex" - "foundry_preview_git_repos:/root/.local/share/foundry/repos" diff --git a/foundry/packages/backend/src/actors/index.ts b/foundry/packages/backend/src/actors/index.ts index 0fdf8aa..4fb9533 100644 --- a/foundry/packages/backend/src/actors/index.ts +++ b/foundry/packages/backend/src/actors/index.ts @@ -8,24 +8,6 @@ import { project } from "./project/index.js"; import { sandboxInstance } from "./sandbox-instance/index.js"; import { workspace } from "./workspace/index.js"; -export function resolveManagerPort(): number { - const raw = process.env.HF_RIVET_MANAGER_PORT ?? process.env.RIVETKIT_MANAGER_PORT; - if (!raw) { - return 7750; - } - - const parsed = Number(raw); - if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) { - throw new Error(`Invalid HF_RIVET_MANAGER_PORT/RIVETKIT_MANAGER_PORT: ${raw}`); - } - return parsed; -} - -function resolveManagerHost(): string { - const raw = process.env.HF_RIVET_MANAGER_HOST ?? process.env.RIVETKIT_MANAGER_HOST; - return raw && raw.trim().length > 0 ? raw.trim() : "0.0.0.0"; -} - export const registry = setup({ use: { workspace, @@ -37,8 +19,6 @@ export const registry = setup({ projectBranchSync, taskStatusSync, }, - managerPort: resolveManagerPort(), - managerHost: resolveManagerHost(), }); export * from "./context.js"; diff --git a/foundry/packages/backend/src/index.ts b/foundry/packages/backend/src/index.ts index d214daf..a20b45a 100644 --- a/foundry/packages/backend/src/index.ts +++ b/foundry/packages/backend/src/index.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { cors } from "hono/cors"; import { initActorRuntimeContext } from "./actors/context.js"; -import { registry, resolveManagerPort } from "./actors/index.js"; +import { registry } from "./actors/index.js"; import { workspaceKey } from "./actors/keys.js"; import { loadConfig } from "./config/backend.js"; import { createBackends, createNotificationService } from "./notifications/index.js"; @@ -69,17 +69,11 @@ export async function startBackend(options: BackendStartOptions = {}): Promise { - try { - // Proxy /api/rivet traffic to the long-lived RivetKit manager rather than - // invoking RivetKit's serverless entrypoints in-process. - const requestUrl = new URL(c.req.url); - const managerPath = requestUrl.pathname.replace(/^\/api\/rivet(?=\/|$)/, "") || "/"; - const targetUrl = new URL(`${managerPath}${requestUrl.search}`, managerOrigin); - return await fetch(new Request(targetUrl, c.req.raw)); - } catch (err) { - if (err instanceof URIError) { - return c.text("Bad Request: Malformed URI", 400); - } - throw err; - } - }; const appWorkspace = async () => await withRetries( @@ -334,8 +313,18 @@ export async function startBackend(options: BackendStartOptions = {}): Promise { + const payload = await c.req.text(); + await (await appWorkspace()).handleAppGithubWebhook({ + payload, + signatureHeader: c.req.header("x-hub-signature-256") ?? null, + eventHeader: c.req.header("x-github-event") ?? null, + }); + return c.json({ ok: true }); + }); + + app.all("/api/rivet", (c) => registry.handler(c.req.raw)); + app.all("/api/rivet/*", (c) => registry.handler(c.req.raw)); const server = Bun.serve({ fetch: app.fetch,