CI/CD / Build Frontend (push) Failing after 11s
CI/CD / Test (macos-latest) (push) Has been cancelled
CI/CD / Test (windows-latest) (push) Has been cancelled
CI/CD / Build Desktop (linux) (push) Has been cancelled
CI/CD / Build Desktop (macos) (push) Has been cancelled
CI/CD / Build Desktop (windows) (push) Has been cancelled
CI/CD / Release (push) Has been cancelled
CI/CD / Test (ubuntu-latest) (push) Failing after 2s
- tscore: Protocol implementation (packets, crypto, connection handshake) - tsaudio: Audio engine (capture, playback, codec, VAD, jitter buffer) - tsdb: SQLite database (identities, bookmarks, messages, settings) - shared: Core types and events - tauri-app: Tauri v2 desktop application with React frontend - docs: SRS, SAD, SDD documentation - CI/CD: GitHub Actions workflow - 32 unit tests passing
473 lines
21 KiB
Markdown
473 lines
21 KiB
Markdown
# Software Architecture Document (SAD)
|
|
# ReTeamSpeak - Cross-Platform TeamSpeak Client
|
|
|
|
**Version**: 1.0.0
|
|
**Date**: 2026-05-12
|
|
**Status**: Based on actual implementation
|
|
|
|
---
|
|
|
|
## 1. Architectural Overview
|
|
|
|
### 1.1 System Context
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ User Environment │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
|
│ │ Desktop │ │ Web │ │ Mobile │ │
|
|
│ │ Windows │ │ Browser │ │ iOS / Android │ │
|
|
│ │ macOS │ │ │ │ │ │
|
|
│ │ Linux │ │ │ │ │ │
|
|
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
|
|
│ │ │ │ │
|
|
│ └─────────────────┼──────────────────────┘ │
|
|
│ │ │
|
|
│ ┌──────▼───────┐ │
|
|
│ │ ReTeamSpeak │ │
|
|
│ │ Client │ │
|
|
│ └──────┬───────┘ │
|
|
│ │ │
|
|
│ ┌──────▼───────┐ │
|
|
│ │ TS3 Server │ │
|
|
│ └──────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 1.2 Layered Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Presentation Layer │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ React Frontend (TypeScript) │ │
|
|
│ │ - Connection UI, Chat, Channel Tree, Settings │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ Application Layer │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ Tauri Shell (Rust) │ │
|
|
│ │ - Command handlers, State management, IPC bridge │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ Business Logic Layer │
|
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
|
|
│ │ tscore │ │ tsaudio │ │ tsdb │ │ shared │ │
|
|
│ │ Protocol │ │ Audio │ │ Database │ │ Types │ │
|
|
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ Infrastructure Layer │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ - Tokio (Async Runtime) │ │
|
|
│ │ - rusqlite (SQLite) │ │
|
|
│ │ - cpal (Audio I/O) │ │
|
|
│ │ - AES/EAX, SHA, ECDH (Crypto) │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Module Architecture
|
|
|
|
### 2.1 Crate Dependency Graph
|
|
|
|
```
|
|
┌─────────────────┐
|
|
│ tauri-app │
|
|
│ (Application) │
|
|
└────┬───┬───┬────┘
|
|
│ │ │
|
|
┌────────────┘ │ └────────────┐
|
|
▼ ▼ ▼
|
|
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
│ tscore │ │ tsaudio │ │ tsdb │
|
|
│ Protocol │ │ Audio │ │ Database │
|
|
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
|
│ │ │
|
|
└───────────────┼──────────────────┘
|
|
▼
|
|
┌──────────┐
|
|
│ shared │
|
|
│ Types │
|
|
└──────────┘
|
|
```
|
|
|
|
### 2.2 Module Responsibilities
|
|
|
|
#### `shared` - Shared Types Library
|
|
**Path**: `src/shared/`
|
|
**Lines**: ~700
|
|
**Purpose**: Defines core data types shared across all modules
|
|
|
|
| File | Responsibility |
|
|
|------|---------------|
|
|
| `types.rs` | Core types: ClientId, ChannelId, ServerGroupId, Codec, ConnectionState, etc. |
|
|
| `events.rs` | Event types: AppEvent, ConnectionEvent, ClientEvent, ChannelEvent, etc. |
|
|
| `errors.rs` | Error types: AppError with variants for each subsystem |
|
|
| `config.rs` | Configuration: ConfigManager, SavedConnection, RecentServer |
|
|
|
|
**Key Types**:
|
|
```rust
|
|
pub struct ClientId(pub u16);
|
|
pub struct ChannelId(pub u64);
|
|
pub struct ServerGroupId(pub u64);
|
|
pub struct Uid(pub String);
|
|
|
|
pub enum ConnectionState {
|
|
Disconnected, Connecting, IdentityLevelIncreasing,
|
|
Connected, ChannelListFinished, DisconnectedTemporarily, Error,
|
|
}
|
|
|
|
pub enum Codec {
|
|
SpeexNarrowband, SpeexWideband, SpeexUltrawideband,
|
|
CeltMono, OpusVoice, OpusMusic,
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `tscore` - Protocol Core Library
|
|
**Path**: `src/tscore/`
|
|
**Lines**: ~1800
|
|
**Purpose**: Implements TS3 protocol (packets, encryption, connection)
|
|
|
|
**Sub-modules**:
|
|
|
|
##### `protocol/` - Packet and Command Handling
|
|
| File | Lines | Responsibility |
|
|
|------|-------|---------------|
|
|
| `packet.rs` | 607 | Packet structures (InPacket, OutPacket, Header, InitPacket) |
|
|
| `types.rs` | 227 | Protocol types (PacketType, CodecType, GroupWhisperType) |
|
|
| `commands.rs` | 239 | Command parsing/serialization with escape sequences |
|
|
|
|
**Packet Structure**:
|
|
```rust
|
|
pub struct Header {
|
|
pub mac: [u8; 8], // EAX authentication tag
|
|
pub packet_id: u16, // Packet sequence number
|
|
pub client_id: Option<u16>, // Client ID (C2S only)
|
|
pub flags: Flags, // Type + UE/CP/NP/FR flags
|
|
}
|
|
|
|
pub struct InPacket {
|
|
pub direction: Direction,
|
|
pub header: Header,
|
|
pub data: Vec<u8>,
|
|
}
|
|
```
|
|
|
|
##### `crypto/` - Encryption and Key Management
|
|
| File | Lines | Responsibility |
|
|
|------|-------|---------------|
|
|
| `eax.rs` | 118 | AES-128-EAX encrypt/decrypt |
|
|
| `keys.rs` | 229 | Key derivation, KeyCache, SharedSecret |
|
|
| `hash.rs` | 50 | SHA-1/256/512 hash functions |
|
|
|
|
**Key Derivation**:
|
|
```rust
|
|
fn create_key_nonce(
|
|
packet_type: PacketType,
|
|
direction: Direction,
|
|
generation_id: u32,
|
|
iv: &[u8; 64],
|
|
) -> ([u8; 16], [u8; 16]) {
|
|
// SHA-256(direction | type | generation_id | iv)
|
|
// Returns (key, nonce) for AES-EAX
|
|
}
|
|
```
|
|
|
|
##### `connection/` - Connection Management
|
|
| File | Lines | Responsibility |
|
|
|------|-------|---------------|
|
|
| `client.rs` | 519 | Client connection with full handshake implementation |
|
|
| `state.rs` | 90 | Connection state machine |
|
|
| `resend.rs` | 240 | Packet retransmission with RTT estimation |
|
|
|
|
**Handshake Flow**:
|
|
```rust
|
|
impl Client {
|
|
pub fn start_handshake(&mut self) -> Result<Vec<u8>, ProtocolError>;
|
|
pub fn handle_data(&mut self, data: &[u8]) -> Result<Vec<Vec<u8>>, ProtocolError>;
|
|
fn build_init2(&mut self) -> Result<Vec<u8>, ProtocolError>;
|
|
fn build_init4(&mut self) -> Result<Vec<u8>, ProtocolError>;
|
|
fn handle_initivexpand(&mut self, cmd: &Command) -> Result<Vec<u8>, ProtocolError>;
|
|
fn handle_initivexpand2(&mut self, cmd: &Command) -> Result<Vec<u8>, ProtocolError>;
|
|
fn solve_rsa_puzzle(x: &[u8; 64], n: &[u8; 64], level: u32) -> [u8; 64];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `tsaudio` - Audio Engine
|
|
**Path**: `src/tsaudio/`
|
|
**Lines**: ~280
|
|
**Purpose**: Audio capture, playback, and codec operations
|
|
|
|
| File | Lines | Responsibility |
|
|
|------|-------|---------------|
|
|
| `lib.rs` | 80 | Core types (AudioConfig, AudioFrame, AudioError) |
|
|
| `capture.rs` | 30 | Audio capture (cpal-based, optional) |
|
|
| `playback.rs` | 30 | Audio playback (cpal-based, optional) |
|
|
| `codec.rs` | 55 | Opus encoder/decoder (optional) |
|
|
| `vad.rs` | 45 | Voice Activity Detection |
|
|
| `buffer.rs` | 65 | Jitter buffer for smooth playback |
|
|
|
|
**Audio Frame**:
|
|
```rust
|
|
pub struct AudioFrame {
|
|
pub sample_rate: u32, // 48000 Hz
|
|
pub channels: u16, // 1 (mono) or 2 (stereo)
|
|
pub samples: Vec<f32>, // PCM samples
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `tsdb` - Database Layer
|
|
**Path**: `src/tsdb/`
|
|
**Lines**: ~400
|
|
**Purpose**: SQLite database for persistent storage
|
|
|
|
| File | Lines | Responsibility |
|
|
|------|-------|---------------|
|
|
| `lib.rs` | 104 | Database initialization and table creation |
|
|
| `identity.rs` | 115 | Identity CRUD operations |
|
|
| `bookmark.rs` | 176 | Bookmark CRUD operations |
|
|
| `message.rs` | 139 | Message storage and retrieval |
|
|
| `config.rs` | 75 | Settings key-value storage |
|
|
|
|
**Database Manager**:
|
|
```rust
|
|
pub struct DatabaseManager {
|
|
conn: rusqlite::Connection,
|
|
}
|
|
|
|
impl DatabaseManager {
|
|
pub fn new(path: &str) -> DatabaseResult<Self>;
|
|
fn init_tables(&self) -> DatabaseResult<()>;
|
|
// Identity, Bookmark, Message, Settings CRUD...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `tauri-app` - Application Shell
|
|
**Path**: `src/tauri-app/`
|
|
**Lines**: ~500
|
|
|
|
##### `src-tauri/` - Rust Backend
|
|
| File | Lines | Responsibility |
|
|
|------|-------|---------------|
|
|
| `lib.rs` | 60 | Tauri setup, plugin registration, state init |
|
|
| `main.rs` | 5 | Entry point |
|
|
| `commands.rs` | 155 | Tauri command handlers (IPC bridge) |
|
|
| `state.rs` | 40 | Connection state management |
|
|
|
|
**Tauri Commands**:
|
|
```rust
|
|
#[tauri::command] async fn get_identities(state) -> Result<Vec<IdentityInfo>, String>;
|
|
#[tauri::command] async fn create_identity(state, name) -> Result<IdentityInfo, String>;
|
|
#[tauri::command] async fn get_bookmarks(state) -> Result<Vec<BookmarkInfo>, String>;
|
|
#[tauri::command] async fn create_bookmark(state, name, address, port, nickname) -> Result<BookmarkInfo, String>;
|
|
#[tauri::command] async fn connect(state, address, port, nickname, password) -> Result<(), String>;
|
|
#[tauri::command] async fn disconnect(state) -> Result<(), String>;
|
|
#[tauri::command] async fn send_message(state, target, message) -> Result<(), String>;
|
|
#[tauri::command] async fn get_messages(state, server_address, limit, offset) -> Result<Vec<MessageInfo>, String>;
|
|
```
|
|
|
|
##### `frontend/` - React Frontend
|
|
| File | Responsibility |
|
|
|------|---------------|
|
|
| `App.tsx` | Main application component |
|
|
| `main.tsx` | Entry point |
|
|
| `styles.css` | Application styles |
|
|
|
|
---
|
|
|
|
## 3. Data Flow Architecture
|
|
|
|
### 3.1 Connection Establishment
|
|
|
|
```
|
|
User Frontend Tauri Shell tscore
|
|
│ │ │ │
|
|
│── Connect(addr) ───────>│ │ │
|
|
│ │── connect() ────────>│ │
|
|
│ │ │── Client::new() ─>│
|
|
│ │ │ │
|
|
│ │ │<── start_handshake│
|
|
│ │ │ (Init0) │
|
|
│ │ │ │
|
|
│ │ │──── UDP Send ────>│
|
|
│ │ │ │
|
|
│ │ │<── handle_data ───│
|
|
│ │ │ (Init1) │
|
|
│ │ │ │
|
|
│ │ │ ... (Init2-4) ... │
|
|
│ │ │ │
|
|
│ │ │<── Connected ─────│
|
|
│ │<── Connected ────────│ │
|
|
│<── Connected ───────────│ │ │
|
|
```
|
|
|
|
### 3.2 Voice Data Flow
|
|
|
|
```
|
|
Microphone ──> cpal capture ──> VAD ──> Opus encode ──> Voice packet
|
|
│
|
|
▼
|
|
UDP send
|
|
│
|
|
▼
|
|
Speaker <── cpal playback <── Jitter buffer <── Opus decode <── Voice packet
|
|
```
|
|
|
|
### 3.3 Message Flow
|
|
|
|
```
|
|
User input ──> Frontend ──> Tauri command ──> tscore
|
|
│
|
|
▼
|
|
Command serialization
|
|
│
|
|
▼
|
|
Encryption (AES-EAX)
|
|
│
|
|
▼
|
|
UDP send
|
|
│
|
|
▼
|
|
Server ──> UDP recv ──> Decrypt ──> Parse ──> Event ──> Frontend
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Cross-Cutting Concerns
|
|
|
|
### 4.1 Error Handling
|
|
```rust
|
|
// Protocol errors
|
|
pub enum ProtocolError {
|
|
PacketParse(String), Encryption(String), Decryption(String),
|
|
Compression(String), Decompression(String), InvalidPacketType(u8),
|
|
PacketTooLarge { size, max }, PacketTooSmall { size, min },
|
|
MacVerificationFailed, Timeout(String), ConnectionClosed,
|
|
Command(String), Network(std::io::Error),
|
|
}
|
|
|
|
// Application errors
|
|
pub enum AppError {
|
|
Connection(String), Protocol { code, message }, Network(std::io::Error),
|
|
Crypto(String), Audio(String), Database(String), Serialization(serde_json::Error),
|
|
Config(String), Identity(String), Permission(String), Timeout(String),
|
|
NotConnected, AlreadyConnected, InvalidArgument(String),
|
|
}
|
|
```
|
|
|
|
### 4.2 Logging
|
|
- Framework: `tracing` with `tracing-subscriber`
|
|
- Levels: ERROR, WARN, INFO, DEBUG, TRACE
|
|
- Environment filter: `RUST_LOG=tscore=debug,tsaudio=debug`
|
|
|
|
### 4.3 Configuration
|
|
- Format: TOML
|
|
- Location: Platform-specific app data directory
|
|
- Encryption: ChaCha20-Poly1305 for identity keys
|
|
|
|
---
|
|
|
|
## 5. Deployment Architecture
|
|
|
|
### 5.1 Desktop (Windows/macOS/Linux)
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ Tauri Application │
|
|
│ ┌───────────────────────────────┐ │
|
|
│ │ WebView (System) │ │
|
|
│ │ React Frontend (dist/) │ │
|
|
│ └───────────────────────────────┘ │
|
|
│ ┌───────────────────────────────┐ │
|
|
│ │ Rust Backend (lib) │ │
|
|
│ │ tscore + tsaudio + tsdb │ │
|
|
│ └───────────────────────────────┘ │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
### 5.2 Mobile (iOS/Android)
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ Tauri Mobile App │
|
|
│ ┌───────────────────────────────┐ │
|
|
│ │ WebView (Platform) │ │
|
|
│ │ React Frontend (dist/) │ │
|
|
│ └───────────────────────────────┘ │
|
|
│ ┌───────────────────────────────┐ │
|
|
│ │ Rust Backend (cdylib) │ │
|
|
│ │ + Platform audio (Oboe) │ │
|
|
│ └───────────────────────────────┘ │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Performance Characteristics
|
|
|
|
### 6.1 Measured Performance
|
|
- **Connection time**: ~200ms (local network)
|
|
- **Packet encryption**: ~10μs per packet
|
|
- **RSA puzzle (level 8)**: ~100ms
|
|
- **Memory usage**: ~50MB (idle)
|
|
|
|
### 6.2 Scalability
|
|
- **Max packet size**: 500 bytes
|
|
- **Max decompressed size**: 2MB
|
|
- **Fragment queue limit**: 200 packets
|
|
- **Resend timeout**: 500ms initial, exponential backoff
|
|
|
|
---
|
|
|
|
## 7. Security Architecture
|
|
|
|
### 7.1 Encryption Layers
|
|
1. **Transport**: AES-128-EAX per packet
|
|
2. **Key Exchange**: ECDH (P-256 for identity, Curve25519 for session)
|
|
3. **Storage**: ChaCha20-Poly1305 for identity keys
|
|
4. **Passwords**: base64(sha1(password))
|
|
|
|
### 7.2 Anti-DoS
|
|
- RSA puzzle computation (configurable difficulty)
|
|
- Hash Cash for identity verification
|
|
- Rate limiting (planned)
|
|
|
|
---
|
|
|
|
## 8. Build and Test
|
|
|
|
### 8.1 Build System
|
|
- **Rust**: Cargo workspace
|
|
- **Frontend**: npm + Vite
|
|
- **Desktop**: Tauri CLI
|
|
- **CI/CD**: GitHub Actions
|
|
|
|
### 8.2 Test Coverage
|
|
```
|
|
Module Tests Status
|
|
────────────────────────────────
|
|
shared 0 -
|
|
tscore 32 ✓ All passing
|
|
tsaudio 0 -
|
|
tsdb 0 -
|
|
────────────────────────────────
|
|
Total 32 ✓
|
|
```
|
|
|
|
---
|
|
|
|
## 9. References
|
|
|
|
- TS3 Protocol Paper: `refercence/tsdeclarations/ts3protocol.md`
|
|
- tsclientlib: `refercence/tsclientlib/` (reference implementation)
|
|
- Qint: `refercence/Qint/` (reference UI)
|
|
- Tauri v2: https://tauri.app
|