Builds on the previous change, which had PID 1, the channel and the trait but mounted nothing. The shares are mounted now: a tag names an export, the descriptor names where it lands, and every share goes on nosuid and nodev whether or not it is writable — a share is data handed to the guest, and no descriptor has a way to ask for a setuid binary or a device node in one. Mounting needs privileges a test does not have, so the arguments and flags are derived by a function the tests can assert, which is where the read-only decision lives. Progress is reported in two messages rather than one. A share that did not mount and a command that did not run are not the same incident, and each carries the reason the operating system gave and the path it happened on: a permission error on a named directory can be acted on, where "the share did not mount" cannot. The second layer is relayed and never read. Bytes arrive on the channel in an envelope, cross a unix socket to the workload, and come back the same way. The body is a string rather than nested JSON on purpose: a document this component can index into is a document it can grow to depend on, and then the layer is no longer opaque and the boundary it exists to draw is gone. An envelope is never logged — not the body, not truncated, not at debug level — and the channel name with a byte count is the whole of what may be said about one. The type's Debug is written by hand for the same reason, because a derived one puts the body one careless format string away from a log line. A write to a channel nobody is reading now ends the session the same way a closed read does. A caller that has stopped listening has also stopped being able to say stop, which is one situation and was two outcomes. The guest listens on the relay socket and the workload dials in, which is the convention the other guest sockets already use and removes the startup ordering problem: a workload that is not running yet has simply not connected yet.
crates/
Shared Rust: a library another crate in this repo depends on. A binary someone
runs goes in apps/; shared JS/TS goes in
packages/. The split is by what a thing is, not by what
language it is written in — same rule on both sides of the repo.
Empty today. Components move here one at a time as they are opened.
Two rules
Every version is pinned in the root Cargo.toml. A member writes
tokio.workspace = true and never a version, so two crates in this tree cannot
disagree about a dependency.
Nothing here may depend on anything closed, or name it. This repo is public. A crate that needs a private component is in the wrong repo, and a comment explaining who calls this wants a category noun — "the caller", "a supervising agent" — rather than a name. The requirement is the interesting part; who currently satisfies it is not.