The user direction after WP0: ENet exists only for Moonlight, so the native
plane must be provably safe and the compat planes a deliberate choice.
Opt-in, everywhere. Windows already was (unchecked installer task). The three
opt-out surfaces are flipped: the shipped systemd user unit (deb/RPM/Arch/
sysext) no longer bakes --gamestream into ExecStart — a new
PUNKTFUNK_GAMESTREAM=1 host.env knob (pf-host-config, OR-ed with the CLI
flag) is the packaged opt-in; the NixOS module default goes true→false, with
a module-check assertion that unset = native-only; the Deck installer takes
--gamestream to opt in (--no-gamestream kept as explicit-off). Docs
(quickstart, running-as-a-service, moonlight, ubuntu/fedora/arch firewall
sections, gnome/sway, how-it-works) rewritten to the opt-in shape; the
CHANGELOG carries the upgrade note.
Enforced-safe. punktfunk-core is #![deny(unsafe_code)] crate-wide — every
module that parses network bytes is safe Rust as a compile error, not a
census result. Carve-outs are exactly two documented classes, neither of
which interprets attacker bytes: the client surface (abi, client) and the
transport syscall-batching shims (udp/{apple,linux,windows}, qos_windows).
In punktfunk-host, the modules a secure-default host exposes — native
(cfg-not-test: its tests exercise the client C ABI on purpose),
native_pairing, mgmt, mgmt_token, discovery, wol — are #[forbid(unsafe_code)].
Gates: Linux amd64 container clippy --all-targets -D warnings clean over
core+host-config+host; core 204 tests green under the deny; mgmt 46/46,
control 6/6. .133 Windows clippy (shipped features, clean-first,
sentinel-checked) clean — covers the qos_windows/udp-windows carve-outs.
macOS + iOS cargo check green (the apple.rs carve-out compiles for real).
117 lines
6.2 KiB
Markdown
117 lines
6.2 KiB
Markdown
---
|
|
title: Connect with Moonlight
|
|
description: Stream from a Punktfunk host using any Moonlight client.
|
|
---
|
|
|
|
Punktfunk speaks the **GameStream** protocol, so [Moonlight](https://moonlight-stream.org/) connects
|
|
to it like it would to any GameStream host — no punktfunk-specific app needed. It's a great option for
|
|
a browser, a smart TV, or any device without a native client.
|
|
|
|
> Many platforms also have a **native Punktfunk client** with lower latency and built-in
|
|
> discovery/pairing — including **Windows** and **Android** (phone and Android TV). See
|
|
> [Clients](/docs/clients) before reaching for Moonlight.
|
|
|
|
## 1. Make sure the host is running with GameStream enabled
|
|
|
|
Moonlight needs the GameStream planes, which are **opt-in on every install route** (the default is
|
|
the secure native-only host):
|
|
|
|
- **Linux packages (apt / dnf / pacman / sysext)** — add `PUNKTFUNK_GAMESTREAM=1` to
|
|
`~/.config/punktfunk/host.env` and `systemctl --user restart punktfunk-host` — see
|
|
[What the unit starts](/docs/running-as-a-service#what-the-unit-starts). (Installs from before
|
|
the opt-in change served Moonlight by default; an upgrade switches them to native-only until you
|
|
set the knob.)
|
|
- **NixOS** — set `services.punktfunk.host.gamestream = true;` (also opens the GameStream firewall
|
|
ports).
|
|
- **SteamOS / Steam Deck** — run the installer with `--gamestream` (re-running it is safe and
|
|
keeps the rest of your config).
|
|
- **Windows** — tick the installer's *Enable GameStream (Moonlight) compatibility* checkbox, or
|
|
turn it on afterwards from an **elevated** prompt (`=off` puts it back) — see
|
|
[Windows Host](/docs/windows-host):
|
|
|
|
```powershell
|
|
punktfunk-host service install --gamestream=on
|
|
punktfunk-host service restart
|
|
```
|
|
|
|
- **Running `serve` by hand** — add the flag yourself:
|
|
|
|
```sh
|
|
punktfunk-host serve --gamestream
|
|
```
|
|
|
|
(Bare `serve` is the secure native-only default and stock Moonlight clients can't connect to it; the
|
|
native plane is always on, and `--gamestream` adds the Moonlight-compat surface.) GameStream pairs over
|
|
plain HTTP and its legacy control encryption is weaker than the native plane's, so only enable it on a
|
|
**trusted LAN**. See [Running as a Service](/docs/running-as-a-service) for the bundled unit. The host
|
|
advertises itself on the network, so Moonlight usually finds it on its own.
|
|
|
|
## 2. Add the host in Moonlight
|
|
|
|
Open Moonlight. Your host should appear automatically on the same network. If it doesn't, use **Add
|
|
Host manually** and enter the host machine's IP address.
|
|
|
|
Still nothing? Two causes account for almost all of it:
|
|
|
|
- **The GameStream ports aren't open on the host.** They are TCP **47984 / 47989 / 48010** and UDP
|
|
**47998 / 47999 / 48000**, plus mDNS on UDP **5353**. On Linux the packages ship a ready-made rule
|
|
for your distro's firewall but never enable it:
|
|
|
|
```sh
|
|
sudo ufw allow punktfunk-gamestream # ufw
|
|
sudo firewall-cmd --permanent --add-service=punktfunk-gamestream # firewalld
|
|
sudo firewall-cmd --reload
|
|
```
|
|
|
|
On Windows the installer adds these rules itself — but only for **Private** and **Domain**
|
|
networks, unless you ticked *Allow connections on Public networks* during setup.
|
|
- **Another GameStream host is running on the same machine.** Sunshine, Apollo and their forks bind
|
|
the same fixed ports and advertise the same mDNS name, so the wrong host answers (or neither
|
|
does). Run `punktfunk-host detect-conflicts` on the host machine — it lists any it finds.
|
|
|
|
Both are covered in more detail in [Troubleshooting](/docs/troubleshooting).
|
|
|
|
## 3. Pair
|
|
|
|
Moonlight's PIN is typed in on the **host** side, so you need the host's
|
|
[web console](/docs/web-console) running — it's the only UI where a Moonlight PIN can be entered.
|
|
|
|
1. Open the console at `https://<host-ip>:47992` and go to **Pairing**.
|
|
2. In Moonlight, select the host and choose **Pair** — it shows a 4-digit PIN.
|
|
3. The console's **Moonlight (GameStream) pairing** card now says a client is waiting. Type the PIN
|
|
there and press **Submit PIN**.
|
|
|
|
The device then appears under **Paired devices**, and Moonlight remembers the host. See
|
|
[Pairing & Trust](/docs/pairing) for the full picture.
|
|
|
|
## 4. Stream
|
|
|
|
Moonlight lists **Desktop** plus the games the host found installed (Steam, Epic, GOG, Xbox), with
|
|
cover art — the same [library](/docs/game-library) the native clients show. Pick one and start
|
|
streaming. The host creates a virtual display at the resolution and frame rate Moonlight requests
|
|
(set these in Moonlight's settings), encodes it on the GPU, and streams it. Mouse, keyboard, and
|
|
controllers flow back to the host — and a Moonlight client that sends pen events, an iPad's Apple
|
|
Pencil included, drives the same host-side tablet a native client would, with pressure and tilt
|
|
intact (see [Pen and stylus](/docs/input#pen-and-stylus)).
|
|
|
|
That **Desktop** entry is the operator base list. An `apps.json` in the host's config directory
|
|
*replaces* it (it doesn't add to it), so put every entry you want in that file if you write one.
|
|
|
|
## Tips
|
|
|
|
- **Set your resolution and frame rate in Moonlight's settings** before connecting — the host matches
|
|
whatever Moonlight asks for, creating the virtual display at that exact mode.
|
|
- **Codec:** HEVC (H.265) is a good default; AV1 is available if your client supports it.
|
|
- **HDR:** Moonlight only offers its HDR toggle when the host advertises a 10-bit codec, which it
|
|
does only when [its capture path *and* its encoder](/docs/hdr#the-chain) can really deliver
|
|
10-bit BT.2020 PQ. If the toggle is there, turn it on and pick HEVC or AV1 — H.264 stays SDR.
|
|
Setting `PUNKTFUNK_10BIT=0` in [`host.env`](/docs/configuration) withdraws the offer entirely; it
|
|
is on by default.
|
|
- **Bitrate:** start moderate and raise it. For very high bitrates, the [native
|
|
clients](/docs/clients) have a built-in speed test; with Moonlight, set the bitrate manually.
|
|
- Moonlight uses the GameStream protocol, not Punktfunk's native FEC/encryption extensions. On a
|
|
solid LAN this is fine; on a lossy link a [native client](/docs/clients) holds up better.
|
|
- Comparing Moonlight's performance overlay with a Punktfunk client's stats HUD? The numbers
|
|
measure different slices of the pipeline — see [Understanding the Stats Overlay](/docs/stats)
|
|
for a line-by-line comparison matrix before drawing conclusions.
|