2026-09-15 17:54:16 +02:00
|
|
|
|
# VoiceCat .NET rewrite
|
|
|
|
|
|
|
2026-09-16 16:55:17 +02:00
|
|
|
|
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.
|
2026-09-15 17:54:16 +02:00
|
|
|
|
|
2026-09-15 22:51:33 +02:00
|
|
|
|
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.
|
|
|
|
|
|
|
2026-09-15 17:54:16 +02:00
|
|
|
|
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
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-16 16:55:17 +02:00
|
|
|
|
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.
|
|
|
|
|
|
|
2026-09-15 17:54:16 +02:00
|
|
|
|
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.
|
|
|
|
|
|
|
2026-09-15 22:51:33 +02:00
|
|
|
|
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.
|
|
|
|
|
|
|
2026-09-15 18:04:20 +02:00
|
|
|
|
## 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.
|
|
|
|
|
|
|
2026-09-15 22:51:33 +02:00
|
|
|
|
## Managed server checkpoint
|
2026-09-15 17:54:16 +02:00
|
|
|
|
|
2026-09-15 22:51:33 +02:00
|
|
|
|
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.
|
|
|
|
|
|
|
2026-09-15 22:53:54 +02:00
|
|
|
|
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.
|
2026-09-15 22:58:16 +02:00
|
|
|
|
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.
|
2026-09-15 22:53:54 +02:00
|
|
|
|
|
|
|
|
|
|
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.
|