One website. Separate logins.
Keep personal, work, and client accounts apart in different Chrome tabs — without a second browser profile.
Website · Install · Status · Architecture · Privacy · License
Session Vault is a Chrome Manifest V3 extension for login isolation. Open the same site in two tabs, assign each tab a session, and keep those accounts from sharing cookies.
It is not Session Buddy. It does not save and restore piles of tabs.
It is not a separate Chrome profile. Isolation is a virtual layer inside the current profile: per-session cookie jars, declarativeNetRequest Cookie rewrite, and a MAIN-world document.cookie proxy. When isolation cannot be proven, the tab stays logged out rather than showing the wrong account.
Current release: 0.1.0 (development). Cookie isolation is implemented in the extension. Chromium Alice/Bob E2E tests, page-storage virtualization, and several product surfaces are not finished. Do not treat this build as V1-complete. See Status.
Chrome shares cookies across every tab of the same site. Two Gmail tabs are the same person. Incognito helps only until you need two logged-in accounts at once. Extra profiles work, but they duplicate extensions, bookmarks, and windows.
Session Vault gives you named sessions on the site you already have open:
- Personal and Work on the same Google account page
- A client login next to yours, on the same CRM
- A temporary session that disappears when its last tab closes
flowchart LR
subgraph chrome [Same Chrome window]
A["Tab · Work"]
B["Tab · Personal"]
end
subgraph site [accounts.google.com]
Site["One origin"]
end
JA["Work cookie jar"]
JB["Personal cookie jar"]
A --> JA --> Site
B --> JB --> Site
For a bound tab, Session Vault:
- Stores that session’s cookies in an IndexedDB jar on this device
- Rewrites the request
Cookieheader for that tab only - Strips native
Set-Cookieso Chrome’s shared jar does not mix accounts - Virtualizes
document.cookiefor page JavaScript
Page storage (localStorage, IndexedDB, Cache Storage, BroadcastChannel, Web Locks) is specified and not yet virtualized. Unbound, degraded, or unproven tabs get cookies stripped. Logged out is acceptable. The wrong login is not.
Read docs/isolation-model.md and docs/architecture.md for the contract and current coverage.
- Per-site sessions — create, rename, delete, and open isolated logins from the toolbar popup
- Always a new tab — creating or opening a session loads that site in a fresh tab after DNR is installed
- Temporary sessions — disposable jars, cleaned up when the last tab closes
- Fail-closed — never fall back to Chrome’s shared cookies for an isolated tab; DNR budget overflow strips cookies
- Child-tab inheritance —
openerTabIdbinds new tabs to the parent session when the destination is in the same domain group - Local-first — no account, no telemetry, no cloud sync
- Honest limitations — Service Workers, SharedWorker, and the HTTP cache are not isolated; the scanner that should name them is not shipped yet
- Keyboard shortcuts —
Alt+Shift+Sopens the switcher;Alt+Shift+Tstarts a temporary session
The Chrome Web Store listing is not published yet. Load an unpacked build:
git clone https://github.com/Mohpfd96/session-vault.git
cd session-vault
pnpm install
pnpm build- Open
chrome://extensions - Turn on Developer mode
- Click Load unpacked
- Select
.output/chrome-mv3
Reload the extension on that page after you pull changes.
Requires Chrome 120+.
- Open the site you want to isolate (for example
mail.google.com) - Click the Session Vault icon
- Create a session — a new tab of that site opens, logged out
- Sign in as that account
- Create another session for the next account
Click a session in the popup to open another tab of it. The toolbar icon fills when the current tab is in a session.
| Shortcut | Action |
|---|---|
Alt+Shift+S |
Open the session switcher |
Alt+Shift+T |
New temporary session |
| (Chrome shortcuts page) | Next / previous session, duplicate into a session |
Everything stays in this Chrome profile on this device.
| Data | Where |
|---|---|
| Session names and metadata | chrome.storage.local |
| Which tab belongs to which session | chrome.storage.session (cleared when Chrome restarts) |
| Cookie jars | Extension IndexedDB |
No analytics, no remote fonts in the extension, no upload unless you export a backup yourself. Backup encryption primitives exist; the import/export UI is not shipped. Public policy: session-vault privacy. Details: docs/privacy.md and docs/permissions.md.
Say this out loud before you rely on it for high-assurance work:
- Existing Service Workers (Chrome applies them origin-wide; DNR does not see those responses)
- SharedWorker, the browser HTTP cache, history, bookmarks, and Chrome sync
- TLS client certificates, the password manager, FedCM, and some enterprise SSO
- Fingerprints, IP address, DNS, and HSTS
localStorage/ IndexedDB / Cache Storage / BroadcastChannel / Web Locks until Phase 3 lands
Session Vault is strong practical isolation for HTTP cookies and document.cookie when DNR rules are installed. It is not a second browser.
pnpm install
pnpm dev # unpacked ext with live reload
pnpm test:unit
pnpm compile
pnpm lint
pnpm format:check
pnpm build| Stack | WXT, React 19, Tailwind, Vitest |
| Package manager | pnpm |
| Output | .output/chrome-mv3 |
Before a pull request: pnpm compile && pnpm lint && pnpm format:check && pnpm test:unit.
Playwright E2E (pnpm test:e2e) currently loads a smoke test only. Isolation proofs against Chromium are listed under Phase 2.
Version 0.1.0. Checklist below is based on the repository as of this writing, not on the original product brief. Items are done only when the code, tests, or UI for that item exist and match the intended behavior. Spec-only schemas, disabled switches, and stubs do not count as done.
Legend: Done · Partial · Not started
Status: Done
The extension builds, lints, and unit-tests. Domain models, persistence, messaging, popup, side panel, and options exist.
| Item | State |
|---|---|
| WXT + Chrome Manifest V3 | Done |
TypeScript (strict, including exactOptionalPropertyTypes) |
Done |
| ESLint + Prettier | Done |
| Vitest unit tests + GitHub Actions CI | Done |
| Session domain model, lifecycle, branded IDs | Done |
| Schema v1, migrations, recovery lock | Done |
| Typed messaging (UI / content / page) + Zod | Done |
| Chrome API adapters | Done |
| Popup (current site, sessions, create / temp) | Done |
| Side panel (list, detail, domain detail, filters) | Done |
| Options / settings (theme, privacy copy) | Done |
| IsolationProvider abstraction | Done |
| Playwright harness (smoke only) | Done |
Status: Partial — implementation landed; Chromium proof has not
Cookie isolation is not complete until the simultaneous same-origin Alice/Bob E2E test passes in Chromium.
| Item | State |
|---|---|
| Managed domains + host permission request | Done |
| Virtual cookie engine (parse, match, jar, header) | Done |
| Native cookie import into Default session | Done |
| DNR compiler (priorities, path rules, fail-closed strip) | Done |
| RuleBudgetManager (degrade + strip, never native fallback) | Done |
Tab binding + chrome.storage.session recovery |
Done |
webRequest Set-Cookie ingest → jar → targeted rebuild |
Done |
MAIN-world document.cookie getter/setter |
Done |
Safe about:blank then bind then navigate |
Done |
| Cookie / DNR / ingest / native-import unit tests | Done |
| Alice/Bob same-origin dual-session Playwright test | Not started |
| HttpOnly / Path / redirect / parent-domain Chromium proofs | Not started |
| Service-worker kill + reload isolation proof | Not started |
Status: Not started
The messaging protocol reserves content.storageOp. The background router rejects it. The page runtime does not wrap storage APIs. IndexedDB has a webStorage object store that is unused.
| Item | State |
|---|---|
localStorage namespace per session |
Not started |
| IndexedDB database-name virtualization | Not started |
| Cache Storage name virtualization | Not started |
BroadcastChannel name virtualization |
Not started |
| Web Locks name virtualization | Not started |
| Prefix-probe rejection | Not started |
| Storage unit + lab tests | Not started |
sessionStorage is correctly not namespaced (tab-scoped browser default). That is intentional, not missing work.
Status: Partial
| Item | State |
|---|---|
Child-tab inheritance via openerTabId |
Done |
Safe create-tab (about:blank → bind → navigate) |
Done |
| Popup: switch / open / create / temporary / delete | Done |
| Duplicate page into another session | Done |
| Move current tab to a session | Done |
| Keyboard commands (switcher, temp, next, previous, dup) | Done |
| Per-site domain groups (registrable domain + matcher) | Done |
| Custom multi-host domain families (Google-style SSO) | Not started |
| Context menu: Open / move / duplicate in session | Not started |
| Chrome Tab Groups integration | Not started |
| Automatic URL routing rules | Not started |
| Side-panel session settings (inherit / tab groups) | Partial (shown, disabled) |
Context menu today: Open side panel on the toolbar icon only. There is no “Open link in Session” picker.
Status: Partial
| Item | State |
|---|---|
| Temporary sessions with last-tab cleanup | Done |
| Delete session (close tabs, drop jar, drop metadata) | Done |
| Browser-restart: tab IDs discarded, unassigned + strip | Done |
| MV3 worker init: restore bindings, drop stale, rebuild DNR | Done |
| Archive / unarchive (domain transitions exist) | Partial |
| Convert temporary → persistent | Not started |
| Grace-period / browser-session temporary cleanup | Not started |
| Clone session (cookies / storage / tabs / metadata) | Not started |
| Selective clear (cookies, JS cookies, storage, site state) | Not started |
| Reset whole session without delete | Not started |
Archive is modeled and filtered in the side panel. There is no archive action in the UI.
Status: Partial
| Item | State |
|---|---|
| Structured logger with secret-key redaction | Done |
| AES-GCM + PBKDF2 envelope (round-trip, wrong password, tamper) | Done |
Message validation (no page-supplied sessionId) |
Done |
| Zod schemas for persisted records | Done |
| Versioned backup envelope (types / docs) | Partial |
| Import preview, collision policies, file I/O UI | Not started |
Encrypted .sessionvault export/import in the product |
Not started |
| Session Inspector / diagnostics | Not started |
| Privacy-safe activity timeline | Not started |
| Redacted diagnostic export | Not started |
| Masked cookie values in UI (Reveal / Copy) | Not started |
Status: Not started
IsolationProvider.getCompatibility() always returns FULL with no reasons. The side panel hard-codes a “full” line. Session strictness is stored but unused.
| Item | State |
|---|---|
| Detect controlling / registered Service Worker | Not started |
| Detect SharedWorker | Not started |
| Per-origin FULL / LIMITED / UNSUPPORTED scoring | Not started |
STRICT mode (block register) |
Not started |
| CLEAN mode (user-confirmed origin wipe) | Not started |
| Surface real limitation strings in UI | Not started |
Status: Partial
| Item | State |
|---|---|
| Light / dark / system theme | Done |
Landing site (site/) + GitHub Pages |
Done |
| Architecture, threat, isolation, schema, privacy docs | Done |
| Keyboard list hook (exists, not wired into session lists) | Partial |
| Toolbar icon fill for bound tabs | Done |
| Session-colored badge text (P / W / …) | Not started |
| WCAG pass (focus, contrast, reduced motion, SR status) | Partial |
| Performance budgets / profiling | Not started |
| Chrome Web Store listing, screenshots, privacy policy page | Partial |
| Playwright E2E in CI | Not started |
Optional host permissions only (no install-time *://*/*) |
Done |
Host access is requested when you isolate a site. The packaged manifest does not declare install-time *://*/*. Store listing copy, 128px PNG icon, and the public privacy page are in docs/chrome-web-store.md. Screenshots still need to be captured from a loaded build.
V1 is not complete until every row is Done.
| # | Criterion | State |
|---|---|---|
| 1 | Two tabs, same origin, different sessions, both open | Partial |
| 2 | Each can hold a different HttpOnly auth cookie | Partial |
| 3 | Refreshing either tab never changes the other’s login | Not started |
| 4 | JavaScript document.cookie isolated |
Done |
| 5 | localStorage isolated |
Not started |
| 6 | IndexedDB isolated for supported sites | Not started |
| 7 | Cache Storage isolated for supported sites | Not started |
| 8 | Same-session tabs share cookie jar | Done |
| 9 | Child tabs inherit the correct session | Done |
| 10 | Survives MV3 service-worker suspension | Partial |
| 11 | Temporary sessions remove state safely | Done |
| 12 | DNR exhaustion fails closed | Done |
| 13 | Untrusted page cannot request another session’s secrets | Done |
| 14 | Sensitive values redacted from logs | Done |
| 15 | Imported data validated | Partial |
| 16 | Backups can be encrypted | Partial |
| 17 | Service Worker limitations surfaced, not hidden | Not started |
| 18 | Required permissions documented | Done |
| 19 | Real Chromium E2E isolation tests | Not started |
| 20 | Zero tolerated cross-session leakage on supported scenarios | Not started |
Rows 1–3 and 19–20 stay Partial / Not started until Alice/Bob runs against Chromium. Cookie engines and DNR compilation are unit-tested; that is not a substitute.
Fingerprint spoofing, canvas/WebGL spoofing, residential proxies, CAPTCHA bypass, stealth automation, password manager, remote account sharing, cloud accounts, enterprise collaboration.
| Architecture | Layers, DNR, fail-closed recovery |
| Isolation model | What is isolated vs what is not |
| Cookie engine | Virtual jar behavior |
| Storage virtualization | Page storage contract (not shipped) |
| Permissions | Why each Chrome permission exists |
| Privacy | Local-first data map |
| Chrome Web Store | Listing copy and upload checklist |
| Threat model | Attacker goals and mitigations |
| Import / export | Backup envelopes |
| Compatibility | SW, SharedWorker, HTTP cache |
| Service Worker limitations | Why DNR cannot see SW responses |
| Data schema | Versioned records and stores |
| Recovery | Worker kill, restart, budget |
| Testing | Unit and E2E strategy |
| Release checklist | Pre-ship gates |
The public landing page lives in site/ and deploys to GitHub Pages.
Enable it once under Settings → Pages → Source: GitHub Actions.
Session Vault is dual-licensed.
GPLv3 — personal use, study, modification, and open-source projects that stay under GPLv3 are free. See LICENSE.
Commercial — closed-source or proprietary use, including organizational closed-source products, needs a separate license. Get in touch.
© 2026 Mohamad Parsaeifard