• v0.31.4
    audit / bun-audit (web) (push) Successful in 24s
    audit / bun-audit (sdk) (push) Successful in 25s
    audit / bun-audit (plugin-kit) (push) Successful in 26s
    audit / docs-site-audit (push) Successful in 24s
    audit / pnpm-audit (push) Successful in 12s
    audit / cargo-audit (push) Successful in 43s
    ci / rust-arm64 (push) Successful in 1m40s
    ci / web (push) Successful in 1m25s
    ci / docs-site (push) Successful in 1m11s
    ci / bun-nix (push) Successful in 33s
    ci / docs-drift (push) Successful in 39s
    audit / license-gate (push) Successful in 6m4s
    audit / c-abi-asan (push) Successful in 7m33s
    audit / miri (push) Successful in 7m48s
    android-screenshots / screenshots (push) Successful in 1m17s
    ci / rust (push) Successful in 16m49s
    windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 8m34s
    android / android (push) Successful in 10m54s
    decky / build-publish (push) Successful in 58s
    arch / build-publish (push) Successful in 12m5s
    sbom / sbom (push) Successful in 42s
    linux-client-screenshots / screenshots (push) Successful in 6m18s
    flatpak / build-publish (push) Successful in 7m39s
    docker / builders-arm64cross (push) Successful in 14s
    docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 18s
    docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 12s
    docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 15s
    docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 14s
    docker / builders (ci/flatpak-ci.Dockerfile, punktfunk-flatpak-ci) (push) Successful in 15s
    docker / builders (ci/gamescope-trixie.Dockerfile, punktfunk-gamescope-trixie) (push) Successful in 15s
    docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 18s
    docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 17s
    docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
    docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m30s
    docker / deploy-docs (push) Successful in 29s
    nix / flake (push) Successful in 25m39s
    deb / smoke-install (push) Successful in 3m41s
    deb / build-publish (push) Successful in 6m45s
    deb / build-publish-host (push) Successful in 5m29s
    deb / build-publish-gamescope (push) Successful in 37s
    deb / build-publish-client-arm64 (push) Successful in 1m28s
    apple / distribute (push) Successful in 13m7s
    apple / swift (push) Successful in 2m0s
    rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m52s
    rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 19m52s
    apple / screenshots (push) Successful in 9m32s
    windows-host / package (push) Successful in 12m0s
    windows-host / winget-source (push) Skipped
    windows-host / canary-manifest (push) Successful in 26s
    web-screenshots / screenshots (push) Successful in 6m16s
    windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 4m12s
    Stable

    enricobuehler released this 2026-08-24 22:27:35 +00:00 | 6806 commits to main since this release

    Wire-compatible with 0.31.x — everything you have already paired keeps working, and you can update one side at a time. Nothing here changes how a host and a client agree on what to send each other, so an old client on a new host, or the other way round, streams exactly as it does today.

    This is a fix release about the machine you stream from behaving itself while you are not in the room with it. Asking for your screens off during a stream only ever worked on a box running a KDE desktop — a Steam Deck, or anything else sitting in Game Mode, kept its television lit for the whole session, playing your game to an empty room. That works now on every kind of Linux box, including ones with no desktop at all. Putting that box to sleep or shutting it down from Steam's own power menu had stopped working during a stream, in two separate ways, and works again. On the client side, a long session on a connection that was never the problem could slide from 57 Mbps down to 5 and stay there; the Mac app could quit outright the moment a stream started; and the web console served a certificate browsers refuse, so the page you are told to open did not load, while on Linux the tray insisted the console was dead the whole time it was running perfectly. There is a little new work too: the gamepad console's bitrate reaches 2 Gbps and takes a number you type, and on Linux there is finally a way into that console that is not a command line.

    TL;DR

    • "Turn my screens off while I stream" now actually turns them off. It only ever worked on a KDE desktop. A Steam Deck or any other box in Game Mode ignored it silently, which is the deployment where you most want it. It works there, and on sway and Hyprland desktops too. GNOME is the one that still cannot be served, and the host now says so instead of leaving you guessing.
    • Steam's power menu did nothing during a stream. Sleep, Restart and Shut Down all work again. Two unrelated faults with one symptom: your host was refusing every suspend request for the length of a stream, including yours, and on some setups the box lost the permission to power itself off at all while it was streaming.
    • A long session could slide to the worst picture quality on a perfectly good connection. One 1440p session walked from 57 Mbps down to 5 over ten minutes without dropping a single packet, and never recovered. Both causes are fixed.
    • Mac: the app could quit the instant a stream started. Not at launch — at the moment you connected, which made it look like the host's fault.
    • The web console would not open in a browser, and on Linux the tray labelled it "not responding" while it was serving fine.
    • New: the gamepad console's bitrate goes to 2 Gbps, steps finely at the low end, and takes a typed number; on Linux the gamepad console gets a button, a menu entry and its own app-grid launcher.

    Before you update

    • Only if your host takes the box over by stopping its login screen, and you want Steam's power menu to work: be in the punktfunk group. That is the same group the takeover already needs to do its job, so if the takeover works for you today you are already in it and there is nothing to do. If you set that up some other way, join it — the docs page titled Steam / gamescope carries the one-line command — because the permission this release adds is granted to that group and nobody else, deliberately.

    New

    • Bitrate in the gamepad console reaches 2 Gbps, and you can type a rate. The picker had seven rungs stopping at 80 Mbps since the console shipped, which someone ran straight into — and the desktop app beside it has always gone far higher, so the two disagreed about what your own machine was allowed to ask for. There are 30 rungs now, from 1 Mbps to 2 Gbps: tight at the bottom, where a single step decides whether a thin connection is watchable, and coarse at the top, where a step is noise. Press Y on that row to type a rate instead, using the same on-screen keyboard as the add-host and pairing fields, including Steam's own keyboard on a Deck. And a rate that is not one of the rungs — typed here, or set from the desktop app — now steps to its neighbour instead of being thrown away and snapped back to Automatic by a single nudge. The desktop app gets the other half of the same complaint: its bitrate box steps 1 Mbps at a time instead of 5, so 3, 4 and 6 are reachable without typing.
    • Linux: a front door to the gamepad console. The couch interface has shipped for a while, but on Linux the only way to reach it was a command-line switch, which is not something you find by looking. There are three ways in now: a gamepad button in the app's host list, a "Console UI" entry in its menu, and a separate launcher in your applications grid that you can also add to Steam as a non-Steam game so it opens straight from the couch. All three land in the same place. This comes with the packaged builds; a from-source Nix build still does not include the separate launcher, because that build leaves the console interface out.

    Improved

    • The adaptive bitrate control no longer gives up on a whole session. Part of the picture-quality fix below: when your client decided the host's encoder was the bottleneck and stopped listening to that signal, it stopped listening for the rest of the session. But the thing that provokes it is temporary by nature — a game hammering the graphics card, a shader storm, a second program on the same card — and all of those end. It now starts listening again after a clean stretch, and if the noise comes straight back it backs off for longer each time rather than flapping.
    • When your host cannot turn a screen off, it says so. Every one of those refusals means "you asked for your screens off and they stayed on", and the most common one was being recorded at a level nobody reads. It is now a warning, with the reason.

    Fixed

    • Your screens stayed lit through the whole stream, on every Linux box except a KDE desktop. Reported on Nobara: launching a game from the library with screens-off selected left the operator's monitor showing live Game Mode for the entire session. Three separate things were wrong. The setting was implemented by asking the desktop to turn its screens off — right when there is a desktop to ask, and a box in Game Mode has none, which is precisely the box whose television you want dark. That case is now handled by switching the display off through the graphics hardware directly, which needs no extra privileges and cannot leave a screen dark if the host crashes, because the screen comes back the moment the host lets go. A sway or Hyprland desktop was not served either, and now is, through each one's own controls, taking care to leave any screen it did not darken alone rather than "fixing" one that was already off. And separately, the part of the host that moves the box's own gaming session out of the way only did so for a Steam launch, because freeing Steam was the only reason anyone had ever needed it — so a non-Steam game from your library left that session running and driving the television regardless. Wanting your screens off is now a second, independent reason to move it aside. GNOME remains a genuine gap, not an oversight: it offers applications no way to turn a screen off, and holds the graphics card itself so the direct route is refused. The host warns instead of pretending.
    • The recommended way of streaming a Game Mode box ignored the setting entirely. The route that gives the best experience — and the only one that can serve your client its own screen at its own resolution — was the one route that never darkened anything, because there was nowhere safe to undo it and a screen left dark after every stream is worse than one left lit. The undo now lives in the single teardown every one of those sessions passes through, so it is honoured. A reconnect does not darken twice, and a session that keeps running between connections stays dark rather than flickering.
    • Steam's power menu did nothing during a stream, and neither did Sleep. Two independent faults, one symptom, both on Linux. Your host holds a lock for the length of a stream so the box cannot suspend out from under someone who is only watching and never touching anything — but that lock refuses every suspend, including the one you asked for by pressing Sleep, and nothing in Steam's interface shows the refusal, so the entry simply did nothing for as long as anyone was connected. The lock is now held only while the stream is genuinely quiet: any input from any client drops it immediately, so a Sleep press cannot lose the race, and it comes back after 30 seconds of silence. The case it was written for is unchanged — a passive viewer sends nothing, and the box still will not sleep under them. The second fault is Shut Down and Restart, on the takeover setups that stop the box's login screen: doing that leaves the box with no signed-in session, and the system's rules only allow powering off from one. All three entries quietly became a password prompt — asked of something that cannot answer, on a screen that is switched off. The packages now ship a rule granting exactly those three actions, to the punktfunk group and to nobody else, and only those three: a box with a second person logged in still asks first.
    • A long session slid to the lowest picture quality on a connection that never lost a packet. Reported from a 1440p60 session that ratcheted 57 Mbps down to 5 over ten minutes, with the host granting every rate it was asked for and nothing at all going wrong on the link. Two causes, both in the part of your client that watches how long the host is taking to compress each frame. Its thresholds were tuned on a 120-frames-a-second session, where one frame is 8 milliseconds; on a 60-frame session a frame is twice that, so an ordinary one-frame hiccup cleared the "something is badly wrong" bar that the identical hiccup at 120 never reached. They are now measured against the session's own frame budget. And the signal was firing for something cutting the rate cannot fix: it exists to find the point where the encoder runs out of compute, where less rate means less work — but when a game is saturating the graphics card that premise is false, the reduction changes nothing, and it would fire again a moment later. Two reductions in a row that buy no improvement now stand the signal down. Everything else your client watches — packet loss, delay, decoding, and the host's own refusal to climb — keeps its full authority throughout, so a real encoder limit is still caught.
    • Mac: the app could quit outright the moment a stream started. Not at launch, which is what made it look like a host problem. Setting up your microphone takes a snapshot of the input device's audio format and then hands that snapshot back a moment later, and on a Mac the input can change underneath in that window — you switch devices, something changes the sample rate, or the app itself switches to the device you chose two lines earlier. Any mismatch was fatal and could not be caught. It now asks the system to use whatever format the device actually has, so there is no snapshot to go stale, and follows the device if it changes rate mid-stream instead of resampling by the wrong ratio and pitch-shifting your voice.
    • The web console would not load in a browser, and the tray called it dead. A Linux operator saw "Open web console (not responding)" in the tray, beside a tooltip that said the host was idle — which was the clue, because that tooltip proved the host was answering perfectly on the very same check. Your host has kept two identities for a while now: a modern one that everything current uses, and an older one kept unchanged so Moonlight-compatible clients keep working. The web console never followed that split, and none of the things that start it could choose — so it served the old identity, which no browser will accept, and the page you were told to open failed to load. Meanwhile the tray was checking on the console using the modern identity, so its check was refused and a completely healthy console was labelled as not responding. The console now serves the modern identity whenever the host has one, and the tray's check no longer insists on any particular one, because it reads nothing, sends nothing, and decides only what a menu entry says. A host that predates the split, or one where you supplied your own certificate, is unaffected and behaves exactly as before.
    • Hyprland: the request to turn a screen off is a toggle, and would have turned a dark screen back on. Found on a live Hyprland box rather than in review, and worth saying because the first version of this fix was wrong in both directions: the command ignores whether you asked for on or off and simply flips whatever the screen is currently doing. So "turn it off now, turn it back on later" would have lit a screen that was already dark at the start of a stream, and darkened a lit one at the end — the operator's screen left off after streaming, which is the exact failure this whole feature exists to avoid. Your host now reads the screen's real state first, acts only if it differs, and checks afterwards that it worked. A screen already in the state you wanted is left completely alone.

    Thanks

    Every fix above started as somebody saying exactly what they saw. The monitor still showing Game Mode through a whole stream, and which kind of game they had launched. The power menu that did nothing, on which box. The 1440p session whose quality walked downhill for ten minutes while every other number stayed flat — that report came with the measurements that separated the two causes, which is the hard half. The bitrate ceiling somebody hit and pointed out was lower than the app right next to it. The Mac that quit on connect. And the tray that said "not responding" beside a tooltip reading "idle" — noticing that those two things contradicted each other is what found the bug. Thank you.

    One caveat we would rather write down than let you discover: the sway arm of the screens-off work has not been run against a real sway session — there is no sway machine in the lab. Hyprland, KDE, and the no-desktop route were all measured on real hardware. If you stream from sway, we would like to hear how it goes.

    For developers

    Protocol, ABI, driver and embedder detail — including the version table — is in CHANGELOG.md.

    The short version: nothing versioned moves, at all. The streaming protocol, the embedding interface, the driver protocol, the gamepad channel, the add-on contract and the management API are exactly where 0.31.3 left them — the embedding header is byte-identical for the third release running, and the API description changes only its stamped version number. No header, package, plugin or integration needs rebuilding, re-pairing or re-publishing in any direction, and the add-on toolkit and TypeScript package are both untouched. Two behaviour changes are worth knowing about if you integrate: the host's sleep veto is now conditional on stream quiet rather than held for the whole session, and the client's encode-attributed backoff both re-arms after standing down and sizes its thresholds from the negotiated refresh rather than in absolute milliseconds.

    Downloads