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

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/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/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/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:

// 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/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/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/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 defines the server interface for the WebContent process:

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, 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/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/master/Services/WebWorker/ConnectionFromClient.cpp), the server might handle a file return as follows:

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. 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), the current implementation uses UNIX domain sockets on macOS. Mach ports are planned for future optimization but are not yet active in the codebase.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →