chore: comment cleanup pass ahead of open-sourcing
Removes leftover debug scaffolding (stray Console.WriteLine/NSLog traces, dead nick_buf_ptr, a no-op --print-config flag now implemented for real), fixes stale/misleading comments (channel passwords are no longer a "future M5+" feature, a wrong cross-reference, a stale TlsContext::close() mention, an incomplete BanRecord::subject_type doc, and a smoke test pointing at a build/m1-dev preset that no longer exists), strips internal M1-M5 milestone jargon from comments now that the roadmap is done, trims comments that just restated the following line, and consolidates a few "why" explanations that were duplicated 2-3 times in the same file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* REAL-TIME RULE: audio-callback threads never allocate, lock, or block (architecture.md §3).
|
||||
* The JitterBuffer and per-stream maps are accessed only under a try_lock; a failed lock
|
||||
* causes PLC for that period (acceptable for M2; lock-free ring buffer is the M3 upgrade).
|
||||
* causes PLC for that period (acceptable today; a lock-free ring buffer would remove even that).
|
||||
*/
|
||||
#ifndef VOICECAT_AUDIO_AUDIO_ENGINE_H
|
||||
#define VOICECAT_AUDIO_AUDIO_ENGINE_H
|
||||
@@ -134,11 +134,11 @@ class AudioEngine {
|
||||
// Callback type for encoded capture frames ready to be sent. `kind` identifies which
|
||||
// local stream this PCM belongs to (a vc_stream_kind value; 0 = MIC for the real capture
|
||||
// device, which is always the "primary" tap). `channels` is the channel count of the PCM
|
||||
// buffer (1 = mono, 2 = stereo interleaved) — the mic capture device is mono in v1, but
|
||||
// buffer (1 = mono, 2 = stereo interleaved) — the mic capture device is mono by default, but
|
||||
// the WASAPI loopback path (SCREEN_AUDIO) captures in the channel's mode when stereo, so
|
||||
// the encoder sees real interleaved L/R PCM rather than a mono upmix. M3: multiple
|
||||
// concurrent local streams are possible (e.g. MIC + SCREEN_AUDIO), each fed via its own
|
||||
// injection tap (see inject_capture) since there is only one real hardware capture device.
|
||||
// the encoder sees real interleaved L/R PCM rather than a mono upmix. Multiple concurrent
|
||||
// local streams are possible (e.g. MIC + SCREEN_AUDIO), each fed via its own injection tap
|
||||
// (see inject_capture) since there is only one real hardware capture device.
|
||||
using CaptureCallback = std::function<void(int kind, const int16_t* pcm, int samples,
|
||||
int channels)>;
|
||||
|
||||
@@ -459,7 +459,7 @@ class AudioEngine {
|
||||
// never called. See on_playback's decode loop.
|
||||
int64_t plc_samples_since_real = 0;
|
||||
|
||||
// M3: listener-chosen, local-only noise reduction (docs/voice.md §10). Lazily
|
||||
// Listener-chosen, local-only noise reduction (docs/voice.md §10). Lazily
|
||||
// created only when enabled — bounded by how many remote streams this listener
|
||||
// subscribes to, so no separate instance cap is needed.
|
||||
bool noise_reduction_enabled = false;
|
||||
@@ -485,7 +485,7 @@ class AudioEngine {
|
||||
uint32_t user_id = 0;
|
||||
uint32_t stream_id = 0;
|
||||
|
||||
// M3: talk-indicator edge detection (docs/voice.md §7) — updated by push_recv_frame
|
||||
// Talk-indicator edge detection (docs/voice.md §7) — updated by push_recv_frame
|
||||
// (already off the real-time audio thread), polled by poll_talk_transitions().
|
||||
std::atomic<int64_t> last_voice_ms{0};
|
||||
bool talking = false;
|
||||
|
||||
@@ -40,10 +40,10 @@ std::vector<uint8_t> make_frame(const voicecat::v1::Envelope& env) {
|
||||
|
||||
} // namespace
|
||||
|
||||
// ── vc_client M1 implementation ───────────────────────────────────────────────
|
||||
// ── vc_client implementation ───────────────────────────────────────────────────
|
||||
|
||||
vc_client::vc_client(const vc_config& cfg, vc_callbacks cb) : cfg_(cfg), cb_(cb) {
|
||||
// M4 TOFU: NULL/empty tofu_store_path falls back to a relative default so existing tests
|
||||
// TOFU: NULL/empty tofu_store_path falls back to a relative default so existing tests
|
||||
// (which never set this field) keep working without real per-user persistence.
|
||||
std::filesystem::path tofu_path = (cfg.tofu_store_path && cfg.tofu_store_path[0])
|
||||
? std::filesystem::path(cfg.tofu_store_path)
|
||||
@@ -228,7 +228,7 @@ void vc_client::run_io(std::string host, uint16_t port) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── TOFU server-identity gate (M4) ──────────────────────────────────────
|
||||
// ── TOFU server-identity gate ────────────────────────────────────────────
|
||||
// Pins the TLS leaf cert's own fingerprint (real, verifiable right here from the
|
||||
// handshake) — NOT the declared Ed25519 server_identity_fingerprint from ServerHello,
|
||||
// which hasn't even arrived yet at this point (it's sent *inside* this now-established
|
||||
@@ -542,7 +542,7 @@ void vc_client::handle_envelope(const voicecat::v1::Envelope& env) {
|
||||
void vc_client::handle_server_hello(const voicecat::v1::ServerHello& msg, uint64_t /*req_id*/) {
|
||||
server_udp_port_ = static_cast<uint16_t>(msg.udp_port());
|
||||
|
||||
// M4: stash the declared Ed25519 fingerprint for vc_get_server_identity_display() —
|
||||
// Stash the declared Ed25519 fingerprint for vc_get_server_identity_display() —
|
||||
// display-only, not the TOFU-pinned value (that's the TLS cert fingerprint, gated before
|
||||
// ClientHello was even sent — see the TOFU block above in run_io()).
|
||||
{
|
||||
@@ -687,7 +687,6 @@ void vc_client::handle_user_event(const voicecat::v1::UserEvent& ue) {
|
||||
ev.user_id = user.id();
|
||||
ev.channel_id = user.channel_id();
|
||||
|
||||
static const char* nick_buf_ptr = nullptr;
|
||||
std::string nick = user.nickname();
|
||||
|
||||
switch (ue.kind()) {
|
||||
@@ -714,7 +713,7 @@ void vc_client::handle_user_event(const voicecat::v1::UserEvent& ue) {
|
||||
case voicecat::v1::UserEvent::UPDATED:
|
||||
ev.type = VC_EVENT_USER_UPDATED;
|
||||
emit(ev);
|
||||
// M5: if this is an update to our own user, reflect server-mute/deafen locally.
|
||||
// If this is an update to our own user, reflect server-mute/deafen locally.
|
||||
if (user.id() == self_user_id_) {
|
||||
server_muted_.store(user.server_muted(), std::memory_order_release);
|
||||
server_deafened_.store(user.server_deafened(), std::memory_order_release);
|
||||
@@ -731,7 +730,6 @@ void vc_client::handle_user_event(const voicecat::v1::UserEvent& ue) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
(void)nick_buf_ptr;
|
||||
}
|
||||
|
||||
void vc_client::handle_text_message(const voicecat::v1::TextMessage& msg) {
|
||||
@@ -799,9 +797,7 @@ vc_result vc_client::join_channel(uint32_t channel_id, const char* password) {
|
||||
req.set_request_id(next_req_id_++);
|
||||
auto* jc = req.mutable_join_channel();
|
||||
jc->set_channel_id(channel_id);
|
||||
// See voicecat.h's vc_join_channel doc comment: wired through to the wire message, but no
|
||||
// in-tree channel has a server-side password to check yet (no channel-creation feature
|
||||
// exists — M5+).
|
||||
// See voicecat.h's vc_join_channel doc comment.
|
||||
if (password) jc->set_password(password);
|
||||
queue_envelope(req);
|
||||
return VC_OK;
|
||||
@@ -828,7 +824,7 @@ vc_result vc_client::send_text(vc_text_scope scope, uint32_t target_id, const ch
|
||||
return VC_OK;
|
||||
}
|
||||
|
||||
// ── M2: UDP binding ───────────────────────────────────────────────────────────
|
||||
// ── UDP binding ────────────────────────────────────────────────────────────────
|
||||
|
||||
void vc_client::start_udp_binding() {
|
||||
voicecat::v1::Envelope req;
|
||||
@@ -977,8 +973,8 @@ int64_t client_now_ms() {
|
||||
}
|
||||
|
||||
// Builds an OpusParams from a wire AudioConfig, applying the same field-by-field mapping on
|
||||
// both the send (local-stream encoder) and receive (remote-stream decoder) paths — fixes the
|
||||
// M2 gap where mode/dtx/complexity/application were silently dropped.
|
||||
// both the send (local-stream encoder) and receive (remote-stream decoder) paths — fixes a
|
||||
// gap where mode/dtx/complexity/application were silently dropped.
|
||||
voicecat::codec::OpusParams opus_params_from_audio_config(const voicecat::v1::AudioConfig& a) {
|
||||
voicecat::codec::OpusParams p;
|
||||
// Opus always runs at 48 kHz internally (docs/voice.md §3): the whole AudioEngine clock is
|
||||
@@ -1038,8 +1034,8 @@ void vc_client::on_capture_frame(int kind, const int16_t* pcm, int samples, int
|
||||
auto it = local_streams_.find(kind);
|
||||
if (it == local_streams_.end() || !it->second.active.load(std::memory_order_acquire)) return;
|
||||
// "Mic muted" only gates the MIC stream — a concurrently-running SCREEN_AUDIO share keeps
|
||||
// playing while the user's mic is muted (docs §M3 scope decision).
|
||||
// M5: server-mute is also a hard gate on MIC transmission.
|
||||
// playing while the user's mic is muted (scope decision — see docs/voice.md).
|
||||
// Server-mute is also a hard gate on MIC transmission.
|
||||
if (kind == static_cast<int>(VC_STREAM_MIC) &&
|
||||
(self_mic_muted_.load(std::memory_order_acquire) ||
|
||||
server_muted_.load(std::memory_order_acquire))) return;
|
||||
@@ -1308,7 +1304,7 @@ void vc_client::sync_remote_streams(const voicecat::v1::User& user) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── M2: stream / device control ──────────────────────────────────────────────
|
||||
// ── Stream / device control ────────────────────────────────────────────────────
|
||||
|
||||
vc_result vc_client::stream_start(const vc_stream_desc& desc, uint32_t* out_stream_id) {
|
||||
if (state_net_.load(std::memory_order_acquire) != VC_STATE_CONNECTED) return VC_ERR_NOT_CONNECTED;
|
||||
@@ -1884,7 +1880,7 @@ vc_result vc_client::list_devices(vc_device_kind kind, vc_device_list* out) {
|
||||
return VC_OK;
|
||||
}
|
||||
|
||||
// ── M4: channel/user/stream snapshot getters ─────────────────────────────────
|
||||
// ── Channel/user/stream snapshot getters ──────────────────────────────────────
|
||||
|
||||
vc_result vc_client::list_channels(vc_channel_list* out) {
|
||||
std::lock_guard<std::mutex> lk(session_model_mu_);
|
||||
@@ -1962,7 +1958,7 @@ vc_result vc_client::list_user_streams(uint32_t user_id, vc_stream_summary_list*
|
||||
return VC_OK;
|
||||
}
|
||||
|
||||
// ── M4: TOFU server-identity gate ─────────────────────────────────────────────
|
||||
// ── TOFU server-identity gate ───────────────────────────────────────────────────
|
||||
|
||||
vc_result vc_client::confirm_server_identity(bool accept) {
|
||||
std::lock_guard<std::mutex> lk(tofu_mu_);
|
||||
@@ -1987,7 +1983,7 @@ vc_result vc_client::get_server_identity_display(char* out_buf, size_t buf_cap,
|
||||
return VC_OK;
|
||||
}
|
||||
|
||||
// ── M5: Moderation & admin ───────────────────────────────────────────────────
|
||||
// ── Moderation & admin ─────────────────────────────────────────────────────────
|
||||
|
||||
vc_result vc_client::kick_user(uint32_t user_id, const char* reason) {
|
||||
if (state_net_.load(std::memory_order_acquire) != VC_STATE_CONNECTED) return VC_ERR_NOT_CONNECTED;
|
||||
|
||||
@@ -68,16 +68,16 @@ struct vc_client {
|
||||
|
||||
vc_result list_devices(vc_device_kind kind, vc_device_list* out);
|
||||
|
||||
// M4: channel/user/stream snapshot getters (read session_model_; see voicecat.h).
|
||||
// Channel/user/stream snapshot getters (read session_model_; see voicecat.h).
|
||||
vc_result list_channels(vc_channel_list* out);
|
||||
vc_result list_users(vc_user_list* out);
|
||||
vc_result list_user_streams(uint32_t user_id, vc_stream_summary_list* out);
|
||||
|
||||
// M4: TOFU server-identity gate (see voicecat.h's VC_EVENT_SERVER_IDENTITY doc comment).
|
||||
// TOFU server-identity gate (see voicecat.h's VC_EVENT_SERVER_IDENTITY doc comment).
|
||||
vc_result confirm_server_identity(bool accept);
|
||||
vc_result get_server_identity_display(char* out_buf, size_t buf_cap, size_t* out_len);
|
||||
|
||||
// M3: effective Opus config for a (user_id, stream_id) — our own pending/active local
|
||||
// Effective Opus config for a (user_id, stream_id) — our own pending/active local
|
||||
// streams, or any peer's broadcast StreamInfo.audio.
|
||||
vc_result get_stream_audio_config(uint32_t user_id, uint32_t stream_id,
|
||||
vc_audio_config* out);
|
||||
@@ -97,7 +97,7 @@ struct vc_client {
|
||||
// TEST-ONLY (see voicecat.h) — deprecated alias for stream_feed_pcm(..., channels=1).
|
||||
vc_result test_inject_capture(uint32_t stream_id, const int16_t* pcm, size_t samples);
|
||||
|
||||
// M5: moderation & admin.
|
||||
// Moderation & admin.
|
||||
vc_result kick_user(uint32_t user_id, const char* reason);
|
||||
vc_result ban_user(uint32_t user_id, const char* reason, uint64_t expires_unix_ms);
|
||||
vc_result set_permission(uint32_t user_id, const vc_permissions* perms);
|
||||
@@ -123,7 +123,7 @@ struct vc_client {
|
||||
vc_config cfg_{};
|
||||
vc_callbacks cb_{};
|
||||
|
||||
// ── M1: TCP/TLS control channel ─────────────────────────────────────────────
|
||||
// ── TCP/TLS control channel ───────────────────────────────────────────────────
|
||||
std::atomic<vc_connection_state> state_net_{VC_STATE_DISCONNECTED};
|
||||
|
||||
// Blocking I/O thread (one per vc_client lifetime)
|
||||
@@ -187,17 +187,17 @@ struct vc_client {
|
||||
std::atomic<int64_t> last_udp_keepalive_ms_{0};
|
||||
|
||||
// Client-side session model. Mutated only on io_thread_ (handle_server_state/
|
||||
// handle_user_event/handle_channel_event), but read from any thread via the M4
|
||||
// handle_user_event/handle_channel_event), but read from any thread via the
|
||||
// list_channels/list_users/list_user_streams getters — session_model_mu_ guards both.
|
||||
voicecat::session::SessionModel session_model_;
|
||||
mutable std::mutex session_model_mu_;
|
||||
|
||||
// M5: last ListAccountsResult snapshot, populated on io_thread_ when
|
||||
// Last ListAccountsResult snapshot, populated on io_thread_ when
|
||||
// VC_EVENT_ACCOUNT_LIST fires and read by vc_get_account_list on caller threads.
|
||||
std::vector<voicecat::v1::AccountEntry> last_account_list_;
|
||||
mutable std::mutex account_list_mu_;
|
||||
|
||||
// ── M4: TOFU server-identity gate ───────────────────────────────────────────
|
||||
// ── TOFU server-identity gate ─────────────────────────────────────────────────
|
||||
std::unique_ptr<voicecat::crypto::TofuStore> tofu_store_; // owns the pin file
|
||||
std::mutex tofu_mu_;
|
||||
std::condition_variable tofu_cv_;
|
||||
@@ -205,7 +205,7 @@ struct vc_client {
|
||||
bool tofu_accept_{false};
|
||||
std::string pending_identity_fp_hex_; // ServerHello's Ed25519 fp, display-only
|
||||
|
||||
// ── M2: UDP / media plane ────────────────────────────────────────────────────
|
||||
// ── UDP / media plane ──────────────────────────────────────────────────────────
|
||||
std::array<uint8_t, 16> udp_token_{};
|
||||
uint16_t server_udp_port_{0};
|
||||
std::string udp_host_;
|
||||
@@ -220,9 +220,8 @@ struct vc_client {
|
||||
|
||||
voicecat::audio::AudioEngine audio_engine_;
|
||||
|
||||
// M3: one LocalStream per concurrently-active stream kind (MIC/SCREEN_AUDIO/AUX_DEVICE
|
||||
// are each singletons for a given client — see docs/roadmap.md §M3). Replaces the M2
|
||||
// single-stream fields (local_encoder_/local_stream_active_/etc).
|
||||
// One LocalStream per concurrently-active stream kind (MIC/SCREEN_AUDIO/AUX_DEVICE
|
||||
// are each singletons for a given client).
|
||||
struct LocalStream {
|
||||
voicecat::codec::OpusEncoder encoder;
|
||||
std::atomic<bool> active{false};
|
||||
@@ -291,7 +290,7 @@ struct vc_client {
|
||||
mutable std::mutex remote_streams_mu_;
|
||||
std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> remote_streams_;
|
||||
|
||||
// M3: talk-indicator polling thread (separate from udp_thread_ / the miniaudio callback
|
||||
// Talk-indicator polling thread (separate from udp_thread_ / the miniaudio callback
|
||||
// thread — see architecture.md §3 real-time rule).
|
||||
std::thread talk_timer_thread_;
|
||||
std::atomic<bool> talk_timer_stop_{false};
|
||||
@@ -307,10 +306,10 @@ struct vc_client {
|
||||
std::atomic<bool> server_muted_{false};
|
||||
std::atomic<bool> server_deafened_{false};
|
||||
|
||||
// M5: permissions from last AuthResult.
|
||||
// Permissions from last AuthResult.
|
||||
vc_permissions own_permissions_{};
|
||||
|
||||
// Follow-up to M3: send-side input gate (docs/voice.md §11). MIC-only — SCREEN_AUDIO/
|
||||
// Send-side input gate (docs/voice.md §11). MIC-only — SCREEN_AUDIO/
|
||||
// AUX_DEVICE are never gated (see PROGRESS.md for the rationale). Pure local state, no
|
||||
// protocol traffic. mic_vad_ is constructed once the MIC stream's StreamAnnounceResult
|
||||
// lands (handle_stream_announce_result, on io_thread_ — not the RT capture callback).
|
||||
@@ -366,7 +365,7 @@ struct vc_client {
|
||||
// Stop every active local stream (used on voice leave — emits STREAM_STOPPED for each).
|
||||
void stop_all_local_streams();
|
||||
|
||||
// ── M2: UDP / media helpers ──────────────────────────────────────────────────
|
||||
// ── UDP / media helpers ────────────────────────────────────────────────────────
|
||||
// Kicks off TCP UdpBinding request; called once after a successful AuthResult.
|
||||
void start_udp_binding();
|
||||
// Opens the UDP socket, sends the plaintext bootstrap packet, starts udp_thread_.
|
||||
@@ -378,7 +377,7 @@ struct vc_client {
|
||||
// (docs/voice.md §6). Plaintext — no AEAD — to avoid racing the audio thread's seal().
|
||||
void send_udp_keepalive();
|
||||
// capture_cb passed to audio_engine_.start(): encode + seal + send one frame for the
|
||||
// given local stream `kind` (M3: multiple concurrent local streams are possible).
|
||||
// given local stream `kind` (multiple concurrent local streams are possible).
|
||||
void on_capture_frame(int kind, const int16_t* pcm, int samples, int channels);
|
||||
// Encode one frame of exactly ls.frame_samples samples-per-channel (upmixing mono→stereo
|
||||
// for a stereo channel as needed), seal it, and send it over UDP, advancing ls.timestamp.
|
||||
@@ -394,7 +393,7 @@ struct vc_client {
|
||||
// Joins udp_thread_, stops audio_engine_, clears media crypto/remote-stream state.
|
||||
// Safe to call multiple times. Called both from run_io()'s cleanup and disconnect().
|
||||
void teardown_voice();
|
||||
// talk_timer_thread_ entry point (M3): polls audio_engine_ for remote talk-state edges
|
||||
// talk_timer_thread_ entry point: polls audio_engine_ for remote talk-state edges
|
||||
// and local capture activity, emitting VC_EVENT_TALK_STATE. Never the audio RT thread.
|
||||
void run_talk_timer();
|
||||
// Find a LocalStream by its client-assigned stream_id (held under local_streams_mu_ by
|
||||
|
||||
@@ -95,14 +95,12 @@ ServerCert ServerCert::generate(const std::string& server_name) {
|
||||
strlen(pers)),
|
||||
"ctr_drbg_seed");
|
||||
|
||||
// Generate ECDSA-P256 key
|
||||
throw_if(mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)),
|
||||
"pk_setup");
|
||||
throw_if(mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(key),
|
||||
mbedtls_ctr_drbg_random, &ctr_drbg),
|
||||
"ecp_gen_key");
|
||||
|
||||
// Build self-signed cert
|
||||
mbedtls_x509write_crt_set_version(&cert, MBEDTLS_X509_CRT_VERSION_3);
|
||||
mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA256);
|
||||
mbedtls_x509write_crt_set_subject_key(&cert, &key);
|
||||
@@ -112,7 +110,6 @@ ServerCert ServerCert::generate(const std::string& server_name) {
|
||||
throw_if(mbedtls_x509write_crt_set_subject_name(&cert, dn.c_str()), "set_subject");
|
||||
throw_if(mbedtls_x509write_crt_set_issuer_name(&cert, dn.c_str()), "set_issuer");
|
||||
|
||||
// Serial = 0x01 (1 byte, value 1)
|
||||
uint8_t serial_raw[] = {0x01};
|
||||
throw_if(mbedtls_x509write_crt_set_serial_raw(&cert, serial_raw, sizeof(serial_raw)),
|
||||
"set_serial");
|
||||
@@ -124,13 +121,11 @@ ServerCert ServerCert::generate(const std::string& server_name) {
|
||||
throw_if(mbedtls_x509write_crt_set_basic_constraints(&cert, 0, -1),
|
||||
"set_basic_constraints");
|
||||
|
||||
// Write PEM cert
|
||||
unsigned char cert_buf[4096] = {};
|
||||
throw_if(mbedtls_x509write_crt_pem(&cert, cert_buf, sizeof(cert_buf),
|
||||
mbedtls_ctr_drbg_random, &ctr_drbg),
|
||||
"write_cert_pem");
|
||||
|
||||
// Write PEM key
|
||||
unsigned char key_buf[4096] = {};
|
||||
throw_if(mbedtls_pk_write_key_pem(&key, key_buf, sizeof(key_buf)), "write_key_pem");
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ struct ServerCert {
|
||||
|
||||
// ── TLS 1.3 context ───────────────────────────────────────────────────────────
|
||||
// Wraps mbedTLS for one TLS connection (server or client side).
|
||||
// All public methods except close() must be called from a single thread at a time.
|
||||
// All public methods must be called from a single thread at a time.
|
||||
class TlsContext {
|
||||
public:
|
||||
enum class Role { Server, Client };
|
||||
@@ -86,7 +86,7 @@ class TlsContext {
|
||||
bool export_keying_material(const char* label, const uint8_t* ctx, size_t ctx_len,
|
||||
uint8_t* out, size_t out_len);
|
||||
|
||||
// M4 TOFU: SHA-256 of the peer's leaf X.509 certificate (DER), valid only after a
|
||||
// TOFU: SHA-256 of the peer's leaf X.509 certificate (DER), valid only after a
|
||||
// successful Role::Client handshake(). This is the value vc_client pins — see
|
||||
// voicecat.h's vc_tofu_status doc comment for why the cert fingerprint is pinned instead
|
||||
// of the declared Ed25519 server_identity_fingerprint. Returns false if no peer cert is
|
||||
@@ -119,7 +119,7 @@ class TlsContext {
|
||||
mbedtls_net_context net_ctx_{};
|
||||
};
|
||||
|
||||
// ── Media AEAD (M2) ───────────────────────────────────────────────────────────
|
||||
// ── Media AEAD ─────────────────────────────────────────────────────────────────
|
||||
// Per-frame voice encryption. Abstracted so the backend is swappable.
|
||||
class MediaCrypto {
|
||||
public:
|
||||
|
||||
@@ -27,7 +27,7 @@ class TofuStore {
|
||||
|
||||
// Check the fingerprint for host:port. Stores on first connect.
|
||||
// Thread-safe (single-writer lock).
|
||||
// NOTE: kept for compatibility; the M4 gated-confirmation flow (vc_client) uses peek()
|
||||
// NOTE: kept for compatibility; vc_client's gated-confirmation flow uses peek()
|
||||
// + pin() instead, since check_and_pin's unconditional first-connect write is wrong for a
|
||||
// flow where the application must approve the fingerprint before it's trusted/persisted.
|
||||
TofuResult check_and_pin(const std::string& host, uint16_t port,
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
*
|
||||
* Design: docs/architecture.md (Net thread), docs/protocol.md §1 (framing).
|
||||
* Implementation uses standalone Asio for sockets and timers.
|
||||
*
|
||||
* Design: docs/architecture.md (Net thread), docs/protocol.md §1 (framing).
|
||||
* Implementation uses standalone Asio for sockets and timers.
|
||||
*/
|
||||
#ifndef VOICECAT_NET_TRANSPORT_H
|
||||
#define VOICECAT_NET_TRANSPORT_H
|
||||
@@ -180,7 +177,7 @@ class TcpAcceptor {
|
||||
std::vector<std::shared_ptr<TcpServerConn>> conns_;
|
||||
};
|
||||
|
||||
// ── UDP media channel (M2) ───────────────────────────────────────────────────
|
||||
// ── UDP media channel ────────────────────────────────────────────────────────
|
||||
// Thin async UDP socket. send_to() is thread-safe. Recv callbacks fire on the
|
||||
// io_context's thread (same thread that runs the io_context::run() loop).
|
||||
class UdpMediaChannel {
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
* request_id ↔ response, and dispatches to handlers. Media frames do NOT come through here
|
||||
* (they use the fixed binary header in voice.md §2).
|
||||
*
|
||||
* STATUS: real. Protobuf codegen is on (core/CMakeLists.txt) for VOICECAT_HAS_NET builds
|
||||
* (`dev`/`release`/`server-release`); FrameCodec below is fully implemented and used by both the
|
||||
* client (net/transport.h) and the server (conn_session.cpp). See protocol/envelope.h for the
|
||||
* Envelope-level encode/decode that sits on top of this.
|
||||
* FrameCodec below is used by both the client (net/transport.h) and the server
|
||||
* (conn_session.cpp). See protocol/envelope.h for the Envelope-level encode/decode that sits
|
||||
* on top of this.
|
||||
*/
|
||||
#ifndef VOICECAT_PROTOCOL_PROTOCOL_H
|
||||
#define VOICECAT_PROTOCOL_PROTOCOL_H
|
||||
|
||||
@@ -148,9 +148,7 @@ void SessionModel::apply_channel_event(const voicecat::v1::ChannelEvent& ev) {
|
||||
|
||||
} else if (ev.kind() == Kind::DELETED) {
|
||||
// deleted_id, not channel().id() — the proto leaves `channel` unset for deletes
|
||||
// (docs/protocol.md, core/proto/voicecat.proto's ChannelEvent). Pre-existing bug, dead
|
||||
// code until something actually emits ChannelEvent (no channel CRUD exists yet — M5+),
|
||||
// fixed here while touching this function for the M4 field-population fix.
|
||||
// (docs/protocol.md, core/proto/voicecat.proto's ChannelEvent).
|
||||
uint32_t cid = ev.deleted_id();
|
||||
channels_.erase(std::remove_if(channels_.begin(), channels_.end(),
|
||||
[cid](const Channel& x) { return x.id == cid; }),
|
||||
|
||||
@@ -46,7 +46,7 @@ struct Stream {
|
||||
int kind{0};
|
||||
std::string label;
|
||||
// Full effective AudioConfig (docs/protocol.md §5), as broadcast by the server in
|
||||
// StreamInfo.audio — mirrors voicecat::v1::AudioConfig field-for-field so M3 per-channel
|
||||
// StreamInfo.audio — mirrors voicecat::v1::AudioConfig field-for-field so per-channel
|
||||
// tuning (mono/stereo, bitrate, FEC/DTX, application) is observable client-side, not just
|
||||
// sample_rate/frame_ms.
|
||||
uint32_t sample_rate{48000};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
/*
|
||||
* voicecat.cpp — C ABI implementation.
|
||||
*
|
||||
* Lifecycle (create/destroy) and trivial accessors are always real. Everything else below
|
||||
* just delegates to vc_client (core/src/core/client.cpp): under VOICECAT_HAS_NET
|
||||
* (`dev`/`release`/`server-release` — see docs/building.md) that's the real M1–M3 implementation;
|
||||
* under the no-deps `skeleton` preset, client.cpp's `#else` branch returns VC_ERR_NOT_IMPLEMENTED
|
||||
* for all of it, to keep that skeleton build green.
|
||||
* Lifecycle (create/destroy) and trivial accessors are handled directly here; everything else
|
||||
* delegates to vc_client (core/src/core/client.cpp).
|
||||
*/
|
||||
#include "voicecat.h"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user