From 03986da3d64d838612eed3199f731435344404f2 Mon Sep 17 00:00:00 2001 From: DongHyeonka Date: Fri, 21 Aug 2026 00:43:54 +0900 Subject: [PATCH] fix: let a signed-out visitor read the public site MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every public screen rendered its terminal error surface, and the network log explained why: no request to /api/v1/public ever left the browser. The credential collaborator asks the Studio helper first, which returns null for any profile it does not own — "not mine, use your own logic". Below that, the fallback reads the session and refuses anything that is not authenticated. The public operations declare the ANONYMOUS profile, so they fell into that fallback, and a signed-out visitor is exactly who the public site is for. An anonymous profile carries no credentials by definition — the registry refuses to install one that even allows a credential header — so it must never consult the session. It now short-circuits with an empty credential patch, keyed on the profile's transport rather than a profile id, so any anonymous operation is covered rather than one named surface. This could only appear once the public source became HTTP; until this week that path had never run in a browser. The suites did not catch it because they exercise the gateway and the screens, not the composition root's credential decision — that seam has no test, and this is what it costs. --- src/bootstrap/runtime-adapters.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/bootstrap/runtime-adapters.ts b/src/bootstrap/runtime-adapters.ts index 9a29bc8..4e1ade5 100644 --- a/src/bootstrap/runtime-adapters.ts +++ b/src/bootstrap/runtime-adapters.ts @@ -513,6 +513,18 @@ export async function createRuntimeAdapters( techLogCsrf, ); if (studioOutcome) return studioOutcome; + // An anonymous profile carries no credentials by definition — the + // registry refuses to install one that allows any credential header. It + // must therefore never consult the session: a signed-out visitor's state + // is `unauthenticated`, and falling through below refused every public + // read before it left the browser. The public site rendered its terminal + // error surface on every screen with no request in the network log. + // + // Keyed on the profile's transport rather than a profile id, so any + // anonymous operation is covered rather than one named surface. + if (INSTALLED_REST_AUTH_PROFILES.get(operation.authProfileId)?.transport === "ANONYMOUS") { + return Object.freeze({ kind: "READY" as const, headers: Object.freeze({}) }); + } const state = authSession.getState(); if (state === "integration-failed") { return Object.freeze({ kind: "UNAVAILABLE" as const });