Documents
IPC Protocol and Migration
IPC Protocol and Migration
Type
Document
Status
Published
Created
Oct 31, 2025
Updated
Oct 31, 2025
Updated by
Dosu Bot

SentinelD Interprocess Communication (IPC) Protocol

SentinelD uses a robust, cross-platform IPC protocol for communication between its components, such as procmond, daemoneye-agent, and daemoneye-cli. The protocol is built on the Rust interprocess crate, with full async/await support via the Tokio runtime. This design ensures reliable, secure, and efficient local communication on Linux, macOS, and Windows.

Migration from Custom IPC to Interprocess Crate#

SentinelD previously used a custom IPC implementation. This approach was replaced in a major migration (PR #75, PR #80) with a unified solution based on the interprocess crate and Tokio networking. The migration involved:

  • Refactoring the codebase to remove all legacy IPC code and configuration.
  • Introducing new dependencies (interprocess, crc32c, bytes) and converting the project to a workspace.
  • Updating documentation and build/test instructions to reflect the new IPC protocol.
  • Enhancing reliability, security, and maintainability of local communication.

All obsolete binary targets, feature flags, and build scripts related to the legacy IPC were removed. The codebase and documentation now exclusively describe the current IPC system.

IPC Protocol Design#

Message Framing#

SentinelD's IPC protocol uses Protocol Buffers for message encoding. Each message is framed as follows:

[Length: u32][CRC32C: u32][Protobuf Message: N bytes]
  • The first 4 bytes (little-endian) specify the length of the protobuf message.
  • The next 4 bytes (little-endian) contain a CRC32C checksum computed over the message bytes.
  • The remaining N bytes are the protobuf-encoded message payload.

This framing enables robust message parsing and detection of data corruption or boundary errors. The implementation uses the prost crate for protobuf encoding and decoding.

Example (Rust):

// Writing a message
let mut message_bytes = BytesMut::new();
message.encode(&mut message_bytes)?;
let crc32 = crc32c::crc32c(&message_bytes);
frame.put_u32_le(message_bytes.len() as u32);
frame.put_u32_le(crc32);
frame.extend_from_slice(&message_bytes);
writer.write_all(&frame).await?;

CRC32C Checksum Usage#

The CRC32C checksum is calculated over the protobuf message bytes using the crc32c crate. It is included in the frame and validated during both read and write operations. If the checksum does not match, the message is rejected and a CRC mismatch error is returned. This mechanism ensures message integrity and guards against data corruption or tampering during IPC communication (source).

Error Handling#

The IPC protocol provides comprehensive error handling, including:

  • Timeouts for read and write operations, implemented using Tokio's timeout function.
  • Message size validation, rejecting zero-length or oversized messages.
  • CRC mismatch detection, returning a specific error if the checksum does not match.
  • IO errors, including connection refused, permission denied, and unexpected EOF.
  • Protobuf encode/decode errors.
  • Peer connection closure detection.
  • Connection limits to prevent resource exhaustion.
  • Circuit breaker activation under attack scenarios.

Error types are mapped to descriptive variants, such as Timeout, TooLarge, CrcMismatch, Io, Decode, Encode, PeerClosed, and InvalidLength. Unknown errors are mapped to a generic InvalidMessage error with a descriptive reason (source).

Example error type:

pub enum IpcError {
    Timeout,
    TooLarge { size: usize, max_size: usize },
    CrcMismatch { expected: u32, actual: u32 },
    Io(io::Error),
    Decode(prost::DecodeError),
    Encode(String),
    PeerClosed,
    InvalidLength { length: u32 },
}

Cross-Platform Considerations#

SentinelD's IPC system is fully cross-platform:

  • On Unix-like systems (Linux, macOS), it uses Unix domain sockets with endpoint paths such as /var/run/daemoneye/procmond.sock.
  • On Windows, it uses named pipes with paths like \\.\pipe\daemoneye\procmond.
  • The API and behavior are identical across platforms, abstracted by the interprocess crate (source).
  • Security is enforced via local-only communication, permission controls (0600 for Unix sockets, user-restricted for Windows pipes), and connection limits.

IPC configuration is managed via the IpcConfig structure, which includes parameters for endpoint path, maximum frame size, read/write timeouts, maximum connections, and CRC32 variant (source).

Example configuration:

pub struct IpcConfig {
    pub endpoint_path: String,
    pub max_frame_bytes: usize,
    pub read_timeout_ms: u64,
    pub write_timeout_ms: u64,
    pub max_connections: usize,
    pub crc32_variant: Crc32Variant,
}

Removal of Legacy IPC Code and Configuration#

The migration to the interprocess crate was accompanied by a thorough removal of all legacy IPC code and configuration (source). This included:

  • Deleting obsolete binary targets, feature flags, and build scripts.
  • Refactoring Cargo.toml and project structure.
  • Updating documentation to reflect only the new IPC protocol.
  • Removing all references to older IPC mechanisms from the codebase and configuration files.

The codebase now exclusively supports the interprocess-based IPC protocol, with no legacy IPC code remaining.

References#