Skip to content

Browser sessions get a vault: encrypted versioned payloads behind a named registry - #242

Open
czpython wants to merge 1 commit into
browser-imagefrom
session-vault
Open

Browser sessions get a vault: encrypted versioned payloads behind a named registry#242
czpython wants to merge 1 commit into
browser-imagefrom
session-vault

Conversation

@czpython

Copy link
Copy Markdown
Owner

What

Second phase of browser sessions as a druks primitive (stacked on #241). The vault: named sessions, encrypted versioned payload storage, the API and settings pane, and a bootstrap import path.

  • Model: BrowserSession — unique slug, status (needs_login | ready | stale), state format (storage_state | profile_dir), active_version, site hint, refresh/use timestamps. Status transitions: create → needs_login, successful upload → ready.
  • Payload store: no cross-store transaction exists between the DB and files, so payloads are immutable AES-GCM envelope files, one per version, under <data_dir>/browser_sessions/<id>/<version>.bin. AAD binds session id + version, so an envelope can't be replayed across sessions or versions. A write lands the file first (0600 temp → fsync → rename), then one compare-and-set UPDATE moves the active pointer; a concurrent writer loses the CAS and gets a typed conflict. The startup reaper removes aged orphan envelopes and stray temp files.
  • Size guard: hard 256 MB cap (checked while streaming the upload and again at the vault door), warning log from 200 MB.
  • API: mutations (create, upload-state, rename, delete) admit the session identity only — same boundary as capability management, enforced in the route-boundary test. Reads stay on the standard account dependency. No download endpoint: state leaves the backend only into a sandbox. Delete commits the row removal before deleting files, so a crash can orphan files but never leave a live row pointing at deleted state.
  • Settings pane: Browser sessions — list with status/last-refreshed/last-used, create, rename, delete. Wire carries identity and status enums only; all copy lives in the frontend.
  • Bootstrap import: scripts/import_browser_session.py opens a headed local Chromium via playwright (not a druks dependency — the script degrades with install instructions), waits for the operator to log in, captures storage_state, and uploads it through the session-authed API. This makes sessions usable before the login window phase.
  • Docs: data-dir contents and key-custody sections cover the vault.

Testing

  • Crash injection at both CAS points: envelope renamed but pointer never moved → reaper clears the orphan, active state intact; DB update rolled back after the file landed → pointer unmoved, orphan reaped.
  • Import → ready → ciphertext unreadable without the key and under a foreign AAD → survives session-cache reset → delete removes row and files.
  • Bearer-PAT requests to every mutation route are rejected; reads succeed.
  • Cap and warning thresholds; reaper keeps the active envelope and clears superseded ones.
  • Frontend: pane test plus settings-modal integration; 101 FE tests, eslint, and production build pass locally. Backend suite runs in CI (shared-DB gated).

…amed registry
A browser session is now a first-class row — unique slug, status
(needs_login / ready / stale), state format, and an active payload
version. Payloads live as immutable AES-GCM envelope files under the
data dir, one per version, authenticated against the session id and
version so an envelope can't be replayed across sessions or versions.
A write lands the envelope (0600 temp, fsync, rename) before a single
compare-and-set UPDATE moves the active pointer; a crash on either
side leaves an orphan file or an untouched pointer, and the startup
reaper clears aged orphans and stray temp files. Uploads cap at
256 MB and log a warning from 200 MB.
Mutation routes admit the session identity only, the same boundary as
capability management; reads take the standard account dependency.
There is no download route — state leaves the backend only into a
sandbox. The settings modal gains a Browser sessions pane (list,
create, rename, delete), and a bootstrap script captures a headed
local login as storage_state and imports it through the API until the
login window ships.
Sign up for freeto 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

@czpython