fix: register the mount as a proxied prefix, so bodyParser stops eating POST bodies

Every POST through /api/vault died with 502 sidecar unreachable while bodyless
GETs worked. Nothing was unreachable: the platform bodyParser reads JSON and
form bodies on the protected router, disturbing the request stream, and the
catch-all proxy then forwards ctx.req.raw.body — an already-consumed stream that
Bun fetch throws on. The throw is caught and reported as the sidecar being down.

Core sidecars never hit this because createSidecarProxy registers the prefix for
them. This router proxies by hand, and the extraction dropped that registration.

Safe across the whole prefix: no handler here reads ctx.get(body), they all call
ctx.req.json() themselves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-08-18 15:12:20 +00:00
parent 4006be0e3b
commit 11a63e52fd
+16
View File
@@ -5,6 +5,7 @@ import { isSuperAdmin } from '@@/super-admin';
import * as errors from '@@/custom-errors'; import * as errors from '@@/custom-errors';
import { mountPrefix } from '@@/plugins/manifest'; import { mountPrefix } from '@@/plugins/manifest';
import { onAccountLifecycle } from '@@/account-lifecycle'; import { onAccountLifecycle } from '@@/account-lifecycle';
import { registerProxiedPrefix } from '@@/sidecar/proxied-prefixes';
import { manifest } from '../manifest'; import { manifest } from '../manifest';
import { getVaultServerUrl } from './sidecar-server'; import { getVaultServerUrl } from './sidecar-server';
import { publicVaultRouter } from './public-router'; import { publicVaultRouter } from './public-router';
@@ -41,6 +42,21 @@ import {
// `router`, so there is nowhere for it to inject the plugin's own directory name. // `router`, so there is nowhere for it to inject the plugin's own directory name.
const PREFIX = `/api${mountPrefix({ appName: 'vault', manifest })}`; const PREFIX = `/api${mountPrefix({ appName: 'vault', manifest })}`;
/*
Tell bodyParser to keep its hands off this prefix.
Everything under it is either forwarded verbatim to the sidecar or parsed by the handler itself
(`ctx.req.json()`), so nothing here wants a body read for it. The platform's bodyParser reads JSON
and form bodies on the protected router, which DISTURBS the request stream — and the catch-all
proxy below then forwards `ctx.req.raw.body`, an already-consumed stream. Bun's fetch throws on it,
the proxy reports 'sidecar unreachable', and the client is told the server is down while every
bodyless GET keeps working.
Core's own sidecars never hit this because `createSidecarProxy` registers the prefix for them. This
router proxies by hand, so it has to say so itself — which extraction dropped on 2026-08-18.
*/
registerProxiedPrefix(PREFIX);
export const router = createRouter(); export const router = createRouter();
/* /*