153 KiB
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
protokollepage: 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 underhttps://yat.qa/resources/. - ReSpeak repositories:
ReSpeak/tsclientlibReSpeak/ts3statsReSpeak/tsdeclarationsReSpeak/QintReSpeak/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:
- A ServerQuery administration system.
- A TeamSpeak 3-compatible bot or custom client.
- A typed command and event schema for TS3.
- Snapshot backup, migration, and recovery tooling.
- Identity generation, vanity UID search, or security-level improvement.
- Statistics reports from TS3 and TS3AudioBot logs.
- 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:
- Generate or centralize constants from
tsdeclarations-style data. - Implement ServerQuery as a strongly typed command system.
- Implement Query event subscriptions and state reducers.
- Implement permission evaluation separately from display logic.
- Implement snapshots and file migration separately because snapshots are not full backups.
- Add identity and security-level tooling as an offline worker.
- 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:
serveradminpassword.- 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.
4.3 Recommended development configuration
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:
- Read until an
error id=...line. - Split data lines by unescaped
|. - Split row fields by unescaped spaces.
- Split key/value by the first
=. - Unescape values.
- 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_namevirtualserver_welcomemessagevirtualserver_maxclientsvirtualserver_passwordvirtualserver_hostmessagevirtualserver_hostmessage_modevirtualserver_hostbanner_urlvirtualserver_hostbanner_gfx_urlvirtualserver_hostbanner_gfx_intervalvirtualserver_hostbutton_tooltipvirtualserver_hostbutton_urlvirtualserver_hostbutton_gfx_urlvirtualserver_download_quotavirtualserver_upload_quotavirtualserver_max_download_total_bandwidthvirtualserver_max_upload_total_bandwidthvirtualserver_antiflood_points_tick_reducevirtualserver_antiflood_points_needed_command_blockvirtualserver_antiflood_points_needed_ip_blockvirtualserver_needed_identity_security_level- log toggles such as
virtualserver_log_client,virtualserver_log_query, etc.
Token / privilege key commands
Historical command names may vary:
privilegekeyaddprivilegekeylistprivilegekeydeleteprivilegekeyuse
Aliases documented by YaTQA include token-style names:
tokenaddtokenlisttokendeletetokenuse
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=channelrequiresid.id=0for 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_identifierclient_nicknameclient_database_idclient_channel_group_idclient_servergroupsclient_type- 0 voice, 1 query.client_flag_avatarclient_talk_powerclient_icon_idclient_countryclient_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:
cfidctid- often 0 when leaving server.reasonidinvokeridinvokernameinvokeruidreasonmsgbantimeclid
notifyclientmoved
Used for client channel movement.
Important fields:
clidctidcfidreasonid- 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_identifierfor identity-level persistence.client_database_idfor server-local account persistence.clidonly 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:
- Server groups.
- Client permissions at server level.
- Channel permissions, unless skipped.
- Channel group permissions, unless skipped.
- 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_powervs. target required kick power.i_channel_join_powervs.i_channel_needed_join_power.i_group_member_add_powervs. group needed member add power.
8.5 Upgrade behavior
TeamSpeak permission upgrades may depend on metadata permissions such as i_group_auto_update_type. If you customize template or default groups, verify that permission upgrades still work as intended.
8.6 Recommended implementation
Build a permission engine as a standalone library with:
- Versioned permission constants.
- Grant ID mapping.
- Permission assignment records.
- Effective permission evaluator.
- Explanation trace.
An explanation trace is critical for admin UI:
{
"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_reducepoints. - 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
channelsubscribeallloops 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:
- Keep the identity keypair fixed.
- Iterate candidate offsets.
- Compute SHA-1 for each offset.
- Count leading zero bits.
- 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.
11.4 Vanity UID search
A vanity UID search tries to find an identity whose UID matches a pattern. MahTsIdentity supports wildcard-like matching where _ acts as a wildcard.
Conceptual usage:
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_identifiervirtualserver_namevirtualserver_welcomemessagevirtualserver_maxclientsvirtualserver_passwordvirtualserver_createdvirtualserver_codec_encryption_modevirtualserver_keypairvirtualserver_hostmessagevirtualserver_hostmessage_modevirtualserver_default_server_groupvirtualserver_default_channel_groupvirtualserver_default_channel_admin_groupvirtualserver_max_download_total_bandwidthvirtualserver_max_upload_total_bandwidthvirtualserver_antiflood_points_tick_reducevirtualserver_antiflood_points_needed_command_blockvirtualserver_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.sqlitedbfiles/*query_ip_allowlistor equivalent allow filequery_ip_denylistor equivalent deny file- server
.inifiles tsdns/tsdns_settings.iniif 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:
- Use Query command such as
ftinituploadorftinitdownload. - Receive transfer key and connection details.
- Open file-transfer TCP connection.
- Send/use the transfer key as expected by the file-transfer protocol.
- Stream bytes.
- 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
15.1 Recommended SRV records
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:
- Direct host:port if the user provides a port.
_ts3._udp.<domain>SRV lookup.- TSDNS lookup if configured or required.
- 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?
-
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._udpor A/AAAA/CNAME is simpler and easier to debug.
-
If you have exactly one virtual server:
- 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:portunless another discovery mechanism is added. - No: use SRV TS3.
- Yes: use A/AAAA or CNAME, but users must connect with
- No: use SRV TS3.
- Yes: ask whether all services under the relevant domain run on the same machine as the TS3 server.
- Yes: ask whether all services under the relevant domain run on the same machine as the TS3 server.
- Ask: Does the server use the standard port
-
If you have more than one virtual server:
- 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, andstaff.example.comeach get their own_ts3._udpSRV record pointing to the correct host and port.
- Example:
- 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:portentries unless SRV is added per subdomain.
- Practical interpretation: each physical server gets a hostname, and users either use default ports or explicit
- Only one machine: ask whether all services under the relevant domain run on the same machine as the TS3 server.
- No: use subdomains with SRV TS3 for every subdomain.
- Ask: Do you like port numbers? In other words, are users expected to type ports manually?
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:
- How many virtual servers do you operate under this domain?
- Does the selected server use UDP voice port
9987? - Are users allowed to type a port manually?
- Do web, mail, and other services for this domain run on the same host as TeamSpeak?
- Are the TeamSpeak servers on one physical host or multiple physical hosts?
- 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._udprecords use UDP and the actual voice port._tsdns._tcprecords use TCP and normally port41144.- 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:
- Identify the update file generation.
- Try gzip when the generation matches the documented old/current updater format.
- Reject unknown formats rather than guessing.
- 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:
- SRV TS3: query
_ts3._udp.<input>. The SRV result's port overrides the user's typed port. - SRV TSDNS: query
_tsdns._tcp.<domain>, where the candidate domain depends on the client version and public-suffix logic. - TSDNS: query candidate domains on TCP port 41144.
- 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._udpfor 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._udpwhenever 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
3030instead of0/0000(0x30is 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
varcharinsettings.db. - The same
badgestable 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:
- Packet parser/serializer tests.
- Crypto handshake tests.
- ACK/retransmit tests.
- Bookkeeping/state tests.
- 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:
- Put TeamSpeak server logs in
Logs/. - Optionally put TS3AudioBot logs in
BotLogs/. - Create
Settings.py. - Run
CreateTimeGraphs.py. - 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
channeleditonly 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,servergrouplistsupport.- 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:portexplicitly. - Check firewall/NAT.
22.2 Invalid client ID (512)
Likely cause: persisted clid from a previous session.
Fix:
- Refresh with
clientlist. - Store
client_unique_identifierorclient_database_idfor 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
-1where 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_permidif 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
serveradminfor applications. - Redact tokens from logs after creation.
- Rate-limit public admin APIs to protect the TS3 server from command floods.
- Validate all user-provided server/channel/client names before passing to Query commands.
- Keep file-transfer paths constrained and normalized.
24. Recommended Engineering Plan
Phase 1: Data and declarations
- Import errors and permissions.
- Define command schemas.
- Build escape/unescape and parser tests.
Phase 2: Query client
- Implement transport.
- Implement login/use/serverinfo/clientlist/channellist.
- Implement typed command builder.
- Implement error handling.
Phase 3: Event-driven state
- Add notifications.
- Build state reducer.
- Add deduplication.
- Add reconnect and resubscribe behavior.
Phase 4: Admin features
- Add channels, groups, permissions, tokens, bans, complaints.
- Add permission explanations.
- Add audit log.
Phase 5: Backup and migration
- Add snapshot create/deploy/verify.
- Add file-transfer backup.
- Add full migration checklist.
Phase 6: Identity and client protocol
- Add identity import/export.
- Add security-level worker.
- If needed, integrate
tsclientlibor equivalent UDP protocol stack.
Phase 7: Analytics
- Add log ingestion.
- Add report generation.
- Add scheduled offline jobs.
25. Reference URLs
Primary resources:
- https://yat.qa/ressourcen/
- https://yat.qa/resources/
- https://yat.qa/resources/server-error-codes/
- https://yat.qa/resources/permission-ids/
- https://yat.qa/resources/client-versions/
- https://yat.qa/ressourcen/definitionen-und-algorithmen/
- https://yat.qa/ressourcen/server-query-kommentare/
- https://yat.qa/ressourcen/server-query-notify/
- https://yat.qa/ressourcen/variablen-parameter/
- https://yat.qa/ressourcen/voice-client-anti-flood/
- https://yat.qa/ressourcen/sicherheitsstufe/
- https://yat.qa/ressourcen/snapshots/
- https://yat.qa/ressourcen/protokolle/
- https://yat.qa/ressourcen/abzeichen-badges/
ReSpeak repositories:
- https://github.com/ReSpeak/tsclientlib
- https://github.com/ReSpeak/ts3stats
- https://github.com/ReSpeak/tsdeclarations
- https://github.com/ReSpeak/Qint
- https://github.com/ReSpeak/MahTsIdentity/
Official support references:
- TeamSpeak 3 support category
- TeamSpeak 3 ports
- TeamSpeak 3 server setup
- TeamSpeak 3 DNS SRV records
- TeamSpeak 3 snapshots
- TeamSpeak 3 migration
- TeamSpeak 3 plugin SDK guidance
26. Final Notes
This manual should be sufficient to start serious TeamSpeak 3 development without re-reading the source websites for every implementation choice. For exact binary compatibility in the low-level UDP client protocol, the best path is still to use or study tsclientlib and tsdeclarations directly, because the protocol includes encryption, compression, fragmentation, acknowledgement, and handshake behavior that must be tested against a real server.
For administration tooling, YaTQA's Query notes and variable matrices are the most important sources of real behavior. For runtime clients, ReSpeak's Rust stack is the strongest public implementation reference. For migration and recovery, always remember that snapshots are not complete backups.
Expanded Engineering Specification (Version 2)
This version expands the earlier manual into a more implementation-oriented reference. It is written to be used as a build specification for a TeamSpeak 3-compatible administration system, ServerQuery client, bot framework, identity utility, migration tool, and log statistics pipeline.
21. Design Goals and Non-Goals
21.1 Design goals
A complete TS3 development stack should support:
- 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.
- Event-driven state tracking: maintain an in-memory model of servers, channels, clients, groups, permissions, and text events using
servernotifyregisterand periodic reconciliation. - Typed protocol declarations: generate message builders, response parsers, event models, and validation metadata from machine-readable schemas such as
tsdeclarations. - Permission explanation: compute and explain effective permissions, not just read assigned permissions.
- Safe backup and restore: distinguish between logical snapshots, file system assets, instance database copies, and YaTQA-style partial backup when snapshot permission is missing.
- Identity tooling: import/export identities, compute UID, compute and raise security level, and optionally search vanity UID prefixes or patterns.
- Statistics tooling: parse server logs and bot logs into time ranges, session graphs, client presence records, and usage summaries.
- Compatibility awareness: handle old TS3 quirks such as CESU-8, inconsistent Query documentation, historical DNS/TSDNS resolution differences, and snapshot hash recalculation.
21.2 Non-goals
A self-contained TS3-compatible system still should not pretend to replace proprietary or unavailable components where public material is incomplete. Specifically:
- The official TeamSpeak voice codec implementation and exact proprietary client behavior cannot be fully reconstructed from YaTQA alone.
- ClientQuery is not documented as thoroughly as ServerQuery in the public YaTQA resources.
- Some YaTQA downloads are historical binaries or inaccessible in a crawler; their existence and intended function can be documented, but their source cannot be reconstructed honestly without access to the downloads.
- Official Plugin SDK details are outside the public YaTQA material and should be taken from the TeamSpeak Plugin SDK package when building native plugins.
22. Recommended Project Layout
A production-grade project should be modular. The following layout works for Rust, TypeScript, Go, Java, C#, or Python with minor naming changes.
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
quitbefore closing. - Use
use sid=<id> -virtualas a stable server selection pattern. - Resubscribe notifications after login, relogin, or server switch.
- Reconcile state with periodic
serverinfo,channellist, andclientlistbecause 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:
serverlistshould support flags for UID, short view, all servers, and only running servers depending on server version.servercreatereturns at leastsid,virtualserver_port, and initial token.serverdeletemust stop a server before deletion if required by server state.serverstartandserverstopoperate on virtual servers bysid.
25.2 Virtual server commands
use
serverinfo
serverrequestconnectioninfo
serveredit
servertemppasswordadd
servertemppassworddel
servertemppasswordlist
servergrouplist
servergroupadd
servergroupdel
servergroupcopy
servergroupaddperm
servergroupdelperm
servergrouppermlist
Key implementation notes:
serverinforeturns more connection and packet statistics than some related commands.- Some quota and bandwidth values may appear as huge unsigned 64-bit values representing
-1in another interpretation. Display them as “unlimited” when appropriate rather than blindly showing 18446744073709551615. servereditaccepts 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:
- Server groups
- Client permissions on server level
- Channel permissions
- Channel group permissions
- 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 can’t 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
sidandvirtualserver_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:
- Verify hash if possible.
- Tokenize rows using Query-style escaping.
- Preserve unknown fields.
- Preserve section order.
- 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.
32.4 Vanity UID search
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 TeamSpeak’s 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:
- Download version metadata.
- Parse Protobuf entries.
- Identify stable/beta/alpha client version records.
- Compare build number and version string.
- 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:
- Parse
client_badgesstring into badge GUIDs and optional metadata. - Resolve GUID to local declaration table.
- Choose best asset size for UI context.
- Cache remote images.
- 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.
D.2 Direct links from the German YaTQA resources page
| Resource/link text | Resource type | Coverage status | What this manual now contains |
|---|---|---|---|
| Server-Backup/Umzug ohne Snapshot-Rechte | YaTQA article | Fully covered | Backup and migration without snapshot permission, including command pacing, channel tree export, group export/import, file backup with YaTQA Pro, icon restoration order, and limitations: no server settings and no avatars. |
| Details under Unicode note | Internal technical anchor | Fully covered | Unicode/CESU-8/BMP handling, legacy TS3 Unicode limitations, malformed Unicode risks, and why some Unicode channel-name tricks require TeaSpeak. |
| Web-basierte Tools / Web-based tools | Tool page | Fully covered | Avatar filename conversion, icon signed/unsigned 32-bit conversion, server UID to cache directory conversion, and implementation notes. |
| UniChars | External Unicode channel-name tool | Covered as external/download artifact | Purpose: formatting channel names and other text using Unicode variants; includes the operational warning that important use cases require TeaSpeak and that Windows clients may render taller rows. |
| Windows Unicode example image | Image example | Covered as external/download artifact | Recorded as a rendering-behavior example for Unicode-styled channel names on Windows. Not treated as a protocol source. |
| Linux Unicode example image | Image example | Covered as external/download artifact | Recorded as a rendering-behavior example for Unicode-styled channel names on Linux. Not treated as a protocol source. |
| Blacklist2-Webclient | JavaScript/AJAX tool page | Fully covered | Blacklist2 HTTPS/protobuf query purpose, webclient role, query fields such as IP/domain/UID/license, and implementation placement in blacklist diagnostics tooling. |
| Identities decoder / tsidentity.teaspeak.de | External identity utility | Covered as external/download artifact | Purpose: decode exported identity strings and extract Omega; limitation: external endpoint was not reliably source-analyzable. Manual links this concept to MahTsIdentity and security-level implementation. |
| DSGVO-Kompatibilitäts-Fix | SQL download | Covered as external/download artifact | Purpose: remove historical IP logging from the TS3 server database using UPDATE clients SET client_lastip = "" WHERE 1;; limitation: manual warns this is unsupported by TeamSpeak and should be treated as a database-maintenance artifact, not normal API behavior. |
| Anydate | Delphi utility/download | Covered as external/download artifact | Purpose: update/downdate TeamSpeak client to any version >= 3.0.3 for compatibility testing; warning to back up TS3Client AppData because old clients may corrupt it. |
| Client versions / Version | Reference page | Fully covered | Version-number history, use in compatibility testing, historical download workflow, and caveat that the list is archival. |
| Hashdog | Security-level utility | Covered as external/download artifact | Purpose: faster security-level improvement; manual connects it to Hashcash-style SHA-1 nonce search and MahTsIdentity implementation. |
| Snapshot Toolkit | Tool collection | Covered as external/download artifact | Purpose: snapshot conversion, rehashing, and Nitrado legacy snapshot handling; manual lists it under snapshot tooling rather than treating it as a normative TS3 format. |
| Changelog download under Snapshot Toolkit | Download/list | Covered as external/download artifact | Recorded as a snapshot-toolkit support artifact. Not a protocol specification. |
| Nitrado-Snapshot-Dekoder | CLI binary | Covered as external/download artifact | Purpose: convert old pre-mid-December-2016 Nitrado .dat snapshot files into normal TS3 snapshots; described pipeline: zlib unpack, Base64 clean/decode, Vigenere decode. |
| Nitrado-Snapshot-Dekoder Delphi source | Source download | Not source-analyzable | Listed as source link; unavailable for full source review in the generated manual. Purpose recorded from the resource page. |
| Nitrado-Snapshot-Enkoder | CLI binary | Covered as external/download artifact | Purpose: convert normal snapshots into old Nitrado-compatible upload files; described pipeline: Vigenere encode, paragraph removal, BOM removal, Base64 encode/chunk, zlib pack; marked educational/obsolete. |
| Nitrado-Snapshot-Enkoder Delphi source | Source download | Not source-analyzable | Listed as source link; unavailable for full source review. Purpose recorded from the resource page. |
| Snapshot-SHA-1-Neuberechner / Rehash | CLI binary | Covered as external/download artifact | Standalone snapshot rehashing tool: removes paragraphs/BOM, recomputes SHA-1, and outputs modified snapshot as a file. |
| Snapshot-SHA-1-Neuberechner Delphi source | Source download | Not source-analyzable | Listed as source link; unavailable for full source review. Purpose recorded from the resource page. |
| Teamoji | Delphi utility/download | Covered as external/download artifact | Purpose: generate emoticons folder assets for third-party iconpacks from Unicode emoji data; limitations around UCS-4/TeaSpeak and combined emoji prioritization. |
| Unicode Emoji List | External Unicode data | Covered as external reference | Used as input data for Teamoji; manual records it as upstream data source rather than TS3-specific protocol documentation. |
| Diverse Teamoji packages | Download package | Covered as external/download artifact | Purpose: ready-made emoji packages using frequently used Twitter emoji plus squirrel; compatibility note: TS3 server >= 3.2.0 or TeaSpeak. |
| Apple Teamoji package | Download package | Covered as external/download artifact | Purpose: ready-made Apple emoji package with 179 emojis, stated to work on all servers. |
| Server-Fehlercodes / Server Error Codes | Reference table | Fully covered | Error-code taxonomy, mapping strategy, common codes, and recommendation to generate a typed error enum/dictionary. |
| Rechte-IDs / Permission IDs | Reference table | Fully covered | Permission IDs, SID naming, grant-ID calculation, historical numbering scheme, permission engine, and upgrade implications. |
| Client-Versionen / Client Versions | Reference table | Fully covered | Client version/build history, compatibility-testing role, and how tools such as Anydate use it. |
| Definitionen und Algorithmen | Technical document | Fully covered | Codecs, permission evaluation, query connection behavior, image/icon/avatar naming, snapshot basics, client cache, BBCode, Unicode/encoding caveats. |
| Server-Query-Kommentare | Technical document | Fully covered | Query behavior corrections, command quirks, token aliases, return codes, use -virtual, file-transfer command edge cases, permission-command behavior, and undocumented commands. |
| Server Query Notify | Technical document | Fully covered | Subscription lifecycle, per-event field lists, duplicate event caveats, tokenused event, client enter/leave, server/channel edits, text-message events. |
| Variablen-Parameter | Technical matrix | Fully covered | Client/channel/server/traffic variable availability and editability; integrated into typed command/event schema recommendations. |
| Voice-Client-Anti-Flood | Technical document | Fully covered | Flood point accumulation/decay, tick behavior, block/IP block thresholds, ignore permissions, and action-cost implementation model. |
| Sicherheitsstufe | Technical document | Fully covered | Hashcash-like SHA-1 leading-zero model, public key/Omega/nonces, expected cost, faster generation approaches, and MahTsIdentity integration. |
| Snapshots | Technical document | Fully covered | Snapshot contents/exclusions, hash= header, pipe separator, Base64-SHA1, field families, deploy semantics, mapping behavior, and limitations. |
| Strings | Placeholder | Covered as absent/planned | The resources page states this was planned; no released document exists to cover. |
| Andere Protokolle / Protokolle | Technical document | Fully covered | See Section 16: Protobuf, update protocol, file-transfer protocol, TSDNS, server nicknames, DNS/SRV behavior, Blacklist/Blacklist2, Weblist, badges, and TLD/public-suffix appendix. |
| Abzeichen-Liste / Badges list | Technical/list document | Fully covered | Badge GUIDs, asset URL bases, client field behavior, history/removed badges, and implementation guidance for rendering/validation. |
| Impressum | Legal/site page | Covered as operational/site reference | Listed as site/legal metadata, not an implementation source. |
| Header/footer navigation: Info & Download, Funktionen, Manual, Changelog, FAQ, Support & Contact, Resources, About, English | Site navigation | Covered as operational/site reference | These are navigational pages. The manual uses relevant operational facts from YaTQA manual/download/FAQ where they affect implementation, but they are not direct resource-document specifications. |
D.3 Protocol page internal coverage checklist
The link https://yat.qa/ressourcen/protokolle/ is now specifically covered by Section 16. Its internal topics are mapped as follows:
| Protokolle topic | Coverage in this manual |
|---|---|
| Protobuf | Field tags, wire types used by TS3, Varint/BigNum encoding/decoding, length-delimited data, repeated fields, and parser strategy. |
| Updates | Historical UDP update request styles, later Protobuf version files, version-type records, updater image/mirror concept, and compatibility guidance. |
| File transfers | ftinitupload/ftinitdownload token exchange, raw byte stream transfer, upload/download directionality, file-list edge cases, and channel file backup implications. |
| TSDNS | Query formats, old/new client behavior, explicit _tsdns._tcp requirement, compatibility pitfalls. |
| Server nicknames (3.1.6+) | Treated as resolver-level aliases and included in DNS/TSDNS decision logic. |
| DNS | _ts3._udp SRV behavior, priority/weight/port/target semantics, and input-normalization guidance. |
| Blacklist | Legacy blacklist behavior summarized as historical compatibility context. |
| Blacklist2 | HTTPS + Protobuf query shape, relevant fields, diagnostics use, and cautions against relying on unofficial blacklist behavior for security decisions. |
| Weblist (server) | Server publication/listing behavior and implementation role. |
| Weblist (client) | Client-side retrieval/consumption role and noncriticality for private administration tools. |
| Badges | Badges field behavior, GUID list source, asset URL bases, and rendering strategy. |
| Disabling badges on an instance | Included as administrative/compatibility behavior rather than core protocol. |
| Historical TLD appendices | Covered as a compatibility-only appendix: old embedded TLD/public suffix behavior should not be used as a modern public suffix list. |
D.4 Review outcome
After this review, every direct link or listed artifact from the YaTQA German resources page is now represented in the manual in one of the following forms:
- as a full technical implementation section;
- as a tool/download artifact with purpose, pipeline, and limitation;
- as a site/navigation/legal reference;
- 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.