Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion apps/code/src/main/di/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ import type { URL_LAUNCHER_SERVICE } from "@posthog/platform/url-launcher";
import type { WORKSPACE_SETTINGS_SERVICE } from "@posthog/platform/workspace-settings";
import type { WorkspaceClient } from "@posthog/workspace-client/client";
import type { DatabaseService } from "@posthog/workspace-server/db/service";
import type { GIT_SERVICE as WS_GIT_SERVICE } from "@posthog/workspace-server/di/tokens";
import type {
BROWSER_TABS_SERVICE,
GIT_SERVICE as WS_GIT_SERVICE,
} from "@posthog/workspace-server/di/tokens";
import type { AgentService } from "@posthog/workspace-server/services/agent/agent";
import type {
AGENT_AUTH,
Expand All @@ -147,6 +150,7 @@ import type {
} from "@posthog/workspace-server/services/archive/ports";
import type { AUTH_PROXY_AUTH } from "@posthog/workspace-server/services/auth-proxy/identifiers";
import type { AuthProxyAuth } from "@posthog/workspace-server/services/auth-proxy/ports";
import type { IBrowserTabsService } from "@posthog/workspace-server/services/browser-tabs/service";
import type {
ENRICHMENT_AUTH,
ENRICHMENT_FILE_READER,
Expand Down Expand Up @@ -490,4 +494,5 @@ export interface MainBindings {
[UI_SERVICE]: UIService;
[MCP_APPS_SERVICE]: McpAppsService;
[SUSPENSION_SERVICE]: SuspensionService;
[BROWSER_TABS_SERVICE]: IBrowserTabsService;
}
14 changes: 14 additions & 0 deletions apps/code/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import log from "electron-log/main";
import "./utils/logger";
import "./services/index.js";
import type { AuthService } from "@posthog/core/auth/auth";
import { getAccountScopeKey } from "@posthog/core/auth/authIdentity";
import { AuthServiceEvent } from "@posthog/core/auth/schemas";
import { focusHostModule } from "@posthog/core/focus/focus-host.module";
import {
FOCUS_SESSION_STORE,
Expand Down Expand Up @@ -38,6 +40,8 @@ import { ENVIRONMENT_CLIENT } from "@posthog/host-router/ports/environment-clien
import { FILE_WATCHER_CONTROL } from "@posthog/host-router/ports/file-watcher-control";
import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events";
import type { DatabaseService } from "@posthog/workspace-server/db/service";
import { BROWSER_TABS_SERVICE } from "@posthog/workspace-server/di/tokens";
import type { IBrowserTabsService } from "@posthog/workspace-server/services/browser-tabs/service";
import type { ExternalAppsService } from "@posthog/workspace-server/services/external-apps/external-apps";
import {
FS_SERVICE,
Expand Down Expand Up @@ -267,6 +271,16 @@ async function initializeServices(): Promise<void> {
// Eagerly start the Discord presence service so it connects when enabled.
container.get<DiscordPresenceService>(DISCORD_PRESENCE_SERVICE);

// Tie the Channels tab strips to the signed-in user: each auth change
// repoints the tabs service at that account's persisted tabs. "restoring"
// is skipped so a slow session restore doesn't flash the signed-out state.
const browserTabsService =
container.get<IBrowserTabsService>(BROWSER_TABS_SERVICE);
authService.on(AuthServiceEvent.StateChanged, (state) => {
if (state.status === "restoring") return;
browserTabsService.setAccountScope(getAccountScopeKey(state));
});

await authService.initialize();

// Initialize workspace branch watcher for live branch rename detection
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/auth/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ describe("AuthService", () => {
expect(service.getState()).toEqual({
status: "anonymous",
bootstrapComplete: true,
accountKey: null,
cloudRegion: null,
orgProjectsMap: {},
currentOrgId: null,
Expand All @@ -257,6 +258,7 @@ describe("AuthService", () => {
expect(service.getState()).toEqual({
status: "anonymous",
bootstrapComplete: true,
accountKey: null,
cloudRegion: "us",
orgProjectsMap: {},
currentOrgId: null,
Expand Down Expand Up @@ -291,6 +293,7 @@ describe("AuthService", () => {
expect(service.getState()).toMatchObject({
status: "authenticated",
bootstrapComplete: true,
accountKey: "user-1",
cloudRegion: "us",
orgProjectsMap: {
"org-1": {
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
private state: AuthState = {
status: "anonymous",
bootstrapComplete: false,
accountKey: null,
cloudRegion: null,
orgProjectsMap: {},
currentOrgId: null,
Expand Down Expand Up @@ -456,6 +457,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
this.updateState({
status: "restoring",
bootstrapComplete,
accountKey: null,
cloudRegion: storedSession.cloudRegion,
orgProjectsMap: {},
currentOrgId: null,
Expand Down Expand Up @@ -776,6 +778,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
this.updateState({
status: "authenticated",
bootstrapComplete: true,
accountKey: session.accountKey,
cloudRegion: session.cloudRegion,
orgProjectsMap: session.orgProjectsMap,
currentOrgId: session.currentOrgId,
Expand Down Expand Up @@ -896,6 +899,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
this.updateState({
status: "anonymous",
bootstrapComplete: partial.bootstrapComplete ?? true,
accountKey: null,
cloudRegion: partial.cloudRegion ?? null,
orgProjectsMap: {},
currentOrgId: null,
Expand Down
16 changes: 16 additions & 0 deletions packages/core/src/auth/authIdentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,19 @@ export function getAuthIdentity(authState: AuthState): string | null {
}
return `${authState.cloudRegion}:${authState.currentProjectId ?? "none"}`;
}

/**
* Storage scope for per-user local state (e.g. browser tabs), stable across
* project/org switches. Null when signed out or when the user's identity
* could not be resolved — callers treat null as "don't touch persisted state".
*/
export function getAccountScopeKey(authState: AuthState): string | null {
if (
authState.status !== "authenticated" ||
!authState.cloudRegion ||
!authState.accountKey
) {
return null;
}
return `${authState.cloudRegion}:${authState.accountKey}`;
}
3 changes: 3 additions & 0 deletions packages/core/src/auth/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ export function pickInitialProjectId(args: {
export const authStateSchema = z.object({
status: authStatusSchema,
bootstrapComplete: z.boolean(),
/** Stable id of the signed-in user (uuid > distinct_id > email); null when
* anonymous/restoring or when the user context could not be resolved. */
accountKey: z.string().nullable(),
cloudRegion: cloudRegion.nullable(),
orgProjectsMap: orgProjectsMapSchema,
currentOrgId: z.string().nullable(),
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/features/auth/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export { getAuthIdentity };
export const ANONYMOUS_AUTH_STATE: AuthState = {
status: "anonymous",
bootstrapComplete: false,
accountKey: null,
cloudRegion: null,
orgProjectsMap: {},
currentOrgId: null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE `browser_windows` ADD `account_scope` text;--> statement-breakpoint
CREATE INDEX `browser_windows_account_scope_idx` ON `browser_windows` (`account_scope`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The file is missing a trailing newline, which can cause issues with some SQL tools and shows as a diff noise marker (\ No newline at end of file) in git diff.

Suggested change
CREATE INDEX `browser_windows_account_scope_idx` ON `browser_windows` (`account_scope`);
CREATE INDEX `browser_windows_account_scope_idx` ON `browser_windows` (`account_scope`);

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Loading
Loading