Two programs that must trust each other
In a Chromium browser, 1Password is two programs: the extension inside the browser and the desktop app beside it. They meet over native messaging, and the desktop app's helper verifies the browser before it answers anything. For a browser it does not already know, the desktop app offers Add browser. Dad uses 1Password, so it was the first case on Voyager's engine gate, W1, before a single brick was laid.
This lesson is the browser's side of that story, told as a debugging case study. It ends where the work really ended, which is not at a fix.
What passed, wall by wall
| Wall | What stood in the way | What removed it |
|---|---|---|
| Missing Chrome names | The background stopped at the first name WebKit leaves out | The compatibility layer (lesson 2) |
| The signature | The helper could not read the family-signed build's signature; 1Password's core checks for a hardened-runtime signature with bundle and team identifiers under Apple's chain | The installed bundle signed with the paid team's Apple Development identity and the hardened runtime. Dad added Voyager through Add browser, and 1Password lists it as a connected browser. Track 2's gate lesson tells that result; track 9 teaches the signing |
| The process group | Every host the bridge started was refused once a minute | posix_spawn in the browser's own group (lesson 3) |
| The direct parent | A relay in between was refused by a second rule | No intermediate: the app itself is the parent (lesson 3) |
| The extension page | The chrome-extension:// address was dropped, its scheme cancelled, and the page refused without the context's own configuration | pageURL(for:) and a page built with that configuration (lesson 3) |
| The fragment | The extension's app page opened as a dark tab | The fragment preserved, with a regression test (below) |
After the fourth row the helper logged that it was verifying Voyager and waiting for partner authorization. That wait is for a message the extension's own background sends when its app page asks it to, which is why the last two rows mattered.
The dark page and the fragment
The app page first showed up as a dark tab titled "1Password". An early note read that as the extension's front-end app failing to mount, and named a framework for it. A later inspection of the installed package corrected both claims. The app's entry point is React with a hash router: it has no route for /, and its welcome route is #/page/welcome. Voyager's pageURL(for:) had been building the page URL without the fragment, so the router had nowhere to go. The probe used to check it had opened the bare app/app.html too, so the instrument shared the bug it was looking for.
The mapping now carries the fragment over with its encoding intact (first code block), and anExtensionPageKeepsItsFragmentAndRendersItsHashRoute holds it: absent, empty and encoded fragments, a route's own query inside the fragment, and a fixture page that renders only when its hash route arrives (second code block). The house sign keeps the lesson in one sentence: "A dark page alone does not establish that its JavaScript failed to mount."
The dead toolbar button got its explanation the same way. Unpaired, 1Password's background clears its own pop-up a few seconds after it loads, so a click belongs to the background's click event, which shows nothing until pairing completes. The dead button was the unpaired state showing through.
What did not pass
Authentication through the extension did not hold. The pop-up and filling a login were never seen working in Voyager. And while the extension stays unpaired it opens a fresh native port every minute, so waiting helpers pile up on the Mac.
One bounded diagnosis followed, with its limits written before it ran: a hidden probe on public API only, fresh isolated controller and data stores, no desktop bridge, no real tabs, no images captured, and no credentials, vaults or protocol bodies read. It ruled one hypothesis out. WebKit gives each extension context a generated origin that changed between processes, yet data stored under it survived a second process, so a changing origin alone does not explain lost sign-in state on that Mac. The public package also rendered its welcome route in an empty profile. Neither finding is a cause. The record says it in its own words: "The actual root cause and a precise corrective patch are not established."
Stopping is part of the diagnosis
Dad's stopping condition ended it there. The attempt is finished, 1Password is parked last with no further hypothesis-and-repair cycles, and Home, accounts, Incognito and Pippa go ahead without it. The record also fences the next attempt: callback, action-state and completion metadata only, or a check Dad assists, and no new install, restart, trust grant or credential use inferred from anything written there. No Voyager commit since has touched 1Password.
So two documents say two true things. The plan and the gate say 1Password lists Voyager as a connected browser. The diagnosis says the integration is unresolved. They describe different halves. One decision from this work stayed: on cwkPippa's own pages, 1Password's content script took over navigator.credentials, and a passkey could not be saved while its pairing was open. That case is why every extension is now kept off the family's hosts (track 6). The next day Dad ruled in the fallback, and the last lesson is that.