apple / swift (pull_request) Successful in 2m17s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 4m59s
android / android (pull_request) Successful in 6m30s
nix / flake (pull_request) Successful in 6m38s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 8m5s
ci / docs-drift (pull_request) Successful in 29s
ci / bun-nix (pull_request) Successful in 32s
ci / web (pull_request) Successful in 1m6s
ci / docs-site (pull_request) Successful in 1m9s
ci / rust-arm64 (pull_request) Successful in 1m25s
ci / rust (pull_request) Successful in 16m10s
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.
128 lines
6.2 KiB
TOML
128 lines
6.2 KiB
TOML
[workspace]
|
|
resolver = "2"
|
|
members = [
|
|
"crates/punktfunk-core",
|
|
"crates/punktfunk-host",
|
|
"crates/punktfunk-host/vendor/usbip-sim",
|
|
# The capability-carrying PyroWave encode worker. A SEPARATE binary by design — never a
|
|
# hardlink of, or a subcommand of, punktfunk-host (design/gpu-priority-capability-worker.md).
|
|
"crates/punktfunk-encode-worker",
|
|
"crates/punktfunk-tray",
|
|
"crates/pf-bitstream",
|
|
"crates/pf-bitstream/vendor/cros-codecs",
|
|
"crates/pf-client-core",
|
|
"crates/pf-clipboard",
|
|
"crates/pf-presenter",
|
|
"crates/pf-console-ui",
|
|
"crates/pf-driver-proto",
|
|
"crates/pf-paths",
|
|
"crates/pf-update",
|
|
"crates/pf-update-check",
|
|
"crates/pf-host-config",
|
|
"crates/pf-gpu",
|
|
"crates/pf-zerocopy",
|
|
"crates/pf-frame",
|
|
"crates/pf-win-display",
|
|
"crates/pf-encode",
|
|
"crates/pf-capture",
|
|
"crates/pf-inject",
|
|
"crates/pf-vdisplay",
|
|
"crates/pf-vkdecode",
|
|
"crates/pf-dxvadec",
|
|
"crates/pf-vaadec",
|
|
"crates/pyrowave-sys",
|
|
"crates/libvpl-sys",
|
|
"clients/probe",
|
|
"clients/cli",
|
|
"clients/linux",
|
|
"clients/session",
|
|
"clients/windows",
|
|
"clients/android/native",
|
|
"tools/cursor-probe",
|
|
"tools/display-disturb",
|
|
"tools/latency-probe",
|
|
"tools/loss-harness",
|
|
]
|
|
# Standalone PoC (built on its own; pulls usbip/tokio/libusb we don't want in the workspace).
|
|
# The vendored `ndk` is a [patch.crates-io] source, not a member: it only compiles for the
|
|
# `*-linux-android` targets, so workspace membership would break host `cargo build --workspace`.
|
|
exclude = [
|
|
"packaging/linux/steam-deck-gadget/usbip-poc",
|
|
"clients/android/native/vendor/ndk",
|
|
# Bring-your-own-hardware measurement tools. `hid-descriptor-dump` pulls `hidapi`, a C library
|
|
# wanting libudev on Linux; `win-input-matrix` is Windows-only and asks the live input stacks
|
|
# what they can see. Neither belongs in `cargo build --workspace` or on a CI leg with no pad.
|
|
"tools/hid-descriptor-dump",
|
|
"tools/win-input-matrix",
|
|
]
|
|
|
|
# ndk 0.9.0 verbatim from crates.io plus ONE visibility change (and two warning fixes — an
|
|
# unnecessary `std::` qualification and a feature-gated `Result` import): `MediaCodec::as_ptr` made public
|
|
# (upstream keeps it private and exposes no frame-rendered binding), so the Android client can
|
|
# call `AMediaCodec_setOnFrameRenderedCallback` via ndk-sys for the HUD's `display` stage
|
|
# (design/stats-unification.md). Drop the patch when upstream exposes the pointer or the callback.
|
|
[patch.crates-io]
|
|
ndk = { path = "clients/android/native/vendor/ndk" }
|
|
|
|
[workspace.package]
|
|
version = "0.31.2"
|
|
edition = "2024"
|
|
rust-version = "1.85"
|
|
license = "MIT OR Apache-2.0"
|
|
authors = ["unom"]
|
|
repository = "https://git.unom.io/unom/punktfunk"
|
|
|
|
# The `unsafe` discipline the `packaging/windows/drivers/*` crates already run, extended to the
|
|
# workspace. `unsafe fn` marks a CONTRACT the caller must uphold; it is not a licence for the whole
|
|
# body to skip checking. Without this lint an `unsafe fn` body is unchecked end to end, so a 600-line
|
|
# function hides which handful of lines are actually the unsafe ones — exactly the reviewer-hostile
|
|
# shape we are working down. (This is the Rust 2024 default; adopting it early also pays off the
|
|
# edition migration.)
|
|
#
|
|
# `deny`, not `warn`. `warn` was never actually a softer setting: CI runs `cargo clippy … -D
|
|
# warnings`, which promotes it to a hard error anyway — that is how adopting this lint turned main
|
|
# red on every platform for a day without the level in this file ever saying `deny`. A level that
|
|
# lies about its own severity is worse than a strict one, so this now states what CI already does,
|
|
# and the exemptions are written down per file instead of hiding in a 689-warning wall nobody reads.
|
|
#
|
|
# THE EXEMPTIONS. Fourteen GPU/FFI backend files carry `#![allow(unsafe_op_in_unsafe_fn)]` with a
|
|
# one-line reason each. They are not "not done yet" — they are where this lint stops paying:
|
|
# their bodies are ash/CUDA/AMF/libav calls almost line for line (measured: 64% of the sites are a
|
|
# single third-party FFI call, and of the 44 `unsafe fn`s in them only 4 have a body containing no
|
|
# unsafe operation at all). Narrowing them means one `unsafe {}` per line plus, since pf-encode also
|
|
# denies `clippy::undocumented_unsafe_blocks`, one hand-written SAFETY comment per line that could
|
|
# only ever restate "an ash call on a live device" — the precise noise that made `unsafe` stop
|
|
# meaning anything here before (see the header of `pf-win-display/src/win_display.rs`).
|
|
#
|
|
# Everything else in the workspace is at zero and enforced. Removing one of those allows, file by
|
|
# file, is real work with a real payoff; blanket-narrowing all fourteen is not. Prefer DELETING an
|
|
# `unsafe fn` marker over wrapping its body: keep the marker only where a caller can actually break
|
|
# something (a raw pointer, a borrowed HANDLE, a GPU object that must not be in flight).
|
|
[workspace.lints.rust]
|
|
unsafe_op_in_unsafe_fn = "deny"
|
|
|
|
# The companion lint: every `unsafe {}` / `unsafe impl` carries a `// SAFETY:` proof. Hoisted here
|
|
# from ~85 per-file `#![deny(...)]` attributes so a NEW crate (or a new module in an old one) is
|
|
# covered on creation rather than on remembering — the per-file form left pf-vkhdr-layer,
|
|
# wdk-probe, and half of pf-clipboard uncovered for months. NOTE: this table reaches only crates
|
|
# with `[lints] workspace = true`; `packaging/windows/drivers` and `packaging/windows/pf-vkhdr-layer`
|
|
# are SEPARATE workspaces and restate it (any "workspace-wide" claim must be made three times or it
|
|
# is false). Of the members, only the two vendored snapshots (pf-bitstream/vendor/cros-codecs,
|
|
# punktfunk-host/vendor/usbip-sim) stay out, deliberately — upstream code stays pristine.
|
|
[workspace.lints.clippy]
|
|
undocumented_unsafe_blocks = "deny"
|
|
|
|
[profile.release]
|
|
opt-level = 3
|
|
lto = "thin"
|
|
codegen-units = 1
|
|
# NOTE: deliberately NOT `panic = "abort"`. punktfunk-core ships as a cdylib/staticlib into
|
|
# third-party apps (Swift/Kotlin/C) and its C ABI catches panics at the boundary
|
|
# (`catch_unwind` → `PunktfunkStatus::Panic`). `panic = "abort"` would make that guard a
|
|
# no-op and let a stray panic abort the embedding application. Unwinding keeps the
|
|
# documented isolation guarantee real.
|
|
|
|
# The per-frame hot path must stay fast even in dev builds.
|
|
[profile.dev.package."*"]
|
|
opt-level = 2
|