slskd: scaffold reverse-proxy sidecar

Add officer-slskd, a singleton sidecar that reverse-proxies to a
self-hosted slskd (Soulseek) instance and reports its loopback port to
the API on connect. All slskd knowledge (URL + API key) lives in the
sidecar; the platform is a thin auth+forward proxy for /api/slskd/* and
holds no slskd credentials. Mirrors the officer-vault pattern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 15:49:09 +00:00
co-authored by Claude Opus 4.8
parent 0325f14770
commit c023082975
7 changed files with 271 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
import { createRouter } from '../../create-router';
import { getSlskdServerUrl } from './sidecar-server';
// Thin reverse-proxy for /api/slskd/*. The platform's ONLY job here is AUTH + FORWARDING: this router
// mounts under the protected /api tree (userMiddleware upstream authenticates the owner), then forwards
// the subpath + query + body to the officer-slskd sidecar, which OWNS the slskd contract and injects the
// slskd API key. The platform holds no slskd credentials.
//
// This is a catch-all with no routes of its own: the full /api/slskd/* contract (slskd's own API, e.g.
// /api/v0/searches, /api/v0/transfers) is documented at the top of the sidecar's fetch handler —
// src/servers/sidecar/slskd/index.ts.
export const slskdRouter = createRouter();
const PREFIX = '/api/slskd';
slskdRouter.all('/*', async (ctx) => {
const baseUrl = getSlskdServerUrl();
if (!baseUrl) return ctx.text('slskd sidecar not available', 503);
const url = new URL(ctx.req.url);
const subpath = url.pathname.slice(PREFIX.length) || '/';
const target = `${baseUrl}${subpath}${url.search}`;
const method = ctx.req.method;
const headers: Record<string, string> = {};
const contentType = ctx.req.header('content-type');
if (contentType) headers['Content-Type'] = contentType;
const range = ctx.req.header('range');
if (range) headers['Range'] = range;
const hasBody = method !== 'GET' && method !== 'HEAD';
let upstream: Response;
try {
upstream = await fetch(target, {
method,
headers,
body: hasBody ? await ctx.req.arrayBuffer() : undefined,
});
} catch (err) {
console.error('[slskd] proxy fetch failed', { target, error: String(err) });
return ctx.text('slskd sidecar unreachable', 502);
}
return new Response(upstream.body, { status: upstream.status, headers: new Headers(upstream.headers) });
});
+19
View File
@@ -0,0 +1,19 @@
import * as sidecar from '@@/sidecar-registry';
// The officer-slskd sidecar starts its slskd reverse-proxy on a random loopback port and reports it here
// on connect. We remember it so `/api/slskd/*` always forwards to the current sidecar. The platform holds
// NO knowledge of slskd itself (URL, API key) — only where the sidecar is listening.
let serverPort: number | null = null;
sidecar.on('slskd:server', (msg) => {
const port = (msg as { port?: number }).port;
if (typeof port !== 'number') return;
serverPort = port;
console.log(`[slskd] sidecar proxy registered on port ${port}`);
});
/** Base URL of the sidecar's HTTP proxy, or null if the sidecar hasn't reported in yet. */
export function getSlskdServerUrl(): string | null {
return serverPort ? `http://127.0.0.1:${serverPort}` : null;
}