diff --git a/extension/src/service-worker/index.ts b/extension/src/service-worker/index.ts index 1a4bc42..8381ae5 100644 --- a/extension/src/service-worker/index.ts +++ b/extension/src/service-worker/index.ts @@ -122,8 +122,12 @@ async function handleMessage(req: Request): Promise { const handle = w.unlock(req.passphrase, imageBytes, meta.salt, meta.paramsJson); session.setCurrent(handle); - // Clear passphrase from scope best-effort. - // (JS strings are immutable; the message object goes out of scope after return.) + // Best-effort scope clearing. JS strings are immutable so this only + // nulls the one reference on `req`; wasm-bindgen already copied the + // string into WASM linear memory before `unlock` returned. Left in + // as a marker — future work may consider a direct-stream KDF that + // never materializes the string, or a `Zeroizing` wrapper on the + // WASM-side incoming buffer. (req as { passphrase: string }).passphrase = ''; manifest = await vault.fetchAndDecryptManifest(git, handle); @@ -178,6 +182,10 @@ async function handleMessage(req: Request): Promise { const entry = manifest.items[req.id]; if (!entry) return { ok: false, error: 'item_not_found' }; // Soft-delete: fetch the item, set trashed_at, write it back. + // TODO(slice-4): not atomic across the two git writes. If the manifest + // write fails after the item write, next sync restores the live manifest + // and the trashed item re-appears. Consider manifest-first write order + // or a retry/rollback pass when router-splitting this handler. const item = await vault.fetchAndDecryptItem(gitHost, handle, req.id); const now = Math.floor(Date.now() / 1000); const updated: Item = { ...item, trashed_at: now, modified: now }; @@ -253,6 +261,12 @@ async function handleMessage(req: Request): Promise { } } +/// TS mirror of the Rust core's `ManifestEntry::from_item` +/// (see crates/relicario-core/src/manifest.rs). These two derivations must +/// stay in sync — if the Rust side learns to handle e.g. trailing-whitespace +/// URL parsing differently, this function drifts and the manifest diverges +/// between CLI-written and extension-written items. A future WASM helper +/// `item_to_manifest_entry(handle, item_json)` would eliminate the duplication. function itemToManifestEntry(item: Item) { return { id: item.id, diff --git a/extension/src/service-worker/session.ts b/extension/src/service-worker/session.ts index e6af83e..89d0992 100644 --- a/extension/src/service-worker/session.ts +++ b/extension/src/service-worker/session.ts @@ -3,6 +3,10 @@ /// α assumes one vault per extension install. The master key lives only /// inside WASM linear memory (wrapped in Zeroizing<[u8;32]>); this module /// just holds the opaque handle that names it. +/// +/// Future multi-vault (β+) would replace `current` with +/// `Map` and thread `vaultId` through every +/// handler. Deliberate α simplicity — not an oversight. import type { SessionHandle } from '../../wasm/relicario_wasm'; diff --git a/extension/src/service-worker/vault.ts b/extension/src/service-worker/vault.ts index e978d0e..044ede8 100644 --- a/extension/src/service-worker/vault.ts +++ b/extension/src/service-worker/vault.ts @@ -124,6 +124,13 @@ export function searchItems( }); } +/// Match manifest entries against a page hostname. +/// +/// icon_hint is derived by the Rust core (crates/relicario-core/src/manifest.rs) +/// from LoginCore.url's hostname, so equality on icon_hint is the cheapest match. +/// α is intentionally coarse: no www.-stripping, no public-suffix matching +/// (`www.github.com` saved items will not match `github.com`, and vice versa). +/// Tighter matching is a 1C-β/γ concern. export function findByHostname( manifest: Manifest, hostname: string, @@ -132,6 +139,4 @@ export function findByHostname( return (Object.entries(manifest.items) as Array<[ItemId, ManifestEntry]>) .filter(([, e]) => e.trashed_at === undefined) .filter(([, e]) => (e.icon_hint ?? '').toLowerCase() === h); - // icon_hint is derived by Rust core from LoginCore.url's hostname, - // so hostname equality on icon_hint is the cheapest match. }