import { createSidecarProxy } from '@@/sidecar/create-proxy'; import { mountPrefix } from '@@/plugins/manifest'; import { manifest } from '../manifest'; // /api/offscale/* — auth, then forward to officer-offscale. No routes of its own and no Headscale // knowledge: this file must never grow app logic. // // The sidecar exposes only Officer-owned routes under `/_officer/` — Headscale's REST shape differs // across releases, and version handling belongs in the sidecar. It holds the admin API key; the platform // does not know any Headscale URL. // // ── The prefix is DERIVED, not written ── // // It was the literal '/api/offscale'. That is wrong in a way that only shows up for someone else's // plugin: the proxy strips `prefix.length` characters to build the sidecar path, so a hardcoded // '/api/offscale' (13 chars) is correct only because `mountPrefix` happens to return `/offscale` for a // first-party publisher. The same plugin published by anyone else mounts at // `/api/p//offscale` and would forward `/alice/offscale/nodes` to a sidecar expecting // `/nodes`. // // `mountPrefix` is the ONE function allowed to know about provenance, so the prefix comes from it. A // literal here is that rule being broken quietly, which is how first-party and third-party become two // systems with only one of them tested. Carried over from music, which hit this first. // // `appName` is a literal because this file cannot see its own directory name — the platform imports // `router.ts` and reads `router`, so there is nowhere to inject it. Same known gap music records. const proxy = createSidecarProxy({ name: 'offscale', prefix: `/api${mountPrefix({ appName: 'offscale', manifest })}`, }); export const router = proxy.router; /** Base URL of the sidecar's HTTP server, or null if it hasn't reported in yet. */ export const getOffscaleServerUrl = proxy.getHttpUrl;