Skip to content

Commit 940e49f

Browse files
committed
Use vanilla Rivet routing in Foundry backend
1 parent 4bccd5f commit 940e49f

5 files changed

Lines changed: 27 additions & 84 deletions

File tree

foundry/CLAUDE.md

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ Use `pnpm` workspaces and Turborepo.
4545
- Stop the preview stack: `just foundry-preview-down`
4646
- Tail preview logs: `just foundry-preview-logs`
4747

48+
## Railway Logs
49+
50+
- Production Foundry Railway logs can be read from a linked workspace with `railway logs --deployment --lines 200` or `railway logs <deployment-id> --deployment --lines 200`.
51+
- If Railway logs fail because the workspace is not linked to the correct project/service/environment, run:
52+
`railway link --project 33e3e2df-32c5-41c5-a4af-dca8654acb1d --environment cf387142-61fd-4668-8cf7-b3559e0983cb --service 91c7e450-d6d2-481a-b2a4-0a916f4160fc`
53+
- That links this directory to the `sandbox-agent` project, `production` environment, and `foundry-api` service.
54+
4855
## Frontend + Client Boundary
4956

5057
- Keep a browser-friendly GUI implementation aligned with the TUI interaction model wherever possible.
@@ -96,39 +103,11 @@ For all Rivet/RivetKit implementation:
96103
pnpm build -F rivetkit
97104
```
98105

99-
## Inspector HTTP API (Workflow Debugging)
100-
101-
- The Inspector HTTP routes come from RivetKit `feat: inspector http api (#4144)` and are served from the RivetKit manager endpoint (not `/api/rivet`).
102-
- Resolve manager endpoint from backend metadata:
103-
```bash
104-
curl -sS http://127.0.0.1:7741/api/rivet/metadata | jq -r '.clientEndpoint'
105-
```
106-
- List actors:
107-
- `GET {manager}/actors?name=task`
108-
- Inspector endpoints (path prefix: `/gateway/{actorId}/inspector`):
109-
- `GET /state`
110-
- `PATCH /state`
111-
- `GET /connections`
112-
- `GET /rpcs`
113-
- `POST /action/{name}`
114-
- `GET /queue?limit=50`
115-
- `GET /traces?startMs=0&endMs=<ms>&limit=1000`
116-
- `GET /workflow-history`
117-
- `GET /summary`
118-
- Auth:
119-
- Production: send `Authorization: Bearer $RIVET_INSPECTOR_TOKEN`.
120-
- Development: auth can be skipped when no inspector token is configured.
121-
- Task workflow quick inspect:
122-
```bash
123-
MGR="$(curl -sS http://127.0.0.1:7741/api/rivet/metadata | jq -r '.clientEndpoint')"
124-
HID="7df7656e-bbd2-4b8c-bf0f-30d4df2f619a"
125-
AID="$(curl -sS "$MGR/actors?name=task" \
126-
| jq -r --arg hid "$HID" '.actors[] | select(.key | endswith("/task/\($hid)")) | .actor_id' \
127-
| head -n1)"
128-
curl -sS "$MGR/gateway/$AID/inspector/workflow-history" | jq .
129-
curl -sS "$MGR/gateway/$AID/inspector/summary" | jq .
130-
```
131-
- 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.
106+
## Rivet Routing
107+
108+
- Mount RivetKit directly on `/api/rivet` via `registry.handler(c.req.raw)`.
109+
- Do not add an extra proxy or manager-specific route layer in the backend.
110+
- Let RivetKit own metadata/public endpoint behavior for `/api/rivet`.
132111

133112
## Workspace + Actor Rules
134113

foundry/compose.dev.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ services:
1010
environment:
1111
HF_BACKEND_HOST: "0.0.0.0"
1212
HF_BACKEND_PORT: "7741"
13-
HF_RIVET_MANAGER_PORT: "8750"
1413
RIVETKIT_STORAGE_PATH: "/root/.local/share/foundry/rivetkit"
1514
# Pass through credentials needed for agent execution + PR creation in dev/e2e.
1615
# Do not hardcode secrets; set these in your environment when starting compose.
@@ -43,8 +42,6 @@ services:
4342
HF_DAYTONA_API_KEY: "${HF_DAYTONA_API_KEY:-}"
4443
ports:
4544
- "7741:7741"
46-
# RivetKit manager (used by browser clients after /api/rivet metadata redirect in dev)
47-
- "8750:8750"
4845
volumes:
4946
- "..:/app"
5047
# The linked RivetKit checkout resolves from Foundry packages to /task/rivet-checkout in-container.

foundry/compose.preview.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ services:
99
environment:
1010
HF_BACKEND_HOST: "0.0.0.0"
1111
HF_BACKEND_PORT: "7841"
12-
HF_RIVET_MANAGER_PORT: "8850"
1312
RIVETKIT_STORAGE_PATH: "/root/.local/share/foundry/rivetkit"
1413
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}"
1514
CLAUDE_API_KEY: "${CLAUDE_API_KEY:-${ANTHROPIC_API_KEY:-}}"
@@ -23,7 +22,6 @@ services:
2322
HF_DAYTONA_API_KEY: "${HF_DAYTONA_API_KEY:-}"
2423
ports:
2524
- "7841:7841"
26-
- "8850:8850"
2725
volumes:
2826
- "${HOME}/.codex:/root/.codex"
2927
- "foundry_preview_git_repos:/root/.local/share/foundry/repos"

