Adds vcpkg as a submodule at vcpkg/, pinned to the exact commit vcpkg.json already declares as builtin-baseline, so the bundled checkout and the manifest's resolved port versions can never drift apart. cmake/voicecat-toolchain.cmake, scripts/common.sh, and clients/apple/scripts/build-xcframework.sh now resolve vcpkg as: VCPKG_ROOT env var (external checkout) > bundled submodule. Docs updated to describe the new one-time setup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.8 KiB
AGENTS.md — working method
This file is the working method for a developer or AI agent picking up VoiceCat. Companion files:
CLAUDE.md— the hub: build/test commands, architecture at a glance, doc map.PROGRESS.md— living tracker: what's done, what's next. Update it as you work.docs/— the source of truth for all design.
Read those, then use the method below.
What this repo is right now
A complete design (docs/) plus a working implementation through M5: real TLS
control plane, encrypted UDP voice (Opus), multi-stream, TOFU identity pinning, channel tree,
permissions, moderation, disconnect/keepalive/reaper. The Windows WinForms C# client is
shipped (M4). The macOS/iOS Swift client is next. The skeleton preset still links a
no-deps stub path (VC_ERR_NOT_IMPLEMENTED) for smoke-check builds.
The working method (important)
A clean compile is the floor, not the goal. Do not treat "make the compiler errors go
away" as done. Each milestone in docs/roadmap.md has an exit
criterion stated as observable behavior — that is what "done" means. Examples:
- M1 done = two
vccliinstances actually chat through a real server over TLS, not "it builds". - M2 done = you can talk between two clients in a channel and hear loss concealment work.
So the loop is:
- Pick the current milestone in
docs/roadmap.md. Read the relevant design doc section. - Write the smallest test (CTest, or a
vccliinteraction) that encodes the exit behavior. - Implement the subsystem until that test passes — not just until it compiles.
- Keep the build green and the existing tests passing on every commit.
Every commit must compile and pass ctest. Behavior tests are how you know you're actually
making progress.
Build
Default development preset (real deps via vcpkg — works on Windows/Linux/macOS). vcpkg is
bundled as a git submodule at vcpkg/, pinned to vcpkg.json's builtin-baseline:
git submodule update --init vcpkg # one-time, after cloning
./vcpkg/bootstrap-vcpkg.sh # .bat on Windows
cmake --preset dev
cmake --build --preset dev
ctest --preset dev
To use an external vcpkg checkout instead, export VCPKG_ROOT=/path/to/vcpkg — it always
takes priority over the bundled submodule.
Skeleton (no third-party deps — works immediately, no vcpkg needed):
cmake --preset skeleton
cmake --build --preset skeleton
ctest --preset skeleton
Windows gotcha — always run
ctestand built binaries via PowerShell, not Git Bash. MinGW-built executables fail in Git Bash with exit code0xc0000139(STATUS_ENTRYPOINT_NOT_FOUND) even though the file exists and is marked executable. PowerShell runs them correctly. Use the PowerShell tool (not Bash) for anyctest,voicecat-server.exe, orvccli.exeinvocation on Windows.
Other presets: release (optimized + tests), server-release (optimized + stripped,
deployment-shaped), windows-client (DLL for C# app), apple-dev/apple-ios/
apple-ios-sim (Apple platform scaffolding). See docs/building.md
for the full matrix.
vcpkg.json pins all deps to a fixed vcpkg baseline — cmake --preset dev resolves them
automatically on first configure. The vcpkg triplet is auto-resolved from the host platform
by cmake/voicecat-toolchain.cmake, which also resolves
VCPKG_ROOT (env var override, else the bundled vcpkg/ submodule).
Where each subsystem lives (and its doc)
| Path | Subsystem | Design |
|---|---|---|
core/include/voicecat.h |
The C ABI every client/server calls | docs/architecture.md §4 |
core/proto/voicecat.proto |
Control-plane wire format | docs/protocol.md |
core/src/net/ |
Asio TCP/UDP transport, framing | docs/architecture.md, docs/protocol.md §1 |
core/src/crypto/ |
TLS 1.3 (mbedTLS), media AEAD (libsodium), anti-replay | docs/security.md |
core/src/codec/ |
Opus encode/decode, FEC/DTX | docs/voice.md §3–4 |
core/src/protocol/ |
Envelope (de)serialize, state machine, routing | docs/protocol.md |
core/src/session/ |
Channels, users, streams, permissions, text | docs/protocol.md §5 |
core/src/audio/ |
Capture/playback (miniaudio), APM DSP, jitter buffer, mixer | docs/voice.md §8–11 |
server/ |
Connection mgr, session registry, SFU relay, SQLite | docs/architecture.md §5 |
tools/vccli/ |
Headless client to drive/verify the protocol | — |
Suggested first steps (M1 spine)
- Wire protobuf + framing (
net/+protocol/): build, then generate C++ fromvoicecat.proto, implement the[u32 length][Envelope]framing over a plain TCP socket (TLS can come right after). Test: round-trip anEnvelopethrough the framer. - TLS 1.3 via mbedTLS (
crypto/): wrap the TCP channel. Test:vcclicompletes a TLS handshake againstvoicecat-serverand exchanges aClientHello/ServerHello. - Auth + state (
session/): guest + admin-provisioned accounts (Argon2id/SQLite), channel tree snapshot/deltas, ephemeral text relay. Test: twovcclichat.
Then proceed to M2 (UDP media) per the roadmap.
House rules
- No GPL/LGPL dependencies, ever (closed-source redistribution is a goal). See docs/tech-stack.md §5. CI should fail on a copyleft transitive dep.
- 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/and code in sync. If you change a wire format or the C ABI, update the doc in the same commit. - The C ABI is the contract for the Swift/C# clients — treat changes to
voicecat.handvoicecat.protoas deliberate, versioned events (docs/protocol.md §8).