mollusk aa0515af1c Merge phase 0b + the peerspeak half of 0c: teardown ordering, reaping, graceful stop
Two invariants land here, both of which phase 6 depends on:

1. The echo-cancel module cannot unload while a pixelpass host is alive. The
   ordering-critical fields moved into `ScreenshareTeardown` with `echo_cancel`
   declared LAST, and killing is no longer taken for reaping — `kill_on_drop`
   only signals, so `ReapOnDrop` blocks on a bounded poll until the child is
   actually gone. The defect was real, not theoretical: `echo_cancel` sat ahead
   of `screenshare_host` in declaration order, so any unwind unloaded the AEC
   first, and unwind is reachable (no `panic=abort`, many `unwrap()`s).

2. Stop Share asks before it insists — SIGINT, a bounded grace, then SIGKILL —
   so pixelpass runs its own cleanup instead of leaking a null-sink module every
   time. SIGINT specifically: pixelpass installs only a `ctrl_c()` handler.

Reviewed by Codex across two rounds: changes-requested (two blocking findings,
both real, both the same shape — a defence disarmed exactly when it was needed)
then approve-with-follow-ups (five P3s, all applied). The mutation matrix was
revised from five to four after one pinned mutation was proved unreachable by
construction, and teardown was hoisted to one unconditional post-loop site so
every loop exit is covered structurally.

Owed and recorded: the live Stop Share SIGINT gate has never been field-run, and
the hoisted call site's live proof belongs to the phase-9 lifecycle row.

638 lib tests, clippy clean, fmt clean.
2026-07-26 19:43:17 -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%