fix(nesinit): the relay may not stall the session, and may not buffer without end

Three problems in the relay, all of them found in review.

Handing an envelope over waited for room. That loop also carries stop,
shutdown and the workload's exit, so a workload slow to read its own mail — or
one that never connected — could hold the lifecycle layer still behind it. It
never waits now: an envelope that will not fit is dropped, which costs nothing,
because what crosses this layer is re-sent when it changes.

Envelopes were queued for a workload that was not there. The queue filled with
copies that would be stale by the time anyone connected, and filling it was
what stalled the session. Nothing is held while the socket has nobody on it.

A frame had no maximum length. The workload can write for as long as it likes
without ever sending a newline, and the process assembling that is the one the
kernel has been told not to kill, so the memory it takes comes out of
everything else in the guest. Past 64 KiB the connection is dropped and the
relay waits for the next one; the failure says how long the frame got and
nothing about what was in it.

Also, a tag or a mount point with a nul byte in it was quietly turned into an
empty string, so an unmountable descriptor arrived later as a mount failure
about something else, after the mount point had already been created. It is
refused by name now, before anything is created.

The relay's tests grew a harness that waits for the connection to be carried
before sending anything down it, because dropping what arrives with nobody
connected made "connected" something a test has to establish rather than
assume.
This commit is contained in:
KAAL1
2026-09-05 07:07:34 +00:00
committed by Wanjohi
parent 7b99f49f62
commit a94b323edb
4 changed files with 381 additions and 66 deletions
+13
View File
@@ -65,6 +65,19 @@ Both directions. Inside the guest an envelope crosses a unix socket at
That socket is a mechanism and expected to change; the envelope is the boundary
and is not.
Nothing is held for a workload that is not on the relay, and nothing waits on
one that is slow to read. An envelope that arrives with nobody connected is
dropped, and so is one that arrives faster than the workload reads: what
crosses this layer is re-sent when it changes, so a queued copy is a stale copy
— and the queue that would hold it is on the same loop that carries stop,
shutdown and the workload's exit, none of which may wait behind it.
A frame is capped at 64 KiB. The workload is on the other end of that socket
and can write for as long as it likes without ever sending a newline; the
process assembling it is the one the kernel has been told not to kill, so an
unbounded buffer there comes out of everything else in the guest. Past the cap
the connection is dropped and the relay waits for the next one.
`body` is a string rather than nested JSON, deliberately. A document nesinit
can index into is a document nesinit can grow to depend on, and then the layer
is no longer opaque and the boundary it exists to draw is gone.