Tabs, profiles and sessions
Profiles, tabs, incognito, cookies and storage, uploads and downloads, and the trace.
jawbd holds this hierarchy: daemon → profiles → sessions → tabs.
The CLI is stateless — everything below lives in the daemon, so
jawb click 48 from one terminal works right after jawb observe from
another.
Profiles
A profile is a named, persistent, encrypted-at-rest storage
partition (cookies, localStorage, cache, permissions). Every persistent
profile is encrypted, including default — there's no opt-in and no
unencrypted option. The key is wrapped by an OS credential store
(Windows DPAPI, macOS Keychain, Linux Secret Service), or by
--vault-keyfile <path> if none is available.
$ jawb profile list
* default encrypted, 0 tabs, not created yet
$ jawb profile create work
created profile work (encrypted: windows-dpapi (current user))
$ jawb profile use work
current profile: work (was default)
$ jawb profile delete work --yes
default cannot be deleted. Profile names match
^[a-z0-9][a-z0-9_-]{0,31}$.
Tabs and navigation
$ jawb open https://example.com
t_bexe91 Example Domain https://example.com/ (3 elements)
navigated -> https://example.com/ (3 elements; run `jawb observe` for the page)
rev 0 → 1 (native, 629ms)
$ jawb tabs
* t_bexe91 rev 1 native default Example Domain https://example.com/
$ jawb open --tab t_bexe91 https://go.dev/
$ jawb back --tab t_bexe91
$ jawb forward --tab t_bexe91
$ jawb reload --tab t_bexe91
$ jawb tab use t_bexe91
current tab: t_bexe91 (already current) https://example.com/ [default]
$ jawb close t_bexe91
Tabs are independent and run in parallel: commands to different tabs run concurrently, while commands to the same tab are serialized in arrival order. Two tabs can be driven at once without coordinating.
Each tab starts on the native engine and is promoted to the
compatibility bridge the first time it needs one (any Act/See command,
or --js). A promoted tab stays on the bridge for the rest of its life —
jawb tabs shows the executor per tab.
Incognito
$ jawb open --incognito https://example.com
Creates an ephemeral session backed only by memory — nothing is written to disk (no cookies, storage, cache, or trace bodies with full URLs), except a download you explicitly ask for. That download is deleted when the session's last tab closes or the daemon stops; use `download --to
Cookies and storage
$ jawb cookies --profile work
sid=abc123 domain=127.0.0.1 path=/ expires 1790593348 httponly
$ jawb cookies clear --profile work
$ jawb storage export --profile work --to work-export.json
$ jawb storage import work-export.json --profile work --yes
storage import asks for confirmation (--yes to skip): it replaces the
profile's cookies and localStorage.
An export file holds live session cookies in plaintext, so anyone who can
read it can sign in as you. storage export --to therefore creates the
file readable by you only: mode 0600 on macOS and Linux, and on Windows an
access list that grants only your user account and does not inherit the
folder's entries. It refuses to write through a symlink, and it
refuses to overwrite an existing file unless you pass --force. Delete the
file when you no longer need it.
Uploads and downloads
upload <id> <file...> sends file bytes over HTTP, never a path — the
daemon can't be used to read arbitrary files off your disk. download <id> [--to <dir>] saves under <data_dir>/downloads/<profile>/ by
default; --to <dir> makes the CLI fetch the bytes itself into a
directory you name. Both are policy-gated — see
policy and confirmation.
Trace
Every command run against the daemon is recorded:
$ jawb trace --tab t_bexe91 --last 3
TS COMMAND TAB EXEC REV MS RESULT
05:55:36.523Z open t_bexe91 native 1 8 ok
05:55:36.545Z observe t_bexe91 native 1 2 ok
...
TS is the step's UTC start time. The --json form carries Unix
milliseconds (ts, ended_ts).
EXEC shows native or bridge* (the * marks reduced fidelity vs. the
native engine). Trace steps also record the policy decision for any
gated action.
Following the trace live
jawb trace --follow prints new steps as they happen. With --json
(jawbd at API level 36) it prints jawb's event stream instead, one JSON
event per line: stream.hello first, then one event per step the trace
records, such as nav.open.after, tab.opened, act.click.after,
page.observed or confirmation.approved:
$ jawb --json trace --follow --types 'act.*.after,tab.opened'
{"v":1,"type":"stream.hello","ts":1791702900000,"data":{"subscriber":"sub_7w2k9c4m","epoch":"19a0c2f1b3e4x9qk","oldest":null,"filter":{"types":["act.*.after","tab.opened"],"tab":null}}}
{"v":1,"id":"19a0c2f1b3e4x9qk-3","type":"tab.opened","ts":1791702901240,"session":"s_4k2m9q","profile":"default","tab":"t_bexe91","executor":"native","client":"jawb/0.1.1","trigger_depth":0,"trace_id":"tr_01m4mj6yye94xj2w61","data":{"tab":"t_bexe91","opener":"caller","url":"https://shop.example/"}}
--types keeps only the types you name; * may stand for the verb
(act.*.after, nav.*.after). --tab keeps one tab's events. Events carry
what the trace carries and no more: URLs with query values replaced
(origins only for incognito tabs), and never a typed value, a secret or a
token. If the connection drops, jawb reconnects and gets what it missed,
or a stream.gap line when jawbd restarted or no longer has it (it keeps
the last 10,000 events, up to 10 minutes).