scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
/*
|
|
|
|
|
* vccli — headless test client.
|
|
|
|
|
*
|
|
|
|
|
* This is the primary way the protocol is exercised and verified from M1 onward (see
|
|
|
|
|
* AGENTS.md). Each milestone's exit criterion is demonstrated by driving two vccli
|
2026-06-16 02:12:50 +02:00
|
|
|
* instances against a real voicecat-server.
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
*/
|
2026-06-16 02:12:50 +02:00
|
|
|
#include <atomic>
|
|
|
|
|
#include <chrono>
|
|
|
|
|
#include <csignal>
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
#include <cstdio>
|
2026-06-16 02:12:50 +02:00
|
|
|
#include <cstring>
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
#include <functional>
|
|
|
|
|
#include <iostream>
|
2026-06-16 02:12:50 +02:00
|
|
|
#include <string>
|
|
|
|
|
#include <thread>
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
|
|
|
|
|
#include "voicecat.h"
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2026-06-16 02:12:50 +02:00
|
|
|
std::atomic<bool> g_stop{false};
|
|
|
|
|
void on_sigint(int) { g_stop.store(true); }
|
|
|
|
|
|
|
|
|
|
struct Stats {
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
std::atomic<bool> auth_done{false};
|
|
|
|
|
std::atomic<bool> auth_ok{false};
|
feat(M4): Windows WinForms client, TOFU identity pinning, VAD threshold + always-on mode
Core ABI extensions (voicecat.h):
- vc_list_channels / vc_list_users / vc_list_user_streams — pull-based snapshot getters
for the channel-tree and user-list UI; session_model_mu_ guards cross-thread reads
- VC_EVENT_JOIN_RESULT / vc_join_channel — channel join with optional password
- VC_EVENT_SERVER_IDENTITY + vc_confirm_server_identity — TOFU gate that blocks io_thread_
until the UI approves or rejects; pins TLS leaf-cert SHA-256 (not declared Ed25519)
- vc_get_server_identity_display — Ed25519 fingerprint for human-readable display only
- VC_INPUT_ALWAYS_ON = 2 in vc_input_mode — transmit unconditionally, no VAD gate
- vc_set_vad_threshold — live RMS threshold update (0.0–1.0); EnergyVadProcessor stores
it atomically so the audio RT path reads without a lock
C++ implementation:
- SessionModel::apply_snapshot / apply_channel_event fixed to populate parent_id,
password_protected, and max_users (were permanently zeroed)
- TlsContext::peer_cert_fingerprint — SHA-256 of peer leaf cert DER via mbedTLS
- TofuStore split into peek (read-only) + pin (write) so first-connect only persists
after user approval; tofu_store_path in vc_config for per-user pin file location
- TcpAcceptor uses dual-stack IPv6+IPv4 fallback (fixes localhost → ::1 on Windows)
- windows-client CMake preset: Release shared DLL, static MinGW runtime, no tools/tests
- New C++ tests: test_channel_user_list_abi, test_tofu_flow (14/14 green)
Windows client (clients/windows/ — .NET 10 WinForms):
- VoiceCat.Interop: LibraryImport P/Invoke surface, UnmanagedCallersOnly callbacks,
Channel<VoiceCatEvent> event delivery drained by 30ms WinForms Timer
- VoiceCat.App: ConnectDialog (saved servers, DPAPI password storage), ServerIdentity-
Dialog (TOFU first-connect / mismatch warning), MainForm (channel TreeView, user
ListBox, RichTextBox chat, voice controls, device pickers, VAD/PTT/always-on mode,
per-user gain/mute/NR tuning, VAD sensitivity TrackBar, level meter ProgressBar)
- PttKeyCaptureDialog — focus-scoped PTT key capture (documented limitation)
- PerUserTuningDialog — real-time gain/mute/NR applied to all of a user's streams
- Accessibility: explicit AccessibleName/Description on every control, & mnemonics,
Activity log ListBox as durable screen-reader record, AutomationNotification for
curated live announcements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 00:35:16 +02:00
|
|
|
// Set right after vc_client_create, before vc_connect — lets on_event auto-confirm the
|
|
|
|
|
// M4 TOFU gate (VC_EVENT_SERVER_IDENTITY below). vccli has no interactive prompt, so it
|
|
|
|
|
// trusts-on-first-connect unconditionally (prints the fingerprint for visibility).
|
|
|
|
|
vc_client* client{nullptr};
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
|
|
|
|
|
// M5 async result tracking. Generic results are used by every moderation/admin/channel
|
|
|
|
|
// request; account-list is its own event. We count generic results so callers can wait
|
|
|
|
|
// for a new one even if several arrived earlier.
|
|
|
|
|
std::atomic<int> generic_result_count{0};
|
|
|
|
|
std::atomic<int> last_result{0};
|
|
|
|
|
std::atomic<uint32_t> last_code{0};
|
|
|
|
|
std::string last_message;
|
|
|
|
|
std::mutex last_message_mu;
|
|
|
|
|
std::atomic<bool> account_list_received{false};
|
2026-06-16 02:12:50 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void on_event(void* user, const vc_event* ev) {
|
|
|
|
|
auto* st = static_cast<Stats*>(user);
|
|
|
|
|
switch (ev->type) {
|
|
|
|
|
case VC_EVENT_CONNECTION_STATE:
|
|
|
|
|
std::printf("[state] -> %d\n", static_cast<int>(ev->connection_state));
|
|
|
|
|
break;
|
feat(M4): Windows WinForms client, TOFU identity pinning, VAD threshold + always-on mode
Core ABI extensions (voicecat.h):
- vc_list_channels / vc_list_users / vc_list_user_streams — pull-based snapshot getters
for the channel-tree and user-list UI; session_model_mu_ guards cross-thread reads
- VC_EVENT_JOIN_RESULT / vc_join_channel — channel join with optional password
- VC_EVENT_SERVER_IDENTITY + vc_confirm_server_identity — TOFU gate that blocks io_thread_
until the UI approves or rejects; pins TLS leaf-cert SHA-256 (not declared Ed25519)
- vc_get_server_identity_display — Ed25519 fingerprint for human-readable display only
- VC_INPUT_ALWAYS_ON = 2 in vc_input_mode — transmit unconditionally, no VAD gate
- vc_set_vad_threshold — live RMS threshold update (0.0–1.0); EnergyVadProcessor stores
it atomically so the audio RT path reads without a lock
C++ implementation:
- SessionModel::apply_snapshot / apply_channel_event fixed to populate parent_id,
password_protected, and max_users (were permanently zeroed)
- TlsContext::peer_cert_fingerprint — SHA-256 of peer leaf cert DER via mbedTLS
- TofuStore split into peek (read-only) + pin (write) so first-connect only persists
after user approval; tofu_store_path in vc_config for per-user pin file location
- TcpAcceptor uses dual-stack IPv6+IPv4 fallback (fixes localhost → ::1 on Windows)
- windows-client CMake preset: Release shared DLL, static MinGW runtime, no tools/tests
- New C++ tests: test_channel_user_list_abi, test_tofu_flow (14/14 green)
Windows client (clients/windows/ — .NET 10 WinForms):
- VoiceCat.Interop: LibraryImport P/Invoke surface, UnmanagedCallersOnly callbacks,
Channel<VoiceCatEvent> event delivery drained by 30ms WinForms Timer
- VoiceCat.App: ConnectDialog (saved servers, DPAPI password storage), ServerIdentity-
Dialog (TOFU first-connect / mismatch warning), MainForm (channel TreeView, user
ListBox, RichTextBox chat, voice controls, device pickers, VAD/PTT/always-on mode,
per-user gain/mute/NR tuning, VAD sensitivity TrackBar, level meter ProgressBar)
- PttKeyCaptureDialog — focus-scoped PTT key capture (documented limitation)
- PerUserTuningDialog — real-time gain/mute/NR applied to all of a user's streams
- Accessibility: explicit AccessibleName/Description on every control, & mnemonics,
Activity log ListBox as durable screen-reader record, AutomationNotification for
curated live announcements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 00:35:16 +02:00
|
|
|
case VC_EVENT_SERVER_IDENTITY:
|
|
|
|
|
std::printf("[tofu] status=%u fingerprint=%s (auto-trusting — vccli has no "
|
|
|
|
|
"interactive prompt)\n", ev->u32a, ev->text ? ev->text : "");
|
|
|
|
|
vc_confirm_server_identity(st->client, 1);
|
|
|
|
|
break;
|
2026-06-16 02:12:50 +02:00
|
|
|
case VC_EVENT_AUTH_RESULT:
|
|
|
|
|
st->auth_ok = (ev->result == VC_OK);
|
|
|
|
|
st->auth_done = true;
|
|
|
|
|
std::printf("[auth] ok=%d user_id=%u %s\n", st->auth_ok.load(), ev->user_id,
|
|
|
|
|
ev->text ? ev->text : "");
|
|
|
|
|
break;
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
case VC_EVENT_CHANNEL_LIST:
|
|
|
|
|
std::printf("[channel] list updated\n");
|
|
|
|
|
break;
|
2026-06-16 02:12:50 +02:00
|
|
|
case VC_EVENT_USER_JOINED:
|
|
|
|
|
std::printf("[user] joined: %s (id=%u, channel=%u)\n", ev->text ? ev->text : "?",
|
|
|
|
|
ev->user_id, ev->channel_id);
|
|
|
|
|
break;
|
|
|
|
|
case VC_EVENT_USER_LEFT:
|
|
|
|
|
std::printf("[user] left: id=%u\n", ev->user_id);
|
|
|
|
|
break;
|
|
|
|
|
case VC_EVENT_USER_UPDATED:
|
|
|
|
|
std::printf("[user] updated: id=%u\n", ev->user_id);
|
|
|
|
|
break;
|
|
|
|
|
case VC_EVENT_STREAM_STARTED:
|
|
|
|
|
std::printf("[voice] stream started: user_id=%u stream_id=%u\n", ev->user_id,
|
|
|
|
|
ev->stream_id);
|
|
|
|
|
break;
|
|
|
|
|
case VC_EVENT_STREAM_STOPPED:
|
|
|
|
|
std::printf("[voice] stream stopped: user_id=%u stream_id=%u\n", ev->user_id,
|
|
|
|
|
ev->stream_id);
|
|
|
|
|
break;
|
|
|
|
|
case VC_EVENT_TEXT_MESSAGE:
|
|
|
|
|
std::printf("[text] from=%u: %s\n", ev->user_id, ev->text ? ev->text : "");
|
|
|
|
|
break;
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
case VC_EVENT_TALK_STATE:
|
|
|
|
|
std::printf("[voice] talk state: user_id=%u stream_id=%u talking=%u\n", ev->user_id,
|
|
|
|
|
ev->stream_id, ev->u32a);
|
|
|
|
|
break;
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
case VC_EVENT_JOIN_RESULT:
|
|
|
|
|
std::printf("[join] result=%d channel=%u %s\n", ev->result, ev->channel_id,
|
|
|
|
|
ev->text ? ev->text : "");
|
|
|
|
|
break;
|
|
|
|
|
case VC_EVENT_GENERIC_RESULT: {
|
|
|
|
|
std::lock_guard lk(st->last_message_mu);
|
|
|
|
|
st->last_result.store(ev->result);
|
|
|
|
|
st->last_code.store(ev->u32a);
|
|
|
|
|
st->last_message = ev->text ? ev->text : "";
|
|
|
|
|
st->generic_result_count.fetch_add(1);
|
|
|
|
|
std::printf("[result] rc=%d code=%u: %s\n", ev->result, ev->u32a,
|
|
|
|
|
ev->text ? ev->text : "");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case VC_EVENT_ACCOUNT_LIST:
|
|
|
|
|
st->account_list_received.store(true);
|
|
|
|
|
std::printf("[accounts] list received (use vc_list_accounts in code to inspect)\n");
|
|
|
|
|
break;
|
2026-06-16 02:12:50 +02:00
|
|
|
case VC_EVENT_ERROR:
|
|
|
|
|
std::fprintf(stderr, "[error] rc=%d: %s\n", ev->result, ev->text ? ev->text : "");
|
|
|
|
|
break;
|
|
|
|
|
case VC_EVENT_DISCONNECTED:
|
|
|
|
|
std::printf("[disconnected] rc=%d: %s\n", ev->result, ev->text ? ev->text : "");
|
|
|
|
|
g_stop.store(true);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool wait_until(std::atomic<bool>& flag, int timeout_ms) {
|
|
|
|
|
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
|
|
|
|
|
while (!flag.load()) {
|
|
|
|
|
if (std::chrono::steady_clock::now() >= deadline) return false;
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
// Wait for a new generic result to arrive. Returns the vc_result from that result.
|
|
|
|
|
vc_result wait_generic_result(Stats& st, int baseline_count, int timeout_ms) {
|
|
|
|
|
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
|
|
|
|
|
while (st.generic_result_count.load() <= baseline_count) {
|
|
|
|
|
if (std::chrono::steady_clock::now() >= deadline) return VC_ERR_TIMEOUT;
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
|
|
|
}
|
|
|
|
|
return static_cast<vc_result>(st.last_result.load());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse "true"/"false"/"1"/"0"/"yes"/"no" (case-insensitive).
|
|
|
|
|
bool parse_bool(const char* s, bool* out) {
|
|
|
|
|
if (!s || !*s) return false;
|
|
|
|
|
std::string v = s;
|
|
|
|
|
for (auto& ch : v) ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
|
|
|
|
|
if (v == "1" || v == "true" || v == "yes" || v == "on") { *out = true; return true; }
|
|
|
|
|
if (v == "0" || v == "false" || v == "no" || v == "off") { *out = false; return true; }
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool parse_u32(const char* s, uint32_t* out, const char* label) {
|
|
|
|
|
if (!s || !*s) { std::fprintf(stderr, "missing value for %s\n", label); return false; }
|
|
|
|
|
try {
|
|
|
|
|
*out = static_cast<uint32_t>(std::stoul(s));
|
|
|
|
|
return true;
|
|
|
|
|
} catch (...) {
|
|
|
|
|
std::fprintf(stderr, "invalid value for %s: %s\n", label, s);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool parse_u16(const char* s, uint16_t* out, const char* label) {
|
|
|
|
|
if (!s || !*s) { std::fprintf(stderr, "missing value for %s\n", label); return false; }
|
|
|
|
|
try {
|
|
|
|
|
int v = std::stoi(s);
|
|
|
|
|
if (v < 0 || v > 65535) throw std::out_of_range("port");
|
|
|
|
|
*out = static_cast<uint16_t>(v);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (...) {
|
|
|
|
|
std::fprintf(stderr, "invalid value for %s: %s\n", label, s);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 02:12:50 +02:00
|
|
|
void print_usage() {
|
|
|
|
|
std::printf(
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
"usage: vccli [--host H] [--port P] [--nick NAME | --username U --password P]\n"
|
|
|
|
|
" [--channel ID] [--voice] [--mute] [--text MSG] [--list-devices]\n"
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
" [--input-device ID] [--input-mode vad|ptt] [--share-screen-audio]\n"
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
" [--wait-ms N]\n"
|
|
|
|
|
" [--kick USER_ID [--reason REASON]]\n"
|
|
|
|
|
" [--ban USER_ID [--reason REASON] [--ban-expires-ms MS]]\n"
|
|
|
|
|
" [--move USER_ID --to-channel ID]\n"
|
|
|
|
|
" [--server-mute USER_ID] [--server-unmute USER_ID]\n"
|
|
|
|
|
" [--server-deafen USER_ID] [--server-undeafen USER_ID]\n"
|
|
|
|
|
" [--set-permission USER_ID --perm-admin B --perm-kick B --perm-ban B\n"
|
|
|
|
|
" --perm-move B --perm-create-temp B --perm-admin-accounts B]\n"
|
|
|
|
|
" [--create-channel --new-channel-name NAME [--new-channel-topic TOPIC]\n"
|
|
|
|
|
" [--new-channel-parent ID] [--new-channel-password PASS]\n"
|
|
|
|
|
" [--new-channel-max-users N]]\n"
|
|
|
|
|
" [--edit-channel --channel-id ID --new-channel-name NAME\n"
|
|
|
|
|
" [--new-channel-topic TOPIC] [--new-channel-parent ID]\n"
|
|
|
|
|
" [--new-channel-password PASS] [--new-channel-max-users N]]\n"
|
|
|
|
|
" [--delete-channel --channel-id ID]\n"
|
|
|
|
|
" [--create-account USER PASS] [--reset-password USER PASS]\n"
|
|
|
|
|
" [--delete-account USER] [--list-accounts]\n"
|
|
|
|
|
" [--self-mute] [--self-deafen]\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Connection / identity:\n"
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
" --host H server host (default 127.0.0.1)\n"
|
|
|
|
|
" --port P server TCP port (default 8384)\n"
|
|
|
|
|
" --nick NAME guest nickname (default vccli-test)\n"
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
" --username U authenticate as registered user U\n"
|
|
|
|
|
" --password P password for --username\n"
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
" --channel ID channel to join after auth (default 1, Lobby)\n"
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
" --wait-ms N timeout for M5 async result events (default 5000)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Voice / devices:\n"
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
" --voice start a MIC stream and stay connected until Ctrl+C\n"
|
|
|
|
|
" --mute start with the mic muted (only meaningful with --voice)\n"
|
|
|
|
|
" --list-devices print input/output devices (vc_list_devices) and exit\n"
|
|
|
|
|
" --input-device ID use device ID (from --list-devices) for the MIC stream\n"
|
|
|
|
|
" --input-mode vad|ptt send-side input gate mode (default vad)\n"
|
|
|
|
|
" --share-screen-audio also start a SCREEN_AUDIO stream (WASAPI loopback on Windows)\n"
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
" --self-mute mute own mic before/without voice mode\n"
|
|
|
|
|
" --self-deafen deafen self before/without voice mode\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Text:\n"
|
|
|
|
|
" --text MSG send MSG to the channel, then exit\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Moderation (require permission):\n"
|
|
|
|
|
" --kick USER_ID [--reason REASON]\n"
|
|
|
|
|
" --ban USER_ID [--reason REASON] [--ban-expires-ms MS] (0 = permanent)\n"
|
|
|
|
|
" --move USER_ID --to-channel ID\n"
|
|
|
|
|
" --server-mute USER_ID, --server-unmute USER_ID\n"
|
|
|
|
|
" --server-deafen USER_ID, --server-undeafen USER_ID\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Permissions (require permission):\n"
|
|
|
|
|
" --set-permission USER_ID --perm-admin B --perm-kick B --perm-ban B\n"
|
|
|
|
|
" --perm-move B --perm-create-temp B --perm-admin-accounts B\n"
|
|
|
|
|
" B = 0|1|true|false|yes|no\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Channel management (require permission):\n"
|
|
|
|
|
" --create-channel --new-channel-name NAME ...\n"
|
|
|
|
|
" --edit-channel --channel-id ID --new-channel-name NAME ...\n"
|
|
|
|
|
" --delete-channel --channel-id ID\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Account management (require permission):\n"
|
|
|
|
|
" --create-account USER PASS\n"
|
|
|
|
|
" --reset-password USER PASS\n"
|
|
|
|
|
" --delete-account USER\n"
|
|
|
|
|
" --list-accounts\n"
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
"\n"
|
|
|
|
|
"While --voice is running, stdin accepts: \"ptt on\", \"ptt off\", \"mode vad\",\n"
|
|
|
|
|
"\"mode ptt\" (PTT key state can't be held interactively in a headless CLI, so it's\n"
|
|
|
|
|
"toggled via these commands instead).\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void print_device_list(vc_client* c, vc_device_kind kind, const char* label) {
|
|
|
|
|
vc_device_list dl{};
|
|
|
|
|
vc_result r = vc_list_devices(c, kind, &dl);
|
|
|
|
|
std::printf("%s devices: vc_list_devices -> %d (%s), count=%zu\n", label, r,
|
|
|
|
|
vc_result_string(r), dl.count);
|
|
|
|
|
for (size_t i = 0; i < dl.count; ++i) {
|
|
|
|
|
std::printf(" [%s] %s%s\n", dl.items[i].id, dl.items[i].name,
|
|
|
|
|
dl.items[i].is_default ? " (default)" : "");
|
|
|
|
|
}
|
|
|
|
|
vc_free_device_list(&dl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Background stdin reader for --voice mode: the most portable way to drive PTT/mode toggles
|
|
|
|
|
// interactively from a headless CLI (no SIGUSR1 equivalent on Windows).
|
|
|
|
|
void run_stdin_commands(vc_client* c, std::atomic<bool>& stop) {
|
|
|
|
|
std::string line;
|
|
|
|
|
while (!stop.load() && std::getline(std::cin, line)) {
|
|
|
|
|
if (line == "ptt on") {
|
|
|
|
|
vc_set_push_to_talk(c, 1);
|
|
|
|
|
std::printf("[ptt] on\n");
|
|
|
|
|
} else if (line == "ptt off") {
|
|
|
|
|
vc_set_push_to_talk(c, 0);
|
|
|
|
|
std::printf("[ptt] off\n");
|
|
|
|
|
} else if (line == "mode vad") {
|
|
|
|
|
vc_set_input_mode(c, VC_INPUT_VOICE_ACTIVATION);
|
|
|
|
|
std::printf("[mode] vad\n");
|
|
|
|
|
} else if (line == "mode ptt") {
|
|
|
|
|
vc_set_input_mode(c, VC_INPUT_PUSH_TO_TALK);
|
|
|
|
|
std::printf("[mode] ptt\n");
|
|
|
|
|
} else if (!line.empty()) {
|
|
|
|
|
std::fprintf(stderr, "unknown command: %s\n", line.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
}
|
|
|
|
|
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
// Issue an M5 request that produces VC_EVENT_GENERIC_RESULT and wait for it.
|
|
|
|
|
// Returns the result code from the event.
|
|
|
|
|
using RequestFn = std::function<vc_result()>;
|
|
|
|
|
|
|
|
|
|
vc_result run_generic_request(Stats& st, int timeout_ms, RequestFn fn, const char* label) {
|
|
|
|
|
int before = st.generic_result_count.load();
|
|
|
|
|
vc_result r = fn();
|
|
|
|
|
std::printf("%s -> %d (%s)\n", label, r, vc_result_string(r));
|
|
|
|
|
if (r != VC_OK) return r;
|
|
|
|
|
vc_result event_rc = wait_generic_result(st, before, timeout_ms);
|
|
|
|
|
if (event_rc == VC_ERR_TIMEOUT) {
|
|
|
|
|
std::fprintf(stderr, "%s: timed out waiting for result event\n", label);
|
|
|
|
|
return VC_ERR_TIMEOUT;
|
|
|
|
|
}
|
|
|
|
|
return event_rc;
|
|
|
|
|
}
|
|
|
|
|
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
2026-06-16 02:12:50 +02:00
|
|
|
// MSVCRT/MinGW treat _IOLBF as full buffering for non-console streams, so go unbuffered
|
|
|
|
|
// to keep output visible immediately when piped to a file or another process.
|
|
|
|
|
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
|
|
|
|
|
|
|
|
|
std::string host = "127.0.0.1";
|
|
|
|
|
uint16_t port = 8384;
|
|
|
|
|
std::string nick = "vccli-test";
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
std::string username;
|
|
|
|
|
std::string password;
|
|
|
|
|
bool have_username = false;
|
|
|
|
|
bool have_password = false;
|
2026-06-16 02:12:50 +02:00
|
|
|
uint32_t channel_id = 1;
|
|
|
|
|
bool voice_mode = false;
|
|
|
|
|
bool start_muted = false;
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
bool self_mute = false;
|
|
|
|
|
bool self_deafen = false;
|
2026-06-16 02:12:50 +02:00
|
|
|
std::string text_msg;
|
|
|
|
|
bool have_text = false;
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
bool list_devices = false;
|
|
|
|
|
std::string input_device;
|
|
|
|
|
bool have_input_device = false;
|
|
|
|
|
vc_input_mode input_mode = VC_INPUT_VOICE_ACTIVATION;
|
|
|
|
|
bool share_screen_audio = false;
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
int wait_ms = 5000;
|
|
|
|
|
|
|
|
|
|
// Moderation
|
|
|
|
|
bool do_kick = false;
|
|
|
|
|
uint32_t kick_user_id = 0;
|
|
|
|
|
std::string kick_reason;
|
|
|
|
|
bool do_ban = false;
|
|
|
|
|
uint32_t ban_user_id = 0;
|
|
|
|
|
std::string ban_reason;
|
|
|
|
|
uint64_t ban_expires_ms = 0;
|
|
|
|
|
bool do_move = false;
|
|
|
|
|
uint32_t move_user_id = 0;
|
|
|
|
|
uint32_t move_channel_id = 0;
|
|
|
|
|
bool do_server_mute = false;
|
|
|
|
|
bool do_server_unmute = false;
|
|
|
|
|
bool do_server_deafen = false;
|
|
|
|
|
bool do_server_undeafen = false;
|
|
|
|
|
uint32_t server_mute_user_id = 0;
|
|
|
|
|
int server_mute_muted = 0;
|
|
|
|
|
int server_mute_deafened = 0;
|
|
|
|
|
|
|
|
|
|
// Permissions
|
|
|
|
|
bool do_set_permission = false;
|
|
|
|
|
uint32_t perm_user_id = 0;
|
|
|
|
|
vc_permissions perms{};
|
|
|
|
|
|
|
|
|
|
// Channel CRUD
|
|
|
|
|
bool do_create_channel = false;
|
|
|
|
|
bool do_edit_channel = false;
|
|
|
|
|
bool do_delete_channel = false;
|
|
|
|
|
uint32_t delete_channel_id = 0;
|
|
|
|
|
vc_channel_info channel_info{};
|
|
|
|
|
|
|
|
|
|
// Account management
|
|
|
|
|
bool do_create_account = false;
|
|
|
|
|
bool do_reset_password = false;
|
|
|
|
|
bool do_delete_account = false;
|
|
|
|
|
bool do_list_accounts = false;
|
|
|
|
|
std::string acct_user;
|
|
|
|
|
std::string acct_pass;
|
2026-06-16 02:12:50 +02:00
|
|
|
|
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
|
|
std::string a = argv[i];
|
|
|
|
|
auto next = [&]() -> std::string { return (i + 1 < argc) ? argv[++i] : std::string(); };
|
|
|
|
|
if (a == "--host") host = next();
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
else if (a == "--port") { if (!parse_u16(next().c_str(), &port, "--port")) return 1; }
|
2026-06-16 02:12:50 +02:00
|
|
|
else if (a == "--nick") nick = next();
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
else if (a == "--username") { username = next(); have_username = true; }
|
|
|
|
|
else if (a == "--password") { password = next(); have_password = true; }
|
|
|
|
|
else if (a == "--channel") { if (!parse_u32(next().c_str(), &channel_id, "--channel")) return 1; }
|
2026-06-16 02:12:50 +02:00
|
|
|
else if (a == "--voice") voice_mode = true;
|
|
|
|
|
else if (a == "--mute") start_muted = true;
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
else if (a == "--self-mute") self_mute = true;
|
|
|
|
|
else if (a == "--self-deafen") self_deafen = true;
|
2026-06-16 02:12:50 +02:00
|
|
|
else if (a == "--text") { text_msg = next(); have_text = true; }
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
else if (a == "--list-devices") list_devices = true;
|
|
|
|
|
else if (a == "--input-device") { input_device = next(); have_input_device = true; }
|
|
|
|
|
else if (a == "--input-mode") {
|
|
|
|
|
std::string m = next();
|
|
|
|
|
if (m == "ptt") input_mode = VC_INPUT_PUSH_TO_TALK;
|
|
|
|
|
else if (m != "vad") { std::fprintf(stderr, "--input-mode must be vad|ptt\n"); return 1; }
|
|
|
|
|
}
|
|
|
|
|
else if (a == "--share-screen-audio") share_screen_audio = true;
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
else if (a == "--wait-ms") {
|
|
|
|
|
std::string v = next();
|
|
|
|
|
try { wait_ms = std::stoi(v); } catch (...) {
|
|
|
|
|
std::fprintf(stderr, "invalid --wait-ms: %s\n", v.c_str()); return 1;
|
|
|
|
|
}
|
|
|
|
|
if (wait_ms < 0) wait_ms = 0;
|
|
|
|
|
}
|
|
|
|
|
// Moderation
|
|
|
|
|
else if (a == "--kick") { do_kick = true; if (!parse_u32(next().c_str(), &kick_user_id, "--kick")) return 1; }
|
|
|
|
|
else if (a == "--reason") {
|
|
|
|
|
std::string r = next();
|
|
|
|
|
if (do_kick) kick_reason = r;
|
|
|
|
|
else if (do_ban) ban_reason = r;
|
|
|
|
|
else { std::fprintf(stderr, "--reason without --kick or --ban\n"); return 1; }
|
|
|
|
|
}
|
|
|
|
|
else if (a == "--ban") { do_ban = true; if (!parse_u32(next().c_str(), &ban_user_id, "--ban")) return 1; }
|
|
|
|
|
else if (a == "--ban-expires-ms") {
|
|
|
|
|
std::string v = next();
|
|
|
|
|
try { ban_expires_ms = static_cast<uint64_t>(std::stoull(v)); } catch (...) {
|
|
|
|
|
std::fprintf(stderr, "invalid --ban-expires-ms: %s\n", v.c_str()); return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (a == "--move") { do_move = true; if (!parse_u32(next().c_str(), &move_user_id, "--move")) return 1; }
|
|
|
|
|
else if (a == "--to-channel") { if (!parse_u32(next().c_str(), &move_channel_id, "--to-channel")) return 1; }
|
|
|
|
|
else if (a == "--server-mute") { do_server_mute = true; server_mute_muted = 1; if (!parse_u32(next().c_str(), &server_mute_user_id, "--server-mute")) return 1; }
|
|
|
|
|
else if (a == "--server-unmute") { do_server_unmute = true; server_mute_muted = 0; server_mute_deafened = 0; if (!parse_u32(next().c_str(), &server_mute_user_id, "--server-unmute")) return 1; }
|
|
|
|
|
else if (a == "--server-deafen") { do_server_deafen = true; server_mute_muted = 1; server_mute_deafened = 1; if (!parse_u32(next().c_str(), &server_mute_user_id, "--server-deafen")) return 1; }
|
|
|
|
|
else if (a == "--server-undeafen") { do_server_undeafen = true; server_mute_muted = 0; server_mute_deafened = 0; if (!parse_u32(next().c_str(), &server_mute_user_id, "--server-undeafen")) return 1; }
|
|
|
|
|
// Permissions
|
|
|
|
|
else if (a == "--set-permission") { do_set_permission = true; if (!parse_u32(next().c_str(), &perm_user_id, "--set-permission")) return 1; }
|
|
|
|
|
else if (a == "--perm-admin") { bool b; if (!parse_bool(next().c_str(), &b)) return 1; perms.is_admin = b; }
|
|
|
|
|
else if (a == "--perm-kick") { bool b; if (!parse_bool(next().c_str(), &b)) return 1; perms.can_kick = b; }
|
|
|
|
|
else if (a == "--perm-ban") { bool b; if (!parse_bool(next().c_str(), &b)) return 1; perms.can_ban = b; }
|
|
|
|
|
else if (a == "--perm-move") { bool b; if (!parse_bool(next().c_str(), &b)) return 1; perms.can_move_users = b; }
|
|
|
|
|
else if (a == "--perm-create-temp") { bool b; if (!parse_bool(next().c_str(), &b)) return 1; perms.can_create_temp_channel = b; }
|
|
|
|
|
else if (a == "--perm-admin-accounts") { bool b; if (!parse_bool(next().c_str(), &b)) return 1; perms.can_admin_accounts = b; }
|
|
|
|
|
// Channel CRUD
|
|
|
|
|
else if (a == "--create-channel") do_create_channel = true;
|
|
|
|
|
else if (a == "--edit-channel") do_edit_channel = true;
|
|
|
|
|
else if (a == "--delete-channel") { do_delete_channel = true; if (!parse_u32(next().c_str(), &delete_channel_id, "--delete-channel")) return 1; }
|
|
|
|
|
else if (a == "--channel-id") { if (!parse_u32(next().c_str(), &channel_info.id, "--channel-id")) return 1; }
|
|
|
|
|
else if (a == "--new-channel-name") channel_info.name = next().c_str();
|
|
|
|
|
else if (a == "--new-channel-topic") channel_info.topic = next().c_str();
|
|
|
|
|
else if (a == "--new-channel-parent") { if (!parse_u32(next().c_str(), &channel_info.parent_id, "--new-channel-parent")) return 1; }
|
|
|
|
|
else if (a == "--new-channel-password") {
|
|
|
|
|
channel_info.password = next().c_str();
|
|
|
|
|
channel_info.password_protected = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (a == "--new-channel-max-users") { if (!parse_u32(next().c_str(), &channel_info.max_users, "--new-channel-max-users")) return 1; }
|
|
|
|
|
// Account management
|
|
|
|
|
else if (a == "--create-account") { do_create_account = true; acct_user = next(); acct_pass = next(); }
|
|
|
|
|
else if (a == "--reset-password") { do_reset_password = true; acct_user = next(); acct_pass = next(); }
|
|
|
|
|
else if (a == "--delete-account") { do_delete_account = true; acct_user = next(); }
|
|
|
|
|
else if (a == "--list-accounts") do_list_accounts = true;
|
2026-06-16 02:12:50 +02:00
|
|
|
else if (a == "--help" || a == "-h") { print_usage(); return 0; }
|
|
|
|
|
else { std::fprintf(stderr, "unknown flag: %s\n", a.c_str()); print_usage(); return 1; }
|
|
|
|
|
}
|
|
|
|
|
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
// Validate auth mode.
|
|
|
|
|
if (have_username != have_password) {
|
|
|
|
|
std::fprintf(stderr, "--username and --password must be used together\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate moderation flags that need extra args.
|
|
|
|
|
if (do_move && move_channel_id == 0) {
|
|
|
|
|
std::fprintf(stderr, "--move requires --to-channel\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if ((do_server_mute || do_server_unmute || do_server_deafen || do_server_undeafen) &&
|
|
|
|
|
server_mute_user_id == 0) {
|
|
|
|
|
std::fprintf(stderr, "server mute/deafen requires a user id\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
// Normalize server mute/deafen into a single request.
|
|
|
|
|
bool do_server_mute_request = do_server_mute || do_server_unmute || do_server_deafen || do_server_undeafen;
|
|
|
|
|
|
|
|
|
|
// Validate channel CRUD.
|
|
|
|
|
if (do_create_channel && (!channel_info.name || !*channel_info.name)) {
|
|
|
|
|
std::fprintf(stderr, "--create-channel requires --new-channel-name\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (do_edit_channel && (channel_info.id == 0 || !channel_info.name || !*channel_info.name)) {
|
|
|
|
|
std::fprintf(stderr, "--edit-channel requires --channel-id and --new-channel-name\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
std::printf("vccli — VoiceCat test client (core %s, protocol v%d)\n", vc_version_string(),
|
|
|
|
|
VOICECAT_PROTOCOL_VERSION);
|
|
|
|
|
|
2026-06-16 02:12:50 +02:00
|
|
|
std::signal(SIGINT, on_sigint);
|
|
|
|
|
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
vc_config cfg{};
|
2026-06-16 02:12:50 +02:00
|
|
|
cfg.client_name = "vccli";
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
cfg.client_version = vc_version_string();
|
2026-06-16 02:12:50 +02:00
|
|
|
cfg.log_level = VC_LOG_INFO;
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
|
2026-06-16 02:12:50 +02:00
|
|
|
Stats st;
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
vc_callbacks cb{};
|
|
|
|
|
cb.on_event = on_event;
|
2026-06-16 02:12:50 +02:00
|
|
|
cb.user = &st;
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
|
|
|
|
|
vc_client* c = vc_client_create(&cfg, cb);
|
|
|
|
|
if (c == nullptr) {
|
|
|
|
|
std::fprintf(stderr, "failed to create client\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
feat(M4): Windows WinForms client, TOFU identity pinning, VAD threshold + always-on mode
Core ABI extensions (voicecat.h):
- vc_list_channels / vc_list_users / vc_list_user_streams — pull-based snapshot getters
for the channel-tree and user-list UI; session_model_mu_ guards cross-thread reads
- VC_EVENT_JOIN_RESULT / vc_join_channel — channel join with optional password
- VC_EVENT_SERVER_IDENTITY + vc_confirm_server_identity — TOFU gate that blocks io_thread_
until the UI approves or rejects; pins TLS leaf-cert SHA-256 (not declared Ed25519)
- vc_get_server_identity_display — Ed25519 fingerprint for human-readable display only
- VC_INPUT_ALWAYS_ON = 2 in vc_input_mode — transmit unconditionally, no VAD gate
- vc_set_vad_threshold — live RMS threshold update (0.0–1.0); EnergyVadProcessor stores
it atomically so the audio RT path reads without a lock
C++ implementation:
- SessionModel::apply_snapshot / apply_channel_event fixed to populate parent_id,
password_protected, and max_users (were permanently zeroed)
- TlsContext::peer_cert_fingerprint — SHA-256 of peer leaf cert DER via mbedTLS
- TofuStore split into peek (read-only) + pin (write) so first-connect only persists
after user approval; tofu_store_path in vc_config for per-user pin file location
- TcpAcceptor uses dual-stack IPv6+IPv4 fallback (fixes localhost → ::1 on Windows)
- windows-client CMake preset: Release shared DLL, static MinGW runtime, no tools/tests
- New C++ tests: test_channel_user_list_abi, test_tofu_flow (14/14 green)
Windows client (clients/windows/ — .NET 10 WinForms):
- VoiceCat.Interop: LibraryImport P/Invoke surface, UnmanagedCallersOnly callbacks,
Channel<VoiceCatEvent> event delivery drained by 30ms WinForms Timer
- VoiceCat.App: ConnectDialog (saved servers, DPAPI password storage), ServerIdentity-
Dialog (TOFU first-connect / mismatch warning), MainForm (channel TreeView, user
ListBox, RichTextBox chat, voice controls, device pickers, VAD/PTT/always-on mode,
per-user gain/mute/NR tuning, VAD sensitivity TrackBar, level meter ProgressBar)
- PttKeyCaptureDialog — focus-scoped PTT key capture (documented limitation)
- PerUserTuningDialog — real-time gain/mute/NR applied to all of a user's streams
- Accessibility: explicit AccessibleName/Description on every control, & mnemonics,
Activity log ListBox as durable screen-reader record, AutomationNotification for
curated live announcements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 00:35:16 +02:00
|
|
|
st.client = c;
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
if (list_devices) {
|
|
|
|
|
// Device enumeration works pre-connect (no server needed) — see docs/voice.md.
|
|
|
|
|
print_device_list(c, VC_DEVICE_INPUT, "input");
|
|
|
|
|
print_device_list(c, VC_DEVICE_OUTPUT, "output");
|
|
|
|
|
vc_client_destroy(c);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 02:12:50 +02:00
|
|
|
vc_result r = vc_connect(c, host.c_str(), port);
|
|
|
|
|
std::printf("vc_connect(%s:%u) -> %d (%s)\n", host.c_str(), port, r, vc_result_string(r));
|
|
|
|
|
if (r != VC_OK) {
|
|
|
|
|
vc_client_destroy(c);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
if (have_username) {
|
|
|
|
|
r = vc_authenticate_user(c, username.c_str(), password.c_str());
|
|
|
|
|
std::printf("vc_authenticate_user(%s) -> %d (%s)\n", username.c_str(), r,
|
|
|
|
|
vc_result_string(r));
|
|
|
|
|
} else {
|
|
|
|
|
r = vc_authenticate_guest(c, nick.c_str());
|
|
|
|
|
std::printf("vc_authenticate_guest(%s) -> %d (%s)\n", nick.c_str(), r,
|
|
|
|
|
vc_result_string(r));
|
|
|
|
|
}
|
2026-06-16 02:12:50 +02:00
|
|
|
|
|
|
|
|
if (!wait_until(st.auth_done, 8000) || !st.auth_ok.load()) {
|
|
|
|
|
std::fprintf(stderr, "authentication failed or timed out\n");
|
|
|
|
|
vc_disconnect(c);
|
|
|
|
|
vc_client_destroy(c);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (channel_id != 1) {
|
|
|
|
|
r = vc_join_channel(c, channel_id, nullptr);
|
|
|
|
|
std::printf("vc_join_channel(%u) -> %d (%s)\n", channel_id, r, vc_result_string(r));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (have_text) {
|
|
|
|
|
r = vc_send_text(c, VC_TEXT_CHANNEL, channel_id, text_msg.c_str());
|
|
|
|
|
std::printf("vc_send_text -> %d (%s)\n", r, vc_result_string(r));
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(300)); // let the relay land
|
|
|
|
|
}
|
|
|
|
|
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
// M5 moderation / admin requests (executed in a sensible order if multiple are given).
|
|
|
|
|
bool m5_error = false;
|
|
|
|
|
|
|
|
|
|
if (self_mute || self_deafen) {
|
|
|
|
|
r = vc_set_self_mute(c, self_mute ? 1 : 0, self_deafen ? 1 : 0);
|
|
|
|
|
std::printf("vc_set_self_mute -> %d (%s)\n", r, vc_result_string(r));
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_kick) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_kick_user(c, kick_user_id, kick_reason.c_str()); },
|
|
|
|
|
"vc_kick_user");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_ban) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_ban_user(c, ban_user_id, ban_reason.c_str(), ban_expires_ms); },
|
|
|
|
|
"vc_ban_user");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_move) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_move_user(c, move_user_id, move_channel_id); },
|
|
|
|
|
"vc_move_user");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_server_mute_request) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_set_server_mute(c, server_mute_user_id, server_mute_muted, server_mute_deafened); },
|
|
|
|
|
"vc_set_server_mute");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_set_permission) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_set_permission(c, perm_user_id, &perms); },
|
|
|
|
|
"vc_set_permission");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_create_channel) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_create_channel(c, &channel_info); },
|
|
|
|
|
"vc_create_channel");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_edit_channel) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_edit_channel(c, &channel_info); },
|
|
|
|
|
"vc_edit_channel");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_delete_channel) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_delete_channel(c, delete_channel_id); },
|
|
|
|
|
"vc_delete_channel");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_create_account) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_create_account(c, acct_user.c_str(), acct_pass.c_str()); },
|
|
|
|
|
"vc_create_account");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_reset_password) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_reset_password(c, acct_user.c_str(), acct_pass.c_str()); },
|
|
|
|
|
"vc_reset_password");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_delete_account) {
|
|
|
|
|
r = run_generic_request(st, wait_ms,
|
|
|
|
|
[&]() { return vc_delete_account(c, acct_user.c_str()); },
|
|
|
|
|
"vc_delete_account");
|
|
|
|
|
if (r != VC_OK) m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m5_error && do_list_accounts) {
|
|
|
|
|
st.account_list_received.store(false);
|
|
|
|
|
r = vc_list_accounts(c);
|
|
|
|
|
std::printf("vc_list_accounts -> %d (%s)\n", r, vc_result_string(r));
|
|
|
|
|
if (r != VC_OK) {
|
|
|
|
|
m5_error = true;
|
|
|
|
|
} else {
|
|
|
|
|
if (!wait_until(st.account_list_received, wait_ms)) {
|
|
|
|
|
std::fprintf(stderr, "vc_list_accounts: timed out waiting for list event\n");
|
|
|
|
|
m5_error = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m5_error && !voice_mode) {
|
|
|
|
|
vc_disconnect(c);
|
|
|
|
|
vc_client_destroy(c);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 02:12:50 +02:00
|
|
|
if (voice_mode) {
|
|
|
|
|
// Give the async UDP binding handshake (TCP UdpBinding -> ack -> plaintext
|
|
|
|
|
// bootstrap packet) a moment to land before announcing a stream.
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
|
|
|
|
|
|
|
|
if (start_muted) vc_set_self_mute(c, 1, 0);
|
|
|
|
|
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
r = vc_set_input_mode(c, input_mode);
|
|
|
|
|
std::printf("vc_set_input_mode(%s) -> %d (%s)\n",
|
|
|
|
|
input_mode == VC_INPUT_PUSH_TO_TALK ? "ptt" : "vad", r, vc_result_string(r));
|
|
|
|
|
|
2026-06-16 02:12:50 +02:00
|
|
|
vc_stream_desc desc{};
|
|
|
|
|
desc.kind = VC_STREAM_MIC;
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
desc.device_id = have_input_device ? input_device.c_str() : nullptr;
|
2026-06-16 02:12:50 +02:00
|
|
|
desc.label = "Microphone";
|
|
|
|
|
|
|
|
|
|
uint32_t stream_id = 0;
|
|
|
|
|
r = vc_stream_start(c, &desc, &stream_id);
|
|
|
|
|
std::printf("vc_stream_start -> %d (%s), stream_id=%u\n", r, vc_result_string(r),
|
|
|
|
|
stream_id);
|
|
|
|
|
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
if (have_input_device && r == VC_OK) {
|
|
|
|
|
r = vc_set_input_device(c, stream_id, input_device.c_str());
|
|
|
|
|
std::printf("vc_set_input_device -> %d (%s)\n", r, vc_result_string(r));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t screen_stream_id = 0;
|
|
|
|
|
if (share_screen_audio) {
|
|
|
|
|
vc_stream_desc sdesc{};
|
|
|
|
|
sdesc.kind = VC_STREAM_SCREEN_AUDIO;
|
|
|
|
|
sdesc.label = "Desktop audio";
|
|
|
|
|
r = vc_stream_start(c, &sdesc, &screen_stream_id);
|
|
|
|
|
std::printf("vc_stream_start(SCREEN_AUDIO) -> %d (%s), stream_id=%u\n", r,
|
|
|
|
|
vc_result_string(r), screen_stream_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::thread stdin_thread(run_stdin_commands, c, std::ref(g_stop));
|
|
|
|
|
|
|
|
|
|
std::printf("voice mode: streaming mic, listening for remote streams. Ctrl+C to stop.\n"
|
|
|
|
|
"(type \"ptt on\"/\"ptt off\"/\"mode vad\"/\"mode ptt\" to drive the input gate)\n");
|
2026-06-16 02:12:50 +02:00
|
|
|
while (!g_stop.load()) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
|
|
|
}
|
|
|
|
|
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
if (share_screen_audio) vc_stream_stop(c, screen_stream_id);
|
2026-06-16 02:12:50 +02:00
|
|
|
vc_stream_stop(c, stream_id);
|
feat: device enumeration, VAD/PTT input gate, stereo playback, WASAPI loopback
Closes the three items PROGRESS.md's M3 section explicitly carried forward as
out of scope:
- Device enumeration (vc_list_devices) + input device selection
(vc_set_input_device), backed by AudioEngine::enumerate_devices() via
miniaudio's ma_context_get_devices. Device ids are opaque hex-encoded
ma_device_id strings.
- VAD/PTT send-side input gate (vc_set_input_mode, vc_set_push_to_talk).
webrtc-audio-processing (the originally-planned APM) has no working
Windows/MSVC build upstream (GCC-only Meson, unfinished MinGW support, hard
abseil-cpp dependency), so VAD is a new lightweight, dependency-free
energy/RMS processor (EnergyVadProcessor) behind the existing ApmProcessor
interface. Gating is MIC-only; SCREEN_AUDIO/AUX_DEVICE always bypass it.
- True stereo playback: AudioEngine's mixer and output device now carry
stereo end-to-end (mono streams upmix L=R) instead of downmixing decoded
stereo streams to mono before mixing.
- Real WASAPI loopback capture for SCREEN_AUDIO (Windows-only, via
miniaudio's loopback device type), replacing test-only injection as the
production capture path.
Also: vccli gains --list-devices, --input-device, --input-mode, and
--share-screen-audio flags, plus a stdin command loop (ptt on/off, mode
vad/ptt) for manual verification. New test_vad_ptt_devices.cpp covers all
four items (ABI-level + a white-box AudioEngine stereo-mix check).
Docs updated to match: voice.md, roadmap.md (decision-log entry superseding
the original webrtc-audio-processing choice), tech-stack.md, README.md,
architecture.md, CLAUDE.md, PROGRESS.md.
Still explicitly out of scope, documented not silently dropped: real
webrtc-audio-processing/AEC (no AEC/NS/AGC exists at all yet), macOS/iOS
SCREEN_AUDIO capture, process-specific loopback, and a pre-existing
RT-thread rule violation in the capture path that predates this work.
Verified: ctest 12/12 green across 3 consecutive full-suite runs (both dev
and m1-dev presets build clean); test_vad_ptt_devices passed 5 consecutive
standalone runs; manually verified live (vccli --list-devices against real
hardware, vccli --voice --input-mode vad streaming without incident).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:11:52 +02:00
|
|
|
|
|
|
|
|
// Unblock the stdin reader and let it exit before the client is torn down.
|
|
|
|
|
if (stdin_thread.joinable()) {
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
// std::getline on a console stdin blocks indefinitely; on Windows there's no
|
|
|
|
|
// portable way to interrupt it from another thread, so detach rather than join —
|
|
|
|
|
// process exit reclaims the thread.
|
|
|
|
|
stdin_thread.detach();
|
|
|
|
|
#else
|
|
|
|
|
stdin_thread.join();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2026-06-16 02:12:50 +02:00
|
|
|
}
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
|
2026-06-16 02:12:50 +02:00
|
|
|
vc_disconnect(c);
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
vc_client_destroy(c);
|
|
|
|
|
std::printf("ok\n");
|
M5: moderation, permissions, channel CRUD, in-app account management
- Server-side moderation & permissions (kick/ban/move/server-mute, channel CRUD).
- Database schema v2: channels, bans; BLAKE2b channel passwords, Argon2id accounts.
- C ABI additions and client-side handling (vc_kick_user, vc_ban_user, vc_set_permission, vc_set_server_mute, vc_move_user, vc_create/edit/delete_channel, vc_create/reset/delete/list_account).
- vccli flags for all M5 operations plus --username/--password auth.
- Four new tests covering permissions, kick/ban/move/mute, admin accounts, channel CRUD.
- Docs: protocol.md envelope updates, security.md channel-password hashing, PROGRESS.md.
2026-06-17 15:08:05 +02:00
|
|
|
return m5_error ? 1 : 0;
|
scaffold: M0 skeleton + agent onboarding (build, architecture, progress)
Turn the design into a buildable, dependency-free M0 skeleton plus the
onboarding layer so a new agent can pick up instantly.
Build system:
- CMake + CMakePresets (dev = no deps; server-release = vcpkg) + vcpkg.json
- Skeleton builds with just a C++20 compiler; deps stay off until needed
- .gitattributes (LF), .gitignore, .clang-format
Core (libvoicecat):
- core/include/voicecat.h: full C ABI (the client/server contract), stubbed
- core/proto/voicecat.proto: control-plane wire format, matches docs/protocol.md
- src/{net,crypto,codec,protocol,session,audio,core}: subsystem stubs that
return VC_ERR_NOT_IMPLEMENTED, each pointing to its design doc
- server/ (voicecat-server) and tools/vccli/ link the core
- tests/: CTest smoke test asserting the C ABI contract (behavior, not just build)
- clients/{apple,windows}: M4 placeholders
Onboarding for agents:
- CLAUDE.md: hub — build/test commands, architecture at a glance, doc map, rules
- AGENTS.md: working method (behavior-driven; clean compile is the floor not the goal)
- PROGRESS.md: living tracker — M0 done, M1 task checklist, "where we left off"
Verified: cmake --preset dev && cmake --build --preset dev && ctest --preset dev → green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:09:09 +02:00
|
|
|
}
|