Skip to content

Runs borrow browser sessions: materialize, exclusive persist, stale parking - #243

Open
czpython wants to merge 1 commit into
session-vaultfrom
session-borrow
Open

Runs borrow browser sessions: materialize, exclusive persist, stale parking#243
czpython wants to merge 1 commit into
session-vaultfrom
session-borrow

Conversation

@czpython

Copy link
Copy Markdown
Owner

What

Third phase of browser sessions as a druks primitive (stacked on #242). The borrow contract: a run gets a logged-in browser materialized from the vault, with read-only parallelism, exclusive persistence, and first-class staleness.

  • Author surface: browser_session (a name; override get_browser_session() to resolve per run) and persist_session on the Workflow — the same declaration pattern as steps_reuse_sandbox. Declaring a borrow makes the run's sandboxes browser-flavored via the P1 client flag, both for the warm host and for per-call ephemeral VMs.
  • Lifecycle: around each agent call's workspace construction, druks decrypts the active payload to a 0600 staging file, uploads it under /work/session/ with state.meta.json, and scrubs /work/session/ again before the agent starts — the extension's deterministic script (which itself invokes session-launch) is the only thing that ever drives the logged-in browser; the agent sees extracted data only. A warm host is also scrubbed for runs that never borrowed, so a host left dirty by a dead worker is clean before its next use. Staging temp files are removed in finally and the boot reaper sweeps stragglers.
  • Writer lock: one Redis family browser_session:<id>, shared with the future login window. Owner token, TTL renewed by a background holder task, atomic compare-and-delete release (Lua) — a stale holder cannot delete the new holder's lock. Read-only borrows never lock and overlap freely.
  • Write-back (persist only, and only when a browser actually ran this call): session-export closes the browser and produces the archive in-sandbox, then the vault's envelope + compare-and-set pointer move — the same path P2 uploads take. A pointer that moved since the borrow raises the typed conflict, which lands as the run's terminal failure (durably, visible after restart); the session stays ready. Read-only borrows close the launcher without exporting.
  • Staleness: workflow.browser_session_invalid() marks the borrowed session stale (as a replay-safe step) and parks the run on the ordinary review() gate; the operator reconnects in settings and answers approve. No auto-resume.
  • Memory: payload-sized decrypt/encrypt runs off the event loop under a single memory slot, so concurrent max-size borrows serialize instead of multiplying peak web-process memory.
  • last_used_at bumps per borrow; docs gain a borrow section in the extension guide.

Testing

  • Parallel read-only borrows materialize identical state and never touch the lock family.
  • Writer lock: renewal past TTL under simulated time; expiry after worker death admits a successor; a stale holder's release leaves the successor's token in place (real-Redis Lua coverage).
  • Persisting borrow holds the lock through export + CAS; the exported profile becomes the active version and the format flips to profile_dir.
  • Stale write-back: the conflict lands as the run's durable failure code and workflow.failed event after the session cache is dropped; the newer version stays active.
  • Warm-host scrub: a later run without a borrow finds /work/session/ empty after a dead worker left state behind.
  • Agent isolation: workspace construction sees the materialized state, the agent's workspace does not.
  • Bounded memory slot, 0600 staging files, reaper coverage. Backend suite runs in CI (shared DB + Redis).

…arking
A workflow declares browser_session (a name; get_browser_session() for
per-run resolution) and optionally persist_session. The run's sandboxes
become browser-flavored, and around each agent call's workspace
construction druks decrypts the active payload, stages it under
/work/session/ with its meta, and scrubs it again before the agent
starts — the deterministic extension script is the only thing that ever
drives the logged-in browser, and a reused warm host is scrubbed even
when the run never borrowed.
Read-only borrows run in parallel and close the launcher without
exporting. A persisting borrow holds the session's Redis writer lock —
owner token, TTL renewed while the sandbox lives, compare-and-delete
release so a stale holder can never free the new holder's lock — and
writes back only when a browser actually ran: session-export in the
sandbox, then the vault's envelope + compare-and-set pointer move. A
pointer that moved since the borrow raises the typed conflict, which
lands as the run's terminal failure and survives restart; the session
stays ready.
workflow.browser_session_invalid() marks the borrowed session stale and
parks the run on the ordinary review() gate; the operator reconnects
and answers approve. Payload-sized crypto runs off the event loop under
one memory slot, plaintext staging files are 0600 and reaped at boot.
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