Repository navigation
Conversation
A self-host database refused every sign-in, invitation and SSO login once it held a second organization, because each path required exactly one. Sessions now need a membership in any organization, an invitation admits into its own organization, and SSO signs in only people who already belong to one: it no longer adds a verified, approved address to the instance's organization. Setup records the organization it creates as the instance's root in a new selfHostRoot table. The private_self_host_migrations step 1_root_organization records an existing install's root when it has exactly one organization and leaves it unset otherwise. Organization creation remains disabled. Co-authored-by: Cursor <cursoragent@cursor.com>
This was referenced Nov 5, 2026
shuv1337
added a commit
that referenced
this pull request
Nov 6, 2026
--rollout pr81 adds two requirements to the general checks: the archive holds exactly one organization, and private_hosted_migrations and private_self_host_migrations are exactly the journals 7c49225 writes, with nothing missing, extra or renamed. The report now counts organizations in every mode and, for the rollout, lists missing pinned entries and counts unexpected ones. Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Why
Self-host is getting real multi-organization support: only owners of the original (root) organization may create organizations, people join only by invitation, and SSO never adds anyone. Today a self-host database that holds a second organization refuses every sign-in, invitation and SSO login, because each path requires exactly one organization. This PR makes the server tolerate several organizations while creation stays disabled, and records the root organization so a later release can authorize creation against it.
Rollout gate: this release must ship and be verified in production before any organization-creation code (#82 and above) is activated there. A server older than this one locks every account out as soon as a second organization exists (measured below), so the release that tolerates several organizations has to be the one running, and the one any rollback lands on, before a second organization can be created.
What changed
registration.ts: removedinstanceOrganization. A session needs a membership in any organization./self-host/registeradmits into the invitation's own organization. SSO no longer auto-joins a verified, approved-domain address: it signs in only people who already belong to an organization and connected SSO on Security; anyone else getssso_not_member("Ask an administrator to invite you.").root-organization.ts: a Better Auth plugin tableselfHostRootwith one row (id = 'root',organizationIdunique, FK toorganizationwithon delete restrict). Setup writes it in the same transaction that creates the first organization.private_self_host_migrations/1_root_organization: records the root only when the database has exactly one organization; zero or several leave it unset (fail closed). Recorded once in its own journal, never replayed. Cloud does not load the plugin or the journal.allowUserToCreateOrganizationstaysfalse.Schema compatibility
Additive only: one new table and one new journal table. The running server (
411a959e) never reads either.Measured by starting both server versions on the same scratch PGlite data directory:
411a959ecode200411a959ecode403"The instance must have exactly one organization"200Verification
bun run check: exit 0.bun run e2e:self-host --test-name '<titles>'on this branch:self-host-many-organizations.spec.ts(new,legacyStoragefixtures): root recorded by setup; an upgrade records an install's only organization once and a restart does not replay it; several organizations without a root leave it unset; with two organizations members sign in, an invitation admits into the inviting organization only, an existing member accepts into a second organization with its role, someone with no membership cannot sign in, setup stays closed across a restart. 3/3 passed.self-host-sso.spec.ts: 9/9 passed, including new "Self-host SSO admits no one automatically and refuses a verified, approved address without an invitation". The HTTPS and UserInfo scenarios now invite the person and connect SSO before the admitted sign-in.origin/v2'sregistration.ts: the several-organization scenario fails at the owner's sign-in (expected 403 to be 200), and the no-auto-join scenario fails because the uninvited address is admitted.The fork CI (
fork-ci.yml) runscheckand the security scenarios; it does not read the block below.