Files
punktfunk/crates/punktfunk-core
enricobuehler 1099c94ca3 fix(client/present): one bogus present stamp no longer wedges the cadence run
Caught on glass during the WP2 baseline, on the first stream the metric ever
measured: judder=0 mode=0 cadN=0 disorder=119, every second, on a perfectly
healthy 118 fps stream with the panel period correctly learned at 8.33 ms.

Every single present was scoring as disordered. The cause was the vendor quirk
decode/display.rs already documents — Android's render callback can deliver a
garbage far-future system_nano on a session's first frames. The rule "hold the
later instant so one reordered delivery cannot corrupt the following spacings"
then latched onto that stamp permanently: every real timestamp afterwards was
behind it, so nothing was ever scored again for the rest of the session.

The rule was right for what it was written for and wrong past a bound. A step
backwards of a few refreshes IS a reordered delivery and the later instant
should win; a step backwards of an hour is a bogus stamp and the run must
re-anchor onto the new sample. One bad sample now costs one sample.

Both implementations get the bound and the regression test, since the two must
agree; the Swift port would otherwise have shipped the same latch-up.

Also makes the statistic self-diagnosing, which is what turned a puzzling
result into a five-minute diagnosis: summary() returning None was
indistinguishable from a window of perfectly smooth zeros in a log line, so
"no cadence is being scored at all" looked exactly like "no judder". The
pf.present line now carries the raw sample/stall/disorder counts and the period
the run is quantising against, whatever the evidence bar.

Gates: punktfunk-core 190 tests green (22 in phase, incl. the new
a_garbage_far_future_stamp_does_not_wedge_the_run); swift test
--filter PresentIntervalsTests 11/11 green; fmt clean; on-glass re-run against
.173 now reports judder 8-36permille with cadN ~119/s.
2026-08-05 23:45:06 +02:00
..

punktfunk-core

The shared protocol core — the one place where punktfunk's transport, forward error correction, and crypto live. It's linked into the host and every native client, so there's exactly one implementation of the wire format everywhere.

Written in Rust with no async on the per-frame path (native threads only). It exposes both a normal Rust API and a stable, versioned C ABI, so the Swift and Kotlin clients — and any C embedder — link the same code as the Rust ones.

What's in here

  • Transport & session (session.rs, transport/, packet.rs) — the punktfunk/1 data plane over raw UDP: packetization, reassembly (with attacker-bounded limits), pacing, and socket tuning.
  • FEC (fec/) — the wall-breaker. Two codes:
    • GF(2⁸) classic ReedSolomon with the Cauchy generator matrix — byte-identical to the nanors library Moonlight uses, so our parity is decodable by a stock Moonlight client.
    • GF(2¹⁶) Leopard-RS (SIMD, O(n log n)) — up to 65535 shards/block, which removes the ~1 Gbps FEC ceiling. punktfunk/1 negotiates this one.
  • Crypto (crypto.rs) — AES-128-GCM session encryption with per-direction nonce salts and sequence-as-AAD; SPAKE2 PIN pairing lives behind the quic feature.
  • QUIC control plane (quic.rs, client.rs, feature quic) — the Hello/Welcome/Start handshake, cert pinning/TOFU, reverse audio, and the embeddable NativeClient connector. This is the only place tokio/quinn are allowed; the feature is off by default so the core stays runtime-free.
  • C ABI (abi.rs) — the versioned surface (punktfunk_abi_version(), PunktfunkConfig carrying its own struct_size) that generates include/punktfunk_core.h via cbindgen at build time.

Build outputs

The crate builds three ways at once (crate-type = ["lib", "cdylib", "staticlib"]):

Output Used by
lib (rlib) the host, probe, and tools link it as a normal Rust crate
cdylib (.so/.dylib) the Swift / Kotlin clients via the C ABI
staticlib (.a) the C test harness and static embedding

Test

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

Design invariants (do not regress)

  • One core, linked everywhere — protocol/FEC/crypto live only here, behind the stable C ABI.
  • No async on the hot path — the per-frame pipeline is native threads only; quic (tokio/quinn) is control-plane only, feature-gated, off by default.
  • Security hardening stays intact — the reassembler bounds attacker-controlled fields before allocating; AES-GCM keeps per-direction nonce salts + seq-as-AAD; the ABI checks struct_size. Regression tests exist — keep them green.
  • punktfunk-host — the streaming host built on this core
  • Clients — the apps that link this core over the C ABI (or directly, in Rust)
  • punktfunk-planning: implementation-plan.md (internal planning repo) — why GF(2¹⁶) FEC, the latency budget, and the architecture thesis