Files
netris-nestri/apps/neshub/README.md
Wanjohi 3c574af2ea feat(neshub): open the media hub
The component nescapture, neswire and nescope all talk to, and the only
thing in the guest that speaks to the client. It muxes their frames into
one iroh QUIC endpoint and fans input back.

Renamed from nestri-guest-hub, which named a location rather than a job.

Four files came across unchanged -- session.rs, ipc_listener.rs,
ticket.rs, screenshot.rs. Between them they mention Steam zero times, and
they import only nesprotocol's open modules; the control feature carrying
LaunchIntent and SteamIdentity is used exclusively by the three files that
are staying closed. The two clusters shared a main.rs and nothing else, so
there was no untangling to do -- only a cut.

main.rs loses --proton, --steamclient-so, --root and the game uid/gid,
and no longer ends by handing the process to a controller. It runs until
it is stopped. Deciding when the box is finished belongs to nesinit.

The ticket used to leave via that controller, so it needed a new way out:
neshub now serves it on a socket and nesinit dials for it. Listening
rather than dialling matches every other socket here and means no startup
ordering to get wrong.

Three tests, where there were none -- the ticket crosses a process
boundary as text now, so a round trip that drops a field would otherwise
be found by whoever cannot connect.
2026-08-26 18:54:09 +03:00

51 lines
2.1 KiB
Markdown

## neshub
One connection out of the box.
Everything inside the guest that produces or consumes a stream talks to neshub
over a Unix socket. neshub muxes it all into a single [iroh](https://www.iroh.computer)
QUIC endpoint and fans client input back the other way. A client dials that
endpoint with a *ticket* and gets video, audio, cursor and stats on it.
### The sockets
| socket | default | direction | carries |
| --- | --- | --- | --- |
| `--video-ipc` | `/tmp/nestri-video.sock` | nescapture → neshub | encoded video frames |
| `--audio-ipc` | `/tmp/nestri-audio.sock` | neswire → neshub | Opus packets |
| `--input-ipc` | `/tmp/nestri-input.sock` | neshub ↔ nescope | input out, cursor and stats back |
| `--stats-ipc` | `/tmp/nestri-stats.sock` | nescapture → neshub | encoder stats |
| `--screenshot-ipc` | `/tmp/nestri-screenshot.sock` | neshub → nescope | a picture of the screen, on request |
| `--ticket-ipc` | `/tmp/nestri-ticket.sock` | neshub → nesinit | the ticket, once |
| — | `/tmp/nescapture-cmd.sock` | neshub → nescapture | IDR requests, encode settings |
neshub is the listener on every one of them and the other side dials in. That
is deliberate: it removes the startup ordering problem entirely, since a
producer that is not running yet simply has not connected yet.
### The ticket
```
nestri:<base64 of {endpoint_addr, stream_name}>
```
Generated once per boot, when the endpoint binds. It is served on a socket
rather than printed because stdout here is a log file inside a virtual machine
and the person who needs it is outside one — `nesinit` reads it and carries it
to the host.
### What it does not do
neshub does not start the payload, know its name, or decide when the box is
finished — `nesinit` owns all three, and shuts the VM down around this process.
The same neshub binary serves a game, a desktop, or anything else that draws to
`nescope`, because it never learns which one it is looking at.
### Running it
```bash
cargo run --bin neshub # every socket at its default
cargo run --bin neshub -- --relay none # direct connections only
RUST_LOG=neshub=debug cargo run --bin neshub
```