# How Lightpanda V8 Isolated Worlds Work: Architecture and Implementation

> Discover how Lightpanda V8 isolated worlds create secure JavaScript execution boundaries using separate V8 Contexts within a shared V8 Isolate for memory efficiency.

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

---

**Lightpanda uses a single shared V8 Isolate per browser instance and creates lightweight isolated worlds as separate V8 Contexts, enabling secure JavaScript execution boundaries while maintaining memory efficiency through shared garbage collection.**

The Lightpanda browser embeds the V8 JavaScript engine to execute web content and extension scripts. Understanding how Lightpanda V8 isolated worlds function requires examining the Zig-based architecture that maps browser concepts to V8 primitives. This implementation follows the Chrome DevTools Protocol (CDP) model while optimizing for resource efficiency.

## The Core Architecture: One Isolate, Many Worlds

Lightpanda’s architecture centers on a **single V8 Isolate** that lives inside the `Env` struct defined in `src/browser/js/Env.zig`. This isolate acts as the heavyweight execution engine responsible for memory management and garbage collection.

An **isolated world** is implemented as a lightweight **V8 Context** within that same isolate. The `IsolatedWorld` struct in `src/cdp/cdp.zig` (lines 748-754) represents this abstraction:

```zig
const IsolatedWorld = struct {
    arena: Allocator,
    browser: *Browser,
    name: []const u8,
    context: ?*js.Context = null,
    grant_universal_access: bool,
    // ...
};

```

Each `IsolatedWorld` maintains its own `js.Context` (defined in `src/browser/js/Context.zig`) while sharing the parent isolate’s heap. This design allows multiple worlds to coexist without duplicating the expensive isolate overhead.

## Creating Isolated Worlds via CDP

The browser exposes world creation through the CDP command `Page.createIsolatedWorld`. The handler in `src/cdp/domains/page.zig` (lines 188-206) orchestrates the instantiation:

```zig
fn createIsolatedWorld(cmd: anytype) !void {
    const params = ...;                     // worldName, grantUniveralAccess
    const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
    const world = try bc.createIsolatedWorld(params.worldName, params.grantUniveralAccess);
    const page = bc.session.currentPage() orelse return error.PageNotLoaded;
    const js_context = try world.createContext(page);
    return cmd.sendResult(.{ .executionContextId = js_context.id }, .{});
}

```

The `BrowserContext.createIsolatedWorld` method allocates the `IsolatedWorld` and stores it in `CDP.isolated_worlds`. The actual **V8 Context instantiation** occurs lazily through `IsolatedWorld.createContext` in `src/cdp/cdp.zig` (lines 771-780):

```zig
pub fn createContext(self: *IsolatedWorld, page: *Page) !*js.Context {
    if (self.context == null) {
        self.context = try self.browser.env.createContext(page);
    }
    ...
    return self.context.?;
}

```

This binds the context to the current `Page`, ensuring the world inherits the page’s navigation state while maintaining a separate global object graph.

## Isolation Guarantees and Security Boundaries

Each isolated world receives its own independent set of global objects (`window`, `document`, etc.). This separation prevents scripts in one world from directly accessing variables or DOM objects in another world.

The **security boundary** is controlled by the `grant_universal_access` boolean parameter:

- **When `false`**: Scripts execute in a strict sandbox with no cross-world access
- **When `true`**: The world receives elevated privileges, similar to extension content scripts

Because all worlds share the underlying V8 Isolate, the **garbage collector** runs across the shared heap, reducing memory overhead compared to spawning multiple isolates.

## Lifecycle Management

Lightpanda implements strict cleanup procedures to prevent stale object IDs in the CDP inspector. When navigation occurs or a page closes, the browser first invokes `removeContext` to tear down V8 contexts, then calls `deinit` to release the world’s resources.

The cleanup logic in `src/cdp/cdp.zig` (lines 755-759) demonstrates this sequence:

```zig
pub fn deinit(self: *IsolatedWorld) void {
    self.removeContext() catch {};
    self.browser.arena_pool.release(self.arena);
}

```

