Files
relicario/docs/superpowers/specs/2026-04-27-relicario-vault-tab-design.md
adlee-was-taken 39ae2ecbf3 style: capitalize "Relicario" in prose / UI / CLI help
Brand name uses capital R in user-facing text — extension UI strings,
CLI clap help / descriptions / error prose, markdown docs. Lowercase
preserved for the binary command, crate names, npm package, file
paths, env vars, and code identifiers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 17:29:10 -04:00

7.4 KiB

Vault Tab UI + Session Timeout — Design Spec

Date: 2026-04-27 Scope: New vault.html full-tab UI, shared session timeout, popup↔vault navigation

Problem

Chrome extension popups close when focus leaves them (e.g., file picker dialogs). The popup is also too cramped for complex operations like editing identity/card items, managing attachments, or bulk vault operations. Currently we work around this with popOutToTab() which opens popup.html in a tab — a hack that reuses popup-sized UI in a full window.

Additionally, there's no session timeout — users must re-enter their passphrase every time they interact with the extension.

Design

Two entry points, one shared core

  • popup.html — quick access: search, copy, autofill, add login/secure_note (without attachments)
  • vault.html — full "desktop" UI in a browser tab: sidebar + detail pane, handles everything including attachments, bulk operations, trash, devices, settings, field history

Both talk to the same service worker, share the same WASM session handle and unlock state.

vault.html layout

Sidebar + detail pane, similar to 1Password's desktop app:

┌──────────────────────────────────────────────────┐
│  🔒 Relicario              [lock] [settings]     │
├────────────────┬─────────────────────────────────┤
│  [search...]   │                                 │
│                │   (detail view for selected     │
│  ── logins ──  │    item, or form when           │
│  GitHub    🔑  │    adding/editing)              │
│  AWS       🔑  │                                 │
│                │                                 │
│  ── notes ──   │                                 │
│  Recovery  📝  │                                 │
│                │                                 │
│                │                                 │
│                │                                 │
├────────────────┤                                 │
│  🗑 trash      │                                 │
│  📱 devices    │                                 │
│  ⚙ settings   │                                 │
└────────────────┴─────────────────────────────────┘
  • Left sidebar (~240px): vault name/lock status at top, search input, item list grouped by type, nav links at bottom (trash, devices, settings)
  • Right pane: detail view for selected item, or add/edit form. Empty state when nothing selected.
  • URL hash tracks current selection (#item/abc123, #add/login, #trash, etc.) for browser back/forward

Session timeout

Lives in the service worker, not in any UI. Shared across popup and vault tab.

Timer logic — new session-timer.ts module alongside existing session.ts:

  • Holds a setTimeout ID, reads config from chrome.storage.local
  • Resets on every message routed through the SW (any popup or vault tab interaction)
  • When it fires: calls clearCurrent() to zero the WASM handle, then broadcasts { type: 'session_expired' } via chrome.runtime.sendMessage
  • Both popup and vault tab listen for this broadcast and show the lock screen

Config shape in chrome.storage.local:

{ "session_timeout": { "mode": "inactivity", "minutes": 15 } }

or:

{ "session_timeout": { "mode": "every_time" } }

Default: { mode: 'inactivity', minutes: 15 }. This is a per-device setting (stored in chrome.storage.local, not in the encrypted vault) since different devices have different risk profiles.

UI for timeout config: In a "device settings" section, a simple toggle:

  • "Lock after inactivity" with a minutes dropdown (5, 15, 30, 60)
  • "Lock every time" (current behavior)

Changing the setting sends an update_session_config message to the SW which immediately applies the new timer.

Navigation between popup and vault

Popup → vault:

  • "Open vault" link on the lock screen and item list toolbar
  • Shift+F keydown listener in popup — opens/focuses the vault tab
  • When navigating from popup with context (e.g., viewing an item), pass item ID via URL: vault.html#item/abc123
  • popOutToTab() now redirects to vault.html instead of popup.html for types that need it

Global shortcut:

  • chrome.commands manifest entry (default unbound, user configures in chrome://extensions/shortcuts)
  • SW listener opens or focuses existing vault tab

Vault → popup:

  • Not needed — vault tab is the superset

Shared components

Form renderers (login, secure-note, identity, card, key, totp, document), field helpers, attachments disclosure, generator panel are currently in popup/components/. These get moved to shared/components/ so both entry points can import them.

The popup wrappers conditionally hide attachments (via isInTab()); the vault versions always show everything.

Keyboard shortcuts

Key Context Action
/ Popup list, vault sidebar Focus search
+ Popup list, vault sidebar New item
↑↓ Popup list, vault sidebar Navigate items
Enter Popup list, vault sidebar Open selected item
Escape Popup Close popup
Escape Vault form/detail Back to list
Shift+F Popup Open/focus vault tab
Global Anywhere in Chrome Open/focus vault tab (user-configured)

New files

extension/
├── src/
│   ├── vault/
│   │   ├── vault.ts           # Entry point, state management, hash routing
│   │   ├── vault-shell.ts     # Layout container, sidebar/pane split
│   │   ├── vault-sidebar.ts   # Search, grouped item list, nav links
│   │   └── vault-pane.ts      # Detail/form/settings renderer
│   ├── shared/
│   │   └── components/        # Moved from popup/components/
│   │       ├── types/         # login.ts, secure-note.ts, etc.
│   │       ├── fields.ts
│   │       ├── attachments-disclosure.ts
│   │       └── generator-panel.ts
│   ├── service-worker/
│   │   └── session-timer.ts   # Inactivity timeout logic
│   └── popup/
│       └── components/        # Thin wrappers that import from shared/
├── vault.html                 # New entry point
└── vault.css                  # Vault-specific layout styles (imports shared)

What stays in popup

The popup keeps its stacked-view navigation and compact layout. It imports form/detail components from shared/ but wraps them in popup-specific chrome (back buttons, condensed headers). Login and secure_note forms render inline in the popup (without attachments); all other types redirect to vault.html.

Messages

New message types:

  • update_session_config — popup/vault → SW, updates timeout settings
  • get_session_config — popup/vault → SW, reads current timeout settings

New broadcast:

  • session_expired — SW → all extension views, triggers lock screen

Out of scope

  • Grouping/tagging/export features (future work, mentioned as eventual goal)
  • Mobile-style responsive layout for vault tab
  • Theme customization
  • Multi-vault support