From cdc55149c2fa3669306713cfb0a219ebbc0b1217 Mon Sep 17 00:00:00 2001 From: Jack Champagne Date: Mon, 20 Jul 2026 08:50:24 -0400 Subject: [PATCH] fix(auth): serve /brain.html + /brain.js without server auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The amico brain graph loads /brain.html (+ /brain.js) in an iframe that can't carry ?auth_token=; with OPENCODE_SERVER_PASSWORD set (always, under the amicode extension) both 401'd and the frame painted opaque white. Same public-shell class as the /assets/* and /amicode/widget-frame exemptions — they were just missed. Regression since the auth refactor between amicode.5 and amicode.7. --- .../opencode/src/server/shared/public-ui.ts | 7 ++++++ .../opencode/test/server/httpapi-ui.test.ts | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/packages/opencode/src/server/shared/public-ui.ts b/packages/opencode/src/server/shared/public-ui.ts index 4a92c9aeac..ee67686ff2 100644 --- a/packages/opencode/src/server/shared/public-ui.ts +++ b/packages/opencode/src/server/shared/public-ui.ts @@ -12,6 +12,13 @@ export const PUBLIC_UI_PATHS = new Set([ // bridge after boot), so it is public-shell class within the same-user // trust model. The widget registry route (/amicode/widgets) stays authed. "/amicode/widget-frame", + // The amico brain graph is the same class: brain-strip.tsx loads /brain.html + // in an iframe (with /brain.js) that cannot carry ?auth_token=, so with a + // password set the frame 401s and paints opaque white. Both are static + // public-shell assets (no secrets; session touches arrive via the same + // postMessage bridge as widgets). Sibling to the widget-frame exemption. + "/brain.html", + "/brain.js", ]) // The app shell's fingerprinted bundles live under /assets/. They are plain diff --git a/packages/opencode/test/server/httpapi-ui.test.ts b/packages/opencode/test/server/httpapi-ui.test.ts index 69e9616aa9..5eb87b84d9 100644 --- a/packages/opencode/test/server/httpapi-ui.test.ts +++ b/packages/opencode/test/server/httpapi-ui.test.ts @@ -502,6 +502,31 @@ describe("HttpApi UI fallback", () => { }), ) + // Regression (amicode): the amico brain graph loads /brain.html (+ /brain.js) + // in an iframe that cannot carry ?auth_token= — same class as widget-frame — + // so with a password set both 401'd and the frame painted opaque white. They + // are static public-shell assets; session touches arrive via the postMessage + // bridge. GET-only; other root paths stay authed. + it.live("serves the brain graph frame + script without auth even when a server password is set", () => + Effect.gen(function* () { + for (const path of ["/brain.html?mode=live&colorScheme=dark", "/brain.js"]) { + const response = yield* uiApp({ + password: "secret", + username: "opencode", + disableEmbeddedWebUi: true, + client: httpClient(new Response("ok")), + }).request(path) + expect(response.status).not.toBe(401) + } + const post = yield* uiApp({ + password: "secret", + username: "opencode", + disableEmbeddedWebUi: true, + }).request("/brain.html", { method: "POST" }) + expect(post.status).toBe(401) + }), + ) + it.live("allows web UI preflight without auth", () => Effect.gen(function* () { const response = yield* app({ password: "secret", username: "opencode" }).request("/", {