Release prep for v0.31.2: version bump, release notes, CHANGELOG section, Play "What's new", and one docs-freshness fix. Cut from main at 48eeae75 (#368 merged).
10 commits since v0.31.1 (6 non-merge).
The number
A patch, and this time the version table does not have to argue for it. Nothing versioned moved:
WIRE_VERSION2, C ABI 25 — include/punktfunk_core.h has no diff at all against the v0.31.1 tag, not even a #define (unlike the last two releases)
driver protocol 6 / min 3, pf-driver-proto unchanged
Host::detect() froze the advertised address at process start, so a cold boot that beat the network pinned 127.0.0.1 for the life of the process — breaking both mDNS adverts, the Moonlight session URL, the WoL mac record and HostInfo together
the firewall rules guarding the ports those addresses point at admitted any program on the machine
The fourth is an Android regression from v0.31.1 (#365); the remaining two are the refactor and test supporting #366.
Two behaviour changes (no build breaks)
Windows service install scopes all five fixed-port rules to the listening executable, keeping their localport=. Externally visible: 5353 is punktfunk's alone now, so anything else on the machine that was reachable on mDNS through our any-program rule needs its own. Fallbacks are asymmetric on purpose — a fixed-port rule that cannot resolve its exe falls back to the old wide form (a looser rule still streams), while the data-plane rule skips (no port to fall back to, so a program-less version would not be looser, it would be open).
HostInfo.local_ip is no longer static for the process. It was a field snapshotted at detect(); it is now a method that re-reads per request. A consumer that cached it at startup was caching a value that could be 127.0.0.1 forever. This is the only api/openapi.json content change — two description strings, no route/schema/field/type.
Also here
docs-site/content/docs/ports.mdx. Its "Video needs nothing opened" bullet has been wrong for Windows since v0.31.1 added the data-plane rule — it now says so and names why (no fixed rule can cover a per-session ephemeral port). The Windows line gains a Callout for the 5353 change, since that is the one thing on the page a reader may have to act on.
Play notes are Android-only per whatsnew/TEMPLATE.txt, which this cycle means the #365 regression alone. The three host-side fixes are deliberately not listed: updating the app fixes none of them, so putting them on the store page would promise something the download does not deliver.
Gates
gate
result
cargo fmt --all --check
clean
cargo metadata --offline
ok, Cargo.lock diff versions-only (36/36)
cargo test -p punktfunk-core --lib
273 passed
C ABI harness
passed, abi_version=25
cbindgen regen of punktfunk_core.h
byte-identical to checked-in and to the v0.31.1 tag
scripts/ci/check-docs-drift.sh
clean
scripts/ci/check-docs-links.sh
clean
android.yml Play notes gate (run verbatim)
357/500 chars, unique
both openapi copies
cmp identical, stamped 0.31.2
notes voice scan
clean — one backticked term in the file
Not run here: clippy and any punktfunk-host build (does not compile on macOS — CI covers it), and the Android unit tests (:kit: and :app: ran on #365 itself; nothing here touches Kotlin).
One call left for the tagger
SECURITY.md promises to credit a reporter in the release notes when the fix is public. The #368 commit records only "a user on 2026-08-21" with no name, so the notes credit them unnamed. If they want their name on it, that is a one-line edit to docs/releases/v0.31.2.mdbefore the tag is pushed.
Merge, then tag per docs/releases/README.md: git tag -a v0.31.2 … && git push origin v0.31.2, wait for every platform green, then dispatch announce.yml.
Release prep for **v0.31.2**: version bump, release notes, CHANGELOG section, Play "What's new", and one docs-freshness fix. Cut from `main` at 48eeae75 (#368 merged).
10 commits since v0.31.1 (6 non-merge).
## The number
A patch, and this time the version table does not have to argue for it. **Nothing versioned moved:**
- `WIRE_VERSION` **2**, C ABI **25** — `include/punktfunk_core.h` has **no diff at all** against the v0.31.1 tag, not even a `#define` (unlike the last two releases)
- driver protocol **6** / min **3**, `pf-driver-proto` unchanged
- gamepad channel **3**, plugin index schema **1**, host event schema **1**
- gamescope **+pfhdr8**, no new patch files
- SDK **0.1.5** and plugin-kit **0.4.4** both untouched
No `!` commit, no `feat`, no route added or removed, no breaking change of any kind. Every non-merge commit is `fix`/`refactor`/`test`.
## The shape of the cycle
Three of the six non-merge commits are the same class of fault — **the host using the wrong local address** — reached from three directions:
| | |
|---|---|
| #367 | the data socket bound `0.0.0.0:0` and let routing pick the video source, which the client's connected socket then dropped in-kernel |
| #366 | `Host::detect()` froze the advertised address at process start, so a cold boot that beat the network pinned `127.0.0.1` for the life of the process — breaking both mDNS adverts, the Moonlight session URL, the WoL `mac` record and `HostInfo` together |
| #368 | the firewall rules guarding the ports those addresses point at admitted **any program on the machine** |
The fourth is an Android regression from v0.31.1 (#365); the remaining two are the refactor and test supporting #366.
## Two behaviour changes (no build breaks)
- **Windows `service install` scopes all five fixed-port rules to the listening executable**, keeping their `localport=`. Externally visible: **5353 is punktfunk's alone now**, so anything else on the machine that was reachable on mDNS through our any-program rule needs its own. Fallbacks are asymmetric on purpose — a fixed-port rule that cannot resolve its exe falls back to the old wide form (a looser rule still streams), while the data-plane rule *skips* (no port to fall back to, so a program-less version would not be looser, it would be open).
- **`HostInfo.local_ip` is no longer static for the process.** It was a field snapshotted at `detect()`; it is now a method that re-reads per request. A consumer that cached it at startup was caching a value that could be `127.0.0.1` forever. This is the only `api/openapi.json` content change — two `description` strings, no route/schema/field/type.
## Also here
`docs-site/content/docs/ports.mdx`. Its **"Video needs nothing opened"** bullet has been wrong for Windows since v0.31.1 added the data-plane rule — it now says so and names why (no fixed rule can cover a per-session ephemeral port). The Windows line gains a `Callout` for the 5353 change, since that is the one thing on the page a reader may have to act on.
Play notes are Android-only per `whatsnew/TEMPLATE.txt`, which this cycle means the #365 regression alone. The three host-side fixes are deliberately **not** listed: updating the app fixes none of them, so putting them on the store page would promise something the download does not deliver.
## Gates
| gate | result |
|---|---|
| `cargo fmt --all --check` | clean |
| `cargo metadata --offline` | ok, `Cargo.lock` diff versions-only (36/36) |
| `cargo test -p punktfunk-core --lib` | 273 passed |
| C ABI harness | **passed**, `abi_version=25` |
| cbindgen regen of `punktfunk_core.h` | byte-identical to checked-in **and** to the v0.31.1 tag |
| `scripts/ci/check-docs-drift.sh` | clean |
| `scripts/ci/check-docs-links.sh` | clean |
| android.yml Play notes gate (run verbatim) | 357/500 chars, unique |
| both openapi copies | `cmp` identical, stamped 0.31.2 |
| notes voice scan | clean — one backticked term in the file |
**Not run here:** clippy and any `punktfunk-host` build (does not compile on macOS — CI covers it), and the Android unit tests (`:kit:` and `:app:` ran on #365 itself; nothing here touches Kotlin).
## One call left for the tagger
`SECURITY.md` promises to credit a reporter in the release notes when the fix is public. The #368 commit records only "a user on 2026-08-21" with no name, so the notes credit them unnamed. If they want their name on it, that is a one-line edit to `docs/releases/v0.31.2.md` **before** the tag is pushed.
---
Merge, then tag per `docs/releases/README.md`: `git tag -a v0.31.2 … && git push origin v0.31.2`, wait for every platform green, then dispatch `announce.yml`.
10 commits since v0.31.1 (6 non-merge). Cut from origin/main 48eeae75 (#368
merged).
THE NUMBER: a patch, and unlike the last cut the version table does not even
have to argue for it. Nothing versioned moved — WIRE_VERSION 2, C ABI 25 with
include/punktfunk_core.h showing NO diff at all against the v0.31.1 tag (not
even a #define, unlike the last two releases), driver protocol 6 / min 3 with
pf-driver-proto unchanged, gamepad channel 3, plugin index schema 1, host event
schema 1, gamescope +pfhdr8 with no new patch files, SDK 0.1.5 and plugin-kit
0.4.4 both untouched. No `!` commit, no feat, no route added or removed, no
breaking change of any kind. Every non-merge commit is fix/refactor/test.
The cycle has a shape: three of the six non-merge commits are the same class of
fault — the host using the wrong local address — reached from three directions.
The data socket bound 0.0.0.0:0 and let routing pick the video source, which the
client's connected socket then dropped in-kernel (#367). Host::detect() froze the
advertised address at process start, so a cold boot that beat the network pinned
127.0.0.1 for the life of the process and broke both mDNS adverts, the Moonlight
session URL, the WoL mac record and HostInfo together (#366). And the firewall
rules guarding the ports those addresses point at admitted any program on the
machine (#368). The fourth is an Android regression from v0.31.1 (#365); the
remaining two are the refactor and test supporting #366.
api/openapi.json changes in DOCUMENTATION ONLY this time — two description
strings on HostInfo, no route, schema, required field or type — plus the stamp.
Re-stamped here, not regenerated: punktfunk-host does not build on macOS, and
#366 regenerated the document itself on a runner where
openapi_document_is_complete_and_checked_in actually executes. "0.31.1" appears
nowhere in either copy afterwards, and the two copies are byte-identical.
That description change is load-bearing rather than cosmetic, so it is called out
as a behaviour change in the CHANGELOG beside the firewall one: HostInfo.local_ip
was a field snapshotted at detect() and is now a method that re-reads per
request, so a consumer that cached it at startup was caching a value that could
be 127.0.0.1 forever.
The other behaviour change is the externally visible one: Windows service install
now scopes all five fixed-port rules to the listening executable while keeping
their localport=, so 5353 is punktfunk's alone and anything else on the machine
that was reachable on mDNS through our any-program rule needs its own. Fallbacks
are asymmetric on purpose — a fixed-port rule that cannot resolve its exe falls
back to the old wide form (a looser rule still streams), while the data-plane
rule skips (it has no port to fall back to, so a program-less version would not
be looser, it would be open).
Also in this commit, because a cut is when docs freshness bites:
docs-site/content/docs/ports.mdx. Its "Video needs nothing opened" bullet has
been wrong for Windows since v0.31.1 added the data-plane rule — it now says so
and names why (no fixed rule can cover a per-session ephemeral port). And the
Windows line gains a Callout for the 5353 change above, since that is the one
thing on this page a reader may have to act on. Callout shape copied from the
proven usage in plugins.mdx (no `title` prop — node_modules is not installed here
and fumadocs' prop surface could not be verified offline).
Play notes are Android-only per whatsnew/TEMPLATE.txt, which this cycle means the
#365 regression alone. The three host-side fixes are deliberately NOT in there:
updating the app does not fix any of them, so listing them on the store page
would promise something the download does not deliver.
Gates: cargo fmt --all --check clean; cargo metadata --offline ok with the
Cargo.lock diff versions-only (36/36); cargo test -p punktfunk-core --lib 273
passed; the C ABI harness PASSED reporting abi_version=25 (needed `brew install
opus` on this Mac to link — the first run failed on the missing library, not on
the code); cbindgen regenerated include/punktfunk_core.h during that build and it
came out byte-identical to the checked-in file AND to the v0.31.1 tag, which is a
stronger check on the ABI row than diffing it; scripts/ci/check-docs-drift.sh
clean; scripts/ci/check-docs-links.sh clean; the android.yml Play notes gate run
verbatim, 357/500 characters and unique; both openapi copies cmp identical and
stamped 0.31.2; notes voice scan clean (one backticked term in the whole file,
the `punktfunk-host service install` command, and the only technical vocabulary
sits inside `## For developers`).
Not run here, and why: clippy and any punktfunk-host build (does not compile on
macOS — CI covers it), and the Android unit tests (:kit: and :app: were run on
#365 itself; nothing in this commit touches Kotlin).
One judgement call left for the tag: SECURITY.md promises to credit a reporter in
the release notes when the fix is public, and the #368 commit records only "a
user on 2026-08-21" with no name. The notes credit them unnamed. If they want
their name on it, that is a one-line edit to docs/releases/v0.31.2.md before the
tag is pushed.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Release prep for v0.31.2: version bump, release notes, CHANGELOG section, Play "What's new", and one docs-freshness fix. Cut from
mainat48eeae75(#368 merged).10 commits since v0.31.1 (6 non-merge).
The number
A patch, and this time the version table does not have to argue for it. Nothing versioned moved:
WIRE_VERSION2, C ABI 25 —include/punktfunk_core.hhas no diff at all against the v0.31.1 tag, not even a#define(unlike the last two releases)pf-driver-protounchangedNo
!commit, nofeat, no route added or removed, no breaking change of any kind. Every non-merge commit isfix/refactor/test.The shape of the cycle
Three of the six non-merge commits are the same class of fault — the host using the wrong local address — reached from three directions:
0.0.0.0:0and let routing pick the video source, which the client's connected socket then dropped in-kernelHost::detect()froze the advertised address at process start, so a cold boot that beat the network pinned127.0.0.1for the life of the process — breaking both mDNS adverts, the Moonlight session URL, the WoLmacrecord andHostInfotogetherThe fourth is an Android regression from v0.31.1 (#365); the remaining two are the refactor and test supporting #366.
Two behaviour changes (no build breaks)
service installscopes all five fixed-port rules to the listening executable, keeping theirlocalport=. Externally visible: 5353 is punktfunk's alone now, so anything else on the machine that was reachable on mDNS through our any-program rule needs its own. Fallbacks are asymmetric on purpose — a fixed-port rule that cannot resolve its exe falls back to the old wide form (a looser rule still streams), while the data-plane rule skips (no port to fall back to, so a program-less version would not be looser, it would be open).HostInfo.local_ipis no longer static for the process. It was a field snapshotted atdetect(); it is now a method that re-reads per request. A consumer that cached it at startup was caching a value that could be127.0.0.1forever. This is the onlyapi/openapi.jsoncontent change — twodescriptionstrings, no route/schema/field/type.Also here
docs-site/content/docs/ports.mdx. Its "Video needs nothing opened" bullet has been wrong for Windows since v0.31.1 added the data-plane rule — it now says so and names why (no fixed rule can cover a per-session ephemeral port). The Windows line gains aCalloutfor the 5353 change, since that is the one thing on the page a reader may have to act on.Play notes are Android-only per
whatsnew/TEMPLATE.txt, which this cycle means the #365 regression alone. The three host-side fixes are deliberately not listed: updating the app fixes none of them, so putting them on the store page would promise something the download does not deliver.Gates
cargo fmt --all --checkcargo metadata --offlineCargo.lockdiff versions-only (36/36)cargo test -p punktfunk-core --libabi_version=25punktfunk_core.hscripts/ci/check-docs-drift.shscripts/ci/check-docs-links.shcmpidentical, stamped 0.31.2Not run here: clippy and any
punktfunk-hostbuild (does not compile on macOS — CI covers it), and the Android unit tests (:kit:and:app:ran on #365 itself; nothing here touches Kotlin).One call left for the tagger
SECURITY.mdpromises to credit a reporter in the release notes when the fix is public. The #368 commit records only "a user on 2026-08-21" with no name, so the notes credit them unnamed. If they want their name on it, that is a one-line edit todocs/releases/v0.31.2.mdbefore the tag is pushed.Merge, then tag per
docs/releases/README.md:git tag -a v0.31.2 … && git push origin v0.31.2, wait for every platform green, then dispatchannounce.yml.