Files
punktfunk/crates/punktfunk-core
enricobuehler 1fc9ef0050
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 52s
decky / build-publish (push) Successful in 18s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 44s
ci / bench (push) Successful in 5m56s
flatpak / build-publish (push) Successful in 6m41s
docker / deploy-docs (push) Successful in 27s
apple / swift (push) Successful in 4m40s
deb / build-publish (push) Successful in 12m7s
arch / build-publish (push) Successful in 14m10s
android / android (push) Successful in 17m16s
ci / rust (push) Successful in 17m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m50s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m15s
windows-host / package (push) Successful in 14m33s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m55s
release / apple (push) Successful in 25m31s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m49s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m11s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 6m9s
apple / screenshots (push) Successful in 19m41s
feat(core,host,clients): typed pairing rejections — every client says WHY, not "not accepted"
A host's pairing-gate rejections (not armed / bound to another device /
rate-limited / identity required / denied / approval timeout / superseded /
wire-version mismatch) used to drop the connection with a bare code-0 close,
and every client collapsed that — plus plain unreachability — into one
"wrong PIN / not accepted" message. A dead network path, a disarmed host,
and an operator denial were indistinguishable, which is exactly the
misdiagnosis behind the recent Android pairing support thread.

- core: new ungated `reject` module — shared close-code block 0x60–0x67
  (+ 0x42 busy promoted from the host), `RejectReason`, and
  `PunktfunkError::Rejected`; `pair()`/`connect()` decode the host's
  ApplicationClosed code into `Rejected` instead of a generic Io error.
  C ABI v7: status block −20…−28 and `punktfunk_connect_ex8` (`status_out`
  reports the failure cause; NULL-return alone can't). Wire unchanged —
  old peers see exactly the old bare close.
- host: every gate rejection `conn.close()`s with its typed code (and the
  human reason as close bytes) before erroring out of the session task.
- pf-client-core: shared `pair_error_message`/`connect_reject_message`
  wording consumed by the Windows + Linux + console-UI + CLI surfaces; a
  connect failure now renders the host's stated reason.
- android: `nativeTakeLastError()` JNI token + `ConnectErrors.kt` — a
  network timeout is no longer reported as "wrong PIN, or the host isn't
  armed", and a typed rejection skips the wake-and-wait fallback (the host
  is demonstrably awake).
- apple: `HostRejection` + `.rejected`; the pair sheet and session alerts
  show the stated reason; connect moves to `ex8`.

Completes the cross-client half of the hunks that rode along in 12148243
(client.rs / trust.rs / punktfunk1.rs) — main did not build without this.

Validated: workspace clippy -D warnings + full test suite green on .21
(EXIT=0, 309 host / 148 core suites); macOS core 147+c_abi green; swift
build green; Android Kotlin + native crate green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:58:43 +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