# How Lightpanda Handles Cookies and Storage via the Chrome DevTools Protocol

> Discover how Lightpanda manages cookies and storage using the Chrome DevTools Protocol. Access HTTP cookies, localStorage, and sessionStorage with this efficient solution.

- Repository: [Lightpanda/browser](https://github.com/lightpanda-io/browser)
- Tags: how-to-guide
- Published: 2026-03-14

---

**Lightpanda implements a minimal but fully functional CDP Storage domain in `src/cdp/domains/storage.zig` that exposes HTTP cookie operations to remote clients, while the same `CookieJar` backs the JavaScript `document.cookie` API and separate in-memory `Lookup` structures handle `localStorage` and `sessionStorage`.**

The Lightpanda browser provides automated control through the Chrome DevTools Protocol (CDP), implementing a focused subset of the Storage domain for cookie management. This architecture bridges external automation tools with internal browser state, exposing cookie operations via CDP while maintaining compatibility with standard Web APIs. Understanding how Lightpanda handles CDP cookies and storage requires examining the Zig-based implementation that separates RFC 6265 cookie jars from key-value storage lookups.

## CDP Storage Domain: Cookie Management in `storage.zig`

Lightpanda's CDP Storage domain handles cookie commands through the `processMessage` function in `src/cdp/domains/storage.zig`. This synchronous dispatcher routes commands to specific handlers that operate on the `BrowserContext` retrieved from `cmd.browser_context`.

### The CookieJar and Internal Structures

The core storage mechanism relies on **`CookieJar`**, a thin wrapper around `std.ArrayList(Cookie)` defined in `src/browser/webapi/storage/Cookie.zig`. The jar maintains a maximum of 1024 entries and provides `forRequest` formatting for HTTP headers. Individual cookies follow RFC 6265 parsing rules with Lightpanda-specific permissive extensions for `Domain`, `Path`, `Secure`, `HttpOnly`, `SameSite`, `Max-Age`, and `Expires` attributes.

CDP clients interact with this system using **`CdpCookie`** structures that support `name`, `value`, `url`, `domain`, `path`, `secure`, `httpOnly`, `sameSite`, `expires`, and `priority` fields. Unsupported fields trigger `error.NotImplemented` responses.

### Command Workflow

**`Storage.clearCookies`** invokes `clearCookies`, which calls `CookieJar.clearRetainingCapacity()` to empty the list while preserving the underlying allocation.

**`Storage.getCookies`** triggers `getCookies`, which first executes `Jar.removeExpired(null)` to purge stale entries, then constructs a **`CookieWriter`** implementing `jsonStringify` to serialize the remaining cookies for the CDP JSON response.

**`Storage.setCookies`** processes arrays through `setCookies`, converting each `CdpCookie` via `setCdpCookie`. This conversion validates supported fields and returns `error.NotImplemented` for unsupported attributes. The function determines final domains via `Cookie.parseDomain`, resolves paths via `Cookie.parsePath`, derives default `secure` flags from URLs when present, and allocates strings using an **`ArenaAllocator`** before final storage via `Jar.add`.

## Web API Storage: localStorage and sessionStorage

Separate from the CDP cookie system, Lightpanda implements the W3C Storage interface through the **`Lookup`** struct in `src/browser/webapi/storage/storage.zig`. This type uses `std.StringHashMapUnmanaged([]const u8)` to store key-value pairs. A hardcoded **5 MiB** quota is enforced by `Lookup.setItem` on every write operation.

### JavaScript Bridge Integration

The **`Lookup.JsApi`** constructs the `"Storage"` class with a prototype chain exposing standard methods: `length` (getter), `getItem(key)`, `setItem(key, value)`, `removeItem(key)`, `clear()`, and `key(index)`. Indexed property accessors enable bracket notation syntax equivalent to `storage[key]`.

In `src/browser/webapi/Window.zig`, the bridge registers storage accessors:

```zig
pub fn getLocalStorage(self: *Window) *storage.Lookup { … }
pub fn getSessionStorage(self: *Window) *storage.Lookup { … }
pub const localStorage = bridge.accessor(Window.getLocalStorage, null, .{});
pub const sessionStorage = bridge.accessor(Window.getSessionStorage, null, .{});

```

These registrations expose `window.localStorage` and `window.sessionStorage` to JavaScript contexts.

### Persistence and Quota Enforcement

Storage objects remain in memory for the lifetime of the `BrowserContext` without disk persistence, providing incognito-mode semantics. The `Lookup.setItem` method calculates total size before insertion and throws **`QuotaExceeded`** if the 5 MiB limit would be exceeded.

## CDP and Web API Interaction Patterns

Lightpanda maintains strict separation between CDP-accessible storage and JavaScript-only storage. The CDP Storage domain exclusively manipulates HTTP cookies through the `CookieJar`, while `localStorage` and `sessionStorage` remain accessible only via JavaScript execution within the page. The `document.cookie` API accesses the same `CookieJar` instance used by CDP commands, ensuring consistency between automation clients and in-page scripts.

### Practical CDP Usage

Remote clients like Puppeteer or Playwright interact with Lightpanda's cookie store using standard CDP messages:

```json
{
  "id": 1,
  "method": "Storage.clearCookies",
  "params": { "browserContextId": "BID-123" }
}

```

```json
{
  "id": 2,
  "method": "Storage.setCookies",
  "params": {
    "cookies": [
      {
        "name": "session",
        "value": "abc123",
        "domain": "example.com",
        "path": "/",
        "secure": true,
        "httpOnly": true,
        "sameSite": "Strict"
      }
    ],
    "browserContextId": "BID-123"
  }
}

```

### In-Page JavaScript Access

Within the page context, JavaScript interacts with the same underlying storage:

```javascript
// Web Storage API
window.localStorage.setItem('theme', 'dark');
console.log(window.localStorage.getItem('theme')); // "dark"

// Cookie manipulation via document.cookie
document.cookie = 'foo=bar; Secure; SameSite=Strict';
console.log(document.cookie); // "foo=bar"

```

## Summary

- **CDP Storage domain** in `src/cdp/domains/storage.zig` provides `clearCookies`, `getCookies`, and `setCookies` commands that manipulate the internal `CookieJar` via `processMessage`.
- **`CookieJar`** is defined in `src/browser/webapi/storage/Cookie.zig`, enforces a 1024-cookie limit, and implements RFC 6265 parsing with Lightpanda-specific permissive rules.
- **`CdpCookie` to `Cookie` conversion** occurs via `setCdpCookie`, handling domain/path parsing through `Cookie.parseDomain` and `Cookie.parsePath` with synchronous error responses.
- **Web Storage APIs** use separate `Lookup` structures with a 5 MiB quota, exposed through `Window.zig` accessors using `bridge.accessor` and persisted only in memory.
- **Architectural separation** ensures CDP clients control HTTP cookies while JavaScript retains exclusive access to `localStorage` and `sessionStorage`.

## Frequently Asked Questions

### How does Lightpanda store cookies internally?

Lightpanda stores cookies in a `CookieJar` struct defined in `src/browser/webapi/storage/Cookie.zig`, which wraps `std.ArrayList(Cookie)` with a maximum capacity of 1024 entries. The jar handles RFC 6265 parsing, expiration checking via `removeExpired`, and HTTP request formatting through `forRequest`.

### Can CDP clients access localStorage and sessionStorage?

No, Lightpanda's CDP Storage domain implementation only exposes cookie operations. The `localStorage` and `sessionStorage` APIs are accessible exclusively through JavaScript execution within the page, using the `Lookup` struct in `src/browser/webapi/storage/storage.zig` that is not bridged to CDP commands.

### What happens when cookie or storage limits are exceeded?

The `CookieJar` enforces a hard limit of 1024 cookies, while `Lookup.setItem` throws a `QuotaExceeded` error if Web Storage operations would exceed 5 MiB. Both mechanisms operate synchronously and return immediate errors to the calling context, either as CDP error responses or JavaScript exceptions.

### How does Lightpanda handle cookie expiration?

Before returning cookies via `Storage.getCookies`, Lightpanda calls `Jar.removeExpired(null)` to purge all stale entries. The internal `Cookie` struct tracks expiration timestamps, and the jar's `clearRetainingCapacity` method preserves allocations when clearing cookies via `Storage.clearCookies`.