# How Ladybird Implements Inter-Process Communication: The LibIPC Framework Explained

> Discover how Ladybird uses LibIPC for inter-process communication. Learn about strongly-typed message passing and file descriptor transfer between browser components for enhanced security and stability.

- Repository: [Ladybird/ladybird](https://github.com/LadybirdBrowser/ladybird)
- Tags: internals
- Published: 2026-03-05

---

**Ladybird isolates browser components into separate processes and uses the LibIPC framework, which abstracts platform-specific socket transports to enable strongly-typed message passing and file descriptor transfer between services like WebContent, RequestServer, and ImageDecoder.**

Ladybird’s multi-process architecture isolates critical components—such as WebContent, RequestServer, ImageDecoder, and WebWorker—into distinct processes for security and stability. All communication between these isolated processes is orchestrated through the **LibIPC** framework, a lightweight abstraction layer that provides type-safe IPC primitives and declarative message definitions. This article examines the core mechanisms, transport implementations, and code-generation pipeline that power inter-process communication in Ladybird.

## Core IPC Primitives in LibIPC

The LibIPC framework provides a small set of high-level primitives that abstract away OS-specific transport details. These classes handle everything from low-level socket management to automatic message serialization.

### IPC::Transport and Socket Abstraction

The **`IPC::Transport`** class, defined in [[`Libraries/LibIPC/TransportSocket.h`](https://github.com/LadybirdBrowser/ladybird/blob/main/Libraries/LibIPC/TransportSocket.h)](https://github.com/LadybirdBrowser/ladybird/blob/master/Libraries/LibIPC/TransportSocket.h), wraps a bidirectional local socket and serves as the foundation for all process-to-process communication. It handles message framing, non-blocking reads, and support for file descriptor (FD) passing via ancillary data on Linux and macOS.

On Windows, the framework uses **`IPC::TransportSocketWindows`**, implemented in [[`Libraries/LibIPC/TransportSocketWindows.h`](https://github.com/LadybirdBrowser/ladybird/blob/main/Libraries/LibIPC/TransportSocketWindows.h)](https://github.com/LadybirdBrowser/ladybird/blob/master/Libraries/LibIPC/TransportSocketWindows.h), which adapts the same API to Windows socket semantics while still using `Core::LocalSocket` under the hood. This ensures consistent behavior across platforms while respecting OS-specific requirements.

### File Descriptor Transfer with IPC::File

The **`IPC::File`** class, located in [[`Libraries/LibIPC/File.h`](https://github.com/LadybirdBrowser/ladybird/blob/main/Libraries/LibIPC/File.h)](https://github.com/LadybirdBrowser/ladybird/blob/master/Libraries/LibIPC/File.h), is a thin wrapper around an OS file descriptor that enables serialization over IPC messages. This primitive is essential for zero-copy hand-offs of open sockets, pipes, or shared-memory handles between processes.

When WebContent needs to establish a connection to the RequestServer, for example, it receives the socket as an `IPC::File` object:

```cpp
// In WebContent::main.cpp
auto request_server_socket = TRY(TRY(request_server_client->take_over_accepted_socket()));
auto request_client = TRY(try_make_ref_counted<Requests::RequestClient>(
    make<IPC::Transport>(move(request_server_socket))));

```

### Connection Endpoints and SingleServer

LibIPC provides template classes for establishing client-server relationships. **`IPC::ConnectionFromClient`**, defined in [[`Libraries/LibIPC/ConnectionFromClient.h`](https://github.com/LadybirdBrowser/ladybird/blob/main/Libraries/LibIPC/ConnectionFromClient.h)](https://github.com/LadybirdBrowser/ladybird/blob/master/Libraries/LibIPC/ConnectionFromClient.h), represents the server-side endpoint that receives messages from clients. It generates concrete APIs from `.ipc` IDL files and is implemented by services such as `WebContent::ConnectionFromClient` and `WebWorker::ConnectionFromClient`.

The client-side counterpart, **`IPC::ConnectionToServer`** ([[`Libraries/LibIPC/ConnectionToServer.h`](https://github.com/LadybirdBrowser/ladybird/blob/main/Libraries/LibIPC/ConnectionToServer.h)](https://github.com/LadybirdBrowser/ladybird/blob/master/Libraries/LibIPC/ConnectionToServer.h)), provides the matching proxy interface for sending messages to servers.

For services that accept only a single client connection, such as ImageDecoder or RequestServer, the **`IPC::SingleServer`** helper ([[`Libraries/LibIPC/SingleServer.h`](https://github.com/LadybirdBrowser/ladybird/blob/main/Libraries/LibIPC/SingleServer.h)](https://github.com/LadybirdBrowser/ladybird/blob/master/Libraries/LibIPC/SingleServer.h)) simplifies socket creation and listening.

## Declarative Message Definitions Using .ipc IDL Files

Rather than writing manual serialization code, Ladybird uses declarative **`.ipc` IDL files** to define message protocols. These files specify the interface, method signatures, and parameter types for each service. The build system processes these definitions to generate the strongly-typed `ConnectionFromClient` and `ConnectionToServer` classes.

For example, [`Services/WebContent/WebContentServer.ipc`](https://github.com/LadybirdBrowser/ladybird/blob/master/Services/WebContent/WebContentServer.ipc) defines the server interface for the WebContent process:

```idl
interface WebContentServer {
    connect_to_web_ui(u64 page_id, IPC::File socket_fd) =|
    connect_to_request_server(IPC::File request_server_socket) =|
    connect_to_image_decoder(IPC::File socket_fd) =|
    // … additional messages
}

```

The `=|` syntax indicates messages that expect no reply. From this definition, the build system generates:
- `WebContent::ConnectionFromClient` (server side) with virtual handlers like `connect_to_web_ui`
- `WebContent::ConnectionToServer` (client side) with matching proxy methods

Additional service definitions, such as [`Services/WebWorker/WebWorkerServer.ipc`](https://github.com/LadybirdBrowser/ladybird/blob/master/Services/WebWorker/WebWorkerServer.ipc), follow the same pattern for worker-specific communication channels.

## Platform-Specific Transport Implementations

While LibIPC presents a unified API, the underlying transport varies by operating system to maximize performance and compatibility.

### Linux and macOS: UNIX Domain Sockets

On Linux and macOS, **`IPC::Transport`** uses UNIX domain sockets via `Core::LocalSocket`. The implementation supports **ancillary data** (SCM_RIGHTS) to transfer file descriptors between processes, enabling zero-copy socket hand-offs. This mechanism is used when WebContent passes a worker’s communication socket back to the parent process or when establishing connections to the ImageDecoder.

### Windows: Local Socket Adaptation

On Windows, **`IPC::TransportSocketWindows`** provides equivalent functionality using Windows local socket semantics. While the underlying handle type differs, the API remains identical to the POSIX implementation, ensuring portable code across the codebase.

### Future: Mach IPC on macOS

The source code contains `// TODO: Mach IPC` markers in files such as [[`Services/WebContent/main.cpp`](https://github.com/LadybirdBrowser/ladybird/blob/main/Services/WebContent/main.cpp)](https://github.com/LadybirdBrowser/ladybird/blob/master/Services/WebContent/main.cpp). While the current implementation falls back to UNIX domain sockets on macOS, Mach ports are planned for future optimization to leverage macOS-specific IPC capabilities.

## IPC in Practice: Connection Lifecycle and File Descriptor Passing

Understanding how these primitives work together requires examining the connection lifecycle from service startup to active message exchange.

### 1. Service Startup and Listening

When a service like ImageDecoder or RequestServer starts, it creates a **`LibIPC::SingleServer`** instance and begins listening on a local socket. This server accepts exactly one client connection, enforcing the one-to-one relationship between service and browser instance.

### 2. Client Connection and Transport Creation

The parent process (Ladybird’s main browser process) accepts the incoming connection and wraps the resulting socket in an **`IPC::Transport`**. It then instantiates the concrete connection class using `ConnectionFromClient::try_create`, binding the transport to the generated message handlers.

### 3. Message Exchange and FD Hand-off

Once connected, processes exchange messages using the generated stubs. When a file descriptor needs to be transferred—such as when returning a worker’s socket—the server wraps the raw FD in an **`IPC::File`** object and includes it in the message payload.

For example, in [[`Services/WebWorker/ConnectionFromClient.cpp`](https://github.com/LadybirdBrowser/ladybird/blob/main/Services/WebWorker/ConnectionFromClient.cpp)](https://github.com/LadybirdBrowser/ladybird/blob/master/Services/WebWorker/ConnectionFromClient.cpp), the server might handle a file return as follows:

```cpp
void ConnectionFromClient::handle_file_return(i32 error,
                                            Optional<IPC::File> file,
                                            i32 request_id) {
    // `file` is an IPC::File that carries the raw fd.
    // The generated client stub will receive an `Optional<IPC::File>` argument.
    // No extra serialization code is required.
}

```

The receiver reconstructs a usable `Core::LocalSocket` from the received file descriptor, enabling immediate communication without additional socket creation overhead.

## Summary

- **LibIPC** provides the foundational framework for all inter-process communication in Ladybird, abstracting platform-specific socket implementations behind a unified API.
- **`IPC::Transport`** handles low-level socket communication using UNIX domain sockets on Linux/macOS and local sockets on Windows, with **`TransportSocketWindows`** providing Windows-specific adaptations.
- **`IPC::File`** enables zero-copy file descriptor passing between processes, allowing efficient hand-off of sockets and handles.
- **`.ipc` IDL files** drive a code-generation pipeline that produces type-safe `ConnectionFromClient` and `ConnectionToServer` classes, eliminating manual serialization boilerplate.
- Services including WebContent, RequestServer, ImageDecoder, and WebWorker communicate through these primitives, maintaining strict process isolation while enabling high-performance coordination.

## Frequently Asked Questions

### What underlying transport protocol does Ladybird use for IPC?

Ladybird uses **UNIX domain sockets** on Linux and macOS, implemented via `Core::LocalSocket` in the `IPC::Transport` class. On Windows, it uses **Windows local sockets** through `IPC::TransportSocketWindows`. Both implementations support message framing and file descriptor (or handle) passing.

### How does Ladybird pass file descriptors between processes?

File descriptors are encapsulated in **`IPC::File`** objects, defined in [`Libraries/LibIPC/File.h`](https://github.com/LadybirdBrowser/ladybird/blob/main/Libraries/LibIPC/File.h). These objects can be serialized into IPC messages and transferred using ancillary data (SCM_RIGHTS) on POSIX systems or equivalent mechanisms on Windows. The receiving process reconstructs the FD from the `IPC::File` wrapper, enabling zero-copy socket hand-off.

### What are .ipc files in the Ladybird codebase?

**`.ipc` files** are Interface Definition Language (IDL) declarations that specify the message protocol between processes. The build system parses these files to generate C++ classes (`ConnectionFromClient` for the server side, `ConnectionToServer` for the client side) with strongly-typed methods, ensuring compile-time safety for cross-process calls.

### Does Ladybird support Mach IPC on macOS?

Currently, **no**. While the codebase contains `// TODO: Mach IPC` comments (notably in [`Services/WebContent/main.cpp`](https://github.com/LadybirdBrowser/ladybird/blob/main/Services/WebContent/main.cpp)), the current implementation uses UNIX domain sockets on macOS. Mach ports are planned for future optimization but are not yet active in the codebase.