Files
chanora/docs/ts3_development_manual_en_v6.md
T

153 KiB
Raw Blame History

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:

+--------------------------------------------------------------+
| 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:

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:

# 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:

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.

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.

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:

command [parameters] [options]

Examples:

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:

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:

clid=5 client_nickname=Alice
error id=0 msg=ok

Rows may be separated by |:

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:

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

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

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

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

serverdelete sid=2

Server editing

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:

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:

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:

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:

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:

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.

def grant_permission_id(permission_id: int) -> int:
    return permission_id + 32768

Example:

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:

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.

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:

{
  "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:

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:

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:

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:

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:

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:

# 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.

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:

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:

hash=<base64_sha1>|<snapshot-data>

The hash is a Base64-encoded SHA-1 of the data after the | separator.

Verification:

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:

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:

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.

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.

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

Use _ts3._udp for voice service discovery:

_ts3._udp.example.com. 86400 IN SRV 0 5 9987 voice.example.com.

If using TSDNS indirection:

_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.<domain> 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:

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.<name> 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.<domain> 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

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

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

_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

# 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

_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

_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

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.

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

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:

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:

field_key || field_value

The field key itself is a BigNum. Decode it as:

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

function decode_ts_protobuf(buffer):
    offset = 0
    result = map<int, list<Value>>()
    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:

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.

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:

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:

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

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:

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:

<encoded-name>\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

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:

https://named.myteamspeak.com/lookup?name=<query>

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 :<port>.
  • 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:

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.<input>. The SRV result's port overrides the user's typed port.
  2. SRV TSDNS: query _tsdns._tcp.<domain>, 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:

_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

Destination: blacklist.teamspeak.com:17385 UDP
Payload:     ip4:<resolved-ip-address-as-text>

Examples:

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:

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

POST /check HTTP/1.1

Host: blacklist2.teamspeak.com

User-Agent: TeamHttp/1.1

Connection: keep-alive

Content-Type: application/x-ts3blacklist

Content-Length: <length>



<protobuf payload>

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:

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:

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:

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:

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

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

CSV/TOML declarations
  -> parser
  -> intermediate schema
  -> generated language types
  -> command builders
  -> response parsers
  -> documentation tables

Generated command example:

pub struct ServerEdit {
    pub virtualserver_name: Option<String>,
    pub virtualserver_maxclients: Option<u32>,
    pub virtualserver_password: Option<String>,
    // ...
}

Generated error example:

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:

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:

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:

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:

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:

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:

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:

{
  "timestamp": "2026-05-23T12:00:00Z",
  "type": "client_connected",
  "client_uid": "...",
  "client_name": "Alice",
  "channel_id": 12
}

Then build sessions:

{
  "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:

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:

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:

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.

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:

ReSpeak repositories:

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.

A production-grade project should be modular. The following layout works for Rust, TypeScript, Go, Java, C#, or Python with minor naming changes.

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.

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:

VirtualServerPath:
  sid: ServerId
  port: ServerPort optional

ChannelFilePath:
  channel_id: ChannelId
  path: string

IconPath:
  /icon_<signed-or-unsigned-icon-id>

AvatarPath:
  /avatar_<base64-or-hash-derived-client-uid>

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

command [parameter=value ...] [array-row|array-row ...]

Typical examples:

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:

key=value key=value|key=value key=value
error id=0 msg=ok

Typical failed response:

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 ` `
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

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

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:

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:

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=<id> -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

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

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

channellist
channelinfo
channelfind
channelcreate
channeldelete
channeledit
channelmove
channelpermlist
channeladdperm
channeldelperm

State model:

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

clientlist
clientinfo
clientfind
clientmove
clientkick
clientpoke
clientupdate
clientedit
clientdblist
clientdbinfo
clientdbfind
clientdbedit
clientgetdbidfromuid
clientgetnamefromuid
clientgetnamefromdbid
clientgetuidfromclid

State model:

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

sendtextmessage targetmode=<1|2|3> target=<id> msg=<text>

Target modes commonly map to:

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

permissionlist
permidgetbyname
permoverview
permget
permfind
servergroupaddperm
servergroupdelperm
clientaddperm
clientdelperm
channeladdperm
channeldelperm
channelgroupaddperm
channelgroupdelperm
channelclientaddperm
channelclientdelperm

Recommended API:

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:

privilegekeyadd
privilegekeydelete
privilegekeylist
privilegekeyuse

Legacy aliases:

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

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.:

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:

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

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:

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:

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

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

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:

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_<permission>

When displaying “why cant I do this?” always show both sides:

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

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:

client_current_points = max(0, client_current_points - points_tick_reduce)

28.3 Action model

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

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

hash=<base64-sha1>|<snapshot-data>

Validation:

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

serversnapshotdeploy <snapshot>

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

Snapshot {
  header_hash: string
  virtual_server: Map<string,string>
  channels: List<Map<string,string>>
  clients: List<Map<string,string>>
  server_groups: List<Map<string,string>>
  channel_groups: List<Map<string,string>>
  permissions: List<Map<string,string>>
  memberships: List<Map<string,string>>
  raw_sections: List<SnapshotSection>
}

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:

ftinitupload clientftfid=<id> name=<path> cid=<channel> cpw=<password> size=<bytes> overwrite=<0|1> resume=<0|1>

Download:

ftinitdownload clientftfid=<id> name=<path> cid=<channel> cpw=<password> seekpos=<offset>

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.

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:

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.

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.

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:

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:

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.

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

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:

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.

function search_vanity(pattern):
    while not cancelled:
        identity = generate_random_identity()
        uid = compute_uid(identity)
        if matches(pattern, uid):
            return identity

Support wildcards:

_ 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 TeamSpeaks Protobuf-related BigNum/Varint behavior. Implement standard unsigned varint:

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

wire_type = key & 7
field_number = key >> 3

Common wire types:

0 = varint
1 = 64-bit
2 = length-delimited
5 = 32-bit

33.3 Length-delimited field

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.<domain> SRV records to map a user-friendly domain to host and port.

Example:

_ts3._udp.example.com. 3600 IN SRV 0 5 9987 voice.example.com.

Resolution algorithm:

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:

{
  "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

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

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

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

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

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:

on protocol_message(msg):
    events = bookkeeping.apply(msg)
    for event in events:
        application_event_bus.emit(event)

Example application events:

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

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

TS3 server logs
TS3AudioBot logs optional
Settings.py-like configuration

39.2 Processing stages

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

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:

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

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:

{ "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:

{ "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

Ts3Error {
  id: integer
  name: string optional
  message: string
  failed_permid optional
  return_code optional
  command optional
  raw_line
  category
  retryable: boolean
}

41.2 Categories

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.
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.