Files
sovereign_browser/plans/per-user-profiles.md

5.4 KiB

Per-User Profile Directory System

Problem

When logging in as a new user (different nsec), the browser:

  1. Restores the previous user's tabs (session restore from global SQLite)
  2. Shows the previous user's Nostr events on the profile page
  3. Shows the previous user's bookmarks, history, and recents

Solution

Create a per-user profile directory keyed by npub, with each user getting their own SQLite database, session, bookmarks, and history.

Directory Structure

~/.sovereign_browser/
├── identity.json          # last used npub (for login dialog default)
├── global.db              # global settings (agent server, theme, shortcuts, inspector)
└── profiles/
    ├── npub1abc.../
    │   ├── browser.db     # per-user: events, history, session, bookmarks, key_value
    │   └── identity.json  # this user's nsec/npub
    ├── npub1def.../
    │   ├── browser.db
    │   └── identity.json
    └── ...

What Stays Global vs Per-User

Global (in ~/.sovereign_browser/global.db)

  • agent_server_enabled, agent_server_port, agent_allowed_origins, agent_login_timeout_ms
  • theme_dark
  • shortcut.* (keyboard shortcuts)
  • inspector_x/y/w/h
  • dev_extras, file_access, universal_access (security settings — these are WebKit-level, not per-user)

Per-User (in ~/.sovereign_browser/profiles/<npub>/browser.db)

  • restore_session, new_tab_url, tab_bar_position, show_tab_close_buttons, middle_click_close, ctrl_tab_switch, max_tabs, tab_drag_reorder
  • bootstrap_relays, search_engine
  • Session (open tabs)
  • History
  • Bookmarks
  • Nostr events (kind 0, 3, 10002)
  • key_value table (per-user settings)

Implementation Steps

Step 1: Add db_init_with_path(const char *path) to db.c

Currently db_init() hardcodes ~/.sovereign_browser/browser.db. Add a variant that takes a path. Keep db_init() for the global database.

Step 2: Add profile directory management

New functions in a profile.c module (or add to key_store.c):

  • profile_get_dir(const char *npub, char *out, size_t out_sz) — returns ~/.sovereign_browser/profiles/<npub>/
  • profile_ensure_dir(const char *npub) — creates the directory if it doesn't exist
  • profile_get_db_path(const char *npub, char *out, size_t out_sz) — returns ~/.sovereign_browser/profiles/<npub>/browser.db
  • profile_get_identity_path(const char *npub, char *out, size_t out_sz) — returns ~/.sovereign_browser/profiles/<npub>/identity.json

Step 3: Split settings into global and per-user

In settings.c:

  • settings_load_global() — loads from global.db (agent server, theme, shortcuts, inspector, security)
  • settings_load_user() — loads from the per-user browser.db (session, tabs, relays, search)
  • settings_save_global() — saves global settings to global.db
  • settings_save_user() — saves per-user settings to browser.db

Or simpler: keep one settings_load() / settings_save() but have it read from the appropriate database based on which keys are global vs per-user.

Step 4: Change the login flow in main.c

Current flow:

  1. db_init() — opens global browser.db
  2. settings_load() — loads all settings from browser.db
  3. shortcuts_load() — loads shortcuts from browser.db
  4. Login (GTK dialog or agent login or CLI)
  5. relay_fetch() — fetches events from relays
  6. session_restore() — restores tabs from browser.db

New flow:

  1. db_init_global() — opens global.db for global settings
  2. settings_load_global() — loads global settings (agent server, theme, shortcuts, inspector)
  3. shortcuts_load() — loads shortcuts from global.db
  4. Login — get npub
  5. profile_ensure_dir(npub) — create ~/.sovereign_browser/profiles/<npub>/
  6. db_init_user(npub) — close global.db, open profiles/<npub>/browser.db
  7. settings_load_user() — load per-user settings from browser.db
  8. relay_fetch() — fetch events from relays into per-user browser.db
  9. session_restore() — restore tabs from per-user browser.db

Step 5: Migration

On first run with the new system:

  • If ~/.sovereign_browser/browser.db exists and ~/.sovereign_browser/profiles/ doesn't:
    • After login, copy browser.db to profiles/<npub>/browser.db
    • Move global settings from browser.db to global.db
    • Keep browser.db as backup (or rename to browser.db.old)

Step 6: Identity persistence

  • ~/.sovereign_browser/identity.json — stores the last-used npub (not nsec)
  • ~/.sovereign_browser/profiles/<npub>/identity.json — stores the nsec/npub for that profile
  • On startup, read the last-used npub from global identity.json to pre-fill the login dialog

Files to Modify

  1. src/db.c / src/db.h — add db_init_with_path(), db_close(), db_init_global()
  2. src/settings.c / src/settings.h — split into global/per-user load/save
  3. src/main.c — change the startup flow
  4. src/key_store.c — update identity persistence to per-profile
  5. src/session.c — no change (already uses db_session_* which will use the per-user db)
  6. src/shortcuts.c — load from global.db instead of browser.db
  7. New: src/profile.c / src/profile.h — profile directory management

Risk Assessment

  • Medium risk — changes the database initialization flow which affects all data access
  • Migration needed — existing users need their data moved to per-profile dirs
  • Testing needed — login with different users, verify data isolation