This ordered destruction ensures that CDP inspector references are invalidated before the underlying memory is returned to the arena pool.

## Practical Implementation Examples

### Creating a World Programmatically in Zig

To create an isolated world from within the browser’s Zig codebase:

```zig
const CDP = @import("cdp.zig");
const Page = @import("../browser/Page.zig");

// Assume initialized CDP instance and active BrowserContext
const worldName = "my-extension-world";
const grantAccess = true;

// Create the world instance
const world = try bc.createIsolatedWorld(worldName, grantAccess);

// Bind to current page (required for context creation)
const page = bc.session.currentPage() orelse return error.NoPage;
const jsCtx = try world.createContext(page);

// Execute JavaScript within the isolated context
const result = try jsCtx.evalScript("globalThis.secretData = 42;", .{});

```

*Source reference:* `src/cdp/cdp.zig` (IsolatedWorld definition and `createContext` implementation).

### Creating a World via CDP JSON API

Clients can create worlds remotely using the Chrome DevTools Protocol:

```json
{
  "id": 1,
  "method": "Page.createIsolatedWorld",
  "params": {
    "frameId": "0.1",
    "worldName": "debug-world",
    "grantUniveralAccess": false
  }
}

```

The server responds with an `executionContextId` that uniquely identifies the new world’s V8 Context for subsequent operations.

*Source reference:* `src/cdp/domains/page.zig` (lines 188-206).

### Executing Scripts in Specific Worlds

Target a specific isolated world using the context ID returned during creation:

```json
{
  "id": 2,
  "method": "Runtime.evaluate",
  "params": {
    "expression": "document.title",
    "contextId": 5
  }
}

```

The `Runtime.evaluate` handler in `src/browser/js/Inspector.zig` maps this ID to the corresponding `js.Context`, ensuring execution occurs within the correct V8 Context boundary.

## Summary

- Lightpanda maintains **one V8 Isolate per browser instance** in `src/browser/js/Env.zig` for efficient memory management.
- **Isolated worlds** are implemented as separate **V8 Contexts** within the shared isolate, defined in `src/cdp/cdp.zig`.
- Worlds are created via the **CDP `Page.createIsolatedWorld`** command, handled in `src/cdp/domains/page.zig`.
- The **`grant_universal_access`** parameter controls whether a world can access objects from other contexts.
- **Lazy initialization** via `createContext` binds worlds to pages only when needed.
- **Ordered cleanup** through `removeContext` and `deinit` prevents inspector state corruption during navigation.

## Frequently Asked Questions

### How does Lightpanda's isolated world implementation compare to Chrome's?

Lightpanda mirrors Chrome’s isolated world model used by extensions and DevTools, but implements it in Zig with a focus on minimal overhead. Both browsers use separate V8 Contexts within a shared isolate, though Lightpanda’s `IsolatedWorld` struct in `src/cdp/cdp.zig` explicitly tracks the `grant_universal_access` permission bit and arena allocation lifecycle.

### Can isolated worlds share memory or objects?

By default, no. Each isolated world maintains its own global object graph (window, document) within its V8 Context. When `grant_universal_access` is set to `true` during world creation, scripts gain elevated privileges, but true cross-world object access still requires explicit value marshalling through the CDP layer in `src/browser/js/Inspector.zig`.

### What happens to isolated worlds during page navigation?

Lightpanda tears down isolated world contexts before navigation completes. The `deinit` method in `src/cdp/cdp.zig` first calls `removeContext` to invalidate V8 handles, then releases the arena memory back to the pool. This prevents stale `executionContextId` references from persisting across page loads in the CDP inspector.

### Is there a performance cost for creating multiple isolated worlds?

The overhead is minimal compared to spawning multiple V8 Isolates. Because Lightpanda’s isolated worlds share a single isolate (and thus a shared garbage collector), memory allocation remains efficient. The primary cost is the creation of a new V8 Context via `Env.createContext`, which is a lightweight operation relative to isolate instantiation.