foundry/packages/backend/src/actors/index.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,6 @@ import { project } from "./project/index.js";
88
import { sandboxInstance } from "./sandbox-instance/index.js";
99
import { workspace } from "./workspace/index.js";
1010

11-
export function resolveManagerPort(): number {
12-
const raw = process.env.HF_RIVET_MANAGER_PORT ?? process.env.RIVETKIT_MANAGER_PORT;
13-
if (!raw) {
14-
return 7750;
15-
}
16-
17-
const parsed = Number(raw);
18-
if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
19-
throw new Error(`Invalid HF_RIVET_MANAGER_PORT/RIVETKIT_MANAGER_PORT: ${raw}`);
20-
}
21-
return parsed;
22-
}
23-
24-
function resolveManagerHost(): string {
25-
const raw = process.env.HF_RIVET_MANAGER_HOST ?? process.env.RIVETKIT_MANAGER_HOST;
26-
return raw && raw.trim().length > 0 ? raw.trim() : "0.0.0.0";
27-
}
28-
2911
export const registry = setup({
3012
use: {
3113
workspace,
@@ -37,8 +19,6 @@ export const registry = setup({
3719
projectBranchSync,
3820
taskStatusSync,
3921
},
40-
managerPort: resolveManagerPort(),
41-
managerHost: resolveManagerHost(),
4222
});
4323

4424
export * from "./context.js";

foundry/packages/backend/src/index.ts

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Hono } from "hono";
22
import { cors } from "hono/cors";
33
import { initActorRuntimeContext } from "./actors/context.js";
4-
import { registry, resolveManagerPort } from "./actors/index.js";
4+
import { registry } from "./actors/index.js";
55
import { workspaceKey } from "./actors/keys.js";
66
import { loadConfig } from "./config/backend.js";
77
import { createBackends, createNotificationService } from "./notifications/index.js";
@@ -69,17 +69,11 @@ export async function startBackend(options: BackendStartOptions = {}): Promise<v
6969
const notifications = createNotificationService(backends);
7070
initActorRuntimeContext(config, providers, notifications, driver, createDefaultAppShellServices());
7171

72-
registry.startRunner();
73-
const managerOrigin = `http://127.0.0.1:${resolveManagerPort()}`;
7472
const actorClient = createClient({
75-
endpoint: managerOrigin,
76-
disableMetadataLookup: true,
73+
endpoint: `http://127.0.0.1:${config.backend.port}/api/rivet`,
7774
}) as any;
7875

79-
// Wrap in a Hono app mounted at /api/rivet to serve on the backend port.
80-
// Uses Bun.serve — cannot use @hono/node-server because it conflicts with
81-
// RivetKit's internal Bun.serve manager server (Bun bug: mixing Node HTTP
82-
// server and Bun.serve in the same process breaks Bun.serve's fetch handler).
76+
// Wrap RivetKit and app routes in a single Hono app mounted at /api/rivet.
8377
const app = new Hono();
8478
const allowHeaders = [
8579
"Content-Type",
@@ -118,21 +112,6 @@ export async function startBackend(options: BackendStartOptions = {}): Promise<v
118112
exposeHeaders,
119113
}),
120114
);
121-
const forward = async (c: any) => {
122-
try {
123-
// Proxy /api/rivet traffic to the long-lived RivetKit manager rather than
124-
// invoking RivetKit's serverless entrypoints in-process.
125-
const requestUrl = new URL(c.req.url);
126-
const managerPath = requestUrl.pathname.replace(/^\/api\/rivet(?=\/|$)/, "") || "/";
127-
const targetUrl = new URL(`${managerPath}${requestUrl.search}`, managerOrigin);
128-
return await fetch(new Request(targetUrl, c.req.raw));
129-
} catch (err) {
130-
if (err instanceof URIError) {
131-
return c.text("Bad Request: Malformed URI", 400);
132-
}
133-
throw err;
134-
}
135-
};
136115

137116
const appWorkspace = async () =>
138117
await withRetries(
@@ -334,8 +313,18 @@ export async function startBackend(options: BackendStartOptions = {}): Promise<v
334313
app.post("/api/rivet/app/webhooks/stripe", handleStripeWebhook);
335314
app.post("/api/rivet/app/stripe/webhook", handleStripeWebhook);
336315

337-
app.all("/api/rivet", forward);
338-
app.all("/api/rivet/*", forward);
316+
app.post("/api/rivet/app/webhooks/github", async (c) => {
317+
const payload = await c.req.text();
318+
await (await appWorkspace()).handleAppGithubWebhook({
319+
payload,
320+
signatureHeader: c.req.header("x-hub-signature-256") ?? null,
321+
eventHeader: c.req.header("x-github-event") ?? null,
322+
});
323+
return c.json({ ok: true });
324+
});
325+
326+
app.all("/api/rivet", (c) => registry.handler(c.req.raw));
327+
app.all("/api/rivet/*", (c) => registry.handler(c.req.raw));
339328

340329
const server = Bun.serve({
341330
fetch: app.fetch,

0 commit comments

Comments
 (0)