The first session record on the new controller (wired fiber, 8 Mbps
ceiling) put capacity at 9 Gbps. The client reads fragments in batches
that share a timestamp, so on a fast path one batched train looks
instantaneous, and the highest sample is exactly that train. The
ceiling capped it there. On a path near our rate it would have sent
every step-up straight to the ceiling and back.
Dispersion error is one-sided at each end (batching reads high,
interleaved cross traffic reads low), so the estimate is now the median
of the samples since the last overload, kept as two heaps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hub side of nescore's 2026-10-16 video-rate spec.
nesprotocol: a per-frame feedback datagram (kind 3, client to hub on the
video connection) carrying bytes, fragments, send time, first-fragment
arrival, arrival spread and first-fragment queueing delay. The hub stats
breakdown gains measured capacity and delivered share.
neshub:
- rate.rs, a per-client path model. Capacity is measured from each
frame's train dispersion. The bottleneck counts as busy when the next
train lands no later than its first fragment's own serialisation after
the last one, and as overloaded when, during a busy run, the first
fragments' queue grows and more is sent than delivered. Overload drops
the rate at once to drain at the rate the queue built, then settles at
the measured share. Steps up go to the measured capacity, or the
highest rate the path has proven quiet at for longer than its own
longest gap between overloads at or below it. Silence holds at the
last share. Tested against a modelled FIFO bottleneck: above the path,
below it, CBR keyframes, a capacity drop and recovery, recurring cross
traffic.
- control.rs: the slowest client's path decides, per frame as feedback
arrives. Gone: the loss thresholds, back-off factor, climb fraction,
deadband and queue thresholds. Ceiling, floor, manual and constant
quality behave as before.
- congestion.rs: an endpoint congestion controller that holds nothing
back (window above u32::MAX, where noq stops pacing), so frames leave
as trains for the client to measure.
media-transport.md now says the hub turns transport pacing off, and why.
The client does not send feedback yet (nescore step 2). Until it does,
the rate stays where the session started.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A client's session is created with its first connection, and its media
channels with it, but each channel's writer only starts when that
carrier's own connection attaches. Every packet broadcast in between
piled up and went out in one burst once the connection was ready.
Client session records showed it as about 160 ms of audio arriving at
once at session start, and over half a second of video.
A session now takes a stream's media only once that stream's writer
exists. Before then there is nobody to give it to, and media is live or
nothing. No threshold: the rule is the attach event.
A video attach also asks for a keyframe and holds deltas until one
goes out. A fresh writer has sent no reference frame, so nothing else
is decodable, and the client no longer has to fail a decode and ask, a
round trip later.
New e2e test: audio broadcast before the audio carrier attaches never
reaches the client (fails without the fix: first position 0), and a
video attach asks for a keyframe. The pipeline-delay tests now build v4
payloads; they had been passing on v3 byte offsets by accident.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Step 1 of nescore's 2026-10-16 media timing spec. Media carried no
send time and audio no time at all, so every layer had to infer lateness
from gaps between arrivals.
- clock: one sender clock, CLOCK_MONOTONIC in u64 microseconds, shared
by every process that stamps media. Off unix the client uses its own
process clock, which is all a receiver needs.
- Datagram headers (and PyroWave's) carry sent_us, stamped by neshub at
hand-off to QUIC. Keyframe streams open with it too.
- Video carries captured_us (from nescapture's present time) in place of
milliseconds since the encoder started. Audio carries its first
sample's position on the PipeWire graph clock, which neswire already
read to fill missed cycles. The IPC stamp is a u64 carrying either.
- delay: microseconds. The baseline no longer expires on a 30 s timer:
clock skew is measured as the least-squares slope of each second's
minimum one-way delay, and only a path change resets the baseline.
observe() returns each unit's queueing delay.
- ReceiverReport carries video and audio queueing delay summaries for
the rate control that will consume them.
- Shared encode/decode helpers for video and audio payloads, so the hub
and receivers stop hand-building byte offsets.
No compatibility kept. STREAM_VERSION is 4. nescore needs a pin and the
client side (step 2) before it can talk to this.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With RLIMIT_RTPRIO alone, neswire's client loop went SCHED_FIFO 83 and
the PipeWire server's graph loop stayed SCHED_OTHER. The server's
module-rt asks for nice -11 first, and when that is refused (Max nice
priority 0) it hands off to rtkit, which grants nothing in a box. The
client config asks for no nice level, which is why only neswire got it.
RLIMIT_NICE 39 (nice down to -19) next to rtprio 95, as PipeWire's own
25-pw-rlimits.conf grants.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inside a box, every PipeWire, wireplumber and neswire thread was running
SCHED_OTHER. The rtkit -> polkit -> system bus chain started but granted
nothing. With a game keeping the vCPUs busy, the graph's 5 ms cycles ran
late. pw-top counted no xruns, but neswire sent packets in bursts, and
the client saw 15-25 ms gaps in audio alone (video arriving on time in
the same seconds), which emptied its 10 ms cushion every 30 s or so.
pipewire and neswire now exec with RLIMIT_RTPRIO 95, set before the uid
drop. module-rt checks that first and sets SCHED_FIFO itself, which is
how PipeWire is meant to run outside a desktop session. The workload
doesn't get the grant, so a game can't take the CPU away from the graph.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
neslet now puts nestri.port= on the boot line and installs its forward
and pinned masquerade for that port before the box boots. nesinit hands
it to neshub as NESTRI_BIND_PORT, and neshub binds 0.0.0.0:<port> in
place of iroh's random default. Every flow, the relay's included, then
leaves on one public port, and the address the relay reports is the one
peers can punch through to. No nestri.port= keeps the old behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An evicted discardable BO has no GTT fallback, so under VRAM pressure a
box could lose its GE rings and fail every graphics submission until
VRAM freed up. Seen live: Control failed 127k submissions after a
neighbour inside its share evicted it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guest's audio stream ran about 3.5% short of realtime: 193 packets a
second where 200 were due, with no network loss at all. The client plays
at its own 48 kHz, so its buffer drained and underran every few seconds,
idle or loaded.
The graph was running 128-frame (2.67 ms) cycles. The 256 floor for VMs
matched on `cpu.vm.name`, which PipeWire reads from DMI and nesbox does
not provide, so it never applied, and neswire's 240-frame request was
rounded down to a power of two. A busy guest missed ~20 of those cycles a
second (pw-top ERR on neswire), and each missed cycle's audio never
reached neswire while the graph clock moved on.
- PipeWire is fixed at 240 frames, power-of-two rounding off: one cycle,
one 5 ms Opus packet, twice the headroom of before.
- neswire reads the graph position each cycle and fills exactly the
frames a missed cycle skipped, as silence, so the stream stays at
48 kHz whatever the scheduler does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each client states what it decodes when it connects, and the hub passed
each statement to the encoder as it arrived. The newest client decided, so
a client that only decodes H.264 lost its picture as soon as one that
decodes AV1 or H.265 joined.
The hub now keeps every client's statement and gives the encoder their
intersection, recomputed when a client joins, when one leaves, and when the
controller returns to automatic. A room settles on the best codec all of
its clients share, and moves up again once the client holding it back has
left. Manual mode still leaves the encoder alone; the change is applied
when the mode goes back to automatic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
send_many_datagrams queues a frame's fragments under one lock hold and
one driver wake instead of one per fragment. It also rejects the whole
frame on TooLarge, so a path that shrinks mid-frame no longer leaves half
a frame on the wire for the client to time out. Eviction behaviour is
unchanged. A refused PyroWave frame now keeps its sequence number, since
nothing went out under it. pyro_bench mirrors both ends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The end that reads the first frame of an attach stream to learn whom the
connection is for then forwards the rest untouched, and what arrived behind that
frame in the same read belongs to the stream.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The host can now ask a box for a shell on a pseudo-terminal, the way a container
runtime attaches to a container. `HostToGuest::Attach` starts the process, and
the guest then dials the host on a vsock port of its own (7001) and carries the
terminal's bytes over a small framed stream: data both ways, a resize, a hangup,
and the exit status last. A screenful of output on the JSON-lines control channel
would hold every other message in the box behind it, which is why it is not
there. A failure to start is the exception, and is said on the control channel
because nothing has connected to the data port yet.
A shell is not a launch: it never ends the box, and the box ending ends it. It
is started through the same reaper as everything else, so it cannot become the
zombie that exists to be prevented. With a terminal on the other end the PTY is
ordinary; without one it is put in raw mode so a command run for its output gets
its bytes back unrewritten. Closing the stream hangs the shell up, process group
and all.
The frame codec is a pure bytes-in, frames-out module in nesprotocol with a
payload cap and no skipping of unknown tags, since a length read from a guest is
read across a trust boundary. No version bump: adding a message does not need
one, and a guest that predates it simply never dials.
The runtime stage gains strace, lsof and less, so a root shell in a box is not
one that cannot trace a process.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There is no service manager in a box, so nothing applies the distribution's
sysctl configuration, and the guest ran on the kernel's own defaults. Two of
them are too low for a modern engine under Proton:
- vm.max_map_count defaults to 65530. A large Unreal Engine 5 title maps far
more regions than that, and the failure is a null allocation deep inside the
engine or Wine rather than a message about mappings. It is set to 2147483642,
the value SteamOS and the distributions that package Proton use. It is read
back after the write, because an accepted write that did not take reports
success while the box is still unconfigured.
- RLIMIT_NOFILE is raised on init, which every process in the box inherits. A
child can lower a limit but cannot raise its hard one, so the unprivileged
workload could never get above what it started with. 524288 is the hard limit
Proton's documentation asks for. It is only ever raised.
Both are best effort and each failure says what it costs.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The distribution's rust package is built against the distribution's LLVM, and
when the two repositories this image draws from disagree about llvm-libs, rustc
itself fails to start with a symbol-lookup error, before any of our code is
involved and in every stage that builds Rust. rustup's toolchain links nothing
of the distribution's, so `rustup default stable` replaces the package.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
`umu-run` is how a title that is not Steam's is launched, so the game fixes
Proton carries apply outside Steam. It is built from a pinned tag in its own
stage rather than installed from the distribution, whose package declares a
long list of lib32 libraries and desktop packages for the Steam Linux Runtime
container this image never runs.
It is built against the distribution's urllib3 and pyzstd instead of the copies
it can vendor: those are built from source with pinned build tooling, and a
rolling distribution moved past a pin (setuptools-scm 10 against a <10
ceiling) and failed the build. The image adds python-xlib, python-urllib3 and
python-pyzstd, none of which pull in anything 32-bit.
Two checks keep it honest. umu-run has to start, because a missing Python module
otherwise shows up as a game that never starts and reads like a script failure;
and no lib32 package may be installed.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The boot descriptor gains `images`: read-only EROFS images mounted on their
own, with no writable layer over them. nesinit mounts them first, before
overlays and shares, with the same nosuid/nodev guards every layer has and
without noexec because what is in one is run. CONTROL_VERSION moves to 5: the
descriptor refuses fields it does not know, and a guest image without Proton is
useless to a host that does not send one, so the mismatch is named at the
handshake.
The guest rootfs no longer contains Proton. The checks that used to guard the
in-image copy (every library its programs and Wine's core link, an interpreter
behind the entry point) move to a compat_check stage that verifies the Proton
named by PROTON_IMAGE and leaves a marker in the image saying which one. The
leaf stages copy that marker out of it so the stage cannot be skipped as
unused. With nothing at a Proton path, a box started without one fails saying
where it looked.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
At this point, why not..
Calling it `nespyro` since it's slightly modified, uses slang shader
language instead, along other things..
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The seed was a release behind, so it now matches the tree it is migrated
onto. The scheduler, block-layer and TCP additions the tree ships are pinned
in the fragment, where the build checks them, rather than left to the seed's
defaults. Transparent huge pages move to always, since games map big heaps
without asking for them.
Clang builds the kernel now: whether thin-LTO is offered at all is decided
when Kconfig runs, so LLVM=1 applies to the config steps as well as the
compile, and missing tools fail before anything starts. Allocation and call
path hardening (init-on-alloc, freelist hardening, stack protector) and
debug info are pinned off alongside the speculation mitigations, and the
slab pin the minimal seed needs is restored.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds `nesgamepad`, basically "diet-coke vimputti" for Nestri's microVM
needs here.
Comes with direct mapping for:
- Sony
- DualShock 4 (v2), DualSense
- Microsoft
- Xbox 360 pad, Xbox One S pad
- Nintendo
- Pro Controller
- Generic pad for unknown controllers
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Get this thing going..
<!-- greptile_comment -->
<!-- greptile_summary -->
<h2><a
href="https://app.greptile.com/api/retrigger?id=63134761"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/RetriggerDark.svg?v=1"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/Retrigger.svg?v=1"><img
alt="Retrigger"
src="https://greptile-static-assets.s3.amazonaws.com/badges/Retrigger.svg?v=1"
align="right"></picture></a>Confidence Score: 5/5</h2>
The PR appears safe to merge; all previous findings are resolved and the
latest readiness change introduces no established actionable regression.
<h3>Summary</h3>
- Establishes required guest filesystems, runtime directories, device
permissions, and service processes.
- Reports initialization and service deaths over the lifecycle channel.
- Supports launch, restart, and shutdown commands for a resident guest.
- Separates service and workload identities and configures per-launch
runtime environments.
- Removes the currently inactive nescope screenshot option and makes
capture-chain verification fail explicitly when compositor readback is
unavailable.
- Reworks the guest image around `nesinit` as PID 1 without a
distribution service manager.
<h3>Diagram</h3>
```mermaid
sequenceDiagram
participant Host
participant Init as nesinit
participant FS as Guest filesystems
participant Services as Service stack
participant Workload
Init->>Host: Ready(protocol version)
Host->>Init: Boot(mount descriptors)
Init->>FS: Establish and mount shares
Init->>Services: Spawn services in order
Services-->>Init: Required sockets ready
Init->>Host: Initialized(service names)
Host->>Init: Launch(id, exec, on_exit)
Init->>Workload: Spawn with isolated UID/runtime
Init->>Host: Started(id)
Workload-->>Init: Exit status
Init->>Host: WorkloadExited(id, status)
Host->>Init: Launch / Restart / Shutdown
```
<sub>Reviews (4) · Last reviewed commit: ["fix(nesinit): readiness is a
socket
that..."](https://github.com/nestrilabs/nestri/commit/731d34df9df30463f67963363424cb9487e89196)</sub>
<!-- /greptile_comment -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
## Description
Next step would be having full DE environment variant I guess? I'll see
later if it's doable in this PR or if I'll do separate one for keeping
things small and manageable for once 😅
- Added easily doable variants for runners, with simple CI build matrix.
- Added playsite in CI builds finally.
- Some CI formatting and naming fixes.
- Removed PR full runner builds as they kept failing due to lack of disk
space on GH runner.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* New dedicated runner images for Steam, Heroic, and Minecraft plus a
common runtime and builder images.
* **Chores**
* CI/workflow reorganization to build and publish more runner variants
and base images.
* Installer and package tweaks (package manager flags, CUDA enablement)
and updated build tooling.
* Unified startup to use a constructed launch command; removed two
default environment exports.
* Added container ignore patterns.
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
### First commit
Restructured protobuf schemas to make them easier to use across
languages, switched to using them in-place of JSON for signaling as
well, so there's no 2 different message formats flying about. Few new
message types to deal with clients and nestri-servers better (not final
format, may see changes still).
General cleanup of dead/unused code along some bug squashing and package
updates.
TODO for future commits:
- [x] Fix additional controllers not doing inputs (possibly needs
vimputti changes)
- [x] ~~Restructure relay protocols code a bit, to reduce bloatiness of
the currently single file for them, more code re-use.~~
- Gonna keep this PR somewhat manageable without poking more at relay..
- [x] ~~Try to fix issue where with multiple clients, static stream
content causes video to freeze until there's some movement.~~
- Was caused by server tuned profile being `throughput-performance`,
causing CPU latency to be too high.
- [x] Ponder the orb
### Second + third commit
Redid the controller polling handling and fixed multi-controller
handling in vimputti and nestri code sides. Remove some dead relay code
as well to clean up the protocol source file, we'll revisit the meshing
functionality later.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added software rendering option and MangoHud runtime config;
controller sessions now support reconnection and batched state updates
with persistent session IDs.
* **Bug Fixes**
* Restored previously-filtered NES-like gamepads so they connect
correctly.
* **Chores**
* Modernized dependencies and protobuf tooling, migrated to
protobuf-based messaging and streaming, and removed obsolete CUDA build
steps.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
Oops.. another massive PR 🥲
This PR contains multiple improvements and changes.
Firstly, thanks gst-wayland-display's PR
[here](https://github.com/games-on-whales/gst-wayland-display/pull/20).
NVIDIA path is now way more efficient than before.
Secondly, adding controller support was a massive hurdle, requiring me
to start another project
[vimputti](https://github.com/DatCaptainHorse/vimputti) - which allows
simple virtual controller inputs in isolated containers. Well, it's not
simple, it includes LD_PRELOAD shims and other craziness, but the
library API is simple to use..
Thirdly, split runner image into 3 separate stages, base + build +
runtime, should help keep things in check in future, also added GitHub
Actions CI builds for v2 to v4 builds (hopefully they pass..).
Fourth, replaced the runner's runtime Steam patching with better and
simpler bubblewrap patch, massive thanks to `games-on-whales` to
figuring it out better!
Fifth, relay for once needed some changes, the new changes are still
mostly WIP, but I'll deal with them next time I have energy.. I'm spent
now. Needed to include these changes as relay needed a minor change to
allow rumble events to flow back to client peer.
Sixth.. tons of package updates, minor code improvements and the usual.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* End-to-end gamepad/controller support (attach/detach, buttons, sticks,
triggers, rumble) with client/server integration and virtual controller
plumbing.
* Optional Prometheus metrics endpoint and WebTransport support.
* Background vimputti manager process added for controller handling.
* **Improvements**
* Multi-variant container image builds and streamlined runtime images.
* Zero-copy video pipeline and encoder improvements for lower latency.
* Updated Steam compat mapping and dependency/toolchain refreshes.
* **Bug Fixes**
* More robust GPU detection, input/fullscreen lifecycle,
startup/entrypoint, and container runtime fixes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
Adds PEER_URL env variable for setting peer URL (query param still takes
priority if set).
- Useful for self-hosters
- Was a pain to figure out
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- New Features
- Support configuring the peer server URL via an environment variable,
with automatic fallback to the URL parameter or a default.
- Server-provided configuration is securely passed to the client to
simplify deployment setup.
- Chores
- Excluded common build artifacts and IDE directories from container
contexts to reduce image size and speed up builds.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
Works in apptainer now.. podman is still the goat since apptainer needs
docker treatment and even more..
- Added container detection so podman can be used to it's fullest, the
non-sane ones are handled separately..
- Added video bit-depth option, cuz AV1 and 10-bit encoding go well
together.
- Some other package updates to nestri-server.
- General tidying up of scripts to make multi-container-engine handling
less of a pain.
- Updated old wireplumber lua script to new json format.
Further changes:
- Removed unused debug arg from nestri-server.
- Moved configs to config file folder rather than keeping them in
containerfile.
- Improved audio configs, moved some into wireplumber to keep things
tidy.
- Bit better arg handling in nestri-server.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Optional 10‑bit video support and auto‑launch of an app after display
setup.
* **Changes**
* Standardized runtime/user env to NESTRI_* with updated home/cache
paths and explicit LANG; password generation now logged.
* Improved container/GPU detection and startup logging; reduced blanket
root usage during startup; SSH setup surfaced.
* WirePlumber/PipeWire moved to JSON configs; low‑latency clock and
loopback audio policies added; audio capture defaults to PipeWire.
* **Chores**
* GStreamer/libp2p dependency upgrades and Rust toolchain pinned; NVIDIA
driver capability exposed.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Adds a basic standalone "play site" that mimics current one in apps/www.
This is so self-hosters don't need to host whole site, but can just use
small version of it.
Yet to test so marking as draft, not at home currently so may take some
time. Also might be good idea to make Caddy-powered container out of
this later?
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
- New Features
- Introduces a standalone Play site with server output, accessible on
0.0.0.0:3000.
- Streams video via WebRTC into a canvas with continuous frame
rendering.
- Fullscreen and pointer lock support with optional keyboard lock for
navigation keys.
- Room-based routing with offline and loading states.
- Responsive 16:9 canvas and improved default layout styling.
- Chores
- Adds a multi-stage container build for efficient runtime images and a
lightweight init process.
- Includes configuration and project setup for the standalone package.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <datcaptainhorse@users.noreply.github.com>
## Description
Adds a basic standalone "play site" that mimics current one in apps/www.
This is so self-hosters don't need to host whole site, but can just use
small version of it.
Yet to test so marking as draft, not at home currently so may take some
time. Also might be good idea to make Caddy-powered container out of
this later?
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- New Features
- Introduces a standalone Play site with server output, accessible on
0.0.0.0:3000.
- Streams video via WebRTC into a canvas with continuous frame
rendering.
- Fullscreen and pointer lock support with optional keyboard lock for
navigation keys.
- Room-based routing with offline and loading states.
- Responsive 16:9 canvas and improved default layout styling.
- Chores
- Adds a multi-stage container build for efficient runtime images and a
lightweight init process.
- Includes configuration and project setup for the standalone package.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <datcaptainhorse@users.noreply.github.com>
## Description
Whew..
- Steam can now run without namespaces using live-patcher (because
Docker..)
- Improved NVIDIA GPU selection and handling
- Pipeline tests for GPU picking logic
- Optimizations and cleanup all around
- SSH (by default disabled) for easier instance debugging.
- CachyOS' Proton because that works without namespaces (couldn't figure
out how to enable automatically in Steam yet..)
- Package updates and partial removal of futures (libp2p is going to
switch to Tokio in next release hopefully)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- SSH server can now be enabled within the container for remote access
when configured.
- Added persistent live patching for Steam runtime entrypoints to
improve compatibility with namespace-less applications.
- Enhanced GPU selection with multi-GPU support and PCI bus ID matching
for improved hardware compatibility.
- Improved encoder selection by runtime testing of video encoders for
better reliability.
- Added WebSocket transport support in peer-to-peer networking.
- Added flexible compositor and application launching with configurable
commands and improved socket handling.
- **Bug Fixes**
- Addressed NVIDIA-specific GStreamer issues by setting new environment
variables.
- Improved error handling and logging for GPU and encoder selection.
- Fixed process monitoring to handle patcher restarts and added cleanup
logic.
- Added GStreamer cache clearing workaround for Wayland socket failures.
- **Improvements**
- Real-time logging of container processes to standard output and error
for easier monitoring.
- Enhanced process management and reduced CPU usage in protocol handling
loops.
- Updated dependency versions for greater stability and feature support.
- Improved audio capture defaults and expanded audio pipeline support.
- Enhanced video pipeline setup with conditional handling for different
encoder APIs and DMA-BUF support.
- Refined concurrency and lifecycle management in protocol messaging for
increased robustness.
- Consistent namespace usage and updated crate references across the
codebase.
- Enhanced SSH configuration with key management, port customization,
and startup verification.
- Improved GPU and video encoder integration in pipeline construction.
- Simplified error handling and consolidated write operations in
protocol streams.
- Removed Ludusavi installation from container image and updated package
installations.
- **Other**
- Minor formatting and style changes for better code readability and
maintainability.
- Docker build context now ignores `.idea` directory to streamline
builds.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
Whew, some stuff is still not re-implemented, but it's working!
Rabbit's gonna explode with the amount of changes I reckon 😅
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a peer-to-peer relay system using libp2p with enhanced
stream forwarding, room state synchronization, and mDNS peer discovery.
- Added decentralized room and participant management, metrics
publishing, and safe, size-limited, concurrent message streaming with
robust framing and callback dispatching.
- Implemented asynchronous, callback-driven message handling over custom
libp2p streams replacing WebSocket signaling.
- **Improvements**
- Migrated signaling and stream protocols from WebSocket to libp2p,
improving reliability and scalability.
- Simplified configuration and environment variables, removing
deprecated flags and adding persistent data support.
- Enhanced logging, error handling, and connection management for better
observability and robustness.
- Refined RTP header extension registration and NAT IP handling for
improved WebRTC performance.
- **Bug Fixes**
- Improved ICE candidate buffering and SDP negotiation in WebRTC
connections.
- Fixed NAT IP and UDP port range configuration issues.
- **Refactor**
- Modularized codebase, reorganized relay and server logic, and removed
deprecated WebSocket-based components.
- Streamlined message structures, removed obsolete enums and message
types, and simplified SafeMap concurrency.
- Replaced WebSocket signaling with libp2p stream protocols in server
and relay components.
- **Chores**
- Updated and cleaned dependencies across Go, Rust, and JavaScript
packages.
- Added `.gitignore` for persistent data directory in relay package.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Philipp Neumann <3daquawolf@gmail.com>
## Description
Made argument parsing and handling much nicer with clap features.
Changed to tracing package for logging and made other improvements
around to hopefully make things more robust and logical.
Default audio-capture-method is now PipeWire since it seems to work
perfectly fine with latest gstreamer 🎉
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Improved command-line argument parsing with stricter validation, type
safety, and clearer help messages.
- Enhanced GPU selection and logging, including explicit GPU info
logging and support for negative GPU indices for auto-selection.
- Added support for new audio and video codec and encoder enums,
providing safer and more flexible codec handling.
- **Bug Fixes**
- Improved error handling and logging throughout the application,
unifying logs under the `tracing` system for better diagnostics.
- Fixed issues with directory ownership and environment variable
handling in startup scripts.
- **Refactor**
- Replaced string-based parsing and manual conversions with strongly
typed enums and value parsers.
- Updated logging from `println!` and `log` macros to the `tracing`
crate for consistency.
- Simplified and unified the handling of pipeline and element references
in the signaling and data channel logic.
- **Chores**
- Updated and cleaned up dependencies, including switching from `log` to
`tracing` and upgrading the `webrtc` crate.
- Removed unused or redundant code and environment variables for
improved maintainability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
Adds DMA-BUF support for non-NVIDIA GPUs using GL elements as conversion
workaround.
Tested with QSV and VA encoders, note that H.264 seems to only work for
QSV encoder, for `vah264(lp)enc` theres major CPU usage with DMA-BUF
enabled.
Don't mind the branch name, I was working on relay before and changed
gears to runner after noticing some DMA-BUF stuff 😅
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Chores**
- Added a `.dockerignore` file to exclude the `target` directory from
Docker builds.
- Updated `.gitignore` to ignore the `target` directory.
- **New Features**
- Enhanced video processing pipeline with updated handling of DMA-BUF
support, including improved compatibility for different GPU vendors and
refined video element configurations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
- Made it so failed NVIDIA driver install won't quit entrypoint script
if other GPU vendors are present (fixes mixed GPU cases).
- Switch to gamescope as compositor, with optional SYS_NICE cap handling
for higher priority.
- Use mangohud preset 2 for stats, which is more compact.
- Fixes to nestri-server lspci regex, to deal with AMD naming scheme.
- Added missing radeon vulkan driver packages.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added support for additional AMD Vulkan drivers.
- Integrated Steam launch directly within the gamescope compositor for a
streamlined startup.
- **Bug Fixes**
- Improved GPU driver fallback handling to ensure smoother operation on
systems without NVIDIA GPUs.
- Enhanced PCI device parsing for more accurate GPU detection.
- **Chores**
- Updated environment configuration to use X11 session type and set
MangoHud preset.
- Removed unused packages and legacy compositor/resolution management
logic.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
- Improves latency for runner
- Fixes bugs in entrypoint bash scripts
- Package updates, gstreamer 1.26 and workaround for it
Modified runner workflow to hopefully pull latest cachyos base image on
nightlies. This will cause a full build but for nightlies should be
fine?
Also removed the duplicate key-down workaround as we've enabled ordered
datachannels now. Increased retransmit to 2 from 0 to see if it'll help
with some network issues.
Marked as draft as I need to do bug testing still, I'll do it after
fever calms down 😅
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced deployment workflows with optimized container image
management.
- Improved audio and video processing for lower latency and better
synchronization.
- Consolidated debugging options to ease command-line monitoring.
- **Refactor**
- Streamlined internal script flow and process handling for smoother
performance.
- Updated dependency management and communication protocols to boost
overall stability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
This PR will work on adding port muxing (share single port for HTTP/WS +
WebRTC connections), along with API communication.
## Type of Change
- [x] Bug fix (non-breaking change)
- [x] New feature (non-breaking change)
## Checklist
- [ ] I have updated relevant documentation
- [x] My code follows the project's coding style
- [x] My changes generate no new warnings/errors
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
## Description
- Updates to latest Rust 2024 🎉
- Make DataChannel messages ordered on nestri-server side
- Bugfixes and code improvements + formatting
## Type of Change
- [x] Bug fix (non-breaking change)
- [x] New feature (non-breaking change)
## Checklist
- [x] I have updated relevant documentation
- [x] My code follows the project's coding style
- [x] My changes generate no new warnings/errors
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Wanjohi <elviswanjohi47@gmail.com>
#### Description
This PR will be fixing issue of runner not working under Ubuntu and
other Debian-based distros with NVIDIA GPUs.
Another fix will be arriving is allowing Steam to run without namespace
requirements, removing the need for `--privileged` flag in certain
situations.
#### Type of Change
- [x] Bug fix (non-breaking change)
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Also includes other improvements and hopefully reducing LOC with some
cleanup.
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Fixes gpu_helpers returning "Non-VGA" devices as GPUs.
I was curious about DeepSeek's so I tried it's hand with this as my head
isn't quite up-to-speed yet 😅
But yeah it seems less error-prone than previous naive string-approach.
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Wanjohi <elviswanjohi47@gmail.com>
Co-authored-by: Wanjohi <71614375+wanjohiryan@users.noreply.github.com>
should fix the missing dbus runtime directory issue, using mesa-git as
it's less cumbersome to deal with package wise than normal mesa
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Wanjohi <elviswanjohi47@gmail.com>
## Description
Adds new `-device` argument for gpu-screen-recorder which allows
specifying a GPU to use (by `/dev/dri/cardN` path)
This fixes an issue with multi-gpu systems when device such as
`/dev/dri/card1` is passed through but gpu-screen-recorder will still
try to access `/dev/dri/card0` for capturing and failing.
Added relevant bits to `gpu_helpers.sh` to find the card path - I assume
all modern modesetting drivers will have a `/drm/` path that tells the
card number. If not, the script will fall back to gpu-screen-recorder's
own method of finding the card.
Edit: Forgot to mention patches are now copied to /tmp/ rather than
/etc/
Co-authored-by: Kristian Ollikainen <DatCaptainHorse@users.noreply.github.com>
## Description
### This is a DRAFT - Changes will be discussed and made upon requests!
In nutshell, this adds support for running Nestri with Intel and AMD
GPU's. Both integrated and dedicated.
It took a few days to find a trick for having output without dummy plugs
or connected displays, but I think I got it.
`gpu-screen-recorder` requires a custom patch to skip the check for
connected displays (as we're using a xrandr workaround which makes them
stay "unconnected")
Most likely fixes#68
### Changes
The NVIDIA sections have been split in their own code branches since
there's some NVIDIA specific things I didn't feel approriate to poke
more than necessary for the goal of this PR.
Added a script with helper functions related to GPU discovery and
gathering some basic info off from them (note: it might be better to
declare the helper script arrays outside it's initially run function).
The helper scripts rely on `lshw`.
NVIDIA code was slightly adjusted to use the bus-id's provided by the
helper functions to have some code re-use.
Cleaned up few things on the side.
---------
Co-authored-by: Kristian Ollikainen <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Wanjohi <71614375+wanjohiryan@users.noreply.github.com>