Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/hive-sock-client/README.md
atlas 1f214f9f01 bound the request line on the host admin and in-agent sockets
hive-c0re's admin socket and hive-agent's in-agent socket (mode 0666) read
the request with an unbounded read_line: no length cap and no deadline, so
a peer that never sends a newline grows a buffer and pins a task forever.

The bounded reader (16 MiB cap) moves from hive-c0re's socket_server into
hive-sock-client, the one crate both daemons already share or can take, and
gains optional deadlines for the first byte and for the rest of the line.
Both sockets now use it and answer an over-long or stalled line with the
same error reply the bounded sockets give, then close. The admin socket
bounds only the started line (clients may idle between requests); the
in-agent socket is single-shot and bounds silence too. The per-agent
sockets keep their behaviour (no deadline).
2026-10-17 09:09:20 +02:00

2.7 KiB

hive-sock-client

One JSON-line-over-unix-socket client, shared by every daemon that speaks to a hyperhive socket, plus the bounded request-line reader the socket servers share.

The wire protocol is the same everywhere: connect, write one line of JSON, read one line of JSON back. Before this crate existed, five daemons each carried their own copy of that — two of them byte-identical — and the retry, response-handling and error-context behaviour drifted between them.

The crate is generic over the request and response types, so it is protocol-agnostic: the host-served control socket and the harness's in-agent socket both use it, with their own wire-type crates.

Shapes

  • request / request_retried — write, then decode the response line. request_retried additionally reports how many retries it took, for callers (MCP tool handlers) that want to tell the model a socket flake happened so it doesn't retry at the LLM level.
  • notify — write, half-close, drain the response line and discard it. For fire-and-forget ops where the reply carries nothing the caller acts on. The drain is not optional: without it the server's write-back lands on a closed socket.

Server side

read_request_line is the one bounded reader for the request line a socket server accepts: at most REQUEST_LINE_MAX_BYTES, with optional LineDeadlines (wait for the first byte, wait for the newline). A server answers RequestLine::refusal() and closes. hive-c0re uses it on the host admin socket and the per-agent sockets; hive-agent on the in-agent socket.

Retry is a policy value, not a fork

Retry::RideOutRestart backs off 2/4/8/16/30s (60s total), sized to ride out a service restart. It is for callers with no natural retry of their own — a stall there costs a surfaced tool error and the tokens to handle it.

Retry::None fails fast. It is for callers already inside a poll loop, where the poll interval is the retry and a second backoff would only stack sleeps and delay the rest of the batch.

Two named policies, not a configurable schedule: nobody needs a third yet, and naming them keeps the reason for the choice at the call site.

Error contract

Errors always name the socket path. That detail is load-bearing: a permission problem on a socket that reads as "is the daemon running?" sends the operator to fix the wrong thing.

A refused or missing socket additionally gets a "may be restarting" hint, and an exhausted retry schedule records how long it tried, so a surfaced error reads as the likely transient it usually is.

Serialisation and deserialisation failures are never retried — retrying identical bytes reproduces the same failure. They are raised outside the retry loop, so only connect/IO/short-read failures ever reach it.