Files
voice-cat/core/proto/voicecat.proto
Talon b332b0972b 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

239 lines
10 KiB
Protocol Buffer
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// VoiceCat control-plane wire format. SOURCE OF TRUTH for the protocol.
// Spec: docs/protocol.md. Each TCP/TLS frame is [u32 length][encoded Envelope].
// Media frames (voice) are NOT here — they use the fixed binary header in docs/voice.md §2.
//
// Extensibility rules (docs/protocol.md §8): never reuse/renumber tags; new oneof arms and
// fields are additive; gate new features behind capability strings in ClientHello/ServerHello.
syntax = "proto3";
package voicecat.v1;
// ── Envelope ──────────────────────────────────────────────────────────────────
message Envelope {
// Nonzero on a request; echoed in the matching response for correlation. 0 = unsolicited.
uint64 request_id = 1;
oneof body {
// Session / handshake (tags 1019)
ClientHello client_hello = 10;
ServerHello server_hello = 11;
AuthRequest auth_request = 12;
AuthResult auth_result = 13;
Disconnect disconnect = 14;
Ping ping = 15;
Pong pong = 16;
// State sync (2029)
ServerStateSnapshot server_state = 20;
ChannelEvent channel_event = 21;
UserEvent user_event = 22;
SubscribeRequest subscribe = 23;
// Channel operations (3039)
JoinChannelRequest join_channel = 30;
JoinChannelResult join_channel_result = 31;
LeaveChannelRequest leave_channel = 32;
CreateChannelRequest create_channel = 33;
EditChannelRequest edit_channel = 34;
DeleteChannelRequest delete_channel = 35;
MoveUserRequest move_user = 36;
GenericResult generic_result = 37;
// Voice signaling — media is on UDP (4049)
StreamAnnounce stream_announce = 40;
StreamAnnounceResult stream_announce_result = 41;
StreamStop stream_stop = 42;
StreamStateUpdate stream_state = 43;
UdpBinding udp_binding = 44;
// Text (5059)
TextMessage text_message = 50;
TextMessageAck text_message_ack = 51;
TypingIndicator typing = 52;
// Moderation / permissions (6069)
KickRequest kick = 60;
BanRequest ban = 61;
SetPermissionRequest set_permission = 62;
// Admin account management — privileged; accounts are admin-provisioned (7079)
CreateAccountRequest create_account = 70;
ResetPasswordRequest reset_password = 71;
DeleteAccountRequest delete_account = 72;
ListAccountsRequest list_accounts = 73;
// Future families: file transfer = 100109. Extension escape hatch = 200+.
Extension extension = 200;
}
}
// ── Enums ──────────────────────────────────────────────────────────────────────
enum ChannelType { CHANNEL_PERMANENT = 0; CHANNEL_TEMPORARY = 1; }
enum ChannelMode { MODE_MONO = 0; MODE_STEREO = 1; }
enum StreamKind { STREAM_MIC = 0; STREAM_SCREEN_AUDIO = 1; STREAM_AUX_DEVICE = 2; }
enum TextScope { TEXT_CHANNEL = 0; TEXT_PRIVATE = 1; TEXT_SERVER = 2; }
enum OpusApplication { OPUS_VOIP = 0; OPUS_AUDIO = 1; OPUS_LOWDELAY = 2; }
// ── Common types ────────────────────────────────────────────────────────────────
message AudioConfig {
uint32 codec = 1; // 0 = OPUS
ChannelMode mode = 2;
uint32 sample_rate = 3; // 48000 recommended
uint32 bitrate_bps = 4;
uint32 frame_ms = 5; // 2.5/5/10/20/40/60
OpusApplication application = 6;
bool fec = 7;
uint32 expected_packet_loss = 8; // %
bool dtx = 9;
uint32 complexity = 10; // 0..10
}
message StreamInfo {
uint32 stream_id = 1; // unique within the user
uint32 ssrc = 2; // media-plane id assigned by server
StreamKind kind = 3;
AudioConfig audio = 4;
string label = 5;
}
message Channel {
uint32 id = 1;
uint32 parent_id = 2; // 0 = root
string name = 3;
string topic = 4;
bool password_protected = 5;
uint32 max_users = 6;
ChannelType type = 7;
AudioConfig audio = 8;
int32 order = 9;
}
message User {
uint32 id = 1;
string nickname = 2;
bool is_guest = 3;
uint32 channel_id = 4;
bool self_mic_muted = 5;
bool self_deafened = 6;
bool server_muted = 7;
repeated StreamInfo streams = 8;
}
message Permissions {
// Minimal v1 flag set; the moderation milestone expands this. Server-side is authoritative.
bool can_create_temp_channel = 1;
bool can_kick = 2;
bool can_ban = 3;
bool can_move_users = 4;
bool can_admin_accounts = 5;
bool is_admin = 6;
}
// ── Session / handshake ─────────────────────────────────────────────────────────
message ClientHello {
uint32 proto_version = 1;
repeated string features = 2; // "opus", "fec", "screen-audio", ...
string client_name = 3;
string client_version = 4;
string preferred_locale = 5;
}
message ServerHello {
uint32 proto_version = 1;
repeated string features = 2;
string server_name = 3;
string server_version = 4;
repeated string auth_methods = 5; // "guest", "password"
uint32 udp_port = 6;
bytes server_identity_fingerprint = 7; // Ed25519 fp for TOFU
}
message GuestAuth { string nickname = 1; }
message PasswordAuth { string username = 1; string password = 2; }
message AuthRequest {
oneof method {
GuestAuth guest = 1;
PasswordAuth password = 2;
}
}
message AuthResult {
bool ok = 1;
string error = 2;
uint64 session_id = 3;
User self = 4;
Permissions permissions = 5;
bytes udp_token = 6; // bind the UDP 5-tuple with this (security.md §3)
}
message Disconnect { uint32 code = 1; string reason = 2; }
message Ping { uint64 nonce = 1; }
message Pong { uint64 nonce = 1; }
// ── State sync ──────────────────────────────────────────────────────────────────
message ServerStateSnapshot {
repeated Channel channels = 1;
repeated User users = 2;
}
message ChannelEvent {
enum Kind { CREATED = 0; UPDATED = 1; DELETED = 2; }
Kind kind = 1;
Channel channel = 2;
uint32 deleted_id = 3;
}
message UserEvent {
enum Kind { JOINED = 0; LEFT = 1; UPDATED = 2; }
Kind kind = 1;
User user = 2;
uint32 left_id = 3;
}
message SubscribeRequest { repeated uint32 channel_ids = 1; bool presence = 2; }
// ── Channel operations ──────────────────────────────────────────────────────────
message JoinChannelRequest { uint32 channel_id = 1; string password = 2; }
message JoinChannelResult {
bool ok = 1; string error = 2;
uint32 channel_id = 3;
repeated User members = 4;
AudioConfig audio = 5; // authoritative channel Opus params
}
message LeaveChannelRequest {}
message CreateChannelRequest { Channel channel = 1; string password = 2; }
message EditChannelRequest { Channel channel = 1; string password = 2; }
message DeleteChannelRequest { uint32 channel_id = 1; }
message MoveUserRequest { uint32 user_id = 1; uint32 channel_id = 2; }
message GenericResult { bool ok = 1; uint32 code = 2; string message = 3; }
// ── Voice signaling ─────────────────────────────────────────────────────────────
message StreamAnnounce { StreamKind kind = 1; AudioConfig requested_audio = 2; string label = 3; }
message StreamAnnounceResult{ bool ok = 1; string error = 2; uint32 stream_id = 3; uint32 ssrc = 4; AudioConfig effective_audio = 5; }
message StreamStop { uint32 stream_id = 1; }
message StreamStateUpdate { uint32 user_id = 1; uint32 stream_id = 2; bool muted = 3; bool talking = 4; }
message UdpBinding { bytes udp_token = 1; bool ack = 2; }
// ── Text (ephemeral — server does not persist history, docs/protocol.md §5) ──────
message TextMessage {
TextScope scope = 1;
uint32 target_id = 2; // channel_id or user_id per scope
uint32 sender_id = 3; // set by server on relay
string body = 4; // UTF-8, server-bounded length
uint64 sent_at_unix_ms = 5; // server timestamp on relay
string client_msg_id = 6; // echoed in ack (dedup)
}
message TextMessageAck { string client_msg_id = 1; bool ok = 2; }
message TypingIndicator { TextScope scope = 1; uint32 target_id = 2; uint32 user_id = 3; }
// ── Moderation / permissions ─────────────────────────────────────────────────────
message KickRequest { uint32 user_id = 1; string reason = 2; }
message BanRequest { uint32 user_id = 1; string reason = 2; uint64 expires_unix_ms = 3; }
message SetPermissionRequest { uint32 user_id = 1; Permissions permissions = 2; }
// ── Admin account management (privileged) ────────────────────────────────────────
message CreateAccountRequest { string username = 1; string password = 2; }
message ResetPasswordRequest { string username = 1; string new_password = 2; }
message DeleteAccountRequest { string username = 1; }
message ListAccountsRequest {}
// ── Extension escape hatch ───────────────────────────────────────────────────────
message Extension { string ns = 1; bytes payload = 2; }