# VoiceCat .NET rewrite The .NET 10 implementation includes protocol, TLS/TOFU and media crypto, the control and UDP server, client state, real-time audio, a console client and the Windows application. The existing C++ implementation remains the conformance oracle while Apple clients move. Codec/DSP wrappers now cover Opus, DRED recovery, RNNoise, and energy VAD. Build the desktop native library before running their tests (CMake and a C compiler required): ```powershell ./dotnet/build-native.ps1 ``` The script downloads upstream Opus 1.5.2 with a pinned SHA-256, builds DRED-enabled Opus and the existing vendored RNNoise model, and stages `voicecat_media` plus license notices under `dotnet/artifacts/native/`. It builds independently of the C++ core and vcpkg. On Windows, Visual Studio's C++ workload works with the default generator; for this repository's MinGW toolchain use: ```powershell ./dotnet/build-native.ps1 -Generator Ninja -CCompiler C:/tools/msys64/ucrt64/bin/cc.exe ``` Linux/macOS can run the same script with PowerShell, or use CMake directly: ```sh cmake -S dotnet/native -B dotnet/artifacts/native-build -DCMAKE_BUILD_TYPE=Release cmake --build dotnet/artifacts/native-build --target voicecat_media --parallel 2 cmake --install dotnet/artifacts/native-build --component DotnetMedia --prefix dotnet/artifacts/native ``` MSBuild copies the staged library into managed build/publish output for the selected RID. Override `VoiceCatNativeRid` or `VoiceCatNativeDirectory` for explicit staging; `RuntimeIdentifier` takes priority over the SDK's host RID. Cross-compilation is not automatic. iOS static linking and audio-device shims belong to later client phases. Native codec/DSP tests require this library; they do not silently skip. From the repository root: ```powershell dotnet restore dotnet/VoiceCat.slnx --locked-mode dotnet build dotnet/VoiceCat.slnx -c Release --no-restore dotnet test dotnet/VoiceCat.slnx -c Release --no-build ``` Run the managed console client interactively: ```powershell dotnet run --project dotnet/src/VoiceCat.Cli -- --host 127.0.0.1 --port 8384 --nickname Alice --trust-first ``` Plain input sends channel text; `/join ID` changes channel and `/quit` exits. Headless conformance options include `--voice`, `--send-text`, `--expect-text`, `--expect-voice`, `--start-delay-ms` and `--timeout-seconds`; `--help` lists the complete syntax. Dependencies are pinned in project files and lock files. Generated protobuf is build output; the schema remains `core/proto/voicecat.proto`. Production dependencies are Google.Protobuf (BSD-3-Clause), BouncyCastle.Cryptography (MIT), and the build-only Grpc.Tools (Apache-2.0). No GPL/LGPL dependencies are permitted. ## C# conventions Use file-scoped namespaces, standard .NET naming, immutable values where useful, and spans for binary data. Invalid arguments throw; invalid network packets use parsing results or protocol exceptions. Async APIs accept cancellation tokens. Comments explain constraints that cannot be made clear in code. Avoid banners, implementation history, and narration. Keep durable design explanations in `docs/`. ## Regenerating C++ fixtures The optional oracle target calls the existing C++ protobuf, header serializer, and libsodium media implementation. From the root, with the development dependencies: ```powershell cmake --preset dev -DVOICECAT_BUILD_DOTNET_ORACLE=ON cmake --build --preset dev --target voicecat-dotnet-oracle New-Item -ItemType Directory -Force dotnet/tests/VoiceCat.Tests/Fixtures ./build/dev/bin/voicecat-dotnet-oracle.exe dotnet/tests/VoiceCat.Tests/Fixtures/cpp-wire.json git diff -- dotnet/tests/VoiceCat.Tests/Fixtures/cpp-wire.json ``` On Linux/macOS, omit `.exe` and create the directory with `mkdir -p`. The oracle writes deterministic JSON directly, avoiding shell output encoding. Fixtures contain a framed ClientHello and media packets at counters 0, 1, 65535, and 65536. Keys contain bytes 0–31; payload bytes count upward from zero. The 20-byte header has type 1, marker flag, codec 0, SSRC `0xcafebabe`, timestamp 960. Both managed crypto backends must match these bytes. The DSP oracle calls the existing C++ `ApmProcessor` with 200 deterministic noise frames and records the final 960 samples. Regenerate its fixture with: ```powershell cmake --build --preset dev --target voicecat-dotnet-dsp-oracle ./build/dev/bin/voicecat-dotnet-dsp-oracle.exe dotnet/tests/VoiceCat.Tests/Fixtures/cpp-noise.json ``` The managed test allows a one-unit PCM difference for floating-point rounding. ## TLS interoperability The optional TLS oracle uses the existing mbedTLS context and libsodium media crypto. The test authenticates an encrypted challenge in both directions, proving exporter compatibility without sending raw keys. It also loads the C++ server's credential files. ```powershell cmake --build --preset dev --target voicecat-dotnet-tls-oracle $env:VOICECAT_TLS_ORACLE = (Resolve-Path build/dev/bin/voicecat-dotnet-tls-oracle.exe).Path dotnet test dotnet/VoiceCat.slnx -c Release --no-restore ``` On Linux/macOS, set `VOICECAT_TLS_ORACLE` to the absolute executable path without `.exe`. Without that variable, only this native interoperability test is skipped; managed TLS loopback, rejection, persistence, and wire tests still run. CI's C++ conformance job requires the native test. See `docs/api-dotnet.md` for ownership and certificate acceptance requirements. ## Managed server checkpoint Run the TLS control server on loopback (optional arguments: data directory, TCP port): ```powershell dotnet run --project dotnet/src/VoiceCat.Server -c Release -- ./voicecat-data 7443 ./build/dev/bin/vccli.exe --host 127.0.0.1 --port 7443 --nick Guest --text "hello" ``` It creates or imports `server_identity.key`, `server.crt`, `server.key`, and `voicecat.db`. An empty channel table gets Lobby and Music Room; existing channels are preserved. Guests are enabled by the CLI; hosting `VoiceServer` directly can disable them. Existing accounts authenticate without resetting passwords. Account creation is currently available through `AccountStore`; bootstrap/admin CLI and wire administration are pending. Tests cover real TLS sockets, authentication retries, snapshots, channel moves, text routing, sender attribution, ping, and disconnect events. Enable native checks: ```powershell cmake --build --preset dev --target voicecat-dotnet-password-oracle voicecat-dotnet-database-oracle vccli $env:VOICECAT_DATABASE_ORACLE = (Resolve-Path build/dev/bin/voicecat-dotnet-database-oracle.exe).Path $env:VOICECAT_VCCLI = (Resolve-Path build/dev/bin/vccli.exe).Path dotnet test dotnet/VoiceCat.slnx -c Release --no-restore ``` The database oracle creates an account/channel using the shipped C++ database code; managed code imports and authenticates it, then C++ authenticates a managed-created account. CI also regenerates the libsodium password fixture. Native checks require the optional `VOICECAT_BUILD_DOTNET_ORACLE=ON` configure flag and a real-deps build. The server also advertises UDP on the TCP port number, supports voice subscription and stream signaling, and reseals encoded audio for subscribers in the same channel. UDP binding fixes the first endpoint for the session; reconnect after endpoint changes. Protected joins, administration, moderation and production configuration remain before Phase 4 completion. The server's media-aware reaper defaults to 45 seconds of inactivity with a 15-second sweep. Parsed control envelopes, valid encrypted voice and keepalives from bound endpoints refresh activity; invalid media does not. `VoiceServerOptions` configures timeouts and capacity; zero idle timeout disables reaping. The constructor overload accepts `TimeProvider` for deterministic expiry tests. Enable deterministic native voice interoperability (no audio hardware required): ```powershell cmake --build --preset dev --target voicecat-dotnet-voice-oracle $env:VOICECAT_VOICE_ORACLE = (Resolve-Path build/dev/bin/voicecat-dotnet-voice-oracle.exe).Path dotnet test dotnet/VoiceCat.slnx -c Release --no-restore ``` Two existing C++ clients authenticate, join Lobby or Music Room, publish three concurrent streams, feed PCM, and verify decoded energy and metadata in both directions. The native clients use external capture/playback to avoid device dependencies in CI. `MediaFanoutTests` separately verifies 50-subscriber routing/resealing without managed allocations after warm-up and reports throughput; socket scheduling is excluded. The transport load test delivers all 2,500 recipient packets from a paced 50 pps sender. Native `vccli --test-tone-ms 4000` runs finite external capture/playback, feeds a tone, and fails without decoded remote audio. Tests start two CLI processes in mono/stereo channels and also verify channel text. Normal `--voice` now explicitly subscribes before announcing its microphone stream. No C ABI or wire changes were needed.