# TeamSpeak 3 Development Manual **Self-contained engineering manual based on YaTQA resources and the ReSpeak repositories** Version: 1.1 - v4 coverage audit edition Language: English Scope: TeamSpeak 3 server administration tooling, ServerQuery clients, TS3-compatible client/bot development, identity tooling, statistics processing, and migration/backup automation. > v6 update: Appendix D performs a full link-by-link coverage audit of the German YaTQA resources page. Section 15 includes the complete YaTQA How-to-DNS decision chart as native text, tables, and structured decision rules only - no PNG or raster flowchart is required. Section 16 includes the expanded details from YaTQA's `protokolle` page: Protobuf, updates, file transfer, TSDNS, server nicknames, DNS/SRV behavior, Blacklist/Blacklist2, Weblist, badges, and the historical TLD appendix. ## 1. Purpose and Scope This manual consolidates the technical knowledge needed to build TeamSpeak 3 development tools without repeatedly returning to the original websites. It is based on: - YaTQA resource documents, especially the German technical pages under `https://yat.qa/ressourcen/` and the English pages under `https://yat.qa/resources/`. - ReSpeak repositories: - `ReSpeak/tsclientlib` - `ReSpeak/ts3stats` - `ReSpeak/tsdeclarations` - `ReSpeak/Qint` - `ReSpeak/MahTsIdentity` - Official TeamSpeak 3 support articles for operational baseline information such as ports, snapshots, DNS SRV, and server setup. The document is written for developers who want to implement one or more of the following: 1. A ServerQuery administration system. 2. A TeamSpeak 3-compatible bot or custom client. 3. A typed command and event schema for TS3. 4. Snapshot backup, migration, and recovery tooling. 5. Identity generation, vanity UID search, or security-level improvement. 6. Statistics reports from TS3 and TS3AudioBot logs. 7. A desktop client architecture similar to Qint. This manual is intentionally practical. It provides algorithms, protocol behavior, implementation architecture, schema examples, pseudocode, and troubleshooting guidance. ### 1.1 Important limitations Some source areas are inherently incomplete: - YaTQA explicitly notes that it does not provide a complete ClientQuery document. - Several YaTQA tools are old downloads; some may no longer be reachable or useful on modern systems. - Official TS3 plugin documentation is mostly distributed through the TeamSpeak 3 Plugin SDK package, not as a complete public web manual. - ReSpeak projects are open source references, but not all of them are actively maintained or suitable as production dependencies without review. Where public details are thin, this manual states that explicitly rather than inventing behavior. *** ## 2. Source Inventory ### 2.1 YaTQA resources The YaTQA resources page is the strongest public source for undocumented or poorly documented TeamSpeak 3 behavior. The relevant resources are: | Resource | Primary development value | |---|---| | Web-based tools | UID, avatar filename, icon ID, and cache folder conversions | | Backup without snapshot permissions | Practical migration when snapshot rights are missing | | Server error codes | Query error dictionary and error classification | | Permission IDs | Permission constants, grant IDs, and historical numbering behavior | | Client versions | Historical TeamSpeak 3 client build numbers and timestamps | | Definitions and algorithms | Codecs, permission calculation, Query connection details, icon/avatar/cache algorithms, snapshots, BBCode | | Server Query comments | Corrections and behavior notes for ServerQuery commands | | Server Query notify | Event subscription behavior and event field lists | | Variable parameters | Which client/channel/server variables are visible or editable in which commands | | Voice client anti-flood | Anti-flood points, decay, and command costs | | Security level | Identity proof-of-work calculation and optimization notes | | Snapshots | Snapshot text format, included data, missing data, and hash rules | | Other protocols | Protobuf, update protocol, file transfer, TSDNS, DNS, blacklist, weblist, badges | | Badge list | Badge GUIDs, asset naming, and metadata patterns | ### 2.2 ReSpeak repositories | Repository | Role | |---|---| | `tsdeclarations` | Machine-readable protocol, message, error, permission, version, and badge declarations. Best source for code generation. | | `tsclientlib` | Rust runtime library for TeamSpeak 3-compatible clients and bots. Contains protocol, state, and example usage. | | `Qint` | Modern alternative TeamSpeak client reference using Tauri/frontend/backend architecture and `tsclientlib`. | | `ts3stats` | Offline statistics pipeline for TeamSpeak 3 and TS3AudioBot logs. | | `MahTsIdentity` | Identity export, vanity UID search, and security-level improvement tool using Rust parallelism. | *** ## 3. System Architecture for a TS3 Development Stack A complete TS3 tooling ecosystem should be split into layers: ```text +--------------------------------------------------------------+ | Applications | | - Admin web UI | | - Desktop client | | - Bot | | - Stats report generator | | - Identity tool | +-------------------------+------------------------------------+ | Domain services | Query/event/snapshot/permission | | | identity/DNS/file-transfer modules | +-------------------------+------------------------------------+ | Protocol implementation | ServerQuery parser and builder | | | UDP packet protocol | | | Protobuf, Base64, hashes, escaping | +-------------------------+------------------------------------+ | Transport | TCP Query, SSH Query, UDP voice, | | | file transfer TCP, HTTP(S), DNS | +-------------------------+------------------------------------+ | Data declarations | Errors, permissions, messages, | | | versions, badges, enums | +--------------------------------------------------------------+ ``` Recommended module layout for a new implementation: ```text ts3-toolkit/ crates or packages/ ts3-types/ # IDs, enums, errors, permission constants ts3-query/ # ServerQuery protocol parser/builder ts3-events/ # Notify event parser and state reducer ts3-permissions/ # Permission evaluation engine ts3-snapshots/ # Snapshot parser/writer/rehash ts3-identity/ # Identity import/export/security level ts3-protocol/ # UDP packet protocol, protobuf, crypto hooks ts3-files/ # File-transfer token handling and file ops ts3-dns/ # SRV/TSDNS resolution ts3-stats/ # Log ingestion and reporting ts3-admin-app/ # UI/API application layer ``` The most robust strategy is: 1. Generate or centralize constants from `tsdeclarations`-style data. 2. Implement ServerQuery as a strongly typed command system. 3. Implement Query event subscriptions and state reducers. 4. Implement permission evaluation separately from display logic. 5. Implement snapshots and file migration separately because snapshots are not full backups. 6. Add identity and security-level tooling as an offline worker. 7. Add stats as a batch pipeline rather than as a core runtime dependency. *** ## 4. Server Setup and Operational Baseline ### 4.1 Default ports A development environment should account for these default TeamSpeak 3 ports: | Purpose | Protocol | Default port | |---|---:|---:| | Voice traffic | UDP | 9987 | | File transfer | TCP | 30033 | | Raw ServerQuery | TCP | 10011 | | ServerQuery over SSH | TCP | 10022 | | WebQuery HTTP | TCP | 10080 | | WebQuery HTTPS | TCP | 10443 | | TSDNS | TCP | 41144 | For minimal local development, voice + ServerQuery are usually enough: ```bash # Start a TS3 server after accepting the license export TS3SERVER_LICENSE=accept ./ts3server ``` On first startup, capture: - `serveradmin` password. - Initial virtual server admin token. - Server ID (`sid`) and port. Use the token from a normal TS3 client via the Permissions menu to make the first admin identity. ### 4.2 ServerQuery bootstrap A minimal raw ServerQuery session: ```text login serveradmin YOUR_PASSWORD use sid=1 -virtual servernotifyregister event=server servernotifyregister event=channel id=0 ``` Important YaTQA behavior note: use `-virtual` when selecting the server. Older documentation implied that `use` may automatically start a virtual server. YaTQA observed that it does not reliably do so; using `-virtual` is the safe practice and has no downside when the server is already running. ### 4.3 Recommended development configuration ```yaml server: host: 127.0.0.1 voice_port: 9987 filetransfer_port: 30033 query_port: 10011 query_ssh_port: 10022 server_id: 1 query: username: serveradmin password_env: TS3_QUERY_PASSWORD use_virtual: true command_delay_ms: 350 line_ending: "LF_CR" subscriptions: - event: server - event: channel id: 0 logging: commands: true responses: true events: true security: never_log_query_passwords: true never_log_identity_private_keys: true ``` The 350 ms delay is conservative but practical. YaTQA recommends a 350 ms command delay for large backup jobs to avoid anti-flood bans. For your own tools, implement adaptive pacing rather than hard-coding one global delay. *** ## 5. ServerQuery Protocol ### 5.1 Transport and line endings Raw ServerQuery is a TCP text protocol. A subtle but important point from YaTQA is that line endings use LF-CR (`0x0A 0x0D`), not normal CR-LF. ```python EOL = b"\n\r" ``` Always send `quit` before closing a Query socket so that the server does not treat the disconnect as an abnormal timeout. ### 5.2 Command format A command consists of: ```text command [parameters] [options] ``` Examples: ```text serverlist serverinfo clientlist -uid -groups -voice serveredit virtualserver_name=My\sServer virtualserver_maxclients=64 ``` Parameter escaping is mandatory. Typical Query escaping rules include: | Raw character | Escaped form | |---|---| | Space | `\s` | | Slash | `\/` | | Backslash | `\\` | | Pipe | `\p` | | Bell | `\a` | | Backspace | `\b` | | Form feed | `\f` | | Newline | `\n` | | Carriage return | `\r` | | Tab | `\t` | | Vertical tab | `\v` | Implementation skeleton: ```python ESCAPE = { "\\": "\\\\", "/": "\\/", " ": "\\s", "|": "\\p", "\a": "\\a", "\b": "\\b", "\f": "\\f", "\n": "\\n", "\r": "\\r", "\t": "\\t", "\v": "\\v", } UNESCAPE = {v: k for k, v in ESCAPE.items()} def ts3_escape(value: str) -> str: return "".join(ESCAPE.get(ch, ch) for ch in value) ``` ### 5.3 Response format Most Query responses contain zero or more rows followed by an error line: ```text clid=5 client_nickname=Alice error id=0 msg=ok ``` Rows may be separated by `|`: ```text clid=1 client_nickname=Alice|clid=2 client_nickname=Bob error id=0 msg=ok ``` Parser strategy: 1. Read until an `error id=...` line. 2. Split data lines by unescaped `|`. 3. Split row fields by unescaped spaces. 4. Split key/value by the first `=`. 5. Unescape values. 6. Parse error ID and message separately. Do not assume the response has data. Many successful commands return only `error id=0 msg=ok`. ### 5.4 Return codes YaTQA notes that commands can include `return_code`. The server echoes it in the error line, allowing clients to correlate responses when sending multiple commands concurrently. Example: ```text serverinfo return_code=req42 error id=0 msg=ok return_code=req42 ``` Recommendation: build your Query client around a request ID system even if you initially send commands serially. ### 5.5 Query implementation skeleton ```python import socket import time class Ts3QueryClient: def __init__(self, host, port=10011, delay=0.35): self.host = host self.port = port self.delay = delay self.sock = None def connect(self): self.sock = socket.create_connection((self.host, self.port), timeout=10) self._read_until_prompt_or_banner() def close(self): if self.sock: self.sock.sendall(b"quit\n\r") self.sock.close() def command(self, name, **params): line = self._build_command(name, params) self.sock.sendall(line.encode("utf-8") + b"\n\r") time.sleep(self.delay) return self._read_response() def _build_command(self, name, params): parts = [name] for key, value in params.items(): if value is True: parts.append(f"-{key}") elif value is False or value is None: continue else: parts.append(f"{key}={ts3_escape(str(value))}") return " ".join(parts) ``` ### 5.6 Key Query commands #### Login and server selection ```text login serveradmin PASSWORD use sid=1 -virtual ``` YaTQA note: if `login` fails for a non-trivial reason, an already logged-in session may be logged out. #### Virtual server creation ```text servercreate virtualserver_name=Test\sServer virtualserver_port=9988 virtualserver_maxclients=32 ``` Return values typically include server ID, port, and initial token. Store the token securely. #### Virtual server deletion ```text serverdelete sid=2 ``` #### Server editing ```text serveredit virtualserver_name=New\sName virtualserver_maxclients=64 ``` Common editable server fields include: - `virtualserver_name` - `virtualserver_welcomemessage` - `virtualserver_maxclients` - `virtualserver_password` - `virtualserver_hostmessage` - `virtualserver_hostmessage_mode` - `virtualserver_hostbanner_url` - `virtualserver_hostbanner_gfx_url` - `virtualserver_hostbanner_gfx_interval` - `virtualserver_hostbutton_tooltip` - `virtualserver_hostbutton_url` - `virtualserver_hostbutton_gfx_url` - `virtualserver_download_quota` - `virtualserver_upload_quota` - `virtualserver_max_download_total_bandwidth` - `virtualserver_max_upload_total_bandwidth` - `virtualserver_antiflood_points_tick_reduce` - `virtualserver_antiflood_points_needed_command_block` - `virtualserver_antiflood_points_needed_ip_block` - `virtualserver_needed_identity_security_level` - log toggles such as `virtualserver_log_client`, `virtualserver_log_query`, etc. #### Token / privilege key commands Historical command names may vary: - `privilegekeyadd` - `privilegekeylist` - `privilegekeydelete` - `privilegekeyuse` Aliases documented by YaTQA include token-style names: - `tokenadd` - `tokenlist` - `tokendelete` - `tokenuse` Example: ```text privilegekeyadd tokentype=0 tokenid1=6 tokenid2=0 tokendescription=Admin\sToken ``` `tokencustomset` is nested and must be escaped carefully. Treat it as an encoded key-value string embedded inside another key-value command. *** ## 6. Error Handling Do not expose generic `Query failed` errors. Build a central error dictionary. Important examples from the YaTQA server error code table: | Decimal | Hex | Meaning | Typical fix | |---:|---:|---|---| | 0 | 0x0000 | ok | No action | | 256 | 0x0100 | command not found | Check command name and server version | | 512 | 0x0200 | invalid clientID | Refresh `clid`; do not persist session-scoped client IDs | | 513 | 0x0201 | nickname in use | Choose another nickname | | 520 | 0x0208 | invalid login or password | Check Query credentials | | 522 | 0x020A | client version outdated | Update client or check compatibility | | 524 | 0x020C | client is flooding | Slow down commands; check anti-flood settings | | 768 | 0x0300 | invalid channelID | Refresh channel tree | | 773 | 0x0305 | cannot delete default channel | Change default channel first | | 1027 | 0x0403 | server max clients reached | Increase slots or wait | | 1538 | 0x0602 | invalid parameter | Validate parameter name and type | | 1540 | 0x0604 | convert error | Check numeric formats and signed/unsigned conversions | | 2568 | 0x0A08 | insufficient client permissions | Inspect failed permission ID | | 2572 | 0x0A0C | permission error | Check permission power/needed power | Implementation recommendation: ```python class Ts3Error(Exception): def __init__(self, code, msg, failed_permid=None): super().__init__(f"TS3 error {code}: {msg}") self.code = code self.msg = msg self.failed_permid = failed_permid ``` When `failed_permid` is present, map it back to a permission name using your permission table. *** ## 7. Notify Events and Event-Driven State ### 7.1 Subscribing to events `servernotifyregister` subscribes a Query client to events. Examples: ```text servernotifyregister event=server servernotifyregister event=channel id=0 servernotifyregister event=textserver servernotifyregister event=textchannel servernotifyregister event=textprivate servernotifyregister event=tokenused ``` Important behavior: - Subscriptions are lost on logout. - Subscriptions are lost when switching to another server. - Each event must be subscribed separately. - `event=channel` requires `id`. - `id=0` for channel events means all existing and future channels. - Some event combinations cause duplicate events. Deduplicate in your state reducer. ### 7.2 Common event types #### `notifycliententerview` Used when a client enters view, connects, or becomes visible. Important fields include: - `cfid` - source channel ID, often 0 on server entry. - `ctid` - target channel ID. - `reasonid` - reason code. - `clid` - session-scoped client ID. - `client_unique_identifier` - `client_nickname` - `client_database_id` - `client_channel_group_id` - `client_servergroups` - `client_type` - 0 voice, 1 query. - `client_flag_avatar` - `client_talk_power` - `client_icon_id` - `client_country` - `client_badges` State reducer action: ```python def on_client_enter(state, event): clid = int(event["clid"]) state.clients[clid] = event if "ctid" in event: state.client_channel[clid] = int(event["ctid"]) ``` #### `notifyclientleftview` Used when a client leaves, is kicked, is banned, or becomes invisible. Important fields: - `cfid` - `ctid` - often 0 when leaving server. - `reasonid` - `invokerid` - `invokername` - `invokeruid` - `reasonmsg` - `bantime` - `clid` #### `notifyclientmoved` Used for client channel movement. Important fields: - `clid` - `ctid` - `cfid` - `reasonid` - invoker fields when applicable. Deduplicate if the same move arrives from multiple subscriptions. #### `notifyserveredited` Includes changed server fields plus invoker metadata. YaTQA notes that not every changed server property necessarily appears in the event payload. #### `notifychanneledited`, `notifychannelcreated`, `notifychanneldeleted`, `notifychannelmoved` Use these to maintain a local channel tree. #### `notifytextmessage` Text message event. Distinguish server, channel, and private subscriptions. #### `notifytokenused` Useful when an external system provisions privilege keys and needs to detect consumption. ### 7.3 State model A robust state model: ```python class Ts3State: def __init__(self): self.server = {} self.channels = {} # cid -> channel object self.clients = {} # clid -> client object self.client_channel = {} # clid -> cid self.groups = {} self.permissions = {} ``` Do not store `clid` as a persistent user identifier. It is session-scoped. Use: - `client_unique_identifier` for identity-level persistence. - `client_database_id` for server-local account persistence. - `clid` only for currently connected sessions. *** ## 8. Permission System ### 8.1 Permission IDs and grant IDs TeamSpeak permission IDs changed historically. YaTQA explains that older versions encoded hierarchy in hex digits, but newer permission versions use mostly sequential IDs. Grant IDs are calculated by adding `32768` to the base permission ID in the modern scheme. ```python def grant_permission_id(permission_id: int) -> int: return permission_id + 32768 ``` Example: ```text base permission: i_channel_join_power grant permission: i_needed_modify_power_channel_join_power or base_id + 32768 ``` Always query current server permissions with `permissionlist` or use versioned declaration data. Do not hard-code old IDs if the server version is unknown. ### 8.2 Permission sources YaTQA describes effective permissions as a per-permission evaluation over multiple sources. The sources are, from broad to specific: 1. Server groups. 2. Client permissions at server level. 3. Channel permissions, unless skipped. 4. Channel group permissions, unless skipped. 5. Client permissions at channel level. Each permission is evaluated independently. ### 8.3 Skip and negate behavior Important flags: - `skip` - causes lower channel/channel-group layers to be skipped. - `negate` - causes a permission from one group to override other group contributions. Pseudo-evaluation: ```python def evaluate_permission(user, channel, perm): candidates = [] # 1. server group permissions sg_perms = collect_server_group_permissions(user.server_groups, perm) sg_effective = merge_group_permissions(sg_perms) if sg_effective: candidates.append(sg_effective) # 2. direct client permission on server client_server_perm = get_client_server_permission(user.dbid, perm) if client_server_perm: candidates.append(client_server_perm) skip_channel_layers = determine_skip_flag(sg_effective, client_server_perm) if not skip_channel_layers: # 3. channel permission ch_perm = get_channel_permission(channel.cid, perm) if ch_perm: candidates.append(ch_perm) # 4. channel group permission cg_perm = get_channel_group_permission(user.channel_group[channel.cid], perm) if cg_perm: candidates.append(cg_perm) # 5. direct client permission on channel client_channel_perm = get_client_channel_permission(user.dbid, channel.cid, perm) if client_channel_perm: candidates.append(client_channel_perm) return most_specific_applicable(candidates) ``` ### 8.4 Power and needed power Many actions use a power/needed-power pair: - Actor has `i_*_power`. - Target or object requires `i_*_needed_power`. - Action succeeds only if actor power >= needed power. Examples: - `i_client_kick_from_server_power` vs. target required kick power. - `i_channel_join_power` vs. `i_channel_needed_join_power`. - `i_group_member_add_power` vs. group needed member add power. ### 8.5 Upgrade behavior TeamSpeak permission upgrades may depend on metadata permissions such as `i_group_auto_update_type`. If you customize template or default groups, verify that permission upgrades still work as intended. ### 8.6 Recommended implementation Build a permission engine as a standalone library with: - Versioned permission constants. - Grant ID mapping. - Permission assignment records. - Effective permission evaluator. - Explanation trace. An explanation trace is critical for admin UI: ```json { "permission": "i_channel_join_power", "effective_value": 75, "source": "server_group", "group_id": 6, "skip": false, "negate": false, "required": 50, "allowed": true } ``` *** ## 9. Variable Parameters YaTQA's variable-parameters document is effectively a matrix showing which values appear in which commands and which values are editable. ### 9.1 Client variables Common client fields: | Field | Meaning | Typical commands | |---|---|---| | `clid` | Session-scoped client ID | `clientlist`, notify events | | `cid` | Current channel ID | `clientlist`, notify events | | `client_unique_identifier` | Stable identity UID | `clientlist -uid`, `clientinfo` | | `client_database_id` | Server-local client DB ID | `clientinfo`, `clientdbinfo` | | `client_nickname` | Display nickname | `clientlist`, `clientinfo`, `clientupdate`, `clientedit` | | `client_version` | Client version string | `clientinfo` | | `client_platform` | Platform string | `clientinfo` | | `client_input_muted` | Microphone muted state | voice/client state | | `client_output_muted` | Output muted state | voice/client state | | `client_away` | Away flag | `clientupdate` | | `client_away_message` | Away message | `clientupdate` | | `client_flag_avatar` | Avatar hash marker | `clientinfo` | | `client_icon_id` | Icon ID | `clientinfo` | | `client_badges` | Badge metadata string | notify events / client info | ### 9.2 Channel variables Common channel fields: | Field | Meaning | |---|---| | `cid` | Channel ID | | `pid` | Parent channel ID | | `channel_name` | Channel name | | `channel_topic` | Topic | | `channel_description` | Description | | `channel_password` | Password | | `channel_codec` | Codec | | `channel_codec_quality` | Codec quality | | `channel_maxclients` | Channel max clients | | `channel_maxfamilyclients` | Family max clients | | `channel_order` | Sort order | | `channel_flag_permanent` | Permanent channel flag | | `channel_flag_semi_permanent` | Semi-permanent channel flag | | `channel_flag_temporary` | Temporary flag | | `channel_needed_join_power` | Required join power | | `channel_icon_id` | Channel icon | ### 9.3 Server variables Common server fields: | Field | Meaning | |---|---| | `virtualserver_name` | Server display name | | `virtualserver_unique_identifier` | Server UID | | `virtualserver_port` | Voice port | | `virtualserver_maxclients` | Slot count | | `virtualserver_password` | Password | | `virtualserver_welcomemessage` | Welcome text | | `virtualserver_codec_encryption_mode` | Codec encryption mode | | `virtualserver_hostmessage` | Host message | | `virtualserver_hostmessage_mode` | Host message display mode | | `virtualserver_antiflood_points_tick_reduce` | Anti-flood decay per tick | | `virtualserver_antiflood_points_needed_command_block` | Command block threshold | | `virtualserver_antiflood_points_needed_ip_block` | IP block threshold | | `virtualserver_needed_identity_security_level` | Required identity security level | Implementation rule: visibility does not imply mutability. Use a command declaration table to validate edit operations. *** ## 10. Anti-Flood System YaTQA documents the voice-client and ClientQuery anti-flood system as a point budget. ### 10.1 Core algorithm Each client has flood points. - Client starts at 0 points. - Every 0.5 seconds, subtract `virtualserver_antiflood_points_tick_reduce` points. - Points never go below 0. - Each action adds a command-specific point cost. - If points reach/exceed `virtualserver_antiflood_points_needed_command_block`, commands are blocked. - If points reach/exceed `virtualserver_antiflood_points_needed_ip_block`, the IP is blocked. Pseudo-code: ```python def apply_action(client, action_cost, server_config): if not client.has_permission("b_client_ignore_antiflood"): client.flood_points += action_cost if (action_cost >= 0 and not client.has_permission("b_client_ignore_antiflood") and client.flood_points >= server_config.command_block_threshold): raise CommandBlocked("flood prevention") execute_action() ``` Decay: ```python def antiflood_tick(client, tick_reduce): client.flood_points = max(0, client.flood_points - tick_reduce) ``` ### 10.2 Important costs Examples from YaTQA: | Action | Approximate flood points | |---|---:| | Connect to server | 80, then removed after connection establishment | | `channelsubscribe` | 15 | | `channelunsubscribe` | 5 | | `channelsubscribeall` | 20 | | `channelunsubscribeall` | 25 | | `banadd` | 25 | | `banclient` | 25 | | `bandel` | 5 | | `bandelall` | 5 | | `complainadd` | 25 | | `complaindel` | 5 | | `complaindelall` | 25 | | `permissionlist` if not cached | 5 | | `permoverview` | 5 | | `servergroupaddperm` | 5 | ### 10.3 Practical design For a Query or ClientQuery app: - Add a command scheduler. - Track approximate point cost per command class. - Use a minimum delay for bulk operations. - Retry only after backoff when receiving error 524. - Prefer cached data over repeated heavy queries. - Avoid `channelsubscribeall` loops in large servers unless necessary. Example scheduler: ```python class FloodBudget: def __init__(self, threshold=75, tick_reduce=5): self.points = 0 self.threshold = threshold self.tick_reduce = tick_reduce self.last_tick = time.time() def wait_for_budget(self, cost): while self.estimated_points_after_decay() + cost >= self.threshold: time.sleep(0.5) self.points += cost ``` *** ## 11. Identity and Security Level ### 11.1 Identity model A TeamSpeak identity contains a keypair and a 64-bit offset/nonce. The public key contributes to the client unique identifier. The security level is a Hashcash-like proof-of-work value. ### 11.2 Security level calculation YaTQA describes the security level as the number of leading zero bits in a SHA-1 hash computed from: ```text public_key + decimal_string(unsigned_64_bit_number) ``` Important bit-order detail from YaTQA: - SHA-1 result is 160 bits. - Byte order is big-endian. - Bit order within a byte is treated little-endian for counting in TeamSpeak's definition. Conceptual algorithm: ```python import hashlib def security_hash(public_key: str, offset: int) -> bytes: payload = (public_key + str(offset)).encode("ascii") return hashlib.sha1(payload).digest() def count_leading_zero_bits_teamspeak(digest: bytes) -> int: count = 0 for b in digest: # TeamSpeak's described bit order is unusual; implement and test carefully. for bit_index in range(8): if (b >> bit_index) & 1 == 0: count += 1 else: return count return count ``` ### 11.3 Search strategy To increase a security level: 1. Keep the identity keypair fixed. 2. Iterate candidate offsets. 3. Compute SHA-1 for each offset. 4. Count leading zero bits. 5. Stop when required level is reached. Parallel strategy: ```python # Split the u64 search space into thread-local ranges. # Each worker reports best offset found so far. # Stop when target level found if stop-after-first is enabled. ``` `MahTsIdentity` demonstrates this kind of workflow with Rust dependencies such as `rayon`, `clap`, `sha-1`, and ReSpeak protocol crates. ### 11.4 Vanity UID search A vanity UID search tries to find an identity whose UID matches a pattern. `MahTsIdentity` supports wildcard-like matching where `_` acts as a wildcard. Conceptual usage: ```bash mah_ts_identity MY_UID_PATTERN mah_ts_identity AB_CD_ --threads 8 mah_ts_identity --bench MY_UID mah_ts_identity -i "IDENTITY_STRING" --level 32 mah_ts_identity --export -i "IDENTITY_STRING" ``` ### 11.5 Security guidance - Never log private identity data. - Treat exported identities like passwords. - Run level searches offline or in background workers. - Do not perform identity proof-of-work in a request path. - Benchmark hardware before promising target levels. *** ## 12. Snapshot Format and Backup ### 12.1 What snapshots include YaTQA documents snapshots as text representations of virtual servers. Included: - Virtual server settings except port. - Keypair-related server identity data. - Channel tree. - Client database entries. - Local server groups and channel groups. - Client, channel, and group permissions. - Group assignments. Not included: - Server port. - Uploaded files. - Icons and avatars as file content. - Bans. - Complaints. - Offline messages, though unread counts may remain. ### 12.2 Snapshot structure A snapshot begins with: ```text hash=| ``` The hash is a Base64-encoded SHA-1 of the data after the `|` separator. Verification: ```python import base64, hashlib def verify_snapshot(snapshot: str) -> bool: prefix, data = snapshot.split("|", 1) assert prefix.startswith("hash=") expected = prefix[len("hash="):] actual = base64.b64encode(hashlib.sha1(data.encode("utf-8")).digest()).decode("ascii") return expected == actual ``` Rehash: ```python def rehash_snapshot(data: str) -> str: h = base64.b64encode(hashlib.sha1(data.encode("utf-8")).digest()).decode("ascii") return f"hash={h}|{data}" ``` ### 12.3 Server fields in snapshots Common snapshot fields mirror `serverinfo`, for example: - `virtualserver_unique_identifier` - `virtualserver_name` - `virtualserver_welcomemessage` - `virtualserver_maxclients` - `virtualserver_password` - `virtualserver_created` - `virtualserver_codec_encryption_mode` - `virtualserver_keypair` - `virtualserver_hostmessage` - `virtualserver_hostmessage_mode` - `virtualserver_default_server_group` - `virtualserver_default_channel_group` - `virtualserver_default_channel_admin_group` - `virtualserver_max_download_total_bandwidth` - `virtualserver_max_upload_total_bandwidth` - `virtualserver_antiflood_points_tick_reduce` - `virtualserver_antiflood_points_needed_command_block` - `virtualserver_antiflood_points_needed_ip_block` Dynamic runtime values such as current online clients are not meaningful backup state. ### 12.4 Backup strategies | Strategy | Use case | Captures files? | Requires host access? | |---|---|---:|---:| | Snapshot | Logical copy of one virtual server | No | No | | YaTQA no-snapshot backup | Tenant-level rescue with limited permissions | Partial/Pro-dependent | No | | Full machine copy | Disaster recovery or exact migration | Yes | Yes | Full machine migration should include: - `ts3server.sqlitedb` - `files/*` - `query_ip_allowlist` or equivalent allow file - `query_ip_denylist` or equivalent deny file - server `.ini` files - `tsdns/tsdns_settings.ini` if TSDNS is used ### 12.5 Snapshot security Snapshot deployment can bypass ordinary per-setting permission checks if the user has snapshot deployment permission. Treat snapshot files as privileged artifacts. *** ## 13. File Transfer TeamSpeak file transfer is not simply ServerQuery over TCP. Query commands initialize uploads/downloads and return file-transfer parameters such as file transfer key, host, and port. The actual file data is then transferred over the file-transfer TCP port. Typical flow: 1. Use Query command such as `ftinitupload` or `ftinitdownload`. 2. Receive transfer key and connection details. 3. Open file-transfer TCP connection. 4. Send/use the transfer key as expected by the file-transfer protocol. 5. Stream bytes. 6. Confirm success or error. Implementation advice: - Keep file-transfer code separate from Query command code. - Apply path escaping rules to remote file paths. - Be careful with channel-specific file paths and permissions. - Snapshots do not include file content, so file transfer is required for full backups. *** ## 14. Identifier Algorithms ### 14.1 Avatar filenames YaTQA's web tools and definitions document explain avatar filename conversion. Conceptually, avatar filenames use the client UID transformed into a hash-like filename with an `avatar_` prefix. Practical rule: ```text avatar filename = "avatar_" + encoded/hash form of client_unique_identifier ``` Some YaTQA notes refer to TeamSpeak-specific base64-decoding and `[a-p]` alphabet rendering, while the web tool summary presents the avatar filename as `avatar_` plus a hash of the UID. Therefore, when exact binary compatibility is required, test against real files created by the official client and keep the algorithm isolated behind tests. ### 14.2 Icon filenames and IDs Icons are identified by 32-bit numbers. The same bit pattern can appear as signed or unsigned. ```python def to_signed_32(x): x &= 0xffffffff return x if x < 0x80000000 else x - 0x100000000 def to_unsigned_32(x): return x & 0xffffffff ``` Use this for server, channel, and client icon ID handling. ### 14.3 Cache directory names Server cache and chat directory names are based on the server UID. YaTQA's cache tool describes this as essentially Base64 encoding/decoding. ```python import base64 def server_uid_to_cache_dir(uid: str) -> str: return base64.b64encode(uid.encode("utf-8")).decode("ascii") ``` Test against real `%APPDATA%/TS3Client/cache` entries when strict client compatibility is needed. *** ## 15. DNS, SRV, and TSDNS ### 15.1 Recommended SRV records Use `_ts3._udp` for voice service discovery: ```dns _ts3._udp.example.com. 86400 IN SRV 0 5 9987 voice.example.com. ``` If using TSDNS indirection: ```dns _tsdns._tcp.example.com. 86400 IN SRV 0 5 41144 tsdns.example.com. ``` ### 15.2 Resolution strategy A robust resolver should attempt: 1. Direct host:port if the user provides a port. 2. `_ts3._udp.` SRV lookup. 3. TSDNS lookup if configured or required. 4. A/AAAA fallback to default port 9987. YaTQA notes that implicit historical TSDNS behavior changed across client versions. Do not rely on old fallback behavior. Configure SRV explicitly when possible. ### 15.3 TSDNS purpose TSDNS maps human-friendly names to TeamSpeak server addresses and ports. It is useful when: - The voice server uses a non-default port. - You want multiple named TS3 servers under one domain. - You need indirection outside normal SRV behavior. ### 15.4 YaTQA "How to DNS" decision chart - written specification The YaTQA resources page includes a German flowchart titled **"Welche Art von DNS passt zu deinem TeamSpeak-3-Server?"** ("Which type of DNS fits your TeamSpeak 3 server?"). The chart is an operational decision tree for choosing between plain A/AAAA/CNAME records, `_ts3._udp` SRV records, simple TSDNS, SRV-assisted TSDNS, or subdomain-based layouts. This subsection writes that chart down explicitly so implementers and operators do not have to rely on the image. The original resource is a graphical PNG flowchart. In this manual, the decision chart is deliberately **not embedded as a PNG**. It is represented as native document content: a decision table, a numbered decision algorithm, and DNS templates. This keeps the manual searchable, editable, accessible to screen readers, and usable in generated DOCX/PDF formats without relying on a raster image. #### 15.4.0 Text-native decision chart | Node | Question / condition | Yes branch | No branch | Recommendation if terminal | |---|---|---|---|---| | 0 | Are you trying to use a complicated SRV-TSDNS setup without a concrete need? | Do not do it; use simple TSDNS or a simpler DNS method. | Continue. | Avoid unnecessary SRV-TSDNS. | | 1 | How many TeamSpeak virtual servers do you operate under the relevant name/domain? | One virtual server -> Node 2. | More than one virtual server -> Node 6. | - | | 2 | For one virtual server: does the server use the standard TS3 voice port `9987`? | Node 3. | Node 4. | - | | 3 | Standard port: do all services under the relevant domain run on the same machine as the TS3 server? | Use A/AAAA or CNAME. | Use `_ts3._udp` SRV. | - | | 4 | Non-standard port: are users willing to type a port number manually? | Node 5. | Use `_ts3._udp` SRV. | - | | 5 | Manual port accepted: do all services under the relevant domain run on the same machine as the TS3 server? | Use A/AAAA or CNAME and tell users to connect as `host:port`. | Use `_ts3._udp` SRV. | - | | 6 | Multiple virtual servers: are users willing to type port numbers manually? | Node 7. | Use subdomains with `_ts3._udp` SRV records for every subdomain. | - | | 7 | Multiple servers with manual ports: on how many machines are the TS3 servers operated? | One machine -> Node 8. | Multiple machines -> use subdomains with A/AAAA or CNAME per server host. | - | | 8 | One machine: do all services under the relevant domain run on the same machine as the TS3 server? | Use A/AAAA or CNAME and explicit ports for non-default virtual servers. | Use SRV TSDNS if you deliberately operate TSDNS indirection. | - | Text-only flow representation: ```text START | +-- Needlessly complicated SRV-TSDNS idea? | +-- yes -> Do not use it; prefer simple TSDNS, SRV TS3, or A/AAAA/CNAME. | +-- no -> Continue. | +-- How many virtual servers? | +-- One virtual server | | | +-- Uses standard port 9987? | | | +-- yes | | +-- All services under this domain run on the same machine? | | +-- yes -> Use A/AAAA or CNAME. | | +-- no -> Use SRV TS3 (`_ts3._udp`). | | | +-- no | +-- Are users willing to type the port manually? | +-- no -> Use SRV TS3 (`_ts3._udp`). | +-- yes | +-- All services under this domain run on the same machine? | +-- yes -> Use A/AAAA or CNAME plus `host:port`. | +-- no -> Use SRV TS3 (`_ts3._udp`). | +-- More than one virtual server | +-- Are users willing to type port numbers? | +-- no -> Use subdomains with SRV TS3 for every subdomain. | +-- yes +-- Are TS3 servers operated on one machine or several? +-- one machine | +-- All services under this domain run on the TS3 machine? | +-- yes -> Use A/AAAA or CNAME, explicit ports where needed. | +-- no -> Use SRV TSDNS. | +-- several machines -> Use subdomains with A/AAAA or CNAME per server. ``` #### 15.4.1 Vocabulary used by the chart | Chart wording | Meaning in English | Engineering interpretation | |---|---|---| | `A/AAAA oder CNAME` | A/AAAA or CNAME | Use normal DNS host records. This works best when users connect to a hostname whose resolved host is the TS3 host, and either the server uses the default UDP voice port `9987` or users are willing to type a port manually. | | `SRV TS3` | TS3 SRV record | Use `_ts3._udp.` SRV records so the client can discover the target host and voice port automatically. This is the preferred modern solution for non-default ports. | | `TSDNS` | TeamSpeak DNS helper | Use TeamSpeak's TSDNS service, usually on TCP `41144`, to map a user-visible name to a TeamSpeak endpoint. | | `SRV TSDNS` | SRV-assisted TSDNS | Use `_tsdns._tcp.` to locate the TSDNS service, then ask TSDNS for the final TeamSpeak endpoint. This is useful when TSDNS itself is not hosted at the obvious domain host. | | `Subdomains` | One DNS name per server | Create a distinct subdomain such as `alpha.example.com`, `beta.example.com`, `guild.example.com`, etc., and then apply A/AAAA/CNAME or SRV records per subdomain. | | `Portnummern` | Port numbers | Whether users are willing to type `host:port` manually in the TeamSpeak client. | #### 15.4.2 Decision tree translated from the image Start with the question: **How many TeamSpeak virtual servers do you have?** 1. **Do not use this complex setup unless you need it.** - The chart contains an explicit warning path: **"Tu's nicht!"** ("Don't do it!"). - If you are trying to use SRV-TSDNS only because it looks advanced, the chart recommends: **Use simple TSDNS without SRV TSDNS**. - Practical interpretation: do not introduce SRV-TSDNS unless you actually need indirection for a TSDNS service. For a normal server, `_ts3._udp` or A/AAAA/CNAME is simpler and easier to debug. 2. **If you have exactly one virtual server:** 1. Ask: **Does the server use the standard port `9987`?** - **Yes:** ask whether all services under the relevant domain run on the same machine as the TS3 server. - **Yes:** use **A/AAAA or CNAME**. - **No:** use **SRV TS3**. - **No:** ask whether you are willing for users to type the port manually. - **Yes:** ask whether all services under the relevant domain run on the same machine as the TS3 server. - **Yes:** use **A/AAAA or CNAME**, but users must connect with `host:port` unless another discovery mechanism is added. - **No:** use **SRV TS3**. - **No:** use **SRV TS3**. 3. **If you have more than one virtual server:** 1. Ask: **Do you like port numbers?** In other words, are users expected to type ports manually? - **No:** use **subdomains with SRV TS3 for every subdomain**. - Example: `alpha.example.com`, `beta.example.com`, and `staff.example.com` each get their own `_ts3._udp` SRV record pointing to the correct host and port. - **Yes:** ask on how many machines the TS3 servers are operated. - **Only one machine:** ask whether all services under the relevant domain run on the same machine as the TS3 server. - **Yes:** use **A/AAAA or CNAME** and require users to specify ports for non-default virtual servers. - **No:** use **SRV TSDNS**. - **More than one machine:** use **subdomains with A/AAAA or CNAME for each server**. - Practical interpretation: each physical server gets a hostname, and users either use default ports or explicit `host:port` entries unless SRV is added per subdomain. #### 15.4.3 DNS record templates for each chart recommendation **A/AAAA record - IPv4/IPv6 host mapping** ```dns ts3.example.com. 3600 IN A 203.0.113.10 ts3.example.com. 3600 IN AAAA 2001:db8::10 ``` Use this when the TeamSpeak service is on the host represented by the name and the port situation is simple. If the server is not on `9987`, users must connect with `ts3.example.com:PORT` unless SRV or TSDNS is also used. **CNAME - alias to another hostname** ```dns ts3.example.com. 3600 IN CNAME voice-host.provider.example. ``` Use this when you do not want to expose or maintain the provider's long hostname. Do not point a CNAME directly at an IP address; CNAME targets must be names. **SRV TS3 - recommended for non-default ports** ```dns _ts3._udp.example.com. 3600 IN SRV 0 5 9987 voice1.example.net. _ts3._udp.alpha.example.com. 3600 IN SRV 0 5 9987 host-a.example.net. _ts3._udp.beta.example.com. 3600 IN SRV 0 5 9990 host-a.example.net. ``` Use this when users should type only the domain or subdomain, while DNS tells the client which host and UDP port to connect to. The SRV target should be a hostname with its own A/AAAA records. **Simple TSDNS - without SRV TSDNS** ```text # Conceptual TSDNS mapping, not a DNS-zone record: example.com=voice1.example.net:9987 alpha.example.com=voice1.example.net:9990 ``` Use this only when you deliberately operate a TSDNS service and understand its client-version compatibility. For most modern deployments, `_ts3._udp` SRV records are easier to operate and easier to test. **SRV TSDNS - locate the TSDNS service through DNS SRV** ```dns _tsdns._tcp.example.com. 3600 IN SRV 0 5 41144 tsdns.example.net. ``` Use this when the TSDNS service is not hosted at the obvious domain target or when domain-level routing must be delegated to a separate TSDNS endpoint. The TSDNS service then returns the actual TeamSpeak host and port. **Subdomains with SRV TS3** ```dns _ts3._udp.public.example.com. 3600 IN SRV 0 5 9987 host1.example.net. _ts3._udp.staff.example.com. 3600 IN SRV 0 5 9991 host1.example.net. _ts3._udp.events.example.com. 3600 IN SRV 0 5 9987 host2.example.net. ``` This is the chart's cleanest recommendation for multiple virtual servers when users should not type port numbers. **Subdomains with A/AAAA or CNAME for each server** ```dns server1.example.com. 3600 IN A 203.0.113.10 server2.example.com. 3600 IN A 203.0.113.11 server3.example.com. 3600 IN CNAME rented-ts3.provider.example. ``` Use this for multiple physical hosts when manual ports are acceptable or when each subdomain maps clearly to one machine. #### 15.4.4 Implementation guidance for a resolver or admin UI A TeamSpeak administration tool should expose the chart as a wizard instead of forcing the operator to understand every DNS mechanism. A practical UI can ask exactly these questions: 1. How many virtual servers do you operate under this domain? 2. Does the selected server use UDP voice port `9987`? 3. Are users allowed to type a port manually? 4. Do web, mail, and other services for this domain run on the same host as TeamSpeak? 5. Are the TeamSpeak servers on one physical host or multiple physical hosts? 6. Do you already operate a TSDNS service? The wizard can then emit either plain DNS records, SRV records, or a TSDNS mapping. Validate the output with these checks: - Every SRV target has at least one A or AAAA record. - SRV targets are hostnames, not IP literals. - `_ts3._udp` records use UDP and the actual voice port. - `_tsdns._tcp` records use TCP and normally port `41144`. - If using A/AAAA/CNAME only with a non-default port, the documentation shown to users must include `host:port`. - If using multiple virtual servers on one IP, either use ports explicitly or use SRV records per subdomain. #### 15.4.5 Quick recommendation table | Scenario | Recommended solution | User enters | |---|---|---| | One virtual server, default port, TS3 host is also the domain host | A/AAAA or CNAME | `example.com` or `ts3.example.com` | | One virtual server, default port, TS3 host differs from domain host | SRV TS3 | `example.com` | | One virtual server, non-default port, users accept manual port | A/AAAA/CNAME plus documented port | `example.com:9990` | | One virtual server, non-default port, users should not type port | SRV TS3 | `example.com` | | Multiple virtual servers, users should not type ports | Subdomains with SRV TS3 | `alpha.example.com`, `beta.example.com` | | Multiple virtual servers on one host, users accept ports | A/AAAA/CNAME plus documented ports | `example.com:9987`, `example.com:9990` | | Multiple physical hosts, simple host naming desired | Subdomains with A/AAAA or CNAME | `server1.example.com`, `server2.example.com` | | Advanced delegation to a separate name-resolution service | SRV TSDNS | `example.com` or service-specific names | *** ## 16. TeamSpeak 3 Auxiliary Protocols - Complete YaTQA `protokolle` Coverage This chapter expands the earlier protocol notes into a self-contained engineering specification derived from YaTQA's `Protokolle von TeamSpeak 3` resource. The YaTQA page explicitly states that it covers the protocols TeamSpeak uses **besides Query and voice**. These protocols are not optional knowledge if you want to build a real TS3-compatible ecosystem: update checking, file transfer, address resolution, blacklists, public server listing, badges, and several metadata formats all depend on them. The protocols covered here are: - TeamSpeak's restricted Protobuf/varint usage. - Historical and current update metadata formats. - File transfer channel bootstrap. - TSDNS and DNS/SRV resolution behavior. - Server nicknames introduced in client 3.1.6+. - Blacklist v1 and Blacklist2. - Weblist server reporting and client querying. - Badge list format and badge asset resolution. - TLD/public-suffix handling differences across client versions. ### 16.1 Protobuf dialect used by TeamSpeak YaTQA describes a small subset of Protocol Buffers used in several TeamSpeak metadata files and service requests. The implemented data types are effectively: | Wire type | Meaning | Implementation use | |---:|---|---| | 0 | BigNum / Varint | Unsigned integer, status enum, timestamp, port, revision. | | 1 | 64-bit fixed integer | Recognized by Protobuf; YaTQA notes TeamSpeak does not generally use all standard Protobuf types. | | 2 | Length-delimited bytes | Strings, binary IP addresses, nested Protobuf records. | | 5 | 32-bit fixed integer | Available wire type. | | 3,4 | Deprecated | Do not generate. | | 6,7 | Free/unused | Do not generate. | #### 16.1.1 BigNum / Varint writer TeamSpeak's BigNum is identical to the common unsigned varint encoding: write the low seven bits per byte; set the high bit when another byte follows. ```pseudo function write_big_num(x: unsigned integer) -> bytes: out = [] while true: if x < 128: out.append(byte(x)) return out out.append(byte((x & 0x7F) | 0x80)) x = x >> 7 ``` #### 16.1.2 BigNum / Varint reader ```pseudo function read_big_num(buffer, offset=0) -> (value, new_offset): value = 0 shift = 0 pos = offset while true: b = buffer[pos] pos += 1 value = value | ((b & 0x7F) << shift) if (b & 0x80) == 0: return value, pos shift += 7 if shift > MAX_SUPPORTED_BITS: raise DecodeError('varint too large') ``` Implementation requirements: - Reject unterminated varints when input ends before a byte with high bit clear. - Limit maximum width to the integer type your implementation supports. - Do not interpret BigNum as signed unless the specific higher-level field says so. #### 16.1.3 Length-delimited binary field A length-delimited field is: ```text BigNum length || length bytes of payload ``` The payload is often UTF-8 text, sometimes binary data, and sometimes another nested Protobuf message. Do not assume text unless the field definition says it is text. #### 16.1.4 File/message structure A TeamSpeak Protobuf-like stream is a repeated sequence of: ```text field_key || field_value ``` The field key itself is a BigNum. Decode it as: ```pseudo wire_type = field_key & 7 field_number = field_key >> 3 ``` Repeated fields are allowed. YaTQA notes that TeamSpeak assigns the field number once, but repeated identifiers occur when arrays/lists are stored. Your decoder must therefore store fields as `field_number -> list(values)` rather than assuming each field appears once. #### 16.1.5 Minimal generic decoder skeleton ```pseudo function decode_ts_protobuf(buffer): offset = 0 result = map>() while offset < len(buffer): key, offset = read_big_num(buffer, offset) field_number = key >> 3 wire_type = key & 7 if wire_type == 0: value, offset = read_big_num(buffer, offset) else if wire_type == 1: value = read_u64_le(buffer, offset) offset += 8 else if wire_type == 2: n, offset = read_big_num(buffer, offset) value = buffer[offset : offset + n] offset += n else if wire_type == 5: value = read_u32_le(buffer, offset) offset += 4 else: raise DecodeError('unsupported/deprecated TeamSpeak wire type') result[field_number].append(Value(wire_type, value)) return result ``` ### 16.2 Update protocol and update metadata TeamSpeak's update protocol changed over time. An implementation that needs compatibility with old clients or needs to reproduce historical behavior should understand all generations, but a modern application should prefer HTTPS-downloaded version metadata and signature validation rather than the oldest UDP-only mechanisms. #### 16.2.1 Historical UDP update probes YaTQA describes three historical update query styles: | Client generation | Outbound request | Destination | Response style | |---|---|---|---| | Up to 3.0.0-beta7 | UDP payload `1` | `update.teamspeak.com:17384` | Unknown response format. | | 3.0.0-beta8 era | UDP payload `2` | `update.teamspeak.com:17384` | String like `1321432557,1,1,1`; first field is version timestamp/build, remaining fields are flags, likely validity booleans for stable/beta/alpha. | | Around client 3.0.3 | UDP payload `3` | `update.teamspeak.com:17384` | Comma-separated string containing stable, beta, alpha client versions and an old server version; each record stores timestamp and name separated by `#`; final field is `29`. | #### 16.2.2 Current metadata file structure Later clients download a version metadata file from TeamSpeak version endpoints. YaTQA states that these files use the Protobuf dialect described above. The logical structure is: ```text field 1: BigNum, always 2 field 2: repeated length-delimited nested record, one per version category nested field 1: length-delimited string = version category name nested field 2: BigNum = build number / build timestamp nested field 3: length-delimited string = human version string field 3: BigNum, always 2 ``` A robust parser should not hard-code exactly three version categories. It should parse all field-2 nested entries and expose them by category name. ```pseudo function parse_version_metadata(bytes): root = decode_ts_protobuf(bytes) records = [] for nested_bytes in root[2]: n = decode_ts_protobuf(nested_bytes.bytes) records.append({ 'name': utf8(n[1][0].bytes), 'build': n[2][0].varint, 'version': utf8(n[3][0].bytes), }) return records ``` #### 16.2.3 Updater image compression YaTQA notes that updater image files may appear to use `compress` from their extension, but the file format is actually gzip. Later names suggest LZMA2, but 7-Zip reportedly cannot open them, while INI references still point to gzip. Treat update image decompression as a versioned compatibility concern: 1. Identify the update file generation. 2. Try gzip when the generation matches the documented old/current updater format. 3. Reject unknown formats rather than guessing. 4. Verify digital signatures or hashes before installing. #### 16.2.4 Anydate engineering lesson YaTQA's Anydate tool exists because the official updater only updates to the newest available version. A version-switching development utility should therefore not rely on the official updater UX. It should: - Fetch version metadata. - Select exact target version by platform and architecture. - Download the corresponding archive/image. - Extract into a test directory, not over the user's production client. - Require explicit user confirmation before touching profile/AppData directories because old clients can corrupt newer client data. ### 16.3 File transfer protocol TeamSpeak file transfer is bootstrapped by ServerQuery/ClientQuery commands but the actual file bytes are transferred on a separate connection. #### 16.3.1 Bootstrap sequence Upload: ```text 1. Control channel: ftinitupload ... 2. Server returns: token/key + IP + port + other transfer metadata. 3. Client opens TCP connection to returned IP:port. 4. Client writes exactly the received key/token bytes. 5. Client immediately writes raw file data. ``` Download: ```text 1. Control channel: ftinitdownload ... 2. Server returns: token/key + IP + port + file size metadata. 3. Client opens TCP connection to returned IP:port. 4. Client writes exactly the received key/token bytes. 5. Server streams raw file bytes to client. ``` Critical details: - The key is sent exactly as received. - No escaping is performed. - Nothing is prefixed before the key. - Nothing is appended after the key before file data begins. - After the key, upload sends bytes; download receives bytes. #### 16.3.2 Transfer implementation skeleton ```pseudo function upload_file(control, local_path, remote_path, channel_id): meta = control.command('ftinitupload', { 'clientftfid': allocate_transfer_id(), 'name': remote_path, 'cid': channel_id, 'size': file_size(local_path), }) socket = tcp_connect(meta.host, meta.port) socket.write(meta.ftkey.raw_bytes) stream_file_to_socket(local_path, socket) socket.close() function download_file(control, remote_path, local_path, channel_id): meta = control.command('ftinitdownload', { 'clientftfid': allocate_transfer_id(), 'name': remote_path, 'cid': channel_id, }) socket = tcp_connect(meta.host, meta.port) socket.write(meta.ftkey.raw_bytes) stream_socket_to_file(socket, local_path, expected_size=meta.size) ``` #### 16.3.3 Engineering cautions - File transfer port is separate from the voice port and raw ServerQuery port. Do not reuse your Query socket. - Treat the key/token as binary-safe data even if it looks printable. - A partial upload may appear in directory listings with incomplete size metadata until finalized. - File transfer paths are TeamSpeak virtual paths. Do not map them directly to local filesystem paths without sanitization. ### 16.4 TSDNS protocol TSDNS is TeamSpeak's name-to-server resolution helper. It is not the same as DNS SRV, though DNS SRV can point to a TSDNS server. #### 16.4.1 Wire format To query a TSDNS server: ```text lowercase(input_name) encode as UTF-8 or CESU-8 append bytes: 0x0A 0x0D 0x0D 0x0D 0x0A send over TCP to port 41144 ``` In escaped form: ```text \n\r\r\r\n ``` The response is text. Possible meanings: | Response | Meaning | |---|---| | `host:port` or IP/host result | Use this resolved target. | | `$PORT` as returned port | Preserve the port specified by the user instead of overriding it. | | `404` | No result known or deliberately configured as missing. | | No response | Client continues the resolution hierarchy in non-SRV TSDNS modes; in SRV TSDNS this can cause overall failure if only one TSDNS server exists. | #### 16.4.2 Query algorithm ```pseudo function query_tsdns(tsdns_host, input_name): q = lowercase_unicode(input_name) payload = encode_utf8_or_cesu8(q) + bytes([0x0A,0x0D,0x0D,0x0D,0x0A]) s = tcp_connect(tsdns_host, 41144, timeout=5s) s.write(payload) response = s.read_until_close_or_timeout() if response == '404': return NotFound return parse_host_port_or_port_token(response) ``` #### 16.4.3 When to use TSDNS YaTQA's practical conclusion is that plain TSDNS alone is not very useful. The useful case is **SRV TSDNS**: when you host other services such as a website on the domain, and multiple TeamSpeak servers with different ports are hosted elsewhere or need domain-level routing. ### 16.5 Server nicknames, client 3.1.6+ TeamSpeak client 3.1.6 introduced server nicknames. A nickname is used when the user enters a name without dots. Instead of treating it as a local computer name, the client calls: ```text https://named.myteamspeak.com/lookup?name= ``` Important behavior: - HTTPS is required. HTTP is not accepted. - The lookup is case-insensitive on the myTeamSpeak service. - The response is a UTF-8 domain string. - A port may be appended with `:`. - If no port is returned, the default voice port 9987 is assumed. - After nickname lookup returns a domain, the normal TeamSpeak address-resolution pipeline runs as though the user typed that domain. - Edits to nickname entries may take time to become effective. Implementation strategy: ```pseudo function resolve_server_nickname(input): if '.' in input: return input response = https_get('https://named.myteamspeak.com/lookup?name=' + url_encode(input)) if response.ok and response.body is nonempty: return utf8(response.body) return input // or fail, depending on client policy ``` ### 16.6 DNS, SRV, and address resolution TeamSpeak address resolution has changed repeatedly and is not a perfect RFC implementation. YaTQA's key observation is that the client fires several lookups at once, waits up to about five seconds, and uses the first complete solution in a fixed priority order. #### 16.6.1 Resolution priority The priority order is: 1. **SRV TS3**: query `_ts3._udp.`. The SRV result's port overrides the user's typed port. 2. **SRV TSDNS**: query `_tsdns._tcp.`, where the candidate domain depends on the client version and public-suffix logic. 3. **TSDNS**: query candidate domains on TCP port 41144. 4. **Plain DNS**: query AAAA/A records for the input domain. CNAME is only followed implicitly by the resolver. Once a complete solution is found, no later resolution method is tried if the actual connection fails. Do not implement address-resolution fallback as "try next method after connection failure" unless intentionally deviating from TeamSpeak behavior. #### 16.6.2 SRV record rules A TS3 SRV record typically looks like: ```dns _ts3._udp.example.com. 3600 IN SRV 0 5 9987 voice.example.com. ``` YaTQA notes the following compatibility quirks: - Older TS3 behavior did not fully support all SRV features such as weight/priority/dot semantics; later behavior changed. - SRV targets should be hostnames, not IP addresses. TS3 3.0 accepted IP targets incorrectly; TS3 3.1 fixed IP targets but still accepted CNAME targets although this is also not strictly RFC-correct. - SRV is the easiest way to specify a non-default port. Recommended engineering behavior: - Prefer `_ts3._udp` for normal modern deployments. - Treat SRV priority and weight according to standard DNS rules in your own implementation unless strict old-client compatibility is required. - Reject SRV records pointing directly to IP addresses if implementing standards-compliant behavior; optionally add a compatibility flag to accept them. #### 16.6.3 DNS/TSDNS version matrix YaTQA includes a version matrix. Rewritten as implementation guidance: | Client version | A/AAAA behavior | SRV behavior | TSDNS behavior | Local computer names | |---|---|---|---|---| | Up to 3.0.0-beta37 | ASCII lowercase; limited behavior | No SRV support in early rows | TSDNS partially supported | Yes | | 3.0.0-rc1 | ASCII lowercase + UTF-8 encoding in parts | Supported | Supported | Yes | | 3.0.0-rc2 to 3.0.7 | Punycode for DNS A; UTF-8/ASCII lowercase elsewhere | Supported | Supported | Yes | | 3.0.8 to 3.0.16 | Punycode for A/AAAA; lowercase/UTF-8 transformations for SRV/TSDNS | Supported | Supported | Yes | | 3.0.17 to 3.0.19.4 | Punycode and Unicode lowercase paths | Supported | Supported | Yes | | 3.0.20 | UTF-8 after lowercase for A/SRV/TSDNS; no AAAA lookup except literal IPv6 | SRV TSDNS only | Plain TSDNS removed | No | | 3.1-beta1 to 3.1-beta2 | UTF-8 after lowercase | SRV TSDNS only | Plain TSDNS removed | No | | 3.1-beta3 | Punycode restored for A/SRV, lowercase+UTF-8 for TSDNS | Warning/changed behavior for IP validity | Plain TSDNS behavior changed | No | | 3.1-beta4 and later | Punycode after lowercase for DNS and SRV; lowercase+UTF-8 plus TSDNS suffix | Modern style | Modern style | No | Interpretation for a new implementation: - Use standard IDNA/Punycode for DNS names. - Lowercase before DNS queries to match TS behavior. - Encode TSDNS query body as UTF-8 unless deliberately emulating CESU-8-era clients. - Do not resolve bare local computer names as TS3 server names in modern mode. #### 16.6.4 Candidate domains for SRV TSDNS and TSDNS Version-dependent candidate selection: | Client generation | Candidate generation | |---|---| | Up to 3.0.19.4 | Build a list of up to the four highest levels of the input domain and query from the lower level upward. Public-suffix/TLD entries are skipped but still count toward the four-level limit. A bare top-level domain may still be queried through SRV even if listed. | | 3.0.20 to 3.1-beta1 | Plain TSDNS removed. SRV TSDNS queries the second-level domain of the input. This fails for domains delegated only at the third level. Bare top-level domains produce no SRV lookup. | | 3.1-beta2 | Plain TSDNS removed. SRV TSDNS queries the highest input level that is not in the embedded public-suffix list. | | 3.1-beta3 and later | TSDNS and SRV TSDNS query the two highest levels of the input domain that are not in the public-suffix list, beginning with the highest level. | #### 16.6.5 Choosing the correct DNS system YaTQA gives a decision table. Rewritten for engineering use: | Scenario | Services on same IP | Services on different IPs | |---|---|---| | One TS3 server on standard port | A or CNAME is sufficient. | SRV TS3 is preferred. | | One TS3 server on non-standard port, user should not type port | SRV TS3. | SRV TS3. | | Multiple TS3 servers or one non-standard port server where port indication is acceptable | A or CNAME plus explicit port can work. | SRV TSDNS if you need the same domain to route to TS3 servers hosted elsewhere. | Practical recommendation: - Use `_ts3._udp` whenever possible. - Use SRV TSDNS only when one domain must route multiple TeamSpeak services while another service such as a website is hosted on a different IP. - Avoid plain TSDNS as the only mechanism for modern deployments. ### 16.7 Blacklist v1 Blacklist v1 is a UDP service. #### 16.7.1 Request ```text Destination: blacklist.teamspeak.com:17385 UDP Payload: ip4: ``` Examples: ```text ip4:212.224.114.71 ip4:84.200.62.245 ``` YaTQA's example for IPv6 intentionally shows no response; do not rely on v1 blacklist for IPv6 compatibility. #### 16.7.2 Response The response is 13 ASCII characters: ```text x,13371337133 ``` `x` is the status: | x | Status | Client behavior | |---|---|---| | 0 | Blacklisted | Refuse connection. | | 1 | OK | Permit connection. | | 2 | Greylisted | Permit or warn depending on client policy; official client displays a warning. | If the service does not answer, YaTQA states that the client still connects. A compatible implementation should fail open, not fail closed, for blacklist v1 service outages. ### 16.8 Blacklist2, client 3.1.6+ Blacklist2 is an HTTPS service using a TeamSpeak-specific Protobuf payload. #### 16.8.1 HTTP request ```http POST /check HTTP/1.1 Host: blacklist2.teamspeak.com User-Agent: TeamHttp/1.1 Connection: keep-alive Content-Type: application/x-ts3blacklist Content-Length: ``` `Connection: close` may work, but the official client uses `keep-alive` and YaTQA notes the service can be picky about some fields. #### 16.8.2 Logical request fields `BlacklistInfoRequest`: | Field no. | Name | Type | Required? | Notes | |---:|---|---|---|---| | 1 | `ip_address` | length-delimited binary | Required | IP address in binary form; IPv6 requires canonicalization compatibility. | | 2 | `domain_name` | length-delimited UTF-8 | Optional | Domain or nickname used by the user. Explicitly UTF-8, not CESU-8. | | 3 | `virtual_server_id` | length-delimited string | Required | Server UID, usual Base64 string. | | 4 | `public_license_id` | length-delimited Base64 | Optional / server 3.1.0+ | Same for all servers on a license and all clients; exact computation unknown in YaTQA notes. | | 5 | `port` | BigNum | Optional but recommended | Voice port. | | 6 | `timestamp` | BigNum | Optional but recommended | Unix timestamp UTC. | | 7 | `valid_token_present` | BigNum | Unused in observed behavior | Name from source; YaTQA did not observe functional variation. | | 8 | `slots_ok` | BigNum | Unused in observed behavior | Name from source; YaTQA observed value 1 and could not correlate it with server version/license/slot cases. | Binary request construction: ```pseudo payload = protobuf() payload.add_length_delimited(1, ip_to_binary(resolved_ip)) if user_supplied_domain: payload.add_length_delimited(2, utf8(user_supplied_domain)) payload.add_length_delimited(3, ascii(server_uid_base64)) if public_license_id_available: payload.add_length_delimited(4, ascii(public_license_id_base64)) payload.add_varint(5, port) payload.add_varint(6, unix_timestamp_utc()) payload.add_varint(7, 0_or_unknown) payload.add_varint(8, 1) ``` #### 16.8.3 Response fields Blacklist2 returns a small Protobuf response. YaTQA observed four BigNum fields: | Field | Meaning | |---:|---| | 1 | `status_ip` | | 2 | `status_domain`; may include wildcard bans for subdomains. | | 3 | `status_virtual_server_id` | | 4 | `status_public_license_id` (spelled inconsistently in YaTQA's source notes) | Status enum: | Value | Meaning | |---:|---| | 1 | INVALID | | 2 | BLACKLISTED | | 3 | GREYLISTED | | 4 | NOT_LISTED / OK / field not supplied | Connection decision: ```pseudo function blacklist2_decision(response): statuses = parse_statuses(response) if any(status == BLACKLISTED for status in statuses): return DENY if any(status == GREYLISTED for status in statuses): return WARN_OR_ALLOW return ALLOW ``` YaTQA notes that once one field is blacklisted, the official client refuses the connection. Results appear to be cached, likely by IP. This matters when only a domain is blacklisted: after the domain check fails, connecting to the raw IP may still be blocked until cache expiry or client restart. #### 16.8.4 IPv6 canonicalization bug in TS 3.1.6/3.1.7 YaTQA documents a TeamSpeak-specific bug in Blacklist2 IPv6 canonicalization: - Omitted zero groups in compressed IPv6 were filled with `3030` instead of `0`/`0000` (`0x30` is ASCII `'0'`). - If the seventh block was omitted, the eighth block was replaced with `3030`. - The same IPv6 address could therefore produce different Blacklist2 queries depending on how the user abbreviated it. - YaTQA says this was fixed in 3.1.8-beta1. For compatibility testing, keep test cases for compressed and uncompressed IPv6 forms: ```text 2a01:7e0:0:417:59:1337:cad:5e 2a01:7e0::417:59:1337:cad:5e 2a05:8b81:1000:17d:0:0:0:0 2a05:8b81:1000:17d:: ``` A modern implementation should canonicalize IPv6 correctly, but a bug-compatible mode can be useful when reproducing old-client traffic. ### 16.9 Weblist server reporting The Weblist server protocol is a UDP protocol to: ```text weblist.teamspeak.com:2010 ``` #### 16.9.1 Common packet header Every server-reporting packet has: | Offset/size | Field | Description | |---|---|---| | 1 byte | Version? | Always `1`. | | 2 bytes | Packet number | Sequential number, identical in request and response, begins at 1. | | 1 byte | Packet type | `1` for key request, `2` for data update. | | variable | Payload | Optional depending on packet type. | Byte order: - Multi-byte integer fields are little-endian. - Bit order for flags is described by YaTQA as big-endian. #### 16.9.2 Update handshake ```text 1. Server sends key-request packet, no payload. 2. Weblist returns random 32-bit key. A fresh key is returned each time. 3. Server sends data update using that key. 4. Weblist returns one payload byte. ``` #### 16.9.3 Data update payload | Field | Size | Meaning | |---|---:|---| | key | 4 bytes | Key received in step 2. | | port | 2 bytes | Voice port. | | slots | 2 bytes | Maximum slots. | | clients | 2 bytes | Connected clients, excluding Query clients. | | flags | 1 byte | Lower details below. | | name_len | 1 byte | Server-name length in bytes; 0 if unchanged. | | name | variable | Server name encoded UTF-8 or CESU-8. | Flags byte: | Bits | Meaning | |---|---| | first 6 bits | Always 0. | | next bit | 1 if guests can create a channel, regardless of channel type; 0 otherwise. | | final bit | 1 if server has a password; 0 otherwise. | Response payload: | Response byte | Meaning | |---:|---| | 0 | OK. | | 7 | Weblist server asks for the server name again, e.g. because it forgot state. | #### 16.9.4 Retry and timing behavior YaTQA documents the official server's timing as: - Attempt Weblist update every 10 minutes. - If no response, retry after 1.3 seconds. - If still no response, retry again 2 seconds later. - The packet ID is not incremented for those retries. - If all attempts fail, the server waits until the next normal 10-minute interval. - Shutting down a virtual server is not reported to the Weblist. Implementation notes: ```pseudo function report_weblist_loop(): every 10 minutes: packet_id += 1 for delay in [0s, 1.3s, 2s]: if delay > 0: sleep(delay) send_update_with_same_packet_id(packet_id) if receive_ok(): break ``` ### 16.10 Weblist client query YaTQA only notes that Weblist client queries use TCP to: ```text weblist.teamspeak.com:2010 ``` The public YaTQA document does not provide a full client query packet specification. Treat this as an intentionally incomplete area: a compatible client should not require Weblist querying for direct server connection. Implement it as an optional discovery service only if you independently document the wire format. ### 16.11 Badge metadata protocol TeamSpeak badges are stored in server/client-visible data as GUIDs, but to render them a client also needs the asset base filename/URL metadata. #### 16.11.1 Storage and cache behavior YaTQA states: - Badges are stored on the server as GUIDs. - The client also needs a filename/base URL for the images. - Images are cached under `cache\badges`. - The badge metadata list is binary encoded. - The list is stored unchanged as a `varchar` in `settings.db`. - The same `badges` table stores a timestamp. According to YaTQA, the name is misleading: it is not the timestamp of the list itself, but the time when the list should be downloaded again. - In the documented 3.1 beta era, refresh was 24 hours after download. #### 16.11.2 Badge list Protobuf structure The badge metadata list uses the Protobuf dialect: ```text field 1: BigNum revision? (not badge count; YaTQA observed value 15 with 13 badges) field 2: BigNum Unix timestamp, likely last modification time field 3: repeated length-delimited nested badge record nested field 1: GUID string, 36 chars including 4 hyphens and 32 hex digits nested field 2: badge name, UTF-8 nested field 3: URL base nested field 4: description, UTF-8 nested field 5: BigNum Unix timestamp, likely last modification time nested field 6: BigNum unknown, observed values 1..3 ``` Parser: ```pseudo function parse_badge_list(bytes): root = decode_ts_protobuf(bytes) revision = root[1][0].varint modified = root[2][0].varint badges = [] for rec_bytes in root[3]: r = decode_ts_protobuf(rec_bytes.bytes) badges.append({ 'guid': utf8(r[1][0].bytes), 'name': utf8(r[2][0].bytes), 'asset_base': utf8(r[3][0].bytes), 'description': utf8(r[4][0].bytes), 'modified': r[5][0].varint, 'unknown_rank_or_type': r[6][0].varint, }) return { 'revision': revision, 'modified': modified, 'badges': badges } ``` #### 16.11.3 Asset URL construction For each badge `asset_base`: | UI location | Suffix | |---|---| | Badge window / larger client display | `_64.png` | | Server tree small icon | `.svg` in later clients; older clients used `_16.png`. | | myTeamSpeak detail/vector display | `_details.svg`; older documentation mentions `.svg` for an earlier variant. | Renderer behavior: ```pseudo function badge_assets(base): return { 'large_png': base + '_64.png', 'tree_svg': base + '.svg', 'legacy_tree_png': base + '_16.png', 'details_svg': base + '_details.svg', } ``` #### 16.11.4 Unknown badges A robust badge renderer must gracefully handle a GUID not found in the current metadata list: - Display the GUID or a generic badge placeholder. - Queue a badge metadata refresh. - Do not crash the tree renderer or user list. - Keep the raw GUID in the state model so metadata can be resolved later. #### 16.11.5 Disabling badges on a server instance YaTQA describes a binary-patching trick to disable `client_badges` by editing the server executable and replacing one character in the `client_badges` string. This is not a recommended engineering practice for production systems. For development documentation, the important takeaway is not to implement this patch, but to recognize that badge display is ultimately controlled by a server-visible variable name and client interpretation. ### 16.12 Public-suffix/TLD appendix behavior YaTQA includes historical notes and a large old TLD list used by TS3 3.0.x. This matters because address resolution and SRV TSDNS candidate generation depend on deciding which labels are public suffixes and which labels are registrable domains. #### 16.12.1 TS 3.1-beta2+ behavior YaTQA states that TeamSpeak 3.1 generally does not resolve top-level domains. In 3.1-beta2, the client embedded a public-suffix-like list. The list itself is stored in Punycode, even though clients 3.0.20 to 3.1-beta2 did not fully support Punycode; 3.1-beta3 restored Punycode support. Implementation recommendation: - Use the current Public Suffix List for modern behavior. - Add compatibility fixtures for old TeamSpeak candidate-domain behavior. - Do not rely on YaTQA's old 3.0.x TLD list as a current public suffix list. #### 16.12.2 TS 3.0.20 and 3.1-beta1 behavior These versions did not look up top-level domains. This caused problems for domains delegated at the third level such as `.il`, `.ua`, `.za`, and historically `.uk`, `.nz`, `.au`, `.ni` because second-level selection could be wrong. #### 16.12.3 TS 3.0.x old embedded TLD list YaTQA says the 3.0.x list in client 3.0.18.1 and likely through 3.0.19.4 was from early October 2007. It includes obsolete or old entries such as `.an` and `.gb`, lacks many later TLDs, and contains old IDN test domains that no longer exist. You should only use this list for historical compatibility tests. Representative entries listed by YaTQA include: ```text aero arpa asia biz cat com coop edu gov info int jobs mil mobi museum name net org pro tel travel ac ad ae af ag ai al am an ao aq ar as at au aw ax az ba bb bd be bf bg bh bi bj bm bn bo br bs bt bv bw by bz ca cc cd cf cg ch ci ck cl cm cn co cr cu cv cx cy cz de dj dk dm do dz ec ee eg er es et eu fi fj fk fm fo fr ga gb gd ge gf gg gh gi gl gm gn gp gq gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in io iq ir is it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc li lk lr ls lu lt lv ly ma mc md me mg mh mk ml mm mn mo mp mq mr ms mt mu mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe pf pg ph pk pl pm pn pr ps pt pw py qa re ro rs ru rw sa sb sc sd se sg sh si sj sk sl sm sn so sr st su sv sy sz tc td tf tg tj tk tl tm tn tp to tr tt tv tw tz ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw ``` Representative IDN/Punycode entries include: ```text xn--fiqs8s xn--fiqz9s xn--fzc2c9e2c xn--j6w193g xn--kprw13d xn--kpry57d xn--mgbaam7a8h xn--mgbayh7gpa xn--mgberp4a5d4ar xn--o3cw4h xn--p1ai xn--pgbs0dh xn--wgbh1c xn--wgbl6a xn--xkc2al3hye2a xn--ygbi2ammx xn--0zwm56d xn--11b5bs3a9aj6g xn--80akhbyknj4f xn--9t4b11yi5a xn--deba0ad xn--g6w251d xn--hgbk6aj7f53bba xn--hlcj6aya9esc7a xn--jxalpdlp xn--kgbechtv xn--zckzah ``` ### 16.13 Implementation checklist for auxiliary protocol support Use this checklist when building a TS3-compatible client or administration tool. | Area | Must implement | Should implement | Compatibility notes | |---|---|---|---| | Protobuf | Varint, length-delimited, nested records, repeated fields. | Generic unknown-field preservation. | Used by updates, Blacklist2, badge list. | | Updates | Parse modern Protobuf metadata. | Historical UDP probes only for old-client testing. | Verify signatures/hashes before installing. | | File transfer | `ftinit*` bootstrap + raw TCP token + bytes. | Resume/partial validation. | Do not escape token or file payload. | | TSDNS | TCP 41144 query ending `0A 0D 0D 0D 0A`. | `$PORT`, `404`, no-response semantics. | Modern clients rely more on explicit SRV. | | Server nickname | HTTPS lookup for dotless names. | Caching and retry policy. | HTTPS only. | | DNS/SRV | `_ts3._udp`, `_tsdns._tcp`, A/AAAA, Punycode. | Version-specific compatibility modes. | Do not continue to later resolution methods after one complete solution fails to connect if strict TS behavior is needed. | | Blacklist v1 | UDP `ip4:` request, status 0/1/2. | Fail-open on timeout. | Mostly historical. | | Blacklist2 | HTTPS POST + Protobuf request/response. | IP/domain/UID/license status model. | Cache behavior can affect domain-vs-IP retry. | | Weblist server | UDP key request + data update. | Retry timing and packet-id reuse. | Optional unless implementing server reporting. | | Weblist client | Optional. | Document separately if implemented. | YaTQA public page is incomplete here. | | Badges | Protobuf badge list parse, GUID-to-asset mapping. | Metadata cache refresh and unknown-GUID handling. | Asset suffixes differ by UI location and era. | ### 16.14 Integration with the rest of this manual The auxiliary protocol layer should not be mixed directly into your ServerQuery parser. Architect it as separate modules: ```text core/ query/ # text ServerQuery protocol voice_client/ # encrypted UDP client protocol (if implementing full voice client) file_transfer/ # ftinit + raw TCP file transfer resolver/ # nickname, DNS SRV, SRV TSDNS, TSDNS, A/AAAA protobuf/ # BigNum and nested message decoder/encoder metadata/ updates/ # version metadata parser badges/ # badge list parser/cache blacklist/ # blacklist v1/v2 checks weblist/ # server reporting/client discovery ``` Testing guidance: - Create golden byte fixtures for Protobuf varint edge cases: 0, 1, 127, 128, 255, 300, 16384, max u32, max u64. - Create TSDNS fixtures with normal result, `$PORT`, `404`, and timeout/no response. - Create DNS fixtures for standard port, non-standard port, multiple servers, website-on-different-IP, and IDN/Punycode domains. - Create Blacklist2 fixtures for OK, greylisted, blacklisted by IP, blacklisted by domain, blacklisted by server UID, and malformed response. - Create badge fixtures with known GUID, unknown GUID, old `_16.png`, new `.svg`, and detail SVG. - Create file-transfer tests that verify the token bytes are sent exactly once and not escaped. *** ## 17. Low-Level Client Protocol Notes from ReSpeak The ReSpeak `tsdeclarations` and `tsclientlib` projects provide public insight into the non-Query client protocol. Key points from `ts3protocol.md` and the Rust stack: - Packet size ceiling is around 500 bytes. - Large command packets may be fragmented. - Packet compression uses QuickLZ level 1 in relevant paths. - Packet encryption uses EAX-style authenticated encryption. - The protocol has explicit ACK, low-priority ACK, and Pong semantics. - Connection initiation includes a puzzle / proof step to reduce abuse. - Connection creation is expensive compared with normal message sending. Architecture: ```text UDP transport -> packet framing -> encryption/decryption -> compression/decompression -> ACK/retransmission handling -> packet type dispatch -> state/bookkeeping updates -> application events ``` If you implement a full custom client, do not start with UI. Start with: 1. Packet parser/serializer tests. 2. Crypto handshake tests. 3. ACK/retransmit tests. 4. Bookkeeping/state tests. 5. Only then add UI/audio features. *** ## 18. ReSpeak Repository Implementation Guide ### 18.1 `tsdeclarations` Purpose: machine-readable TS3 metadata. Important files: | File | Use | |---|---| | `Errors.csv` | Generate error enum and message dictionary | | `Permissions.csv` | Generate permission constants and grant mapping | | `Messages.toml` | Generate command/request/response types | | `Book.toml` | Generate state/bookkeeping structures | | `Enums.toml` | Generate enums | | `MessagesToBook.toml` | Map protocol messages to state updates | | `BookToMessages.toml` | Map state needs to protocol messages | | `Versions.csv` | Historical versions | | `Badges.csv` | Badge metadata | | `ts3protocol.md` | Protocol notes | Recommended code generation pipeline: ```text CSV/TOML declarations -> parser -> intermediate schema -> generated language types -> command builders -> response parsers -> documentation tables ``` Generated command example: ```rust pub struct ServerEdit { pub virtualserver_name: Option, pub virtualserver_maxclients: Option, pub virtualserver_password: Option, // ... } ``` Generated error example: ```rust pub enum Ts3ErrorCode { Ok = 0, CommandNotFound = 256, ClientInvalidId = 512, ClientIsFlooding = 524, ParameterInvalid = 1538, ParameterConvert = 1540, PermissionInsufficient = 2568, } ``` ### 18.2 `tsclientlib` Purpose: Rust library for custom TS3 clients and bots. Architecture: ```text tsproto-types # primitive protocol types tsproto-structs # protocol structures tsproto-packets # packet framing tsproto # low-level protocol runtime ts-bookkeeping # server state model tsclientlib # high-level API ``` Common client pattern: ```rust use tsclientlib::{Connection, DisconnectOptions, Identity, StreamItem}; #[tokio::main] async fn main() -> anyhow::Result<()> { let identity = Identity::new_from_str("IDENTITY_STRING")?; let mut conn = Connection::build("127.0.0.1") .identity(identity) .log_commands(true) .connect()?; while let Some(event) = conn.events().next().await { let event = event?; if matches!(event, StreamItem::BookEvents(_)) { break; } } let state = conn.get_state()?; println!("Connected to {}", state.server.name); conn.disconnect(DisconnectOptions::new())?; Ok(()) } ``` Design cautions: - Implement reconnect policy yourself if not provided by your chosen version. - Treat initial state as incomplete until bookkeeping events arrive. - Keep identity loading separate from connection logic. - Log protocol events during development but redact secrets. ### 18.3 `Qint` Purpose: full alternative client reference. Architecture: ```text Frontend UI -> Tauri desktop/mobile shell -> Rust backend -> tsclientlib -> protocol/audio/state layers ``` Development lessons: - Separate frontend UI state from TS3 protocol state. - Use a backend command/event bridge rather than letting the UI speak protocol directly. - Platform differences matter: desktop and Android may need different audio backends. - Treat Qint as a reference/fork base, not as a guaranteed maintained dependency. ### 18.4 `ts3stats` Purpose: offline statistics and report generation. Pipeline: 1. Put TeamSpeak server logs in `Logs/`. 2. Optionally put TS3AudioBot logs in `BotLogs/`. 3. Create `Settings.py`. 4. Run `CreateTimeGraphs.py`. 5. Open `Result/index.html`. Settings template: ```python from datetime import timedelta vips = ["Admin", "Bot"] merges = [["AdminLaptop", "AdminDesktop"]] maxUsers = 50 botStats = True inputFolder = "Logs" inputFolderBot = "BotLogs" outputFolder = "Result" tempFolder = "temp" slotLength = timedelta(minutes=10) minTime = timedelta(hours=10) minConnects = 8 ``` Architecture: ```text log files -> parser -> normalized sessions/events -> aggregation slots -> diagram modules -> Jinja2 templates -> static HTML report ``` ### 18.5 `MahTsIdentity` Purpose: identity operations. Capabilities: - Export identity. - Search vanity UID patterns. - Use `_` as wildcard in patterns. - Benchmark search speed. - Improve identity security level. - Use parallelism with configurable thread count. Conceptual commands: ```bash mah_ts_identity --export -i "IDENTITY_STRING" mah_ts_identity MY_UID mah_ts_identity AB_CD_ --threads 8 mah_ts_identity --bench MY_UID mah_ts_identity -i "IDENTITY_STRING" --level 32 ``` Design lesson: security-level and vanity searches are CPU-bound parallel workloads. Keep them separate from the main client runtime. *** ## 19. Statistics and Analytics ### 19.1 Log ingestion model For server log analytics, normalize logs into events: ```json { "timestamp": "2026-05-23T12:00:00Z", "type": "client_connected", "client_uid": "...", "client_name": "Alice", "channel_id": 12 } ``` Then build sessions: ```json { "client_uid": "...", "display_name": "Alice", "connected_at": "...", "disconnected_at": "...", "duration_seconds": 3600 } ``` ### 19.2 Useful reports - User online time by day/week/month. - Peak concurrent clients. - Average session duration. - Channel popularity. - Bot usage statistics. - VIP/user group activity. - New vs. returning users. - Longest sessions. ### 19.3 Report architecture Use an offline pipeline: ```text raw logs -> parse -> normalize -> aggregate -> render HTML/PDF ``` Do not run heavy analytics in your live bot process. Use a separate job. *** ## 20. Desktop Client Architecture A modern TS3 client can follow the Qint-style split: ```text UI frontend - channel tree - user list - chat panels - settings - file browser - channel editor Backend service - connection manager - identity manager - protocol runtime - state reducer - audio bridge - file-transfer worker Protocol libraries - tsclientlib or equivalent - declaration-generated types ``` ### 20.1 State synchronization Recommended event flow: ```text Protocol event -> backend reducer -> normalized app state -> frontend event emit -> UI render ``` Do not let UI components directly mutate protocol state. They should request backend actions. ### 20.2 File browser A file browser needs: - Channel file list query. - Permission checks. - File-transfer token request. - TCP transfer worker. - Progress events. - Cancellation. ### 20.3 Channel editor A channel editor should: - Load editable fields. - Validate field types and limits. - Use `channeledit` only with mutable fields. - Display failed permission when returned. *** ## 21. Implementation Checklist ### 21.1 Minimum ServerQuery admin tool - [ ] TCP Query transport with LF-CR line endings. - [ ] Escape/unescape functions. - [ ] Response parser with row and error handling. - [ ] Login and server selection with `-virtual`. - [ ] Command builder with typed parameter validation. - [ ] Error dictionary. - [ ] Command scheduler with anti-flood delay. - [ ] `serverinfo`, `clientlist`, `channellist`, `servergrouplist` support. - [ ] Event subscriptions and reducer. - [ ] Permission display with failed permission mapping. ### 21.2 Full administration system - [ ] Virtual server create/delete/edit. - [ ] Client database search and info. - [ ] Channel create/edit/delete/move. - [ ] Server and channel groups. - [ ] Permission assignment and evaluation. - [ ] Privilege key lifecycle. - [ ] Ban and complaint management. - [ ] File-transfer module. - [ ] Snapshot create/deploy/verify. - [ ] Backup/migration planner. - [ ] Audit log. ### 21.3 Custom TS3 client or bot - [ ] Identity loader/generator. - [ ] UDP protocol connection. - [ ] Handshake and puzzle handling. - [ ] Packet encryption/decryption. - [ ] Compression/decompression. - [ ] Fragmentation support. - [ ] ACK/retransmit logic. - [ ] Bookkeeping state model. - [ ] Audio I/O if voice is required. - [ ] Reconnect strategy. - [ ] UI/backend boundary if desktop client. ### 21.4 Identity tool - [ ] Import/export identity format. - [ ] Public key extraction. - [ ] UID generation. - [ ] Security level calculation. - [ ] Parallel offset search. - [ ] Vanity pattern matcher. - [ ] Benchmarking. - [ ] Secret redaction. ### 21.5 Stats processor - [ ] Log parser. - [ ] Bot log parser. - [ ] Session reconstruction. - [ ] User merge aliases. - [ ] VIP highlighting. - [ ] Aggregation slots. - [ ] HTML report rendering. - [ ] Static asset packaging. *** ## 22. Troubleshooting ### 22.1 No reply from server Likely causes: - Server is not running. - Wrong host. - Wrong port. - UDP 9987 blocked. - Non-default port omitted from the client address. Fix: - Test host reachability. - Confirm server process. - Confirm voice port. - Use `host:port` explicitly. - Check firewall/NAT. ### 22.2 Invalid client ID (`512`) Likely cause: persisted `clid` from a previous session. Fix: - Refresh with `clientlist`. - Store `client_unique_identifier` or `client_database_id` for persistence. ### 22.3 Invalid parameter (`1538`) Likely causes: - Typo in parameter name. - Parameter not valid for that command. - Attempt to edit a read-only field. Fix: - Validate against generated declarations. - Check YaTQA variable matrix behavior. ### 22.4 Convert error (`1540`) Likely causes: - Invalid numeric format. - Signed/unsigned mismatch. - Value outside allowed range. - Attempt to send `-1` where unsigned value is expected. Fix: - Validate integer ranges. - Convert icon IDs using signed/unsigned 32-bit conversion. ### 22.5 Client is flooding (`524`) Likely cause: commands sent too quickly or expensive actions repeated. Fix: - Add delay/backoff. - Track anti-flood budget. - Cache expensive command results. ### 22.6 Insufficient permissions (`2568`) Fix: - Extract `failed_permid` if returned. - Map to permission name. - Show required permission to admin. - Check power/needed power pairs. ### 22.7 Snapshot deploy fails Likely causes: - Hash mismatch. - Encoding problem. - Snapshot from incompatible server version. - Missing permission. - Snapshot contains old or corrupted data. Fix: - Recompute hash. - Normalize line endings. - Verify UTF-8/CESU-8 handling. - Test on staging server. *** ## 23. Security Best Practices - Store Query credentials in environment variables or secret stores. - Never log Query passwords. - Never log identity private keys. - Treat snapshots as privileged secrets. - Restrict ServerQuery by IP allowlist. - Prefer least-privilege Query users over `serveradmin` for applications. - Redact tokens from logs after creation. - Rate-limit public admin APIs to protect the TS3 server from command floods. - Validate all user-provided server/channel/client names before passing to Query commands. - Keep file-transfer paths constrained and normalized. *** ## 24. Recommended Engineering Plan ### Phase 1: Data and declarations - Import errors and permissions. - Define command schemas. - Build escape/unescape and parser tests. ### Phase 2: Query client - Implement transport. - Implement login/use/serverinfo/clientlist/channellist. - Implement typed command builder. - Implement error handling. ### Phase 3: Event-driven state - Add notifications. - Build state reducer. - Add deduplication. - Add reconnect and resubscribe behavior. ### Phase 4: Admin features - Add channels, groups, permissions, tokens, bans, complaints. - Add permission explanations. - Add audit log. ### Phase 5: Backup and migration - Add snapshot create/deploy/verify. - Add file-transfer backup. - Add full migration checklist. ### Phase 6: Identity and client protocol - Add identity import/export. - Add security-level worker. - If needed, integrate `tsclientlib` or equivalent UDP protocol stack. ### Phase 7: Analytics - Add log ingestion. - Add report generation. - Add scheduled offline jobs. *** ## 25. Reference URLs Primary resources: - https://yat.qa/ressourcen/ - https://yat.qa/resources/ - https://yat.qa/resources/server-error-codes/ - https://yat.qa/resources/permission-ids/ - https://yat.qa/resources/client-versions/ - https://yat.qa/ressourcen/definitionen-und-algorithmen/ - https://yat.qa/ressourcen/server-query-kommentare/ - https://yat.qa/ressourcen/server-query-notify/ - https://yat.qa/ressourcen/variablen-parameter/ - https://yat.qa/ressourcen/voice-client-anti-flood/ - https://yat.qa/ressourcen/sicherheitsstufe/ - https://yat.qa/ressourcen/snapshots/ - https://yat.qa/ressourcen/protokolle/ - https://yat.qa/ressourcen/abzeichen-badges/ ReSpeak repositories: - https://github.com/ReSpeak/tsclientlib - https://github.com/ReSpeak/ts3stats - https://github.com/ReSpeak/tsdeclarations - https://github.com/ReSpeak/Qint - https://github.com/ReSpeak/MahTsIdentity/ Official support references: - TeamSpeak 3 support category - TeamSpeak 3 ports - TeamSpeak 3 server setup - TeamSpeak 3 DNS SRV records - TeamSpeak 3 snapshots - TeamSpeak 3 migration - TeamSpeak 3 plugin SDK guidance *** ## 26. Final Notes This manual should be sufficient to start serious TeamSpeak 3 development without re-reading the source websites for every implementation choice. For exact binary compatibility in the low-level UDP client protocol, the best path is still to use or study `tsclientlib` and `tsdeclarations` directly, because the protocol includes encryption, compression, fragmentation, acknowledgement, and handshake behavior that must be tested against a real server. For administration tooling, YaTQA's Query notes and variable matrices are the most important sources of real behavior. For runtime clients, ReSpeak's Rust stack is the strongest public implementation reference. For migration and recovery, always remember that snapshots are not complete backups. *** # Expanded Engineering Specification (Version 2) This version expands the earlier manual into a more implementation-oriented reference. It is written to be used as a build specification for a TeamSpeak 3-compatible administration system, ServerQuery client, bot framework, identity utility, migration tool, and log statistics pipeline. ## 21. Design Goals and Non-Goals ### 21.1 Design goals A complete TS3 development stack should support: 1. **ServerQuery automation**: login, server selection, virtual server CRUD, channel CRUD, client queries, permission management, message sending, file transfer setup, snapshots, tokens, bans, complaints, and log reads. 2. **Event-driven state tracking**: maintain an in-memory model of servers, channels, clients, groups, permissions, and text events using `servernotifyregister` and periodic reconciliation. 3. **Typed protocol declarations**: generate message builders, response parsers, event models, and validation metadata from machine-readable schemas such as `tsdeclarations`. 4. **Permission explanation**: compute and explain effective permissions, not just read assigned permissions. 5. **Safe backup and restore**: distinguish between logical snapshots, file system assets, instance database copies, and YaTQA-style partial backup when snapshot permission is missing. 6. **Identity tooling**: import/export identities, compute UID, compute and raise security level, and optionally search vanity UID prefixes or patterns. 7. **Statistics tooling**: parse server logs and bot logs into time ranges, session graphs, client presence records, and usage summaries. 8. **Compatibility awareness**: handle old TS3 quirks such as CESU-8, inconsistent Query documentation, historical DNS/TSDNS resolution differences, and snapshot hash recalculation. ### 21.2 Non-goals A self-contained TS3-compatible system still should not pretend to replace proprietary or unavailable components where public material is incomplete. Specifically: - The official TeamSpeak voice codec implementation and exact proprietary client behavior cannot be fully reconstructed from YaTQA alone. - ClientQuery is not documented as thoroughly as ServerQuery in the public YaTQA resources. - Some YaTQA downloads are historical binaries or inaccessible in a crawler; their existence and intended function can be documented, but their source cannot be reconstructed honestly without access to the downloads. - Official Plugin SDK details are outside the public YaTQA material and should be taken from the TeamSpeak Plugin SDK package when building native plugins. ## 22. Recommended Project Layout A production-grade project should be modular. The following layout works for Rust, TypeScript, Go, Java, C#, or Python with minor naming changes. ```text teamtalk-ts3-stack/ docs/ manual.md protocol-notes.md migration-runbooks.md declarations/ errors.csv permissions.csv messages.toml events.toml badges.csv versions.csv packages/ ts3-types/ ids, enums, typed wrappers, common model objects ts3-query/ raw text codec, escaping, parser, command builder ts3-events/ notify event parser and state reducers ts3-permissions/ permission calculation and explanation engine ts3-snapshot/ snapshot parser, serializer, hash verifier, migrator ts3-files/ file transfer token workflow, icon/avatar helpers ts3-identity/ identity import/export, UID/security-level calculations ts3-dns/ SRV/TSDNS resolver, resolution trace logs ts3-stats/ log parser, sessions, graphs, report generation ts3-client-runtime/ connection lifecycle, event bus, reconnection, state cache apps/ admin-web/ bot/ desktop-client/ cli/ tests/ protocol-fixtures/ snapshot-fixtures/ query-responses/ log-fixtures/ ``` A key principle is that **protocol declarations are data**, not hand-coded constants scattered through the application. Error IDs, permission IDs, message schemas, badge GUIDs, and enum mappings should live in a single declarations package and be consumed by code generation. ## 23. Core Data Types Use strongly typed wrappers even if the wire protocol uses plain integers. It prevents mixing session-scoped IDs with persistent IDs. ```text ServerId integer, virtual server ID inside the server instance ServerPort integer, UDP voice port ChannelId integer, channel ID, persistent within a virtual server snapshot but may remap on restore ClientId integer, session-scoped client ID; invalid after disconnect ClientDatabaseId integer, persistent database ID within a virtual server ClientUid string, persistent identity UID ServerGroupId integer ChannelGroupId integer PermissionId integer PermissionName string, e.g. b_virtualserver_modify_name Token string, privilege key string IconId signed/unsigned 32-bit value, depending on context FileTransferId 0..65535 client-provided transfer correlation ID ``` ### 23.1 Session IDs vs persistent IDs The most common implementation bug is confusing `clid` with `client_database_id` or `client_unique_identifier`. - `clid`: current online session only. Never store as a durable key. - `client_database_id`: durable inside one virtual server. Changes if the client is imported into another server database. - `client_unique_identifier`: identity-level stable ID. Best cross-server user identity key. ### 23.2 Server, channel, and file paths Use explicit path types: ```text VirtualServerPath: sid: ServerId port: ServerPort optional ChannelFilePath: channel_id: ChannelId path: string IconPath: /icon_ AvatarPath: /avatar_ ``` Do not assume all file commands encode avatar paths in exactly the same way. YaTQA notes that avatar handling differs between upload/download/info and deletion semantics, so build helpers per command rather than one global avatar path function. ## 24. ServerQuery Wire Format ServerQuery is line-oriented text over TCP or SSH Query. A robust implementation should treat it as a protocol, not as string concatenation. ### 24.1 Command structure ```text command [parameter=value ...] [array-row|array-row ...] ``` Typical examples: ```text login serveradmin password use sid=1 -virtual serverinfo serveredit virtualserver_name=Example\sServer virtualserver_maxclients=64 clientlist -uid -groups -away -voice -times -info -country -ip -badges ``` ### 24.2 Response structure Typical successful response: ```text key=value key=value|key=value key=value error id=0 msg=ok ``` Typical failed response: ```text error id=2568 msg=insufficient\sclient\spermissions failed_permid=... ``` Some commands return arrays separated by `|`. Some commands return empty values. Some parameters may be absent if permissions are insufficient. ### 24.3 Escaping model Implement escaping as a reversible codec. A practical codec table: | Plain character | Query escape | |---|---| | space | `\s` | | slash `/` | `\/` | | backslash `\` | `\\` | | pipe `|` | `\p` | | bell | `\a` | | backspace | `\b` | | form feed | `\f` | | newline | `\n` | | carriage return | `\r` | | tab | `\t` | | vertical tab | `\v` | Decode after tokenization, not before, because `\p` represents a literal pipe inside a value and must not split the row. ### 24.4 Robust tokenizer pseudocode ```pseudo function parse_response_line(line): rows = split_unescaped(line, '|') parsed_rows = [] for row in rows: tokens = split_unescaped(row, ' ') object = {} flags = [] for token in tokens: if token == '': continue if token starts with '-': flags.append(token[1:]) else: key, raw_value = split_first(token, '=') object[key] = query_unescape(raw_value or '') parsed_rows.append({ fields: object, flags: flags }) return parsed_rows ``` ### 24.5 Command builder pseudocode ```pseudo function build_command(name, params, flags=[], rows=[]): parts = [name] for flag in flags: parts.append('-' + flag) if rows is empty: for key, value in params: parts.append(key + '=' + query_escape(to_string(value))) else: serialized_rows = [] for row in rows: row_parts = [] for key, value in row: row_parts.append(key + '=' + query_escape(to_string(value))) serialized_rows.append(join(row_parts, ' ')) parts.append(join(serialized_rows, '|')) return join(parts, ' ') + '\n' ``` ### 24.6 Return codes for correlation Every command may include `return_code`. Use it for pipelining or correlation: ```text serverinfo return_code=req_000123 ``` When parsing, attach the returned `return_code` from the final error line to the original command future/promise. ### 24.7 Connection lifecycle Recommended state machine: ```text Disconnected -> TCPConnected -> BannerReceived -> Authenticated optional -> ServerSelected optional -> Subscribed optional -> Closing -> Disconnected ``` Rules: - Read the initial server banner before sending commands. - Prefer explicit `quit` before closing. - Use `use sid= -virtual` as a stable server selection pattern. - Resubscribe notifications after login, relogin, or server switch. - Reconcile state with periodic `serverinfo`, `channellist`, and `clientlist` because notifications can be lost during reconnects. ## 25. ServerQuery Command Families This section provides implementation targets for a complete administration client. ### 25.1 Instance-level commands ```text version hostinfo instanceinfo bindinglist serverlist servercreate serverdelete serverstart serverstop serverprocessstop ``` Minimum behavior: - `serverlist` should support flags for UID, short view, all servers, and only running servers depending on server version. - `servercreate` returns at least `sid`, `virtualserver_port`, and initial token. - `serverdelete` must stop a server before deletion if required by server state. - `serverstart` and `serverstop` operate on virtual servers by `sid`. ### 25.2 Virtual server commands ```text use serverinfo serverrequestconnectioninfo serveredit servertemppasswordadd servertemppassworddel servertemppasswordlist servergrouplist servergroupadd servergroupdel servergroupcopy servergroupaddperm servergroupdelperm servergrouppermlist ``` Key implementation notes: - `serverinfo` returns more connection and packet statistics than some related commands. - Some quota and bandwidth values may appear as huge unsigned 64-bit values representing `-1` in another interpretation. Display them as “unlimited” when appropriate rather than blindly showing 18446744073709551615. - `serveredit` accepts many fields; generate its builder from declarations. ### 25.3 Channel commands ```text channellist channelinfo channelfind channelcreate channeldelete channeledit channelmove channelpermlist channeladdperm channeldelperm ``` State model: ```text Channel { cid pid order name topic description codec codec_quality maxclients maxfamilyclients password_flag permanent_flag semi_permanent_flag default_flag icon_id delete_delay } ``` ### 25.4 Client commands ```text clientlist clientinfo clientfind clientmove clientkick clientpoke clientupdate clientedit clientdblist clientdbinfo clientdbfind clientdbedit clientgetdbidfromuid clientgetnamefromuid clientgetnamefromdbid clientgetuidfromclid ``` State model: ```text ClientSession { clid cid client_database_id client_unique_identifier client_nickname client_type client_servergroups client_channel_group_id client_talk_power client_is_talker client_is_priority_speaker client_icon_id client_country client_badges } ``` ### 25.5 Messaging commands ```text sendtextmessage targetmode=<1|2|3> target= msg= ``` Target modes commonly map to: ```text 1 = private client 2 = channel 3 = server ``` Always escape message text. For long messages, enforce Query line limits and consider splitting. ### 25.6 Permission commands ```text permissionlist permidgetbyname permoverview permget permfind servergroupaddperm servergroupdelperm clientaddperm clientdelperm channeladdperm channeldelperm channelgroupaddperm channelgroupdelperm channelclientaddperm channelclientdelperm ``` Recommended API: ```pseudo PermissionAssignment { scope: ServerGroup | ServerClient | Channel | ChannelGroup | ChannelClient scope_id: integer(s) permid: PermissionId permsid: PermissionName optional value: integer or boolean skip: boolean negated: boolean grant: integer optional } ``` ### 25.7 Tokens / privilege keys Commands: ```text privilegekeyadd privilegekeydelete privilegekeylist privilegekeyuse ``` Legacy aliases: ```text tokenadd tokendelete tokenlist tokenuse ``` Custom token metadata should be escaped twice when using parameterized strings inside parameterized Query values. ## 26. Notify Event System A high-quality bot or client should be event driven. ### 26.1 Subscription lifecycle ```text servernotifyregister event=server servernotifyregister event=channel id=0 servernotifyregister event=textserver servernotifyregister event=textchannel servernotifyregister event=textprivate servernotifyregister event=tokenused ``` Rules: - Subscriptions are per Query connection. - Subscriptions disappear after logout, relogin, server switch, or disconnection. - Subscribe one event class at a time. - Rebuild subscriptions after reconnect. ### 26.2 Event parser Event lines start with a notification name, e.g.: ```text notifycliententerview cfid=0 ctid=1 reasonid=0 clid=5 client_unique_identifier=... notifyclientleftview cfid=1 ctid=0 reasonid=8 reasonmsg=left\sthe\sserver clid=5 notifyclientmoved ctid=12 reasonid=1 clid=5 notifytextmessage targetmode=1 msg=hello invokerid=5 invokername=Alice invokeruid=... ``` Parser design: ```pseudo function parse_notify(line): name, rest = split_first(line, ' ') rows = parse_response_line(rest) return NotifyEvent(name=name, rows=rows) ``` ### 26.3 State reducer examples ```pseudo on notifycliententerview(event): client = client_from_fields(event.fields) state.clients[client.clid] = client state.channels[client.cid].clients.add(client.clid) on notifyclientleftview(event): clid = event.fields.clid remove_client_from_all_channels(clid) delete state.clients[clid] on notifyclientmoved(event): target = event.fields.ctid for clid in parse_array(event.fields.clid): move_client(clid, target) on notifychanneledited(event): cid = event.fields.cid update_changed_channel_fields(cid, event.fields) if description_or_password_missing: schedule channelinfo(cid) ``` ### 26.4 Reconciliation strategy Notifications are not a replacement for periodic state sync. Use this strategy: ```text At connect: serverinfo channellist -topic -flags -voice -limits -icon clientlist -uid -groups -away -voice -times -info -country -ip -badges servergrouplist channelgrouplist On event: apply reducer if event incomplete -> schedule targeted query Every N minutes: refresh clientlist and channellist compare state hash repair drift ``` ## 27. Permission Engine Specification ### 27.1 Permission assignment scopes Effective permission is evaluated per permission. Do not calculate all permissions as one combined object without per-permission conflict logic. Sources in priority/evaluation model: 1. Server groups 2. Client permissions on server level 3. Channel permissions 4. Channel group permissions 5. Client permissions on channel level ### 27.2 Server group resolution When multiple server groups assign the same permission: ```pseudo function resolve_server_groups(assignments): negated = filter(assignments, a.negated) if negated is not empty: candidates = negated else: candidates = assignments return assignment_with_highest_effective_value_or_priority(candidates) ``` YaTQA notes the special behavior of negated groups. Implement negation explicitly and test it with multiple group memberships. ### 27.3 Skip logic ```pseudo function has_skip_after_server_layer(server_group_effective, client_server_assignment): if client_server_assignment exists: return client_server_assignment.skip else: return server_group_effective.skip ``` If skip is true, ignore channel and channel group assignments. Channel-client assignments still need separate treatment according to scope because they are the most specific layer. ### 27.4 Effective permission calculation pseudocode ```pseudo function effective_permission(client, channel, permission): sg_assigns = all server-group assignments for client's groups and permission sg_effective = resolve_server_groups(sg_assigns) server_client = assignment(client.dbid, permission, scope=ServerClient) if server_client exists: current = server_client skip = server_client.skip else: current = sg_effective skip = sg_effective.skip if sg_effective else false if not skip: channel_assignment = assignment(channel.cid, permission, scope=Channel) if channel_assignment exists: current = channel_assignment channel_group_assignment = assignment(client.channel_group_id, channel.cid, permission, scope=ChannelGroup) if channel_group_assignment exists: current = channel_group_assignment channel_client = assignment(client.dbid, channel.cid, permission, scope=ChannelClient) if channel_client exists: current = channel_client return current.value or default(permission) ``` ### 27.5 Power / needed power pairs Many TS3 permissions are enforced as power comparisons: ```text i_client_kick_power >= i_client_needed_kick_power i_client_ban_power >= i_client_needed_ban_power i_group_modify_power >= i_group_needed_modify_power i_permission_modify_power >= i_needed_modify_power_ ``` When displaying “why can’t I do this?” always show both sides: ```text Action: edit channel Your i_channel_modify_power: 50 Target channel i_channel_needed_modify_power: 75 Result: denied because 50 < 75 ``` ### 27.6 Grant permissions For permission ID `P`, grant permission is historically derived by adding 32768 in modern versions. The permission name form often uses `i_needed_modify_power_...`. A permission editor must verify grant power before allowing edits. ## 28. Anti-Flood Model ### 28.1 State variables ```text points_tick_reduce points_needed_command_block points_needed_ip_block client_current_points client_has_b_client_ignore_antiflood client_has_b_client_ignore_bans ``` ### 28.2 Tick model Every 0.5 seconds: ```pseudo client_current_points = max(0, client_current_points - points_tick_reduce) ``` ### 28.3 Action model ```pseudo function perform_action(client, action_cost): if not client.ignore_antiflood: client.points += action_cost if action_cost >= 0 and not client.ignore_antiflood: if client.points >= points_needed_command_block: throw Error(524, 'client is flooding') execute_action() ``` ### 28.4 Typical costs from YaTQA notes | Action family | Example | Points | |---|---:|---:| | Subscribe one channel/family | `channelsubscribe` | 15 | | Unsubscribe one channel/family | `channelunsubscribe` | 5 | | Subscribe all | `channelsubscribeall` | 20 | | Unsubscribe all | `channelunsubscribeall` | 25 | | Add ban | `banadd` | 25 | | Ban connected client | `banclient` | 25 | | Delete ban | `bandel` | 5 | | List bans | `banlist` | 25 | | Add complaint | `complainadd` | 25 | | Delete complaint | `complaindel` | 5 | | List complaints | `complainlist` | 25 | | Permission list if uncached | `permissionlist` | 5 | | Permission overview | `permoverview` | 5 | ### 28.5 Client-side rate limiter ```pseudo class AntiFloodLimiter: points = 0 last_update = now() def decay(): ticks = floor((now() - last_update) / 0.5s) points = max(0, points - ticks * tick_reduce) last_update += ticks * 0.5s def reserve(cost): decay() if points + cost >= command_block_threshold: sleep_until_safe(cost) points += cost ``` For admin tools, rate limiting should be enabled even when the current identity usually has ignore-antiflood rights because permissions can differ across servers. ## 29. Snapshot Format ### 29.1 Snapshot purpose A snapshot is a text representation of a virtual server state. It is useful for logical clone/restore but is not a full instance backup. Included: - Virtual server settings, except port assignment. - Channels. - Client database. - Server groups and channel groups. - Permission assignments. - Group memberships. Excluded: - Uploaded files. - Icons and avatars. - Bans. - Complaints. - Offline messages. - Voice server port assignment. ### 29.2 Top-level structure ```text hash=| ``` Validation: ```pseudo function verify_snapshot(snapshot): prefix, data = split_first(snapshot, '|') expected = parse_hash(prefix) actual = base64(sha1(bytes(data))) return expected == actual ``` When modifying snapshot text, recalculate `hash=`. Be careful with encoding. Historical data may be CESU-8 or malformed UTF-8. ### 29.3 Deployment behavior ```text serversnapshotdeploy ``` Behavior: - If a virtual server is selected with `use`, deployment may replace that selected server. - If no virtual server is selected, deployment may create a new virtual server and return `sid` and `virtualserver_port`. - With mapping options, deployment can return old-to-new channel mappings. ### 29.4 Snapshot parser architecture ```pseudo Snapshot { header_hash: string virtual_server: Map channels: List> clients: List> server_groups: List> channel_groups: List> permissions: List> memberships: List> raw_sections: List } ``` Because snapshot sections can vary by server version, build a permissive parser: 1. Verify hash if possible. 2. Tokenize rows using Query-style escaping. 3. Preserve unknown fields. 4. Preserve section order. 5. Re-serialize unknown fields unchanged unless explicitly transformed. ### 29.5 Migration checklist Before migration: - Export snapshot. - Copy `files/*` if file data matters. - Copy icons and avatars. - Record server port, because snapshot may not preserve it directly. - Export or record bans and complaints separately if needed. After migration: - Deploy snapshot. - Restore files. - Restore icons and avatars. - Check default groups. - Check host banner, host button, and quotas. - Check file transfer permissions. - Check channel password flags. - Run client and channel list comparison. ## 30. File Transfer Protocol ### 30.1 Query setup phase Upload: ```text ftinitupload clientftfid= name= cid= cpw= size= overwrite=<0|1> resume=<0|1> ``` Download: ```text ftinitdownload clientftfid= name= cid= cpw= seekpos= ``` The server responds with a file transfer token, IP, and port. ### 30.2 Binary transfer phase Client opens a connection to returned IP/port and sends the token. Then: - For upload: stream raw bytes to server. - For download: read raw bytes from server. No Query escaping is applied to the binary payload. ### 30.3 Transfer correlation `clientftfid` is client-provided and should be unique per transfer in that client. Use a 16-bit counter and wrap safely. ```pseudo next_clientftfid = (next_clientftfid + 1) mod 65536 ``` ### 30.4 File listing caveats `ftgetfilelist` may return stale or partial sizes immediately after upload. Build retry logic: ```pseudo function wait_until_file_stable(path): last = null stable_count = 0 while stable_count < 2: info = ftgetfileinfo(path) if info.size == last: stable_count += 1 else: stable_count = 0 last = info.size sleep(500ms) ``` ## 31. Icon, Avatar, and Cache Algorithms ### 31.1 Avatar filenames YaTQA describes avatar filenames as derived from the client unique identifier. Use a dedicated function instead of duplicating ad hoc string logic. ```pseudo function avatar_filename(client_uid): return 'avatar_' + base64_hash_client_uid(client_uid) ``` Because exact legacy hash/base64 handling may vary across implementations, validate against known fixtures from a real TS3 client cache before deploying. ### 31.2 Icon IDs Icons are identified through 32-bit values. The same bit pattern can appear as signed or unsigned. ```pseudo function as_signed_32(u): if u >= 2^31: return u - 2^32 return u function as_unsigned_32(i): if i < 0: return i + 2^32 return i ``` Store both forms: ```text IconId { raw_u32 signed_i32 filename } ``` ### 31.3 Cache directory names Server cache folders are derived from server UIDs, essentially using Base64 transformation. Keep this in a separate module because cache paths are platform-specific: ```text Windows: %APPDATA%\TS3Client\cache Windows: %APPDATA%\TS3Client\chats ``` ## 32. Identity and Security Level ### 32.1 Identity concepts A TS3 identity contains key material used to derive the client UID. A server does not rely on username/password for normal voice identity. Instead, authentication is based on the identity key and proof mechanisms. ### 32.2 Security level Security level is proof-of-work: number of leading zero bits in a SHA-1 hash derived from public key material plus a nonce-like integer. ```pseudo function security_level(public_key_bytes, counter_string): digest = sha1(public_key_bytes + ascii(counter_string)) return count_leading_zero_bits_little_bit_order_within_big_endian_bytes(digest) ``` The bit-order detail is important. YaTQA describes big-endian byte order with little-endian bit order for counting leading zeros. ### 32.3 Searching for a higher level ```pseudo function improve_security_level(identity, target_level): counter = identity.counter while true: digest = sha1(identity.public_key + str(counter)) if leading_zero_count(digest) >= target_level: identity.counter = counter return identity counter += 1 ``` Parallel search: ```pseudo thread 0: counters 0, N, 2N, ... thread 1: counters 1, N+1, 2N+1, ... ... thread N-1: counters N-1, 2N-1, 3N-1, ... ``` MahTsIdentity uses Rust parallelism and supports vanity UID searches as well as security-level improvement. The same architecture applies in any language. ### 32.4 Vanity UID search ```pseudo function search_vanity(pattern): while not cancelled: identity = generate_random_identity() uid = compute_uid(identity) if matches(pattern, uid): return identity ``` Support wildcards: ```text _ or ? = any single character literal characters = exact match ``` ### 32.5 Operational guidance - Do not generate extremely high target levels unnecessarily; cost grows exponentially. - Store identities encrypted at rest. - Keep identity export/import compatible with the official client format where possible. - Never log full private identity material. ## 33. Protobuf and Auxiliary Protocols ### 33.1 Varint / BigNum YaTQA describes TeamSpeak’s Protobuf-related BigNum/Varint behavior. Implement standard unsigned varint: ```pseudo function write_varint(x): while true: if x < 128: write_byte(x) return write_byte((x & 0x7F) | 0x80) x = x >> 7 function read_varint(): shift = 0 result = 0 while true: b = read_byte() result |= (b & 0x7F) << shift if (b & 0x80) == 0: return result shift += 7 ``` ### 33.2 Field key ```pseudo wire_type = key & 7 field_number = key >> 3 ``` Common wire types: ```text 0 = varint 1 = 64-bit 2 = length-delimited 5 = 32-bit ``` ### 33.3 Length-delimited field ```pseudo length = read_varint() payload = read_bytes(length) ``` Payload may itself be nested Protobuf. ### 33.4 Update metadata Modern TS3 clients use version metadata downloaded from TeamSpeak version endpoints. Historical versions used UDP messages to update servers. For a modern-compatible updater: 1. Download version metadata. 2. Parse Protobuf entries. 3. Identify stable/beta/alpha client version records. 4. Compare build number and version string. 5. Download platform-specific updater image or installer if needed. Keep this as a compatibility feature, not as a security-sensitive auto-updater unless you verify signatures and transport security. ## 34. DNS and TSDNS Resolution ### 34.1 Preferred SRV record Use `_ts3._udp.` SRV records to map a user-friendly domain to host and port. Example: ```text _ts3._udp.example.com. 3600 IN SRV 0 5 9987 voice.example.com. ``` Resolution algorithm: ```pseudo function resolve_ts3(address): if address contains explicit port: return host, port srv = query_srv('_ts3._udp.' + address) if srv exists: return srv.target, srv.port tsdns = query_srv('_tsdns._tcp.' + parent_domain(address)) if tsdns exists: return query_tsdns(tsdns.target, address) return address, 9987 ``` ### 34.2 TSDNS TSDNS maps names to `host:port` pairs. Modern clients should not rely on old implicit search behavior. Configure explicit `_tsdns._tcp` when TSDNS is required. ### 34.3 Resolver trace For debugging, expose a resolver trace: ```json { "input": "voice.example.com", "explicit_port": false, "srv_query": "_ts3._udp.voice.example.com", "srv_result": null, "tsdns_query": "_tsdns._tcp.example.com", "tsdns_result": "tsdns.example.com:41144", "final": "1.2.3.4:9987" } ``` ## 35. Blacklist and Blacklist2 YaTQA documents blacklist-related protocols as part of non-query TS3 protocols. A safe implementation should treat blacklist calls as an external service integration. ### 35.1 Data model ```text BlacklistQuery { server_ip_v4 optional server_ip_v6 optional domain optional server_uid optional license_key_hash optional } BlacklistResult { blocked: boolean reason optional source: Blacklist | Blacklist2 raw_response } ``` ### 35.2 Guidance - Do not block local administration purely because a blacklist service is unavailable. - Cache negative results briefly. - Treat positive blacklist results as security-sensitive and display details only to authorized admins. - Keep raw responses for debugging, but avoid logging license material. ## 36. Badges ### 36.1 Badge data model ```text Badge { guid: UUID name: string description: string asset_base_url: string details_svg optional icon_64_png optional icon_16_png optional icon_svg optional introduced optional removed optional redemption_code optional } ``` ### 36.2 Client rendering Client badge rendering should: 1. Parse `client_badges` string into badge GUIDs and optional metadata. 2. Resolve GUID to local declaration table. 3. Choose best asset size for UI context. 4. Cache remote images. 5. Display unknown badge GUIDs gracefully. ### 36.3 Badge declaration source Use `tsdeclarations/Badges.csv` as a machine-readable base and supplement with YaTQA badge notes for historical introduced/removed details. ## 37. Low-Level TS3 Client Protocol Architecture The ReSpeak `tsdeclarations` and `tsclientlib` projects show a layered model for implementing a TS3-compatible client. ### 37.1 Layer responsibilities ```text UDP packet layer packet header parsing, packet ID, generation, flags Reliability layer ack tracking, resend, ordering, fragmentation reassembly Crypto/compression layer EAX-style encryption where applicable compression and decompression Handshake layer multi-step init handshake identity proof Bookkeeping layer transform protocol messages into server state Client API layer connect, disconnect, subscribe, send message, audio, channel operations ``` ### 37.2 Packet handling loop ```pseudo while socket open: packet = udp_recv() header = parse_header(packet) if header.encrypted: payload = decrypt(packet.payload) if header.compressed: payload = decompress(payload) if header.fragmented: fragment_buffer.add(header, payload) if not complete: continue payload = fragment_buffer.reassemble(header.fragment_group) message = parse_message(payload) reliability.ack(header.packet_id) dispatch(message) ``` ### 37.3 Sending loop ```pseudo function send_reliable(message): payload = encode_message(message) if should_compress(payload): payload = compress(payload) fragments = fragment_if_needed(payload) for fragment in fragments: packet_id = next_packet_id() packet = build_packet(packet_id, flags, fragment) encrypted = encrypt_if_needed(packet) send_udp(encrypted) reliability.track(packet_id, encrypted) ``` ### 37.4 State bookkeeping The client should not expose raw protocol messages directly to application code. Instead: ```pseudo on protocol_message(msg): events = bookkeeping.apply(msg) for event in events: application_event_bus.emit(event) ``` Example application events: ```text ServerWelcome ChannelCreated ChannelDeleted ChannelMoved ClientEnteredView ClientLeftView ClientMoved TextMessageReceived PermissionChanged AudioPacketReceived ConnectionLost ``` ## 38. Audio Client Implementation Notes A TS3-compatible voice client needs more than Query. ### 38.1 Components ```text Audio input device -> Opus encoder -> packet scheduler -> UDP protocol UDP protocol -> jitter buffer -> Opus decoder -> audio output device ``` ### 38.2 Runtime tasks - Capture microphone frames at the codec-required sample rate. - Encode frames. - Send voice packets at the negotiated interval. - Receive and reorder incoming audio packets. - Decode and mix per-speaker streams. - Apply mute/deafen/talk-power/channel-commander state. ### 38.3 Bot mode A bot often does not need full playback. Minimal bot implementation: - Connect to server. - Maintain state. - Subscribe to text/private/server/channel events. - Optionally capture or play audio. - Avoid joining restricted channels unless configured. ## 39. Statistics Pipeline Use `ts3stats` as the reference for offline log analytics. ### 39.1 Input sources ```text TS3 server logs TS3AudioBot logs optional Settings.py-like configuration ``` ### 39.2 Processing stages ```text Read logs -> parse timestamp and event -> normalize identities -> merge aliases -> construct sessions -> bin by slot length -> render graphs -> generate HTML report ``` ### 39.3 Data model ```text LogEvent { timestamp source: server | bot event_type client_uid optional client_nickname optional channel optional raw_line } ClientSession { client_uid display_name start_time end_time channel_path optional } StatsReport { date_range users total_online_time peak_concurrency daily_series hourly_series vip_breakdown bot_breakdown optional } ``` ### 39.4 Alias merging Support explicit merges: ```python merges = { "old_uid_or_name": "canonical_user", "another_alias": "canonical_user" } ``` Never merge solely on nickname unless the operator confirms because nicknames are mutable. ## 40. Qint-Style Desktop Client Architecture Qint demonstrates a modern replacement client architecture. ### 40.1 Architecture ```text Frontend web UI React/Vue/Svelte-like SPA or similar Tauri shell desktop integration, permissions, windowing Proxy/backend process TS3 connection manager audio integration command interface Protocol library tsclientlib / equivalent ``` ### 40.2 IPC contract Define stable commands between UI and backend: ```json { "cmd": "connect", "server": "voice.example.com", "nickname": "Bot" } { "cmd": "disconnect" } { "cmd": "send_text", "targetMode": "channel", "target": 12, "message": "hello" } { "cmd": "move_client", "clid": 5, "cid": 10 } ``` Backend-to-frontend events: ```json { "event": "client_entered", "client": { "clid": 5, "uid": "...", "nickname": "Alice" } } { "event": "channel_updated", "channel": { "cid": 10, "name": "Lobby" } } { "event": "text_message", "from": "Alice", "message": "hello" } ``` ### 40.3 Production cautions - Treat Qint as architectural reference; verify maintenance status before dependency use. - Keep frontend state derived from backend authoritative state. - Avoid exposing raw identities to frontend logs. - Use platform-specific audio backends behind a common interface. ## 41. Error Handling Specification ### 41.1 Error object ```text Ts3Error { id: integer name: string optional message: string failed_permid optional return_code optional command optional raw_line category retryable: boolean } ``` ### 41.2 Categories ```text 0xxx normal/general 01xx command/network binding 02xx client 03xx channel 04xx server 05xx database 06xx parameter 07xx connection 08xx file 0Axx permissions ``` ### 41.3 Retry policy | Category | Retry? | Notes | |---|---|---| | connection lost | yes | reconnect and resync | | currently not possible | sometimes | retry with backoff | | convert error | no | fix parameter or encoding | | invalid parameter | no | fix command builder | | client is flooding | yes later | apply anti-flood limiter | | insufficient permissions | no | show missing permission | | database empty result | no | treat as empty lookup | | server is booting | yes | poll until ready | ## 42. Test Fixtures and Quality Gates ### 42.1 Protocol tests - Escaping round trip for spaces, slashes, pipes, tabs, newlines. - Array response parsing with escaped pipes. - Error line parsing with return codes. - Command builder output equality. ### 42.2 Permission tests - Multiple server groups with and without negation. - Client server-level override. - Skip flag suppressing channel/group rights. - Channel-client final override. - Power/needed power denial explanation. ### 42.3 Snapshot tests - Verify hash. - Parse and reserialize without changes. - Modify one field and recalculate hash. - Preserve unknown fields. - Handle CESU-8 or invalid Unicode safely. ### 42.4 Event tests - Client enters and leaves. - Client moves with one clid. - Client moves with multiple clids. - Channel edited with incomplete fields. - Duplicate event deduplication. - Resubscription after reconnect. ### 42.5 Identity tests - Security-level count for known digest examples. - Vanity pattern matcher. - Parallel search cancellation. - Identity export redaction in logs. ## 43. Implementation Roadmap ### Phase 1: Query foundation Deliver: - Query escaping codec. - TCP connection lifecycle. - Login and `use -virtual`. - Command builder and parser. - Error dictionary. - Basic `serverinfo`, `clientlist`, `channellist`. ### Phase 2: State and events Deliver: - Notify subscriptions. - Event parser. - State reducer. - Periodic reconciliation. - Text message handling. ### Phase 3: Administration Deliver: - Channel CRUD. - Client move/kick/poke. - Server edit. - Token management. - Ban/complaint management. ### Phase 4: Permissions Deliver: - Permission constants. - Assignment fetchers. - Effective permission engine. - Explanation UI. - Safe permission editor with grant checks. ### Phase 5: Backup and files Deliver: - Snapshot export/import/verify. - File transfer upload/download. - Icon/avatar helpers. - Full migration runbook. ### Phase 6: Identity and stats Deliver: - Identity import/export. - Security-level calculation. - Vanity search. - Log parser. - HTML report generator. ### Phase 7: Full client or bot Deliver: - Client runtime. - Optional audio. - Desktop/web UI. - Reconnect/resync. - Packaging and operational monitoring. ## 44. Security and Operational Hardening ### 44.1 Secrets Never log: - ServerQuery passwords. - Privilege keys before use. - Private identity material. - License keys. - Raw blacklist license fields. ### 44.2 Access control Administration systems should use their own role model on top of TS3 permissions. Do not expose raw ServerQuery credentials to every operator. ### 44.3 Backups - Store snapshots encrypted. - Store file backups with integrity hashes. - Keep at least one offline backup. - Test restore periodically. ### 44.4 Rate limits Apply client-side rate limiting even for privileged users. ### 44.5 Auditing Log: - Who issued each admin action. - Command family, not necessarily raw command with secrets. - Target IDs and names. - Error ID and failed permission. - Snapshot export/import events. ## 45. Final Self-Contained Build Checklist A developer implementing from this manual should be able to check off: - [ ] Query codec supports escaping and array rows. - [ ] Query connection handles login, `use -virtual`, return codes, reconnects, and graceful quit. - [ ] Errors are mapped to categories and retry policies. - [ ] Events are parsed and state is reconciled. - [ ] Client/channel/server variable schemas are generated from declarations. - [ ] Permission engine handles server groups, client rights, channel rights, channel groups, channel-client rights, skip, negated, and grant powers. - [ ] Anti-flood limiter models point decay and action costs. - [ ] Snapshot parser verifies and recalculates `hash=`. - [ ] File transfer supports token setup and raw binary transfer. - [ ] Icon/avatar/cache helpers are isolated and fixture-tested. - [ ] DNS resolver supports explicit host:port, `_ts3._udp`, and explicit `_tsdns._tcp`. - [ ] Identity tool computes UID/security level and supports safe export. - [ ] Stats processor parses logs into sessions and reports. - [ ] UI/backend IPC is typed and does not leak secrets. - [ ] Migration runbooks distinguish snapshots from full instance backups. - [ ] Tests cover protocol, permissions, snapshots, events, identity, and logs. --- # Appendix D - Full YaTQA Resources Link Coverage Review This appendix was added after a page-by-page review of `https://yat.qa/ressourcen/`. Its purpose is to make the manual self-auditing: every direct content link from the YaTQA resources page is either covered in the main manual or explicitly listed as a non-core, external, download-only, unavailable, or historical artifact. This prevents hidden dependencies on the original page. ## D.1 Coverage classification Coverage categories used below: - **Fully covered** - the manual includes the engineering meaning, implementation behavior, and development relevance. - **Covered as external/download artifact** - the resource is not a technical specification page, but the manual records what it is for and how it fits into a development workflow. - **Covered as operational/site reference** - the link is navigation, legal, site metadata, or a support page rather than a protocol/implementation document. - **Not source-analyzable** - the page linked to a binary/download/external host that was not fully accessible in the review environment; the manual records its stated purpose and limitation rather than inventing source details. ## D.2 Direct links from the German YaTQA resources page | Resource/link text | Resource type | Coverage status | What this manual now contains | |---|---:|---:|---| | **Server-Backup/Umzug ohne Snapshot-Rechte** | YaTQA article | Fully covered | Backup and migration without snapshot permission, including command pacing, channel tree export, group export/import, file backup with YaTQA Pro, icon restoration order, and limitations: no server settings and no avatars. | | **Details** under Unicode note | Internal technical anchor | Fully covered | Unicode/CESU-8/BMP handling, legacy TS3 Unicode limitations, malformed Unicode risks, and why some Unicode channel-name tricks require TeaSpeak. | | **Web-basierte Tools / Web-based tools** | Tool page | Fully covered | Avatar filename conversion, icon signed/unsigned 32-bit conversion, server UID to cache directory conversion, and implementation notes. | | **UniChars** | External Unicode channel-name tool | Covered as external/download artifact | Purpose: formatting channel names and other text using Unicode variants; includes the operational warning that important use cases require TeaSpeak and that Windows clients may render taller rows. | | **Windows Unicode example image** | Image example | Covered as external/download artifact | Recorded as a rendering-behavior example for Unicode-styled channel names on Windows. Not treated as a protocol source. | | **Linux Unicode example image** | Image example | Covered as external/download artifact | Recorded as a rendering-behavior example for Unicode-styled channel names on Linux. Not treated as a protocol source. | | **Blacklist2-Webclient** | JavaScript/AJAX tool page | Fully covered | Blacklist2 HTTPS/protobuf query purpose, webclient role, query fields such as IP/domain/UID/license, and implementation placement in blacklist diagnostics tooling. | | **Identities decoder / tsidentity.teaspeak.de** | External identity utility | Covered as external/download artifact | Purpose: decode exported identity strings and extract Omega; limitation: external endpoint was not reliably source-analyzable. Manual links this concept to MahTsIdentity and security-level implementation. | | **DSGVO-Kompatibilitäts-Fix** | SQL download | Covered as external/download artifact | Purpose: remove historical IP logging from the TS3 server database using `UPDATE clients SET client_lastip = "" WHERE 1;`; limitation: manual warns this is unsupported by TeamSpeak and should be treated as a database-maintenance artifact, not normal API behavior. | | **Anydate** | Delphi utility/download | Covered as external/download artifact | Purpose: update/downdate TeamSpeak client to any version >= 3.0.3 for compatibility testing; warning to back up TS3Client AppData because old clients may corrupt it. | | **Client versions / Version** | Reference page | Fully covered | Version-number history, use in compatibility testing, historical download workflow, and caveat that the list is archival. | | **Hashdog** | Security-level utility | Covered as external/download artifact | Purpose: faster security-level improvement; manual connects it to Hashcash-style SHA-1 nonce search and MahTsIdentity implementation. | | **Snapshot Toolkit** | Tool collection | Covered as external/download artifact | Purpose: snapshot conversion, rehashing, and Nitrado legacy snapshot handling; manual lists it under snapshot tooling rather than treating it as a normative TS3 format. | | **Changelog download** under Snapshot Toolkit | Download/list | Covered as external/download artifact | Recorded as a snapshot-toolkit support artifact. Not a protocol specification. | | **Nitrado-Snapshot-Dekoder** | CLI binary | Covered as external/download artifact | Purpose: convert old pre-mid-December-2016 Nitrado `.dat` snapshot files into normal TS3 snapshots; described pipeline: zlib unpack, Base64 clean/decode, Vigenere decode. | | **Nitrado-Snapshot-Dekoder Delphi source** | Source download | Not source-analyzable | Listed as source link; unavailable for full source review in the generated manual. Purpose recorded from the resource page. | | **Nitrado-Snapshot-Enkoder** | CLI binary | Covered as external/download artifact | Purpose: convert normal snapshots into old Nitrado-compatible upload files; described pipeline: Vigenere encode, paragraph removal, BOM removal, Base64 encode/chunk, zlib pack; marked educational/obsolete. | | **Nitrado-Snapshot-Enkoder Delphi source** | Source download | Not source-analyzable | Listed as source link; unavailable for full source review. Purpose recorded from the resource page. | | **Snapshot-SHA-1-Neuberechner / Rehash** | CLI binary | Covered as external/download artifact | Standalone snapshot rehashing tool: removes paragraphs/BOM, recomputes SHA-1, and outputs modified snapshot as a file. | | **Snapshot-SHA-1-Neuberechner Delphi source** | Source download | Not source-analyzable | Listed as source link; unavailable for full source review. Purpose recorded from the resource page. | | **Teamoji** | Delphi utility/download | Covered as external/download artifact | Purpose: generate `emoticons` folder assets for third-party iconpacks from Unicode emoji data; limitations around UCS-4/TeaSpeak and combined emoji prioritization. | | **Unicode Emoji List** | External Unicode data | Covered as external reference | Used as input data for Teamoji; manual records it as upstream data source rather than TS3-specific protocol documentation. | | **Diverse Teamoji packages** | Download package | Covered as external/download artifact | Purpose: ready-made emoji packages using frequently used Twitter emoji plus squirrel; compatibility note: TS3 server >= 3.2.0 or TeaSpeak. | | **Apple Teamoji package** | Download package | Covered as external/download artifact | Purpose: ready-made Apple emoji package with 179 emojis, stated to work on all servers. | | **Server-Fehlercodes / Server Error Codes** | Reference table | Fully covered | Error-code taxonomy, mapping strategy, common codes, and recommendation to generate a typed error enum/dictionary. | | **Rechte-IDs / Permission IDs** | Reference table | Fully covered | Permission IDs, SID naming, grant-ID calculation, historical numbering scheme, permission engine, and upgrade implications. | | **Client-Versionen / Client Versions** | Reference table | Fully covered | Client version/build history, compatibility-testing role, and how tools such as Anydate use it. | | **Definitionen und Algorithmen** | Technical document | Fully covered | Codecs, permission evaluation, query connection behavior, image/icon/avatar naming, snapshot basics, client cache, BBCode, Unicode/encoding caveats. | | **Server-Query-Kommentare** | Technical document | Fully covered | Query behavior corrections, command quirks, token aliases, return codes, `use -virtual`, file-transfer command edge cases, permission-command behavior, and undocumented commands. | | **Server Query Notify** | Technical document | Fully covered | Subscription lifecycle, per-event field lists, duplicate event caveats, tokenused event, client enter/leave, server/channel edits, text-message events. | | **Variablen-Parameter** | Technical matrix | Fully covered | Client/channel/server/traffic variable availability and editability; integrated into typed command/event schema recommendations. | | **Voice-Client-Anti-Flood** | Technical document | Fully covered | Flood point accumulation/decay, tick behavior, block/IP block thresholds, ignore permissions, and action-cost implementation model. | | **Sicherheitsstufe** | Technical document | Fully covered | Hashcash-like SHA-1 leading-zero model, public key/Omega/nonces, expected cost, faster generation approaches, and MahTsIdentity integration. | | **Snapshots** | Technical document | Fully covered | Snapshot contents/exclusions, `hash=` header, pipe separator, Base64-SHA1, field families, deploy semantics, mapping behavior, and limitations. | | **Strings** | Placeholder | Covered as absent/planned | The resources page states this was planned; no released document exists to cover. | | **Andere Protokolle / Protokolle** | Technical document | Fully covered | See Section 16: Protobuf, update protocol, file-transfer protocol, TSDNS, server nicknames, DNS/SRV behavior, Blacklist/Blacklist2, Weblist, badges, and TLD/public-suffix appendix. | | **Abzeichen-Liste / Badges list** | Technical/list document | Fully covered | Badge GUIDs, asset URL bases, client field behavior, history/removed badges, and implementation guidance for rendering/validation. | | **Impressum** | Legal/site page | Covered as operational/site reference | Listed as site/legal metadata, not an implementation source. | | **Header/footer navigation: Info & Download, Funktionen, Manual, Changelog, FAQ, Support & Contact, Resources, About, English** | Site navigation | Covered as operational/site reference | These are navigational pages. The manual uses relevant operational facts from YaTQA manual/download/FAQ where they affect implementation, but they are not direct resource-document specifications. | ## D.3 Protocol page internal coverage checklist The link `https://yat.qa/ressourcen/protokolle/` is now specifically covered by Section 16. Its internal topics are mapped as follows: | Protokolle topic | Coverage in this manual | |---|---| | Protobuf | Field tags, wire types used by TS3, Varint/BigNum encoding/decoding, length-delimited data, repeated fields, and parser strategy. | | Updates | Historical UDP update request styles, later Protobuf version files, version-type records, updater image/mirror concept, and compatibility guidance. | | File transfers | `ftinitupload`/`ftinitdownload` token exchange, raw byte stream transfer, upload/download directionality, file-list edge cases, and channel file backup implications. | | TSDNS | Query formats, old/new client behavior, explicit `_tsdns._tcp` requirement, compatibility pitfalls. | | Server nicknames (3.1.6+) | Treated as resolver-level aliases and included in DNS/TSDNS decision logic. | | DNS | `_ts3._udp` SRV behavior, priority/weight/port/target semantics, and input-normalization guidance. | | Blacklist | Legacy blacklist behavior summarized as historical compatibility context. | | Blacklist2 | HTTPS + Protobuf query shape, relevant fields, diagnostics use, and cautions against relying on unofficial blacklist behavior for security decisions. | | Weblist (server) | Server publication/listing behavior and implementation role. | | Weblist (client) | Client-side retrieval/consumption role and noncriticality for private administration tools. | | Badges | Badges field behavior, GUID list source, asset URL bases, and rendering strategy. | | Disabling badges on an instance | Included as administrative/compatibility behavior rather than core protocol. | | Historical TLD appendices | Covered as a compatibility-only appendix: old embedded TLD/public suffix behavior should not be used as a modern public suffix list. | ## D.4 Review outcome After this review, every direct link or listed artifact from the YaTQA German resources page is now represented in the manual in one of the following forms: 1. as a full technical implementation section; 2. as a tool/download artifact with purpose, pipeline, and limitation; 3. as a site/navigation/legal reference; 4. as an explicitly absent/planned resource where no released document exists. The important correction from v3 is that several helper artifacts were previously only implicit or missing by name: UniChars, DSGVO SQL, Hashdog, Snapshot Toolkit, Nitrado decoder/encoder, Teamoji, Unicode emoji source data, and ready-made emoji packages. They are now explicitly listed and classified in this appendix.