Library / SDK
servo/ipc-channel avatar
servo/ipc-channel

ipc-channel extends Rust channels across the process boundary

A multiprocess drop-in replacement for Rust channels

1,129 stars163 forksRustApache-2.0

At a glance

What is it?
ipc-channel is a Rust crate that takes the standard library's channel types and makes them work between processes, with an API mapping that keeps the code almost unchanged, a one-shot server for bootstrapping the first connection, and an implementation built on the native primitive of each platform, file descriptor passing over Unix sockets, Mach ports on macOS and named pipes on Windows. It is developed by the Servo project and is dual licensed under MIT and Apache 2.0.
Who is it for?
ipc-channel is the right crate when you are splitting a Rust program into separate processes and want the message passing to look like the code you already wrote, because the API mapping is a one-line change at the type level and the channels nest, so you can send a channel over a channel.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The API mapping is three lines, and that is the point

The crate is described as an inter-process implementation of Rust channels, and the channels it imitates are, in the README's words, a unidirectional, FIFO queue of messages which can be used to send messages between threads in a single operating system process. The channels idea came from CSP, which the footnote attributes to Tony Hoare, with Go's channels also inspired by it. The design goal is stated as drop-in replacement, and the mapping is short. channel() becomes ipc::channel().unwrap(), Sender<T> becomes ipc::IpcSender<T> and requires T: Serialize, and Receiver<T> becomes ipc::IpcReceiver<T> and requires T: Deserialize. There is one property that makes the design work, that both IpcSender<T> and IpcReceiver<T> implement Serialize and Deserialize, so you can send IPC channels over IPC channels freely, just as you can with Rust channels. The audience is a Rust codebase being broken into processes, most obviously a browser engine, and the crate is maintained by the Servo project developers.

Four differences, and they are the design

The README has a section on the semantic differences from Rust channels, and reading it is the way to understand what the crate actually is. Rust channels can be either unbounded or bounded, whereas ipc-channels are always unbounded and send() never blocks. Rust channels do not consume OS IPC resources, whereas ipc-channels consume IPC resources such as sockets, file descriptors, shared memory segments and named pipes, depending on the OS. Rust channels transfer ownership of messages, whereas ipc-channels serialize and deserialize them. And Rust channels are type safe, whereas ipc-channels depend on the client and server programs using identical message types, or at least message types with compatible serial forms. That last one is the cost that matters, since the compiler can no longer see the other end of the channel, so a mismatch between the two processes becomes a runtime failure rather than a build error.

Bootstrapping the first connection with a one-shot server

Since a channel needs a name before it exists, the crate provides a one-shot server to establish one. Creating an IpcOneShotServer generates a server name, which is returned along with the server, and the client process calls connect() with that name to get the sender end of a channel from the client to the server. The restriction is that connect() may be called at most once per one-shot server. The server calls accept(), which blocks until a client has connected and sent a message, and it then returns a pair consisting of the receiver end of the client-to-server channel and that first message. In practice you create the one-shot server, pass the server name to the client, perhaps through an environment variable or a command line flag, and the client connects. The repository points at two worked examples, spawn_one_shot_server_client() in integration_test.rs for the environment variable approach and cross_process_embedded_senders_fork() in test.rs for the Unix fork approach, with a footnote on the semantic rough edges of fork() and a pointer to the Hot Topics in Operating Systems paper on the subject.

The native primitive on each platform

The implementation is in terms of native IPC primitives, and the mapping is stated directly. On Unix variants it is file descriptor passing over Unix sockets, on macOS it is Mach ports, and on Windows it is named pipes. One-shot server names are implemented as a file system path on the Unix variants, with the file system path bound to the socket, and as other kinds of generated names on macOS and Windows. That last detail is the kind of thing that only matters once you debug it, because a name that is a path can be inspected, checked for existence and cleaned up, while a generated name on macOS cannot. The crate is therefore not one implementation with a platform abstraction bolted on, it is three implementations that happen to share an API, and the portability claim in the README is a claim about the API rather than about the code.

One client per server, and that is the missing feature

