Skip to content

client: path-scope vault BroadcastChannel under isolate_storage#69

Merged
dolonet merged 2 commits into
mainfrom
fix/vault-bc-path-scope
May 17, 2026
Merged

client: path-scope vault BroadcastChannel under isolate_storage#69
dolonet merged 2 commits into
mainfrom
fix/vault-bc-path-scope

Conversation

@dolonet
Copy link
Copy Markdown
Owner

@dolonet dolonet commented May 17, 2026

Summary

new BroadcastChannel('websh_vault') used a static name across the origin, so under isolate_storage two tabs at different URL paths shared the channel. A sign-out on /team-a/ would reach /team-b/, whose handler tears down live vault panes, invalidates the in-memory caches, and sets _vaultRecentlySignedOut — blocking the unrelated tenant's active session and refusing subsequent saves until doConnect clears the flag.

User-visible impact (under isolate_storage): tenant on one path signs out → unrelated tenant's vault panes drop with a "Vault key missing" reconnect bar, and any in-flight save in their tab silently aborts.

Fix

Include storagePrefix in the channel name. Module init opens 'websh_vault:' (storagePrefix='' before /api/config resolves), then loadServerConfig re-inits with the now-known prefix — a no-op when isolate_storage is off, a distinct channel when it's on. _initVaultBroadcast became idempotent on name match so the bfcache pageshow restore and the loadServerConfig call don't double-mint.

The eager module-init open is kept so the brief boot window still catches sign-outs (with the default name); the re-init upgrades to the path-scoped name as soon as the config is in.

Tests

Two new regressions:

  • vault BroadcastChannel name is path-scoped under isolate_storage — asserts module-init opens 'websh_vault:' and loadServerConfig re-opens with 'websh_vault:/team-a/'.
  • sibling tab on a different path does NOT trigger sign-out handler — uses a name-filtering BroadcastChannel mock (more faithful to real BC semantics than the permissive mock used in the existing tests) and asserts the cross-path signed_out leaves _idbHasKeyCache and _vaultRecentlySignedOut untouched. Same-path sanity-check confirms scoping is the discriminator, not a global block.

Two existing same-channel tests updated to use the new 'websh_vault:' name (storagePrefix empty under default config). 497 frontend tests pass (was 490 before this PR).

Test plan

  • cd tests/frontend && node test_connect.js — 497 / 0
  • python3 test_server.py -q — 420 / 0 (unchanged; client-only fix)
  • Manual: spin up two paths under isolate_storage, sign out on one, verify other tenant's session stays alive

Refs #67 (follow-up from holistic review).

dolonet added 2 commits May 17, 2026 18:51
new BroadcastChannel('websh_vault') used a static name across the
origin, so under isolate_storage two tabs at /team-a/ and /team-b/
shared the channel. A sign-out on /team-a/ would broadcast into
/team-b/, where the handler tears down live vault panes, invalidates
the in-memory caches, and sets _vaultRecentlySignedOut — blocking
the unrelated /team-b/ tenant's active session and refusing
subsequent saves until doConnect clears the flag.

Fix: include storagePrefix in the channel name. Module init opens
'websh_vault:' (storagePrefix='' until /api/config resolves), then
loadServerConfig re-inits with the now-known prefix — a no-op when
isolate_storage is off, a distinct channel when it's on. _initVault-
Broadcast became idempotent on name match so the bfcache pageshow
restore and the loadServerConfig call don't double-mint.

Tests: two new regressions pin the contract — the channel name
includes the URL path under isolate_storage, and a cross-path
sibling's signed_out does NOT reach this tab's handler (with a
same-path sanity-check that scoping is the discriminator, not a
global block). Existing same-channel sign-out tests updated to use
the new name. 497 frontend tests pass (was 490).
Code-review nit: align the path-scoped channel-name shape with the
existing storageKey() / _idbScopedKey() convention (prefix-then-name).
The new shape is 'websh_vault' under the empty default and
'/team-a/websh_vault' under isolate_storage — equivalent uniqueness,
consistent with the rest of the codebase.

Side benefit: the empty-prefix name reverts to plain 'websh_vault',
so the two existing same-channel tests in this file don't need their
mock-name strings updated.
@dolonet dolonet merged commit 6fd6084 into main May 17, 2026
5 checks passed
@dolonet dolonet deleted the fix/vault-bc-path-scope branch May 17, 2026 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant