Decisions, dependencies and deviations from the agent-upgrade master prompt.
Everything else is documented user-facing in AGENTS.md, GET /api/capabilities and GET /api/openapi.json.
Approach: server-side session cookie, set when the UI page is served.
When TREEMAP_TOKEN is set, GET / (or /index.html) responds with an
HttpOnly; SameSite=Strict cookie carrying the token, and the /api auth
middleware accepts either Authorization: Bearer <token> or that cookie.
Why this and not the alternatives:
- The frontend was frozen at the time (R1, since lifted): its literal
fetch('/api/…')calls andnew EventSource('/api/…')cannot be taught to send headers — andEventSourcecannot send custom headers at all, so header-only auth would silently kill every SSE stream the UI depends on. Cookies ride along on both automatically. - A "same-origin requests are exempt" rule would trust
Origin/Refererheaders, which any non-browser client can forge — that's an auth bypass, not an exemption.
Threat model (stated plainly, also in AGENTS.md): the token gates API access
for non-browser clients, and SameSite=Strict + CORS-off blocks cross-site
browser attacks; anyone who can load the UI page itself gets a session. If
the port is exposed beyond localhost, front it with a reverse proxy that
authenticates page loads and terminates TLS.
@modelcontextprotocol/sdk— the official MCP implementation; hand-rolling the protocol would be invented risk. Runtime dep (the MCP server runs fromdist/with production deps only).zod— peer dependency of the SDK and the input-validation layer for the MCP tools' schemas.- CORS is implemented in-tree (
src/middleware/cors.ts): the policy is a handful of headers; the MITcorspackage would add a dependency for no behavior we need. - Zero frontend dependencies added;
public/index.htmlwas left untouched at the time (R1, since lifted).
prepareOffloadwas extracted fromstartOffload(same file, byte-same checks) so dry-run and real-run share one source of truth;startOffloadaccepts the prepared plan back to avoid planning twice.- The agent policy (
agent-policy.json) is deliberately not writable via the API — a policy an agent could rewrite for itself would be theatre.GET /api/policyshows the resolved policy and the file path; the human edits the file. - MCP
scan_pathaccepts eitherpath(start) orscanId(keep waiting) — the polling pattern for scans that outlive one tool-call timeout. The same pattern applies tofind_duplicatesandoffload(viajobId). - MCP tool responses skip MCP
outputSchema: the structured payloads are typed againstsrc/models/types.tsat compile time, which keeps one source of truth instead of a second, silently-driftable schema layer. - Audit entries record refusals too (
outcome: "refused", with the policy code) — an agent probing the rails leaves a trace. - In the container the server binds
0.0.0.0(a 127.0.0.1 bind is unreachable through a published port);docker-compose.ymlrestores the localhost-only posture by publishing on127.0.0.1:4280— widen the mapping and setTREEMAP_TOKENdeliberately for remote use. Containers have no OS Trash, so the container profile is scan-and-analyze; the compose file suggests read-only data mounts.
- Phase 6 gate (
docker build) could not be executed on this machine: Docker is not installed. Everything Docker would validate short of the image layer itself was proven directly: the exact runtime recipe (package.json+npm ci --omit=dev+dist/+public/+node dist/index.js) was staged in a clean directory and served/api/system,/api/capabilitiesand the UI correctly with production dependencies only. Rundocker build .on a Docker-equipped machine (or CI) to close the loop. Since 18 Sep 2026src/also imports the native module's type declarations,native/index.d.ts, so the build stage copies that file too (2 Oct 2026: the build stage's copy set was rebuilt in a scratch folder —tscfailed with six TS2307 errors without it and passed with it;tests/buildUi.test.tsderives the set fromsrc/'s imports). The image carries no native module: a container scans with the built-in walker. - No other deviations: every existing endpoint's response shape is unchanged (golden + contract suites), and with all new env vars unset the server's behavior is byte-identical to before (explicit tests assert the historical shapes and header-free responses).