Repository navigation
Keep older clients and plain-HTTP deployments working - #2121
Merged
ppXD merged 1 commit intoOct 18, 2026
Merged
Conversation
Four places where the session-cookie, invitation, CSP and paging hardening broke something that worked before. Session cookie: sign-in, change-password and invitation accept now set the HttpOnly cookie only when the request carries X-CodeSpace-Csrf, which the SPA sends on every request. A client that predates the cookie (an older SPA bundle, an API client, the CLI) cannot clear one: only the API can, and the older bundle's sign-out empties its own storage. Handed one anyway, it held a 24-hour session it could never end. It keeps the session in the body, as before. POST /api/auth/session still sets the cookie unconditionally, since asking for it is that endpoint's whole job. Invitation preview: bring back accountExists as an [Obsolete] alias of forSignedInAccount for one release. The API is always replaced before the SPA, so the previous invite page reads the old name throughout every rollout, and without it asked a signed-in invitee to create an account. The alias is derived from the new field, so it states the same fact about the caller and not the old existence oracle. SPA image: allow http: in the CSP's img-src, because a self-managed provider on a plain-HTTP deployment serves avatars and README images over http, and img-src does not protect the session (https: already admits every host). init.sh writes its nginx include under /tmp/codespace instead of /etc/nginx/codespace, so that step needs nothing under /etc to be writable. That is the one write moved, not a read-only root filesystem: dist/env.js is still substituted in place, npx keeps a cache, and nginx writes its pid file and cache directories. Paging: a page past MaxPage was clamped to MaxPage, so a client that kept paging on received page 1000 again for as long as it asked. The pull request and issue list handlers and the git.list_prs node now answer an empty page and ask the provider nothing, and the MaxPage documentation on both queries says so. The node reads a number by its value: one too large for an Int32 (3000000000) had been taken for a missing page and read as page 1, which is the same wrong-rows answer for a page far on; it now counts as past the last page, or as the ceiling for a page size. A number written as 1001.0 or 1.001e3 is the whole number it says.
ppXD
changed the base branch from
fix/never-lose-a-rotated-oauth-pair
to
main
October 18, 2026 11:21
ppXD
force-pushed
the
fix/keep-older-clients-and-plain-http-deployments-working
branch
from
October 18, 2026 11:21
2d598a0 to
0f96f74
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.
Summary
X-CodeSpace-Csrf, which the SPA sends on every request. A client that predates the cookie (an older SPA bundle, an API client, the CLI) cannot clear one, so it never gets one. It keeps the session in the response body, as before.POST /api/auth/sessionstill sets the cookie unconditionally.accountExistson the invitation preview as an[Obsolete]alias offorSignedInAccountfor one release. The API is always replaced before the SPA, so the previous invite page reads the old name throughout a rollout. The alias is derived from the new field, so it describes the caller and is not the old account-existence oracle.http:in the SPA'simg-src, so a self-managed provider on a plain-HTTP deployment can show avatars and README images.img-srcdoes not protect the session:https:already admits every host, andscript-src,connect-srcandform-actionare unchanged.init.shgenerates under/tmp/codespaceinstead of/etc/nginx/codespace, so generating it needs nothing under/etcto be writable. This is the only write moved. The image does not yet start with a read-only root filesystem:dist/env.jsis still substituted in place,npxkeeps a cache, and nginx writes its pid file and cache directories.MaxPageon the pull request list, the issue list andgit.list_prsis now an empty page and the provider is not asked, instead of page 1000 again for a client that keeps paging. TheMaxPagedocumentation on both queries says so.git.list_prsreads a number by its value. A number too large for an Int32 (3000000000) was taken for a missing page and read as page 1. It now counts as past the last page, or as the ceiling for a page size. A number written as1001.0or1.001e3is the whole number it says. A page size of3000000000is now 100, where it used to be the default of 30, and a fractional value is read at its whole part.Test plan
GitListPullRequestsNodeTests(36), covering overflow, fraction and exponent spellings, and the ceilings; session cookie and invitation preview wire tests; both list handlers. Full unit suite 13862 passed, 1 skipped.AgentToolReadBoundsFlowTests(9) through the real MCP handler, the pull request service and the GitLab provider against a loopback GitLab, including pages2147483648and3000000000.static-hosting.test.tsandrequest.test.ts(58 tests),pnpm lint(0 errors),tsc --noEmit -p tsconfig.app.json.frontend/docker/e2e/run.sh(spa-image build and run, checking the include at/tmp/codespace/api-origin.confand nothing left under/etc/nginx/codespace). It was not run locally because Docker was unavailable.