molluskandClaude Opus 5 9eab6c118d audio/ownership: let libspa say where the object closes
Round 12 review, finding 1 — a measured fail-open, and the third
distinct door into the same failure.

A trailing comment is valid SPA-JSON and *ends the document*
(`case __COMMENT: return 0` in spa/utils/json-core.h), so an object may
close before the last `}` in the string. `merge_pipewire_props` located
the closing brace with `rfind('}')`, which is a byte scan and not a
parse, so for

    { "target.object" = "my-sink" } # trailing }

it selected the comment's brace and spliced both ownership carriers
*into the comment*. The re-validation did not catch it, because the
result parses perfectly well — as `{ target.object = "my-sink" }`, with
neither carrier present. Confirmed against `spa-json-dump -s`.

That is an untagged node, so no taint root, so echo — exactly what
rounds 10 and 11 each closed by a different route. Latent rather than
live: pixelpass's evaluate() is still audit-only, so today it corrupts
an audit classification and becomes a leak when phase 6 consumes
eligibility.

The whole thesis of round 11 was "do not re-implement someone else's
grammar". The scanner went, but this brace hunt stayed behind in the
caller, which is the same defect wearing different clothes.

So spa_object now reports the object's own closer, taken from libspa:
closing a container at depth 0 writes the brace's position back to the
parent iterator, and spa_json_enter made `outer` that parent. Read
before the trailing check, which advances past it.

Also:
- whatever followed the object is preserved, so a user's trailing
  comment survives instead of being silently deleted;
- the output check now asks whether the object closes where we put our
  brace, not merely whether the string parses. A parse-only check is
  what this finding defeated.

Mutation-verified: restoring `rfind` fails the new test, and dropping
the tail fails it on the deleted comment. Honest note in the code —
mutation cannot distinguish the closer comparison or the is-object
test; both are labelled belt-and-braces rather than presented as
tested.

621 -> 622 lib tests, fmt clean, clippy clean, and the ignored
spa-json-dump differential still agrees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 01:51:14 -04:00
2026-07-19 15:57:05 -04:00
2026-07-19 15:57:05 -04:00
2026-07-19 15:57:05 -04:00
2026-07-19 15:57:05 -04:00

PeerSpeak icon PeerSpeak

Decentralized, peer-to-peer voice chat — full-mesh, NAT-traversing, with no central server. Built in Rust on iroh (QUIC), PipeWire audio, the Opus codec, and an iced GUI.

Create a room, share the join ticket, and talk. Everyone connects directly to everyone else; relays are only used to punch through NATs when a direct path isn't available.


Screenshots

Launch screen In a room
Launch screen In a room
Settings
Settings

Features

Rooms & sessions

  • Create a room → shareable join ticket; join by pasting a ticket.
  • Full-mesh multi-peer rooms with live presence.
  • Recent-rooms list to hop back into a room someone's still in.
  • Remembered nickname and in-call duration timer.

Audio

  • PipeWire capture/playback, selectable input and output devices, per-app gain.
  • Opus codec (48 kHz mono, 20 ms frames) with an adaptive jitter buffer + packet-loss concealment.
  • Noise gate with a draggable threshold on a live mic meter (test your mic off-call too).
  • Mix-bus soft limiter and opt-in echo cancellation (PipeWire WebRTC AEC + noise suppression).

Voice controls

  • Self-mute, deafen, and rebindable push-to-talk.
  • Per-peer volume, local mute, and speaking indicators.

Text chat

  • In-room text chat over the gossip plane, with clickable links and inline image/audio attachments.
  • Drag-selectable, copyable messages; right-click context menu on all text fields.

Shared music listening

  • Build a personal playlist of local audio files with a full transport (play/pause, seek, reorder).
  • Let others tune in: peers stream your current track, timeline-synced and gapless, sitting under voice at their own volume.

Screen share (via pixelpass)

  • Share your screen; peers click 👁 Watch to open the stream in mpv (vlc fallback).
  • Live badges on sharing peers; per-app audio capture.

Recording & notifications

  • Local call recording (mic + incoming mix → WAV in ~/peerspeak-recordings/).
  • Desktop notifications and event chimes with per-event custom sound overrides.

UI & networking

  • Selectable room layouts (3-Column, Bottom Dock, Drawer) with draggable, persisted dividers.
  • 10 built-in themes (Catppuccin, Dracula, Nord, Tokyo Night, Gruvbox, Solarized…), all WCAG-AA checked.
  • Network mode picker (relay-no-discovery default, full n0, or direct-only); retained-address reconnect.
  • Config, window size/position, and all preferences persisted to ~/.config/peerspeak/.

See docs/FEATURES.md for the full inventory and field-test status, and docs/ARCHITECTURE.md for internals.

Roadmap

  • Contacts & invites — friends list with invite-notification one-click join (design in docs/contacts-plan.md).
  • Spatial audio & per-peer EQ.
  • Soundboard — play short clips into the call mix.
  • Room persistence / invite links beyond the raw ticket.
  • Windows support — cross-compiles and launches under Wine today; needs a real WASAPI audio pass (see docs/WINDOWS.md).

Building

PeerSpeak builds with a stable Rust toolchain (edition 2024). Install the system dependencies below, then:

cargo build --release
./target/release/peerspeak

System dependencies

Arch Linux

sudo pacman -S --needed rust pipewire opus pkgconf git

Debian / Ubuntu

sudo apt install build-essential pkg-config clang libclang-dev \
  libpipewire-0.3-dev libopus-dev libasound2-dev libxcb1-dev

Plus a Rust toolchain via rustup. clang/libclang are needed for the PipeWire bindings (bindgen).

At runtime you need a running PipeWire server. Screen sharing additionally requires pixelpass on your PATH, and mpv (or vlc) to watch a peer's share.

Packaging

  • Arch: cd packaging && makepkg -si (uses packaging/PKGBUILD).
  • Debian/Ubuntu: .deb is built with cargo-deb from the [package.metadata.deb] block in Cargo.toml. Build inside a Debian/Ubuntu environment so the binary links that distro's glibc.
  • Windows: see docs/WINDOWS.md.

License

PeerSpeak is licensed under the MIT License, © 2026 mollusk.

Third-party components bundled with PeerSpeak (the Rust dependency tree, the statically bundled Opus codec on some builds, and embedded fonts) are all under permissive licenses; their texts and a full dependency manifest are collected in THIRD_PARTY_LICENSES.

S
Description
No description provided
Readme MIT
15 MiB
2026-07-19 19:57:06 +00:00
Languages
Rust 98.8%
Shell 0.4%
Nix 0.4%
Python 0.2%
Inno Setup 0.2%