Local IPC vs HTTP
Local IPC vs HTTP
When two processes on the same machine need to communicate, you have two paths: stick local (Unix socket, named pipe, shared memory) or go through HTTP-over-loopback. The local-IPC path is faster, simpler, more secure, and harder to expose accidentally. HTTP is easier to consume from browsers and remote consumers.
This is one of the more important "default-choice" decisions in tool design. Most local daemons should default to local IPC.
The two paths
Local IPC HTTP over loopback
───────── ──────────────────
Client process Client process
│ │
│ socket(AF_UNIX, SOCK_STREAM) │ HTTP client (curl, fetch)
▼ ▼
Kernel buffer TCP/IP stack
│ │
▼ ▼
Daemon process loopback (lo) interface
│
▼
TCP/IP stack
│
▼
HTTP server (handler dispatch)
│
▼
Daemon process
HTTP-over-loopback adds two TCP/IP traversals plus HTTP parsing. Local IPC skips both.
Performance
Order-of-magnitude figures (rough, modern hardware, single message):
| Mechanism | Round-trip latency | Throughput (small msgs) |
|---|---|---|
| Function call (in-process) | ~1ns | – |
| Shared memory + lock | ~50ns | – |
| Unix socket (stream) | ~5–10µs | 1M+ msgs/sec |
| TCP loopback | ~15–30µs | 300K msgs/sec |
| HTTP over loopback (no keep-alive) | ~100–200µs | 30K msgs/sec |
| HTTP over loopback (with keep-alive) | ~30–60µs | 100K msgs/sec |
Per-call latency rarely matters. Throughput at scale does. Most CLI/agent invocations are infrequent enough that even HTTP loopback is fine; for a tool like a language server doing 1000s of completions/sec, local IPC is the right default.
Security
Unix socket security via filesystem permissions:
- Mode
0700— owner-only. Other users on the same machine cannot connect. - Mode
0660+ group ownership — group access. - The directory containing the socket also matters (can't symlink to it from elsewhere).
HTTP-over-loopback security:
- Bound to
127.0.0.1— accessible to any process running as any user on the same machine. - No filesystem-style permission system.
- Can be exposed to the network accidentally (
0.0.0.0bind). - Vulnerable to DNS rebinding attacks if a browser is the client.
For a per-user daemon holding sensitive state (email, debug session with secrets, project state), Unix sockets with 0700 are dramatically safer than HTTP-on-localhost.
Browser accessibility
The single biggest reason to use HTTP-over-loopback: browsers can't open Unix sockets directly. If your daemon needs to be consumed from a web app running in the user's browser, HTTP (or WebSocket) is forced.
Workarounds:
- HTTP bridge process — small extra binary that translates HTTP requests to local IPC calls. Browser → bridge (HTTP) → daemon (Unix socket). Common pattern; gives you both worlds.
- Run a local web server inside the daemon — sidesteps the bridge but exposes the daemon to all local processes.
Most "local web UI" tools do the bridge. Some (Jupyter, gitkraken) run web servers directly.
Discoverability
HTTP wins on tooling:
- Browse the API with
curl, Postman, Insomnia - View traffic with browser devtools
- Document with OpenAPI / Swagger
- Mock with
wiremock
Unix sockets need:
socat UNIX-CONNECT:/path/sock STDIOto inspect manually- Custom debug clients
- Less standard documentation tooling
For tools where third parties build clients, HTTP's tooling advantage matters. For tools where the project ships its own clients (mxr, lazydap), local IPC's simplicity wins.
Versioning
Both can be versioned, but conventions differ:
- HTTP: URL paths (
/v1/...,/v2/...), orAccept: application/vnd.foo.v1+jsonheaders. - Local IPC: protocol version field in the envelope. Simpler. mxr and lazydap both put
version: u32in every IpcMessage; clients refuse to talk on mismatch.
When to use HTTP locally
- The daemon needs browser clients.
- The daemon will eventually be exposed remotely (and you want one wire format).
- Third-party clients are a major use case and you want maximal tooling.
- You need OpenAPI / Swagger documentation.
When to use Unix sockets
- All clients are processes you control or co-design with.
- Performance matters (high message rate).
- Security via filesystem permissions is appealing.
- You want simpler, more debuggable wire format.
- Cross-machine isn't a near-term concern.
What mxr and lazydap do
Both use Unix sockets. Both could (in principle) ship a separate mxr-bridge / lazydap-bridge binary that exposes HTTP-over-loopback for browser consumers. Neither has bothered yet — there's no concrete browser frontend asking for one.
If a future Electron app or web dashboard arrives, the bridge is a one-weekend project on top of the existing protocol. The local IPC stays clean; the bridge handles HTTP-specific concerns (CORS, sessions, etc.).
See also
- Unix Domain Sockets — the local IPC mechanism
- Inter-Process Communication (IPC) — broader menu
- TCP vs Unix Sockets vs Named Pipes vs Shared Memory — comparative
- How Processes Talk to Each Other — synthesis