Files
voice-cat/dotnet
Talon 52f7f51e59
.NET port / test (macos-latest) (push) Canceled after 0s
.NET port / test (ubuntu-24.04) (push) Canceled after 0s
.NET port / test (windows-latest) (push) Canceled after 0s
.NET port / apple-client (push) Canceled after 0s
.NET port / cpp-conformance (push) Canceled after 0s
Add managed server profiles and account login
2026-09-16 22:13:34 +02:00
..

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):

./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:

./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:

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:

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:

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:

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 031; 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:

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.

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):

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:

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):

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.