Repository navigation
Add the hosted frontend adapter for external auth mode - #483
Open
alex-clickhouse wants to merge 4 commits into
Open
alex-clickhouse wants to merge 4 commits into
alex-clickhouse wants to merge 4 commits into
Conversation
alex-clickhouse
added this pull request to stack #407
October 5, 2026 07:30
alex-clickhouse
marked this pull request as ready for review
October 5, 2026 09:21
A hosted actor comes from the gateway and has no local account, so it has no login name. The label "Unnamed account" is wrong for it and does not tell two such actors apart. actorName() uses the display name, then the login name, then "Nerve" for the system actor, then the first eight characters of the actor id. ActorLabel gives the id also when the actor directory does not have the actor yet. The title still shows the full id. The two tests that asserted the "Unnamed account" fallback assert the short id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In external mode, Nerve runs behind the gateway. The gateway signs the browser in with its own session cookie and gives Nerve the actor. The web UI must not use the local login, setup, accounts or token paths there, because they do not exist and a stale bearer gives 401. GET /api/auth/status reports the mode. A missing or unknown mode is local, so the UI works with an older backend. The new module api/hosted.ts holds the hosted parts, so the tests that mock api/client with a fixed export list stay valid. In hosted mode: - Startup always reads /api/auth/me and does no passwordless login. The login page, the setup page, the session-expired overlay, the /accounts and /setup routes and the Accounts nav item do not show. - Requests send no Authorization header, do not read or write the local token, and send X-Nerve-CSRF: 1 on unsafe methods. - A 401 writes the composer text and the queued WebSocket messages to the drafts, then goes to /_nerve/login with return_to. - A gateway 403 access_denied, 410 or 503 agent_archived, and 503 or 502 unavailable or backend_unavailable show their own screens. The first two stop the app and its WebSocket. - Before each WebSocket retry, the socket asks GET /_nerve/session. The gateway clears its session cookie when it refuses an upgrade, so the check must run before the next upgrade to see 403 and not 401. - Logout purges the account state, posts /_nerve/logout and goes to /. API errors are an ApiError with status and reason. The message keeps the "NNN: body" format that errorDetail parses. Image, file and WebSocket URLs get ?token= only in local mode and only with a token, so no URL gets token=null. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 401 in hosted mode always started a gateway login. When Nerve itself
refuses a request while the gateway session is valid, for example for
an actor header that it cannot read, the login comes back to the same
401 and the browser loops through the control-plane login.
The gateway answers login_required when a request has no session. Only
that 401 signs in again at once. Any other 401 asks /_nerve/session: a
401 there signs in again, 403 and 410 show their screens, and a valid
session shows the unavailable screen. Both fetch paths use this rule.
A gateway error body ({"reason", "requestId"}) also proves that the
page runs behind the gateway. When the status call fails with such a
body, for example while the agent VM starts, the mode becomes external
and the matching screen shows with its retry, not the local login
page. A failure without a gateway body keeps the local behavior. Only
the gateway writes this body shape; Nerve puts error details below
"detail".
The gateway answers 503 agent_starting, agent_paused and
agent_unavailable and 504 backend_timeout for an agent that cannot
answer at this time. They show the unavailable screen. 503
agent_archived keeps the archived screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alex-clickhouse
force-pushed
the
alex/external-mode-02-web
branch
from
October 5, 2026 09:31
4f8e287 to
659a734
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Add the hosted web adapter for external authentication mode (#482). The gateway owns browser authentication and agent admission; Nerve reads the verified actor and attributes work to that person. Stacked on #482; this PR changes only
web/.Behavior
GET /api/auth/statuswithout a bearer before reading identity or opening a WebSocket. A stale local token cannot override the gateway cookie. Missing or unknown modes retain compatibility with local backends.?token=, and addX-Nerve-CSRF: 1to unsafe requests, including uploads. Local requests retain their token authentication.401 login_requiredstarts re-entry; other 401s probe the gateway session first to avoid backend-authentication login loops.Validation
tsc -b && vite build) passes.Limits
926d9eb6. It passed for that revision and has not been rerun for these changes.