Why Dad kept several browsers open
Before Voyager, holding two accounts of one site meant holding two browsers: one signed in as the first account, another as the second, and Dad switching windows to switch who he was. Voyager's house sign lists it among the frictions the browser exists to remove. He "runs several browsers at once to hold two accounts of one site."
A browser keeps a site's sign-in in cookies and site storage, and a browser profile has one jar of them. Two accounts of one site need two jars. Voyager calls a jar an identity, and architecture invariant 7 states the rule in one line: "An identity is an isolated store, and a tab has exactly one."
Three small types
The parent target holds identities as plain values in Sources/VoyagerCore/Identity.swift. Its header ends with a sentence worth keeping: "Nothing here is a credential."
IdentityIDwraps a UUID. It encodes as the UUID's string and refuses to decode anything that is not one.Identityis that id plus a name and a colour, written#RRGGBB. The default colour is a grey,#8A8F98.IdentityBookis the fileidentities.json: the list, a schema version, and the default identity a tab gets when nothing names one. A first run mints a single identity called "Main", so its store starts empty.
One id is reserved. IdentityID.privateSession is a fixed UUID that belongs to no book. Incognito uses it, and lesson 3 opens it.
The id is the store's name
The engine child turns an id into a WebKit store with one call, WKWebsiteDataStore(forIdentifier:). Apple's documentation is plain about it: if no store exists for that identifier, the system creates one, and the method is how you "get a data store for a profile". The engine keeps every store it has made in a dictionary, so all pages of one identity share one object, and every WKWebViewConfiguration it builds sets websiteDataStore to the store of that page's identity.
That is the whole isolation mechanism. Voyager writes no cookie partitioning, no request filter and no profile directory of its own. Cookies, local storage and the rest of a site's data live wherever WebKit keeps that store, and a page whose configuration names another store has no way to reach them.
| Question | WebKit | Chromium |
|---|---|---|
| How does a tab get its own accounts? | A public API: WKWebsiteDataStore(forIdentifier:), macOS 14 and iOS 17 | Its own request context, which is a whole profile, so extensions and history split too; logins alone took a patch set carried in a fork |
| Who already ships it? | Orion, on the same data-store API | Brave, which carried its patch set for eight months before it landed |
That row of the engine evaluation is one of the reasons Voyager runs on WebKit (track 2 tells the rest). Firefox reaches the same place with its containers.
Two tabs of one site, two identities, one window. Each tab is signed in as itself because each web view was born with a different store.
The test that proves it
The real-WebKit suite carries twoIdentitiesShareNothing. It opens three pages, two in identity A and one in B. Page A writes a cookie and a local-storage value. The second page of A must see both; it is another web content process, so the test polls for up to three seconds (60 tries, 50 ms apart). Page B must see nothing, null|. Then each identity's store is removed, with retries, because WebKit refuses to remove a store while a web view of it still exists. The house sign keeps that as a gotcha: "An identity's data store is removed only when no web view of it is left."
The same check is case W7 of the WebKit gate: "Two accounts of one site in two tabs of one window; a pop-up stays in its tab's identity." The gate cites this suite as its evidence. It is a test on synthetic pages; the row records no sign-in of two real accounts, and I would not claim one for it.
When the book cannot be read
Because the id is the only name a store has, losing an id loses the way back to its store. Voyager guards that at launch. If identities.json cannot be read, the run starts with a fresh book in memory, says so in a notice ("This run uses a fresh one and leaves the file alone"), and never writes to that file for the rest of the run. The damaged file keeps the old ids for whoever repairs it, and IdentityBook's own comment calls the file "Safe to rebuild by hand."