Docs

ADR-0005: Per-window browser session isolation

Status: Active · Date: 2026-07-07

Context

Every browser tab in Tranquil shared Electron’s default session — no <webview> set a partition except HAR-replay tabs (ADR-0004). A login on a domain in one tab was a login in every tab and every window, so you could not be signed into the same site under two accounts at once (work vs. personal, multi-tenant testing, comparing two users’ views).

Upstream Pulsar offers nothing to build on: it uses an ephemeral nextId++ window counter, sets no partition on any BrowserWindow, and persists windows keyed only on their project roots. Any per-window-session mechanism is therefore something Tranquil invents.

Decision

An Electron window instance is a session. Every window is assigned a durable windowSessionId (a uuid) at creation; all its browser webviews use partition = persist:tb-window-<windowSessionId>, so each window is its own cookie/storage/login jar. The id is persisted per-window in application.json and restored on relaunch, so sessions survive quit/restart. Isolation is automatic (every window, no opt-in) and needs no browser UI — no picker, registry, or per-tab controls. Within one window all tabs share that window’s session; two accounts means two windows.

Options considered

  • Option A: Per-tab named containers — a registry of named, colored containers picked per tab (Firefox Multi-Account Containers). Only option that supports two logins side by side in one window. Rejected: heavy UI surface (picker, registry, colored chips, five entry points, per-tab serialization) for a need that “two windows” already covers.
  • Option B: Per-project window session — derive the partition from the window’s project roots (persist:tb-window-<sha1(projectRoots)>), reusing Pulsar’s own getStateKey identity. Idiomatic and needs no new persisted state. Rejected: it’s really project-scoped — two windows on the same project would share a session (the opposite of the requirement), and no-folder browser-only windows would have no stable/isolated session.
  • Option C: Durable per-window id (chosen) — assign each window a uuid, persisted additively in application.json. True per-window isolation independent of project, persisted across restart. Cost: a new persisted field (no upstream precedent) — but additive, reusing the existing application.json save/restore path with no schema-version bump.
  • Ephemeral per-window (sub-variant, rejected) — a runtime-only id, no persist:. Trivial, but every window resets to logged-out on restart, failing the “keep a second account logged in” goal.

Consequences

  • Migration: once windows own their sessions, existing sites live in the old default jar, so there’s a one-time re-login. A cookie-copy migration was considered and left out of scope.
  • No same-window dual login: two accounts require two windows (accepted — matches the chosen mental model).
  • “Open Link/Tab in New Window” revived: the previously-dead application:new-window-open-url channel now has a main-process handler (AtomApplication.openUrlInNewWindow) that opens a fresh window and has its renderer open the URL — making “open this link in a new isolated login” a one-click action.
  • Two same-project windows across restart: their sessions persist correctly (cookies live in the partition, keyed by windowSessionId), but Pulsar already keys tab-layout state on project paths alone, so two same-project windows can’t restore distinct tab arrangements — an unrelated pre-existing limitation.
  • Orphaned partitions: each window id creates on-disk partition data under ~/.tranquil; abandoned windows leave it behind. A cleanup that prunes persist:tb-window-* partitions absent from application.json is a future item.
  • HAR replay unaffected: a tab with an explicit opt.partition (HAR) still wins over the per-window default.
  • Re-evaluation triggers: if same-window dual login becomes necessary, revisit Option A; if orphaned partition storage grows unbounded, add lifecycle cleanup.

Validation

Verified live on Electron 30.5.1: with three windows open, application.json recorded three distinct windowSessionIds and ~/Library/Application Support/Tranquil/Partitions/ held a matching tb-window-<uuid> directory per window (1:1), confirming each window’s webviews load into their own persistent session. The decision record is these docs; implementation notes are in Per-Window Session Isolation — Delivery Notes.