enricobuehler 520d7342dd
ci / rust (push) Has been cancelled
feat: M3 — full lumen/1 session planes: audio, gamepads+rumble, pinned trust, persistent listener
m3-host is now a real host, not a one-shot demo. Everything validated live on this box
(two back-to-back sessions, pinned + TOFU, ~200 audio pkts/s, p50 0.84 ms at 720p60).

lumen-core:
- quic.rs: QUIC-datagram side planes demuxed by first byte — Opus audio 0xC9
  ([magic][u32 seq][u64 pts_ns][opus], host→client) and rumble 0xCA ([magic][pad][low][high]).
- Trust: endpoint::server_with_identity (persistent PEM identity) and
  endpoint::client_pinned — SHA-256 cert-fingerprint pinning with TOFU (observed
  fingerprint reported back for persisting). The verifier checks the TLS 1.3
  CertificateVerify signature for real (an MITM replaying the host's public cert without
  its key is rejected; cert pinning alone would not prove key possession).
- client.rs: NativeClient gains pin + host_fingerprint, audio/rumble receivers
  (next_audio / next_rumble); pull methods take &self so the C ABI's per-plane threads
  never alias a &mut (per-plane mutexed borrow slots in abi.rs).
- abi.rs: lumen_connect(pin_sha256, observed_sha256_out) + lumen_connection_next_audio /
  next_rumble. input.rs: documented gamepad wire contract (GameStream buttonFlags bits,
  XInput axis conventions, +y = up) — exported as LUMEN_BTN_*/LUMEN_AXIS_* (bare BTN_*
  collides with <linux/input-event-codes.h> at different values).

lumen-host (m3):
- Persistent accept loop: sessions back to back on one endpoint (--max-sessions, 0 =
  forever); per-session failures log and the loop keeps serving; 10 s handshake deadline
  so a silent client can't wedge the sequential accept queue; teardown on every exit path
  (stop flag → conn.close → join audio+input threads).
- Audio plane: desktop PipeWire capture → Opus 48 kHz stereo 5 ms CBR → datagrams; ONE
  capturer reused across sessions via an AudioCapSlot (PipeWire streams have no cheap
  teardown — per-session opens would leak a thread + core connection + live node each).
- Gamepad routing: incremental GamepadButton/GamepadAxis datagrams accumulate into
  per-pad state feeding the uinput xpad manager; force feedback returns as rumble
  datagrams, with current state re-sent every 500 ms (idempotent-state healing for the
  lossy channel). QUIC endpoint serves the persistent ~/.config/lumen identity and logs
  the pinnable fingerprint.

lumen-client-rs: --pin (malformed values abort — never silently downgrade to TOFU),
TOFU fingerprint logging, audio/rumble datagram counters, gamepad events in --input-test.

clients/apple: scaffold synced — pinSHA256/hostFingerprint (wrong-size pin throws,
fail-closed), nextAudio/nextRumble, gamepad event constructors; README handoff updated
(persistent listener, audio decode notes, trust UX).

Adversarially reviewed (5-dimension multi-agent pass over the diff, 2-skeptic
verification): fixed the MITM signature-check gap, a Y-axis contract inversion, header
macro collisions, ABI aliasing UB, the PipeWire per-session leak, the missing handshake
deadline, fail-open pin parsing, and teardown-on-error paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:26:18 +00:00

lumen

A ground-up low-latency desktop streaming stack, built Linux-first, with a shared Rust protocol core and native clients per platform.

lumen is a placeholder codename. The bet: ship a Linux virtual-display streaming host that speaks the existing Moonlight protocol (every Moonlight/Artemis client works day one), then break the ~1 Gbps FEC wall with a GF(2¹⁶) Leopard-RS transport as a negotiated extension. See docs/implementation-plan.md.

Status

Milestone State
M1 — lumen-core + C ABI done & tested (FEC, packetization, crypto, session, lumen_core.h)
M0 — pipeline spike (wlroots→PipeWire→NVENC→file→lumen-core) done & verified on NVIDIA (RTX 5070 Ti / driver 595)
M2 — P1 host → stock Moonlight 🟡 capture+encode landed in M0; pairing/RTSP/vdisplay pending
M3 — measurement harness 🟡 tools/loss-harness runs; latency-probe scaffolded
M4 — P2 transport + Rust client 🟡 GF(2¹⁶) core done; lumen-client-rs scaffolded
M5 — Apple client scaffolded (clients/apple)

lumen-core is complete and verified: it builds and its full test suite (FEC recovery, loopback round-trip under loss, property tests, and a C ABI harness) passes on macOS/aarch64. M0 is done: lumen-host captures a headless wlroots output via the ScreenCast portal + PipeWire, encodes it with NVENC, writes a playable H.265 file, and round-trips every access unit through a lumen_core host→client session (see docs/linux-setup.md). M2 is in flight: the GameStream control plane (gamestream/) and the management REST API (mgmt.rs, OpenAPI spec in docs/api/) are implemented; the remaining Linux host backends (KWin/Mutter virtual displays, libei input) are #[cfg(target_os = "linux")] seams — defined and compiling, implementations pending.

Layout

crates/
  lumen-core/        protocol · FEC · pacing · crypto — the C ABI (lib + cdylib + staticlib)
  lumen-host/        Linux host: vdisplay · capture · encode · inject · gamestream · mgmt
  lumen-client-rs/   reference client (M4): VAAPI decode + wgpu present
clients/{apple,android}/   native client scaffolds (import lumen_core.h)
include/lumen_core.h       cbindgen-generated C header (checked in)
tools/{latency-probe,loss-harness}/   measurement (plan §10)
docs/implementation-plan.md

Build & test

cargo build --workspace          # green on Linux and macOS
cargo test  --workspace          # unit + loopback + proptest + C ABI harness
cargo clippy --workspace --all-targets

cargo run -p loss-harness        # FEC loss-resilience sweep (no network needed)
bash crates/lumen-core/tests/c/run.sh   # standalone C-ABI link+round-trip proof

The C header regenerates from crates/lumen-core/src/abi.rs on every build (cbindgen via build.rs) into include/lumen_core.h.

Design invariants

  • One core, linked everywhere. Protocol/FEC/crypto/pacing live in lumen-core exactly once, exposed over a stable, versioned C ABI (lumen_abi_version(), LumenConfig carries its own struct_size).
  • No async on the hot path. The per-frame pipeline uses native threads only; tokio/quinn are gated behind the off-by-default quic feature (control plane only).
  • FEC is the wall-breaker. GF(2⁸) (≤255 shards/block) for Moonlight compat; GF(2¹⁶) (≤65535 shards/block, SIMD, O(n log n)) to push past ~1 Gbps.

License

MIT OR Apache-2.0.

S
Description
next gen game streaming - built using rust, back compatible with game stream clients, and supporting virtual displays for kde/kwin, gnome and gamescope.
Readme 16 MiB
v0.2.1 Latest
2026-06-28 12:51:55 +00:00
Languages
Rust 72%
Swift 12.3%
TypeScript 4.1%
Kotlin 3.2%
Shell 3.2%
Other 5.1%