Storage
A render starts with empty browser storage. Two sources fill it before the page is requested: a storage file, which carries a session from one render to the next, and a signed-in browser profile, which borrows a login from Chrome, Firefox or Safari.
| Source | Flag | Holds | Written back |
|---|---|---|---|
| Storage file | --storage | Cookies, localStorage, sessionStorage, IndexedDB, Cache Storage | Yes, unless ro: |
| Browser profile | --storage-chrome-profile, --storage-firefox-profile, --storage-safari-profile | Cookies, localStorage | Never |
With both, the profile is the base and the file wins. A --cookie replaces a stored cookie with the same name, domain and path.
Storage Files
The file uses Playwright’s storageState format, so a file from either tool loads in the other. ZShot adds sessionStorage and cacheStorage, which Playwright ignores.
Storage is saved for every origin the page loaded a document from:
- Cache Storage exists on https and localhost origins. A cached cross-origin response fetched without CORS cannot be read and is skipped.
- IndexedDB values JSON cannot hold are saved in Playwright’s encoding. A Map, Set or DataView loads in Playwright as a plain object. Blob and File values are skipped.
- Storage a cross-site iframe writes, service workers, and the storage of tabs a page opens are not saved.
The file holds session cookies and tokens, so ZShot writes it readable by its owner only. When a write would pass --storage-limit, ZShot reads each origin’s localStorage, sessionStorage, IndexedDB and then Cache Storage, and leaves out and logs each item that would pass it.
Browser Profiles
A profile flag reads a browser’s own profile directory and never writes it. Only the target URL’s registrable domain is seeded, plus the origins --navigator-credential-origin allows; a * glob there seeds the whole profile. Safari’s localStorage is read only for the target URLs’ origins.
sessionStorage, IndexedDB and Cache Storage are not read from a profile.
Chrome Encryption
Chrome encrypts its cookies. On macOS ZShot reads the key from the “Chrome Safe Storage” keychain item, which prompts once. On Linux it reads the key from the desktop keyring with secret-tool when that is installed.
--storage-chrome-profile-key supplies the key when the profile and its key come from another machine, or the Linux keyring has none. On the machine that wrote the profile, macOS prints the key with security find-generic-password -wgs "Chrome Safe Storage", and Linux with secret-tool lookup application chrome. A key from a macOS or a Linux profile works on either OS.
--storage-chrome-profile=PATH reads any Chromium-based browser’s profile directory.
Safari Full Disk Access
Reading Safari’s profile on macOS needs Full Disk Access for the app that runs ZShot, such as a terminal. Grant it in System Settings > Privacy & Security > Full Disk Access and restart that app. Without it, ZShot exits with code 3 before rendering, even with the bare flag.
Server and --cdp
Under --server or --cdp, a --storage file is a default that is never written. A server reads it for each request and --cdp for each new browser context, so an edit reaches the next one. A file that cannot be read fails that request or context.
A request’s with_storage chooses what it starts from. With true or a storage object, the response links the storage the render ends with as an additional asset; send it back as the next request’s with_storage to carry the session on.
A profile is read for each request, scoped to its URL, and for each new --cdp browser context. A context has no URL, so without --navigator-credential-origin every origin is seeded. A server or --cdp instance asks the keychain for Chrome’s key once, at startup, and keeps it for every read.