U6 — The vault as the extension's storage layer
An earlier draft of this pack compressed U3's remote-code finding into "the platform prohibits remotely hosted code entirely" and let it read as though vaults were unavailable to an extension. That was wrong — this pack's error, not the brief's — and it is recorded here rather than quietly fixed, per guide 01 §3: the corrected version is a better design than either document had alone.
The constraint and the capability are different things
Remotely hosted code is prohibited; remotely fetched data is ordinary. A vault is encrypted bytes over HTTP. An extension that bundles its own client, fetches vault objects, decrypts them with in-package code and renders the result is doing nothing the platform restricts — it is fetch() plus local logic, which is what every well-behaved extension does.
Why this is the answer to U4's problem, not a workaround
U4's whole difficulty is storage. The synchronised store gives cross-device sync for free and then imposes 102,400 bytes across 512 items, none above 8,192, is not stated to be encrypted, and carries vendor guidance steering sensitive data elsewhere. U4's resolution — carry ciphertext and nothing else — is correct and still leaves the extension inside a hundred-kilobyte budget it did not choose.
A vault removes the ceiling rather than working around it:
| Synchronised store | Vault | |
|---|---|---|
| Capacity | 102,400 bytes / 512 items | unbounded |
| Encryption | not stated | zero-knowledge by construction |
| History | none | versioned, git-like |
| Reach | one browser vendor's profile | any browser, any device, any agent |
| Who could read it | the vendor, unverifiably | nobody upstream, by design |
The composition writes itself. The sync store holds a small handle — a vault id and a wrapped key comfortably inside the 8,192-byte item limit — and the vault holds everything else. The passphrase never leaves the device; the derived key unwraps the handle; the handle reaches the vault. U4's "untrusted transport" framing gets stronger, not weaker: the store now carries a reference, not even the payload.
And it upgrades U3's "hold a channel, not a key" from a limitation into a general pattern. A write-only channel to a vault is exactly a durable, encrypted, cross-device outbox that no browser vendor mediates.
The consequence for the estate
This makes a vault a first-class extension capability, not an integration. Every extension developer has the same unsolved problem — storage that is encrypted, larger than a sync quota, and present on all of a user's machines — and the browser platforms do not offer one. The estate already ships it.
An extension is one of the most natural vault clients there is, because it is exactly the kind of software that needs state it cannot keep locally and must not hand to a vendor. See guide 04, scoped to this project, for the code/data split this composes with.
The design details this raises — flagged, not settled
- Cross-origin access. An extension fetching a vault API needs
host_permissionsfor that one origin (or CORS from the server). This does not conflict with U3'sactiveTabrecommendation — they answer different questions:activeTabis how the extension reads the page, one named host permission is how it reaches its own backend. One declared host is a far narrower ask than<all_urls>and does not attract the broad-host review delay. - The crypto must be in the package. Bundled, not fetched — the remote-code rule applying where it genuinely does.
- Offline and conflict behaviour. A vault is versioned; two browsers writing the same vault need the estate's existing merge story, and the extension needs to behave when the vault is unreachable. Neither is new, but neither has been written down for this surface.