Files
voice-cat/CLAUDE.md
Talon b2af1a3001 feat(macos): validate dev + apple-dev presets on macOS, fix 3 cross-platform bugs
macOS port groundwork — core, server, tools, and tests now build and run on
macOS 26.5 / Apple Silicon. ctest --preset dev green 21/21 (2 consecutive runs).
apple-dev produces valid arm64 libvoicecat.a + XCFramework for the Swift Package.

Three real cross-platform bugs found and fixed (all latent on Windows/Linux):

1. test_m2_voice.cpp POSIX branch missing <netdb.h> — Linux glibc transitively
   includes it, macOS doesn't. Would fail on any strict POSIX system.

2. SIGPIPE killing processes on macOS — writing to a closed TCP socket raises
   SIGPIPE by default (doesn't exist on Windows, benign on Linux). Fixed by
   ignoring SIGPIPE in both core client init and server startup (POSIX-only,
   #ifndef _WIN32). Production fix, not just tests.

3. Use-after-free of Asio's kqueue reactor on server shutdown — the
   deterministic test_tofu_flow segfault. TcpServerConn's tls_read_loop runs on
   a blocking-I/O thread; when Server::run() returned, io_context was destroyed
   while those threads were still running. On macOS kqueue the reactor pointer
   is null'd immediately -> segfault in socket.close(). Latent on Windows IOCP
   and Linux epoll. Fix: TcpAcceptor now tracks connections; new shutdown()
   closes all + joins threads before io is destroyed; Server::stop() now closes
   acceptor + media_relay too (was just io.stop()).

Verified: dev + apple-dev presets build green, 21/21 tests pass, server starts
+ two vccli text chat over TLS (M1 on Mac), vccli --voice starts MIC stream via
CoreAudio (M2 protocol-level), vccli --list-devices enumerates CoreAudio
devices, xcodebuild -create-xcframework produces valid VoiceCatCore.xcframework.

No ABI or proto changes. Docs updated: building.md, clients/apple/README.md,
PROGRESS.md, CLAUDE.md status line.
2026-06-18 13:24:42 +02:00

158 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md — agent hub for VoiceCat
Auto-loaded each session. This is the **map**: build commands, architecture at a glance, and
where everything is. For the *working method* read [`AGENTS.md`](AGENTS.md); for *what's done
and what's next* read [`PROGRESS.md`](PROGRESS.md); for *design* read [`docs/`](docs/).
> **One-line status:** M5 (moderation & admin UI) is complete — permissions, kick/ban/move,
> server-mute, channel CRUD, in-app account management, disconnect/keepalive/reaper. Windows
> WinForms C# client is shipped (M4). **macOS port validated** — `dev` + `apple-dev` presets
> build green, 21/21 tests pass on macOS. `ctest --preset dev` green — 21/21 tests.
> macOS/iOS Swift client is next. See [`PROGRESS.md`](PROGRESS.md).
VoiceCat = self-hosted native voice & text chat (TeamSpeak/Mumble-style). Plain TCP (control)
+ UDP (media), no WebRTC, encrypted by default. A shared C++ core (`libvoicecat`) drives
native clients (Swift on macOS/iOS, C# on Windows) and the server.
---
## Build & test commands
The default development preset is **`dev`** — it builds everything (server + tools + tests)
with real vcpkg deps. The `skeleton` preset (no deps, stubs only) is a fast smoke check; see
[`docs/building.md`](docs/building.md) for the full preset matrix.
```bash
# Configure + build (default development preset; needs VCPKG_ROOT)
cmake --preset dev
cmake --build --preset dev
# Run the tests (21 behavior tests — grows per milestone)
ctest --preset dev # or: ctest --test-dir build/dev --output-on-failure
# Run the binaries (Windows adds .exe; Linux/macOS no extension)
./build/dev/bin/vccli # headless test client
./build/dev/bin/voicecat-server --help
./build/dev/bin/voicecat-server --name "My Server"
# Build a single target / be verbose
cmake --build --preset dev --target vccli
cmake --build --preset dev --verbose
# Clean
rm -rf build/dev # nuke; or:
cmake --build --preset dev --target clean
```
Other presets (see [`docs/building.md`](docs/building.md) for full detail):
```bash
cmake --preset skeleton # no-deps stub smoke (no VCPKG_ROOT needed) — 2 tests
cmake --preset release # optimized + tests on, symbols kept (profile/debug-friendly)
cmake --preset server-release # optimized + stripped, no tests (deployment-shaped)
cmake --preset windows-client # voicecat.dll for the C# WinForms client (Windows only)
cmake --preset apple-dev # libvoicecat.a for macOS Swift Package (scaffolding, macOS only)
```
Vcpkg triplet is auto-resolved from the host platform by
[`cmake/voicecat-toolchain.cmake`](cmake/voicecat-toolchain.cmake) — `x64-mingw-static` on
Windows, `x64-linux` on Linux, `arm64-osx` on Apple Silicon. See docs/building.md §1
"Platform matrix" for details.
One-time vcpkg setup:
```bash
# one-time: git clone https://github.com/microsoft/vcpkg && ./vcpkg/bootstrap-vcpkg.sh (.bat on Windows)
export VCPKG_ROOT=/path/to/vcpkg # works on Linux / macOS / Windows
```
Other useful toggles (pass with `-D` at configure time):
```bash
cmake --preset dev -DVOICECAT_BUILD_SHARED=ON # build libvoicecat as a .dll/.so/.dylib (for the C# client)
cmake --preset dev -DVOICECAT_BUILD_SERVER=OFF # core + tools only
cmake --preset dev -DVOICECAT_BUILD_TESTS=OFF
```
Formatting: `clang-format` config is `.clang-format` (Google base, 100 cols, 4-space).
```bash
git ls-files '*.cpp' '*.h' | xargs clang-format -i
```
---
## Architecture at a glance
Full detail: [`docs/architecture.md`](docs/architecture.md). The short version:
```
Swift (macOS/iOS) ─┐ ┌─ C# (Windows)
├──▶ libvoicecat (C ABI: voicecat.h) ◀──┤
voicecat-server ───┘ net · crypto · codec · protocol · └─ all UIs are thin
(links core) session · audio the core owns audio
```
- **One core, many faces.** Protocol, Opus, crypto, networking, jitter buffer, and mixing
live once in C++. Clients call the **C ABI** (`core/include/voicecat.h`); the server links
the same core, so framing/crypto never drift between ends.
- **Two transports.** TCP + **TLS 1.3** (control, protobuf `Envelope`) and UDP + **exported-key
ChaCha20-Poly1305 AEAD** (media, fixed binary voice frame). Encryption is mandatory.
- **Threading.** Real-time audio threads never allocate/lock/block; they exchange data with
the net thread via lock-free ring buffers; a worker pool absorbs blocking work.
### Subsystem map (code ↔ design doc)
| Path | Subsystem | Design |
|------|-----------|--------|
| `core/include/voicecat.h` | The C ABI (client/server contract) | architecture.md §4 |
| `core/proto/voicecat.proto` | Control-plane wire format (source of truth) | protocol.md |
| `core/src/net/` | Asio TCP/UDP transport, `[u32 len][payload]` framing | protocol.md §1, voice.md §2 |
| `core/src/crypto/` | TLS 1.3 (mbedTLS), media AEAD (libsodium), anti-replay | security.md |
| `core/src/codec/` | Opus encode/decode, FEC/DTX | voice.md §34 |
| `core/src/protocol/` | Envelope (de)serialize, request/response, dispatch | protocol.md |
| `core/src/session/` | Channels, users, streams, permissions, ephemeral text | protocol.md §5 |
| `core/src/audio/` | miniaudio I/O, APM DSP, jitter buffer, mixer | voice.md §811 |
| `core/src/core/` | `vc_client` — the handle behind the C ABI | architecture.md §4 |
| `server/` | Connection mgr, session registry, SFU relay, SQLite | architecture.md §5 |
| `tools/vccli/` | Headless client that drives/verifies the protocol | — |
| `clients/apple/`, `clients/windows/` | Native GUIs (M4) | architecture.md §4 |
---
## Documentation index (source of truth)
Read [`docs/`](docs/) before changing behavior. Order:
1. [docs/README.md](docs/README.md) — overview, locked decisions, glossary
2. [docs/architecture.md](docs/architecture.md) — core, C ABI, threading, server
3. [docs/protocol.md](docs/protocol.md) — control plane, Envelope, message catalog
4. [docs/voice.md](docs/voice.md) — UDP media, Opus, multi-stream, two-sided NR, VAD/PTT
5. [docs/security.md](docs/security.md) — mandatory encryption, TLS+AEAD, accounts, threat model
6. [docs/tech-stack.md](docs/tech-stack.md) — libraries, permissive-license rule, tooling
7. [docs/deployment.md](docs/deployment.md) — zero-config self-host (Docker / binary / source)
8. [docs/roadmap.md](docs/roadmap.md) — milestones + resolved decisions
9. [docs/building.md](docs/building.md) — what each CMake preset is for + manual server/`vccli` testing
---
## Keeping track of progress
**[`PROGRESS.md`](PROGRESS.md) is the living status file.** When you finish a task, check it
off there and note the next step, so the next agent can pick up instantly. Treat it as part of
the work, not an afterthought — update it in the same commit as the code.
---
## House rules (hard constraints)
- **A clean compile is the floor, not the goal.** "Done" = the milestone's observable exit
criterion in [`docs/roadmap.md`](docs/roadmap.md) passes (e.g. M1 = two `vccli` actually chat
over TLS). Encode it as a test. See [`AGENTS.md`](AGENTS.md).
- **No GPL/LGPL dependencies, ever** (closed-source redistribution is a goal). docs/tech-stack.md §5.
- **Encryption is mandatory** — never add a plaintext transport path. docs/security.md.
- **Real-time audio threads** never allocate, lock, or block. docs/architecture.md §3.
- **Keep docs + code in sync.** Changing a wire format (`voicecat.proto`) or the C ABI
(`voicecat.h`) is a deliberate, versioned act — update the doc in the same commit (protocol.md §8).
- Every commit must build (`cmake --build --preset dev`) and pass `ctest --preset dev`.