Skip to content

Sessions and State

Sam LABBE edited this page Oct 13, 2026 · 1 revision

Sessions and State

navette's session model: named, lazily-created, isolated contexts inside one daemon. One session per site or per task; cookies persist between calls inside the session; nothing persists across daemon restarts unless you explicitly export it.

The model

  • You never "open" a session — any route's optional "session" field creates it on first use (default: "default").
  • GET /sessions lists live sessions with their url + title.
  • POST /sessions/close {"session":S} closes one. Etiquette: only close sessions you created — another workflow may depend on an existing one (the shared daily.dev session feeding n8n is the canonical example).
  • Ghost windows: sessions live off-screen. A real OS window is attached only when needed — a /screenshot, or a session_show. This is why 79 MB peak RAM beats Chromium's 599 MB.

Isolation — CI-enforced on every engine

Engine Mechanism
macOS non-persistent WKWebsiteDataStore per session
Windows one WebView2 profile per session + InPrivate (InPrivate alone shares one profile across controllers — measured, issue #6; the named profile is what isolates cookies/storage/cache)
Linux fresh WebKitGTK WebContext per session; cookies never touch disk

The CI assertion: a cookie imported into session A must not appear in session B's exported jar, or the build fails — on all three OSes.

Known residual on Linux: HTTP cache and HSTS state still land in ~/.cache/navette and ~/.local/share/navette (no cookies, no logins — a fingerprint/disk surface only; wry 0.57 has no ephemeral-context lifetime fix yet, tracked in issue #6). See Security.

Human moments — session_show / session_hide

One-time human steps happen on-screen, then the machine takes over again:

curl -s -X POST http://127.0.0.1:8765/sessions/show -d '{"session":"work"}' -H 'Content-Type: application/json'
# the ghost becomes a titled, keyable, centered window with its page state intact —
# the human does the OAuth / 2FA / captcha / ToS click
curl -s -X POST http://127.0.0.1:8765/sessions/hide -d '{"session":"work"}' -H 'Content-Type: application/json'
# parked off-screen again; session and state untouched

Design rules that come with it:

  • Never solve a captcha, never try to bypass one. session_show IS the answer — the human takes over. This is a hard policy, not a suggestion.
  • On macOS, show_window is also the focus gate for native key input: the window becomes the key window (confirmed via isKeyWindow) before any CGEvent is posted, so a keystroke can never land in someone else's app.

State portability — cookies as the unit of login

Logged-in state moves explicitly, never implicitly:

# export the current jar (JSON — the Playwright storageState equivalent)
curl -s -X POST http://127.0.0.1:8765/sessions/state -d '{"session":"work"}' -H 'Content-Type: application/json'

# better: park it in the OS keychain — the jar never crosses the wire
navette state save devto --session work

# restore into a fresh session (any run, any day)
curl -s -X POST http://127.0.0.1:8765/sessions/load -d '{"session":"corpus","store":"devto"}' -H 'Content-Type: application/json'

# inventory and cleanup
curl -s http://127.0.0.1:8765/states
navette state delete devto
  • Nothing persists a session's cookies across restarts on any engine — state moves only through state_export/state_import (token-gated when --token is on).
  • macOS keychain ACLs re-prompt when the binary changes — an implicit per-release consent to keychain access.
  • state_export returns live session cookies — it is the product (portability) and the risk (jar exfiltration by any local process when no --token is set). The loopback trust model covers this; --token closes it.

Viewport

curl -s -X POST http://127.0.0.1:8765/sessions/viewport -d '{"width":1440,"height":900,"session":"work"}' -H 'Content-Type: application/json'

Default 1280x800. Set it before navigating for repeatable screenshots — capture is viewport-sized by design (full-page capture is not implemented; see Roadmap and Known Gaps). In containers, NAVETTE_SIZE=1920x1080 sets the boot size process-wide (see Docker).

Clone this wiki locally