Files
chanora/docs/designs/file-transfer-protocol-design.md
T
Edison Jwa fe3da41e41 docs: add TSDNS/file transfer designs, review p256 PR draft (TODO-057,066,067)
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).
2026-06-11 21:44:31 +09:00

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

  1. Command Phase — Client sends ftinitdownload over main encrypted UDP connection
  2. 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

  1. Open TCP connection to server_ip:port
  2. Send ftkey followed by newline
  3. Read exactly size bytes of raw file data
  4. 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.rsFileTransferService (344 lines)
  • crates/chanora_cache/src/lib.rsBlobCache (cacache-backed)
  • crates/chanora_protocol/src/adapter.rsuid_to_avatar_path() (line 1561)

Features Implemented

  • ftinitdownload command over encrypted UDP
  • TCP data transfer with ftkey authentication
  • 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)