Repository navigation
Conversation
…switcher's unknown answer Five callers racing to set up a fresh instance leave one owner, organization and root record. A setup that fails at its last write leaves no rows and a retry succeeds. Creation ignores a body naming the owner and refuses the owner's own API key and MCP OAuth token, each shown valid where it was issued. The switcher offers no creation while the capability answer is pending or failed, and recovers on reload. Co-authored-by: Cursor <cursoragent@cursor.com>
The released-image suite gains two scenarios that run the native image with SSO configured against the synthetic OpenID provider. A sidecar owns the container network and forwards the provider's HTTPS address, so the image trusts it through NODE_EXTRA_CA_CERTS. A verified, approved address without a membership, an unverified address and an unapproved domain are refused with their codes and no session or membership. An invited member who connected SSO signs in, and is refused once removed. Co-authored-by: Cursor <cursoragent@cursor.com>
scripts/inspect-self-host-root.ts loads an executor-host export archive into an in-memory PGlite and compares it with the identity the operator recorded before the upgrade: one organization ID and its full set of owners. It resolves PGlite from the repository, refuses any version but the product's 0.5.8 before running it, and requires Postgres 18. It passes only when the archive has exactly one root record, id root, naming that organization, the organization's owners are exactly the recorded set, and the journal records 1 root_organization. It exits 1 when the gate fails, 2 for an invalid invocation or baseline and 3 when the archive or PGlite cannot be used, and prints a fixed report without rows, SQL errors or stack traces. e2e:root-inspection runs it as a separate process over synthetic PGlite archives for each pass, failure, baseline and error case. Co-authored-by: Cursor <cursoragent@cursor.com>
--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 multi-organization support (#81 to #84) is on hold until executed evidence and an operational gate exist. This PR adds the scenarios that were missing and records where the retained runs live. It changes no product code: the diff against #84 is e2e only. #81 to #84 keep their heads.
What changed
self-host-setup-integrity.spec.ts: five callers racing to set up a fresh instance leave one owner, organization and root record ([200, 403, 403, 403, 403]); a setup that fails at its last write (a database trigger onmember) leaves no rows, and a retry succeeds.self-host-organization-creation.spec.ts: a body naming the owner'suserIddoes not change the creator (admin403, member403, anonymous401); the owner's API key and MCP OAuth token, each first shown valid where it was issued, get401on create and on capabilities.self-host-organization-switcher.spec.ts: no create action while the capability answer is pending or answered503; a reload with the server answering shows it.docker-release-sso-admission.spec.ts(released-image suite): runs the native image with SSO against the synthetic OpenID provider. A sidecar owns the container network and forwards the provider's HTTPS address, so the image trusts it throughNODE_EXTRA_CA_CERTS. A verified, approved address without a membership, an unverified address and an unapproved domain are refused with their codes and no session; an invited member who connected SSO signs in and is refused once removed.scripts/inspect-self-host-root.ts: the offline root-record gate. It loads anexecutor-host exportarchive copy into an in-memory PGlite and compares it with the baseline the operator recorded before the upgrade,{ "version": 1, "organizationId", "ownerUserIds": [...] }. It resolves PGlite from the repository's exact0.5.8pin and refuses any other version before running it, and requires Postgres 18. It passes only for exactly one root record,id = 'root', naming the recorded organization, whose owners are exactly the recorded set, with the journal recording1 root_organization. Exit codes: 0 pass, 1 gate failed, 2 invalid invocation or baseline, 3 archive or PGlite unusable. It prints a fixed report without rows, SQL errors or stack traces. It replaces a rehearsal script that exited 0 on a failed gate, accepted one owner and imported PGlite from a fixed path.--rollout pr81on the same script adds the Let self-host serve several organizations while creation stays off #81 rollout gate: exactly one organization, andprivate_hosted_migrations(9 entries) andprivate_self_host_migrations(1 entry) exactly as 7c49225 declares them, nothing missing, extra or renamed. The entries were derived from that commit'smigrateProductStepsrecords and Effect's<id>_<name>key parsing.self-host-root-inspection.spec.ts(bun run e2e:root-inspection): runs the gate as a separate process over synthetic PGlite archives. Two recorded owners pass in any order. An omitted or added owner, an admin recorded as owner, an unrecordedadmin,owner, a duplicate owner row, a missing, duplicate, renamed or other-organization root record, a missing organization, and a missing or wrong journal fail with exit 1. Malformed baselines and invocations exit 2. A missing or non-archive file, a Postgres 17 data directory, missing PGlite and PGlite 0.4.0 exit 3. Every case checks that stderr is empty and that neither stream has the session token planted in the archive, SQL text or stack frames.bun.lockis unchanged.Evidence
Retained on the author's machine under
executor-evidence/multi-org-2026-11-05/; each run records head, tree,status --porcelain, dirty diff, command, exit, log and results. Credential-bearing exports and volume backups are not part of it.7c4922535596a6fe86938a8acbe0078107ce2bca, empty status689f8bdd9695a038125d4af8ba2e5ec91fccf947sha256:367abe95…08ed875565d7420d64ad35d3bb92cec2b51cf451c7be5sha256:b95d4619…92fd4Each new scenario was also run against a deliberately broken product and failed: setup without its guard (
[200, 503, 503, 503, 503]), setup outside its transaction (rows survive), creation allowed for everyone (admin spoof200), a switcher that offers creation unless told no (shown while pending).Offline gate and restore, on disposable local volumes with images built from the exact commits:
executor-host exportfrom a stopped copy and inspection of a separate copy with PGlite 0.5.8loadDataDir: exactly oneselfHostRootrow (id = root) naming the recorded pre-upgrade organization, the recorded owner as its only owner, journal1 root_organization.403) once a second organization exists; Let self-host serve several organizations while creation stays off #81 on that same volume signs both back in.Root gate with the #81 rollout profile (
c4331c50c22474d0d12c96c6e880682c78087858, clean tree): synthetic suite 5/5 passed. The rollout group passes a one-organization archive with the full pinned journals, and fails two organizations, a missing, extra or renamed hosted entry, no hosted journal, an extra self-host entry and a missing self-host entry; the general gate still accepts two organizations. Three scratch mutations (organization count not enforced, journal read only atmigration_id = 1, unexpected entries ignored) each fail it. Earlier, at4b144f658081d0639967182b4fa2c905ae4d2119: synthetic suite 4/4 passed. Two scratch mutations (exit 0 on failure; ignore unrecorded owners) each fail it. On real archives restored from the full-volume backups and exported with the matching images, the gate exits 0 for the #81-upgraded volume, 1 for the pre-upgrade volume (no root table, no journal) and 1 for a baseline with an extra owner; stderr empty each time.bun run check: exit 0. Not run: the Cloud target for the isolation specs, and per-PR runs for #82 and #83 (not required for the #81 rollout).