Two receive-path findings from the networking audit:
1. The anti-replay window (4096 seqs) silently re-tightened the "late ≠ lost"
fix: at 1 Gbps (~125k pkt/s) it spans only ~33 ms, so a Wi-Fi-retry-delayed
shard the reassembler's 120 ms loss window would still use was dropped HERE
first as "older than the window" — recreating the false-loss → recovery-IDR
churn the time-based loss window was built to kill, exactly on the high-rate
links punktfunk targets. Widened to 32768 (covers 120 ms up to ~270k pkt/s,
≈2 Gbps+); the bitmap costs 4 KiB per session and the replay-hiding bound
stays finite.
2. Every received datagram still paid one Vec allocation in the AES-GCM open
(and a to_vec on the plaintext probe path) — ~125k allocs/s of cross-thread
allocator churn at line rate, the same class of overhead that was the
documented single-core wall on the macOS receive path. New
`SessionCrypto::open_in_place` (mirror of seal_in_place; GCM verifies the
tag BEFORE decrypting, so a forged packet never yields plaintext) lets
`poll_frame` decrypt inside the recv ring and hand the reassembler a slice.
Byte-identical semantics, unit-tested against `open` incl. tamper/runt
cases; criterion entry added next to seal_in_place.
Tests: 94 core unit + loopback/c_abi suites green; clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The host sealed every packet with ~3 heap allocations: aes-gcm's convenience
encrypt() allocates the ciphertext Vec, seal_for_wire allocates the seq||ct||tag
wire Vec, and seal_frame allocated a fresh Vec<Vec<u8>> per frame. At line rate
(~250k–500k pkt/s for 2.5–5 Gbps) that's the single-core allocator wall.
- SessionCrypto::seal_in_place uses AeadInPlace::encrypt_in_place_detached to
encrypt into the caller's buffer and write the detached tag at the end —
byte-identical to seal's ciphertext||tag, no allocation (unit-tested for byte
equality + decrypt).
- Session keeps a wire_pool the caller returns via reclaim_wires; seal_frame
seals each packet in place into the reused buffers (clear() keeps capacity), so
after warmup there's no per-packet ciphertext/wire allocation. paced_submit and
submit_frame reclaim the pool after sending.
End-to-end encrypted/lossless multi-frame tests stay green (validates the pool
reuse doesn't corrupt across frames). Next: write packetize directly into a
contiguous send buffer (kills the remaining shard allocs + GSO's coalescing copy).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Full project rename, decided 2026-06-10:
- Crates/binaries: punktfunk-core / punktfunk-host / punktfunk-client-rs.
- C ABI: punktfunk_* symbols, Punktfunk* types, include/punktfunk_core.h,
PUNKTFUNK_FEATURE_QUIC guard (header regenerated; cbindgen renames updated, incl.
PUNKTFUNK_BTN_*/PUNKTFUNK_AXIS_* wire constants).
- Protocol: punktfunk/1 — control-plane magic LMN1 → PKF1, nonce salt lmn1 → pkf1.
WIRE BREAK: clients must be rebuilt from this revision.
- Env knobs: PUNKTFUNK_VIDEO_SOURCE / PUNKTFUNK_COMPOSITOR / PUNKTFUNK_ZEROCOPY / ….
- Host config dir: ~/.config/punktfunk (the box's dir was migrated in place — the
persistent identity is unchanged, pinned fingerprints stay valid).
- Swift package: PunktfunkKit + PunktfunkCore.xcframework + PunktfunkConnection
(Sources/PunktfunkClient app + tests renamed with it); build-xcframework.sh updated.
- scripts/: 60-punktfunk.rules, punktfunk-host.service; OpenAPI doc regenerated.
Also: scripts/headless/run-headless-kde.sh — full headless Plasma bringup. Root cause of
"desktop but no apps/settings" over the stream: plasmashell launched without
XDG_MENU_PREFIX=plasma-, so the launcher resolved a nonexistent applications.menu and
rendered an empty menu. The script sets the complete KDE session env (menu prefix,
KDE_FULL_SESSION, session version) and rebuilds ksycoca before starting plasmashell.
Gate: 97/97 tests, clippy -D warnings (both feature sets), fmt, C-ABI harness PASS,
zero lumen references left outside .git.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>