TSDNS: verified fully implemented in chanora_resolver (port 41144, magic bytes, TCP query). File transfer: verified fully implemented for avatars/icons (ftinitdownload, TCP data, cacache). p256 PR draft reviewed and corrected (function path, probability figure).
5.8 KiB
5.8 KiB
File Transfer Protocol Design
Date: 2026-06-11
Status: Implemented
Location: core/chanora_core/src/file_transfer.rs
Overview
TeamSpeak 3 file transfer protocol for downloading avatars and icons. Uses a two-phase approach: command phase over encrypted UDP, then raw TCP data transfer.
Protocol Specification
Two-Phase Transfer
- Command Phase — Client sends
ftinitdownloadover main encrypted UDP connection - Transfer Phase — Client opens TCP connection to server's file transfer port (default 30033), sends
ftkey, receives raw bytes
Relevant Commands
| Command | Direction | Purpose |
|---|---|---|
ftinitdownload |
Client → Server | Initialize download, returns ftkey, port, size |
ftgetfileinfo |
Client → Server | Get file metadata |
ftgetfilelist |
Client → Server | List files in channel repository |
ftinitupload |
Client → Server | Initialize upload |
ftdeletefile |
Client → Server | Delete a file |
ftcreatedir |
Client → Server | Create directory |
ftrenamefile |
Client → Server | Rename/move file |
ftinitdownload Command
ftinitdownload clientftfid={id} name={path} cid={channelId} cpw={password} seekpos={seek} proto=0
Parameters:
| Parameter | Type | Description |
|---|---|---|
clientftfid |
u16 |
Client-side transfer ID |
name |
string |
File path (e.g., /avatar_abcdef) |
cid |
ChannelId |
Channel scope (0 = server-level) |
cpw |
string |
Channel password (empty for server-level) |
seekpos |
u64 |
Resume offset (0 for fresh download) |
proto |
u8 |
Protocol version (always 0) |
Server Response:
| Field | Type | Description |
|---|---|---|
clientftfid |
u16 |
Echo of client transfer ID |
serverftfid |
u16 |
Server-side transfer ID |
ftkey |
string |
One-time transfer key (hex) |
port |
u16 |
File transfer TCP port (usually 30033) |
size |
u64 |
File size in bytes |
proto |
u8 |
Protocol version echo |
ip |
string (optional) |
Override IP for TCP connection |
TCP Transfer Flow
- Open TCP connection to
server_ip:port - Send
ftkeyfollowed by newline - Read exactly
sizebytes of raw file data - Close TCP connection
File Paths
Files are addressed by path scoped to channel ID:
cid=0— Server-level repository (avatars, icons live here)cid=N— Channel-specific repository
Avatar path: /avatar_<hex> where <hex> is derived from client UID:
fn uid_to_avatar_path(uid_b64: &str) -> String {
let decoded = BASE64_STANDARD.decode(uid_b64).unwrap_or_default();
let mut rendered = String::with_capacity(decoded.len() * 2);
for byte in decoded {
rendered.push((b'a' + (byte >> 4)) as char);
rendered.push((b'a' + (byte & 0x0f)) as char);
}
rendered
}
Icon path: /icon_<id> where <id> is unsigned CRC32 of icon bytes.
Implementation Details
Architecture
Bridge (get_avatar/get_icon)
↓
FileTransferService
↓
1. Check BlobCache → hit? return bytes
2. Check negative cache → hit? return None
3. Check in-flight map → already downloading? await existing
4. Acquire concurrency semaphore (max 2)
5. Download via ProtocolClient
6. Store in BlobCache
7. Notify all waiters
Code Location
core/chanora_core/src/file_transfer.rs—FileTransferService(344 lines)crates/chanora_cache/src/lib.rs—BlobCache(cacache-backed)crates/chanora_protocol/src/adapter.rs—uid_to_avatar_path()(line 1561)
Features Implemented
ftinitdownloadcommand over encrypted UDP- TCP data transfer with
ftkeyauthentication - Avatar download (
/avatar_<hex>) - Icon download (
/icon_<crc32>) - Content-addressed blob cache (cacache-backed)
- Request coalescing (multiple requests for same hash = 1 download)
- Rate limiting (max 2 concurrent downloads)
- Negative cache (5-minute TTL for 404s)
- Cache integrity verification (SSRI)
- Cross-server dedup (same content = same blob)
Cache Architecture
Storage: <cache_dir>/chanora/blobs/ managed by cacache
Key mapping:
| Protocol key | cacache key | Example |
|---|---|---|
| Avatar MD5 | av_<md5hex> |
av_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 |
| Icon CRC32 | ic_<crc32u> |
ic_123456789 |
Why content-addressed:
client_flag_avatar= MD5 of avatar bytes (computed by uploader)icon_id= CRC32 of icon bytes (computed by uploader)- Same content on any server = same hash = stored once
Error Handling
| tsclientlib error | ProtocolError | Action |
|---|---|---|
| Permission denied | ServerRejected { code, message } |
Negative cache (5 min) |
| File not found | ServerRejected { code, message } |
Negative cache (5 min) |
| Network/TCP failure | Backend(String) |
Retry with backoff |
| Timeout | Timeout |
Retry with backoff |
| Connection lost | Lost(String) |
Fail all pending downloads |
Anti-Flood Strategy
- Max 2 concurrent downloads per server
- 5-second pause on flood error, then resume at reduced rate
- Lazy download (only when UI needs to display)
- No proactive download of all avatars on connect
Testing
Unit tests in core/chanora_core/src/file_transfer.rs:
returns_cached_avatar_without_connection(line 295)negative_cache_short_circuits_not_connected(line 313)returns_cached_icon_without_connection(line 333)
References
- YaTQA Reference §8.3: File transfer protocol details
docs/architecture/file-transfer-design.md— Full design document (737 lines)docs/architecture/file-transfer-research.md— Research findings (770 lines)docs/architecture/file-transfer-implementation-plan.md— Implementation plan (1315 lines)core/chanora_core/src/file_transfer.rs— Implementation (344 lines)