The Major missing features section has one entry and it is worth reading before you design around the crate. Each one-shot server accepts only one client connect request. The README says this is fine if you simply want to use the API to split your application up into a fixed number of mutually untrusting processes, but it is not suitable for implementing a system service, and an API for multiple clients may be added later if demand exists for it. That is a precise statement of the intended use, and it is a narrower one than the type mapping suggests. The crate is for a fixed topology, a supervisor that spawns N workers and hands each one a name, not for a service that accepts connections from anywhere. If your design needs the second thing, the standard library's TCP or Unix socket facilities are the tool, and this crate is not a substitute for them.

The crate's shape, and what the features are for

The packaging metadata shows a small, carefully scoped dependency list. The runtime dependencies are crossbeam-channel, thiserror, uuid with the v4 feature, serde_core, libc, and postcard with the use-std feature, with the platform specifics added per target, mio, rustc-hash and tempfile on the Unix targets and rand on macOS. The features are the interesting part. The default is nothing, force-inprocess is there for tests, async pulls in futures-core and futures-channel, win32-trace is a Windows debugging aid, and enable-slow-tests gates the expensive cases. The rust-version field pins the minimum supported Rust version to 1.86.0 with a comment that it must be kept in sync with the msrv_build job in CI and that it may not be higher than the MSRV in the main Servo project, which is a good way to see how tightly the crate is coupled to its parent. The benches directory holds five benchmarks with the criterion harness turned off, named platform, ipc, ipc_receiver_set, ipc_shared_mem and struct_ipc, which is a hint that the interesting costs are the platform difference and the shared memory path.

Testing across platforms, and what the crate is really for

The testing section explains how much of this code is platform dependent, which is the honest framing. Some tests are platform dependent, so for completeness it would be necessary to run the tests on all platforms, and the list is iOS, macOS, the Unix variants including Android, FreeBSD, Illumos, Linux and OpenBSD, WASI and Windows, with the platforms marked as covered by CI being macOS, Linux and Windows. The commands are the ordinary ones:

bash
cargo test
cargo bench

The overall picture is a small crate with a hard platform surface, written by the Servo project for a browser engine, and the reason to adopt it is that it already solved the hard parts on eleven platforms rather than that it is a general purpose IPC library. The licence is the standard Rust dual licence, MIT OR Apache-2.0, and the release history is steady, with v0.21.0 in February 2026, v0.22.0 in April and v0.23.0 on 2026-09-01, which is the same date as the last push to the repository.

Editorial conclusion

ipc-channel is the right crate when you are splitting a Rust program into separate processes and want the message passing to look like the code you already wrote, because the API mapping is a one-line change at the type level and the channels nest, so you can send a channel over a channel. It is the wrong tool if you need many clients on one endpoint, since each one-shot server accepts a single connect request and the README says an API for multiple clients may be added later if demand exists. Before you adopt it, read the semantic differences rather than the type mapping, because the four of them, unbounded queues, consumed OS resources, serialization instead of ownership transfer, and the loss of static type safety across the boundary, are the facts your design has to account for.

Frequently asked questions

How closely does ipc-channel match the Rust channel API?

Very closely. channel() becomes ipc::channel().unwrap(), Sender<T> becomes ipc::IpcSender<T> which requires T: Serialize, and Receiver<T> becomes ipc::IpcReceiver<T> which requires T: Deserialize. Both ends also implement Serialize and Deserialize, so you can send IPC channels over IPC channels.

What are the semantic differences from a Rust channel?

Ipc-channels are always unbounded and send() never blocks, they consume OS IPC resources such as sockets, file descriptors, shared memory segments and named pipes, they serialize and deserialize messages rather than transferring ownership, and they depend on both ends using identical or compatible message types rather than being type safe.

How do two processes establish a channel with ipc-channel?

One process creates an IpcOneShotServer, which returns a generated server name, and the other calls connect() with that name to get the sender end. The server calls accept(), which blocks until a client connects and sends a message, and it returns the receiver end together with that first message. connect() may be called at most once per one-shot server.

Which native IPC mechanisms does ipc-channel use?

File descriptor passing over Unix sockets on the Unix variants, Mach ports on macOS, and named pipes on Windows. One-shot server names are a file system path bound to the socket on Unix and other generated names on macOS and Windows. The crate is dual licensed under MIT and Apache 2.0 and requires Rust 1.86.0 or newer.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. servo/ipc-channel on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/servo-ipc-channel.svg)](https://hysenlabs.com/projects/servo-ipc-channel)

Was this useful?