Repository navigation
Conversation
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Runtime
server.base_pathconfig — added to the full and partial server schemas with an arktypenarrowregex that rejects empty/root paths, trailing slashes,//, and./..segments; mirrored inconfig.example.yaml,nix/options.nix, and the regenerateddocs/NixOS-options.md, defaulting to build-time/admin. - Runtime basename override —
app/server/app.tssets the process prefix and feeds React Router a patched build (configureBuildin prod) so route matching, SSR handoff, redirects, and the client'swindow.__reactRouterContext.basenameall reflect the configured path. - Asset-manifest prefixing — new
app/server/assets.tsrewritesassets.url, entry/route module/imports/css, and client chunk modules; Vitebaseis now./in builds andapp.tsserves the rewritten manifest dynamically (no-cache) ahead of the stale on-disk copy viadynamicPaths. - Client prefix accessor — new
~/utils/prefixgetPrefix()replaces all client__PREFIX__uses (favicon, SSE, SSHwindow.open, WASM/font URLs, preload); module-level constants were moved into functions to avoid pre-hydration evaluation. - Server plumbing — cookie path, OIDC
redirect_uri/post-logout, OIDC state cookie, listener basename, and the Docker listen-file healthcheck URL all useconfig.server.base_path. - Tests/docs — new base-path unit + platform integration tests (prod build booted under
/admin,/web,/tools/web), an OIDC callback/logout unit, and updated docs/changelog.
I verified the new unit tests (75 passed), the platform integration test (3/3 passed on a real production build), lint (0 errors), and formatting. The pnpm run typecheck failures are pre-existing in untouched test files (describe.sequential on Vitest 5) and unrelated to this PR.
One carry-forward risk (not blocking): the approach depends on React Router internals — the window.__reactRouterContext.basename handoff and the spread-patchable server build object. The integration test is the right guard for that.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This incremental review covers commit 567e5e2f, the only commit since the prior pullfrog review at 1f5bd192.
- Client prefix now sourced from an SSR-emitted meta tag —
app/utils/prefix.tsgetPrefix()readsmeta[name="headplane-base-path"]in the browser instead ofwindow.__reactRouterContext.basename, andapp/root.tsxrenders that tag during SSR so the runtime prefix is available before hydration and for SSE, WASM/font, SSH, and favicon URLs. - Asset-manifest unit tests — new
tests/unit/config/assets.test.tsassertsconfigureBuildrebases entry/route/module/css/client URLs and leaves the source build unmutated. - Prefix unit tests — new
tests/unit/config/prefix.test.tscovers the SSR value, the meta-tag read before hydration, and the build-time fallback. - Browser integration coverage — extended
tests/integration/platform/base-path.test.tswith a Playwright navigation check (hydration, API-key login, client navigation, no stray/adminrequests, no console errors) and installed Chromium in CI. - Dependency/docs —
playwrightdevDependency + lockfile,nix/package.nixhash bump, anddocs/CONTRIBUTING.mdbrowser-test instructions.
I verified the two new unit suites (5 tests passing) and that pnpm install --frozen-lockfile --lockfile-only is consistent. The remaining typecheck failures are pre-existing describe.sequential errors in untouched files; the extra playwright module-not-found errors are local to this environment (dependencies were installed before the PR lockfile) and CI installs it via build.sh's pnpm install --frozen-lockfile.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
tale
left a comment
There was a problem hiding this comment.
I left 1 nit but there's some bigger structural issues that I don't mind cleaning up.
- There's quite a few AI generated comments across your changes, I'd appreciate if they were trimmed up and necessary ones were hand-written.
- We can just drop the full __PREFIX and PREFIX calculation stuff in vite.config.ts, it's still leftover for some reason
- I'm not a fan of the tests and listener, your AI agent has written some older style TS. We are on Node 24 (soon to be 26 in a few weeks), we have modern APIs.
| const basePath = type("string").narrow( | ||
| (value, ctx) => | ||
| (/^\/[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*$/.test(value) && | ||
| !value.split("/").some((segment) => segment === "." || segment === "..")) || | ||
| ctx.mustBe("an absolute path of non-empty URL-safe segments without a trailing slash"), | ||
| ); |
There was a problem hiding this comment.
I'm fairly sure that arktype has a type available to us instead of needing to do a custom narrow. You might have to upgrade the version of arktype.

Summary
Adds runtime configuration of the dashboard URL prefix through
server.base_path, without rebuilding Headplane. Existing installations continue to use/adminby default; custom paths such as/headplaneand/tools/webare supported.For example:
The setting can also be supplied through
HEADPLANE_SERVER__BASE_PATHor the corresponding NixOS option. Base paths must contain non-empty URL-safe segments, start with/, and have no trailing slash. The root path/and./..segments are rejected.Closes #637.
Changes
window.__reactRouterContext.Validation
/admin,/web, and/tools/web. These cover assets and fonts, manifest discovery, redirects, login/logout cookies, live-data streaming, and healthcheck URLs./tools/webtest also runs Chromium to verify hydration, API-key login, and client navigation between Machines and Users without document reloads, JavaScript errors, or requests to/admin.pnpm run typecheckstill reports existing errors in unchanged test files: the Vitestdescribe.sequentialAPI and implicit parameter types in the filesystem test helper. No new type errors were reported in the changed files.Compatibility
The default
/adminprefix remains unchanged. Deployments choosing a custom prefix need to update their reverse-proxy paths and registered OIDC callback/logout URLs accordingly.The server still adapts React Router's generated
ServerBuildand asset manifest. Unit tests and production browser coverage guard this integration when React Router is upgraded.