Documents
ADR-0004 - Migrate IPC Transport to Interprocess Crate
ADR-0004 - Migrate IPC Transport to Interprocess Crate
Type
External
Status
Published
Created
Apr 18, 2026
Updated
Apr 18, 2026
Updated by
Dosu Bot
Source
View

Status#

Accepted

Status note (2026-04-18 backfill): Prior version of this page carried status "In Progress," which conflated the decision state with the implementation state. The decision was accepted at authorship; Phase 1 (Tokio native APIs) has since shipped via PRs #75, #80, and #81. Implementation status belongs in Git/CI, not the ADR.

Context#

DaemonEye historically implemented IPC communication between procmond and daemoneye-agent using custom hand-rolled code:

  • Custom Unix socket server (~400 lines) with manual varint length framing, CRC32 validation, and connection management
  • Custom Windows named pipe server with platform-specific error handling
  • Complex protocol layer with custom message parsing and flow control
    This imposed a real maintenance burden. Specific issues:
IssueImpact
High maintenance burdenComplex custom networking code duplicates standard library functionality
Platform complexitySeparate implementations for Unix and Windows with different error handling
Limited testingCustom implementation lacks battle-testing compared to standard libraries
Development velocityTime spent on IPC infrastructure instead of core security features
Requirements that any replacement must preserve:
  • Security — all current security features (socket permissions, connection limits, timeouts, max frame size).
  • Protocol compatibility — existing protobuf message format and CRC32 validation.
  • Performance — no regression in message throughput or latency.
  • Rollback capability — ability to revert to the legacy implementation if issues arise.

Decision#

Migrate in two phases.

Phase 1 — Simplify with Tokio native APIs#

Replace custom socket implementations with standard Tokio APIs:

PlatformAPI
Unixtokio::net::UnixListener / tokio::net::UnixStream
Windowstokio::net::windows::named_pipe
Preserve the existing protobuf + CRC32 framing protocol. Feature flags: ipc-interprocess (default) vs ipc-legacy (rollback).

Phase 2 — Interprocess crate migration (future)#

Evaluate and potentially migrate to interprocess::local_socket for true cross-platform unification once Phase 1 is validated. This is optional — Phase 1 already solves the maintenance and reliability issues; Phase 2 buys additional uniformity.

Implementation details#

Transport layer configuration:

pub struct IpcConfig {
    pub transport: TransportType,
    pub endpoint_path: String, // Unix socket path or Windows pipe name
    pub max_frame_bytes: usize, // 1MB default
    pub accept_timeout_ms: u64, // 5 seconds
    pub read_timeout_ms: u64, // 30 seconds
    pub write_timeout_ms: u64, // 10 seconds
    pub max_connections: usize, // 16 default
    pub crc32_variant: Crc32Variant, // IEEE default
}

Wire protocol (preserved from legacy):

Frame format (little-endian):
├── u32 length (N) of protobuf message bytes
├── u32 crc32 checksum over message bytes
└── N bytes protobuf message (prost-encoded)

Security preservation:

ControlImplementation
Unix socket permissionsDirectory 0700, socket 0600 (owner-only access)
Connection limitsTokio semaphore-based connection limiting
TimeoutsPer-operation timeouts with graceful degradation
Network isolationLocal sockets/pipes only, verified by tests
Rollback strategy: feature flag ipc-legacy maintains original implementation; config toggle ipc.transport = "legacy"; runtime telemetry identifies active transport; zero protocol changes enable hot rollback.

Consequences#

Positive#

  • Reduced complexity. ~600 lines of custom code replaced with ~200 lines using standard APIs.
  • Better reliability. Tokio's battle-tested networking stack.
  • Improved maintainability. Standard error handling and lifecycle management.
  • Enhanced debugging. Standard tooling (strace, pipe inspection, socket utilities) works with Tokio sockets.
  • Future extensibility. Phase 2 enables true cross-platform unification and additional transports (shared memory, enhanced named-pipe support).

Negative#

  • Performance regression risk. Likelihood low — Tokio APIs are highly optimized. Mitigation: comprehensive benchmarks comparing old vs new; acceptance criterion <5% latency regression.
  • Platform-specific issues. Likelihood medium — different behavior across platforms. Mitigation: extensive cross-platform testing (Linux, macOS, Windows); acceptance criterion all current tests pass on all platforms.
  • Security boundary changes. Likelihood low — same permission model maintained. Mitigation: security-focused testing with permission validation; acceptance criterion identical access control and privilege separation.
  • Interprocess crate limitations (Phase 2). Likelihood medium — newer crate with less real-world usage. Mitigation: Phase 1 with Tokio provides stable fallback; revert to Phase 1 if Phase 2 issues arise.

Neutral#

  • Success metrics for Phase 1:
    • Functional — all existing IPC tests pass; zero protocol changes required; feature-flag rollback works; cross-platform compatibility maintained.
    • Performance<5% latency regression; equal or better throughput; memory within 10% of current.
    • Security — Unix socket permissions enforced (0700 dir, 0600 socket); connection limits respected under load; all timeouts trigger appropriately; no unintended network listeners.
    • Operational — zero breaking changes for operators; configuration backward compatibility; logging and metrics parity.
  • Implementation status at time of writing: Phase 1 shipped via PRs #75, #80, #81 (Tokio native APIs on Unix + Windows named pipes). Phase 2 evaluation has not started; no current plan to proceed unless Phase 1 reveals specific gaps.

Alternatives Considered#

Keep the custom implementation#

No migration risk, complete control over every byte of the protocol. Ongoing maintenance burden and limited feature expansion; platform-specific bugs that battle-tested crates have already fixed; time spent on IPC plumbing instead of security features. Rejected — the maintenance cost is the reason this ADR exists.

Direct migration to the interprocess crate#

Single migration effort, immediate cross-platform benefits via one API. Higher risk (bigger diff, less opportunity to identify regressions), less mature ecosystem at the time, harder rollback if the interprocess crate has a bug that surfaces only under DaemonEye's traffic pattern. Rejected — phased approach (Tokio-native first, interprocess second) reduces risk at the cost of two migrations.

gRPC or other RPC framework#

Industry standard, rich features, good tooling (code generation, streaming, deadline propagation). Protocol breaking changes (existing protobuf + CRC32 framing would be replaced), network-stack dependency even for local IPC, heavy overhead for a two-process local-only use case. Rejected — protocol compatibility is a hard requirement and local-only IPC doesn't justify the gRPC machinery.