- Shipped
- August 26, 2026 at 5:10 AM UTC
- Author
- kamo
- Commit
- 7c4c7bc
Open KamoCRM in one tab and another organization in a second, refresh the first, and it rendered as the second. Close that tab, refresh again, and it came back. Reported as session data bleeding between tabs, and that is what it was. The session id is per-tab in sessionStorage, but the root layout resolved the tenant from the forwarded *** COOKIE, and a cookie is per-ORIGIN. Every organization without a domain of its own now shares internal.<platform>, so all of their tabs share one cookie jar, and /api/session/extend re-plants that cookie from whichever tab last refreshed. The shell it chose was the last writer's — branding, and the record that carries its features. The cookie cannot go: the universe/ksem EventSources send withCredentials, cannot set headers and have no other carrier. So the consumer changed instead. The tenant now comes from ?org=, the only per-tab thing a document request carries — middleware puts it where a layout can read it, since App Router layouts get no searchParams; /validate names the org it just entered so the first paint is already right; and OrgUrlSync puts it back after client navigations, which drop the query, so a refresh three pages deep still identifies the right tenant. It is a REFERENCE, not a credential: it selects public branding, the same record /org/public serves anyone. Data and rights stay gated by the per-tab token on the client's own calls, so a hand-edited value gets somebody's logo and no access. The middleware always sets the header, empty included, so a caller cannot forge it. Also here: theme assets are served from the apex being browsed rather than the org's own, which may not resolve yet, and the create wizard waits for provisioning to finish instead of opening the new workspace onto a theme that has not been written.