Skip to content

Move /rpc into Astro; one function owns /api/\*\*


date: 2026-05-10 status: accepted outranks: []

Section titled “date: 2026-05-10 status: accepted outranks: []”

Move /rpc into Astro; one function owns /api/**

Section titled “Move /rpc into Astro; one function owns /api/**”

Per-user Airlock auth (TASKSET 4) shipped with /api/auth/callback and /api/auth/logout as Astro APIRoutes inside dashboard/src/pages/api/, while /rpc continued to live as a root-level Vercel function api/rpc.ts declared in vercel.json functions.

In production, /console/* correctly redirected to the Airlock handoff, but /api/auth/callback returned 404 on the return leg — breaking sign-in for every user. Verified against the petrova-host deploy of fb5fb23: middleware ran, the Astro _render function was reachable at /console/*, but /api/** paths didn’t dispatch to _render for routes other than the root-declared api/rpc.ts. Vercel’s filesystem handler appears to claim the /api namespace once any root-level function is declared, short-circuiting the Build Output API routes copied in from dashboard/.vercel/output.

/api/rpc is now an Astro APIRoute at dashboard/src/pages/api/rpc.ts. The root-level api/rpc.ts and the functions block in vercel.json are removed. A single Astro server function (_render.func) owns the entire /api/** and /console/** surface. The /rpc → /api/rpc rewrite stays so existing callers (the CLI MCP client, dashboard/src/lib/rpc.ts, the production smoke test) keep working unchanged.

Data files (registry.yaml, state/**, docs/**, contracts/**, spec/verbs/**, host/src/**, cli/dist/**, the eva-mini and grace-mini fixtures) are staged into .vercel/output/functions/_render.func/ by vercel.json buildCommand after astro build. They were previously bundled via the functions.api/rpc.ts.includeFiles glob, which no longer applies once the function is gone. Astro’s adapter includeFiles only accepts paths inside the project root; ours live one level up at the monorepo root, hence the explicit cp -a step.

  • Sign-in works again. /api/auth/callback is reachable, the EdDSA handoff JWT verifies, the __petrova_session cookie gets minted, and the user lands at /console.
  • One function, one cold-start, one set of env vars. Bearer (PETROVA_HOST_BEARER), session secret, Octokit App credentials, and Airlock URL all live on the same function now.
  • The dashboard package now depends on @octokit/auth-app, @octokit/rest, @petrova/cli (file:), ajv, ajv-formats, commander, glob, js-yaml, and picocolors — these moved from the root @petrova/host-vercel shell, which is now dependency-less and exists only as a Node 20 hint for Vercel.
  • MR-10: verification round for this fix is the post-deploy smoke test against https://petrova.host/api/auth/callback?token=… — expected to redirect (302) instead of returning 404.