No description
  • Rust 78.2%
  • HTML 21.3%
  • Dockerfile 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Julian Imhof 15b835b147
All checks were successful
docker / build (push) Successful in 3m31s
Walk a full session through the myTeamSpeak account services
New mytsaccount check (needs MYTS_EMAIL/MYTS_PASSWORD, skipped without):
log in with a session, validate it, read the settings sync
(requestServerItems), the badge service (getBadges) and the chat backend
(getActiveIdentifierList), then log out with deleteSession. A green check
means those backends served a real account, not just that login answers.
Every run opens and closes one session on the account.

All calls were verified against the live API: session validation answers
ErrorCommon 102, the sync status 300/301/302 all mean the service works,
getBadges lists badges with no error field on success, and the tschat
error_handling must carry ChatRequestReturnCode 1 (SUCCESS). The password
hash question is settled too: the client's binary salts PBKDF2 with
lower(email)+"ts3Login"+password for 10000 rounds, exactly like the web
app. Matrix credentials stay unreadable: the sync items are end-to-end
encrypted, so the chat check ends at the tschat backend.
2026-10-24 02:04:52 +02:00
.forgejo/workflows Add CI to build the Docker image 2026-10-22 20:07:28 +02:00
src Walk a full session through the myTeamSpeak account services 2026-10-24 02:04:52 +02:00
static Check service functionality, not only reachability 2026-10-24 00:30:00 +02:00
.dockerignore Check service functionality, not only reachability 2026-10-24 00:30:00 +02:00
.gitignore Check service functionality, not only reachability 2026-10-24 00:30:00 +02:00
Cargo.lock Log in with a real myTeamSpeak account when one is configured 2026-10-24 01:27:53 +02:00
Cargo.toml Log in with a real myTeamSpeak account when one is configured 2026-10-24 01:27:53 +02:00
config.toml Walk a full session through the myTeamSpeak account services 2026-10-24 02:04:52 +02:00
docker-compose.yml Log in with a real myTeamSpeak account when one is configured 2026-10-24 01:27:53 +02:00
Dockerfile Put non-local visitors through Anubis 2026-10-22 21:23:45 +02:00
README.md Walk a full session through the myTeamSpeak account services 2026-10-24 02:04:52 +02:00

TeamSpeak online status

Small self-hosted status page for TeamSpeak's online services. Unofficial.

Checked by default (see config.toml):

Group Check
Website https://teamspeak.com/ answers 2xx and contains "TeamSpeak"
myTeamSpeak web app, API index, products and addons from the database (plus one addon file), login, account round trip (sync, badges, chat backend)
Chat (Matrix) chat.teamspeak.com plus every chat-<n>.tmspk.net found via DNS, and federation between them
Downloads every server and client release in the versions JSON exists; one server release's checksum
Other community forum, chat push gateway (Sygnal /health), badge list (plus one badge image), license server

Matrix homeservers are up when /_matrix/client/versions answers, and degraded when /_matrix/federation/v1/version does not, or when they cannot federate: each homeserver is asked for the next one's signing key (in a ring, plus any peers, by default matrix.org for chat.teamspeak.com), newer than it can have cached, so it has to fetch it over federation right then. These requests use the TS chat client's User-Agent (matrix_user_agent), because TeamSpeak's proxy answers 503 to other clients on most homeservers.

The login check uses the protobuf API the TeamSpeak client uses (POST https://clientapi.myteamspeak.com/authentication, content type application/ts3cloud), because the web app's /api/login is behind a Cloudflare bot check that plain HTTP clients fail. The body is the RPC method name with a one byte length prefix followed by the serialized request message; the answer is the serialized reply. The check sends login with LoginData { email, password, skipSession }, the password hashed like the client does, every 15 minutes:

  • With MYTS_EMAIL and MYTS_PASSWORD set, the account must log in (code 200). skipSession keeps this from creating a session. Use a separate account without 2FA. If the credentials are rejected or need 2FA, the check says so, shows as unknown and falls back to the made-up account until the next restart, so a wrong password is not retried until the account locks.
  • Without them, a made-up account must be rejected (code 202, ERROR_LOGIN_FAILED). That shows the gateway and login backend answer, but not that a real login works. Failed logins from one address might one day trip TeamSpeak's abuse protection, another reason to prefer an account.

The account check (mytsaccount, same environment variables, also every 15 minutes) walks one session through the services behind login: log in with a session, validate it (session), read the settings sync (requestServerItems), the badge service (getBadges) and the chat backend (getActiveIdentifierList), then log out (deleteSession). A green result means those backends served a real account; each run opens and closes one session, which may show in the account's session list. It is skipped without the environment variables. All calls are read-only except for the session lifecycle itself; write calls (bookmarks, contacts, voice servers) are never made. The chat backend hands out Matrix identifiers, but the synced data that would contain Matrix credentials is end-to-end encrypted, so a scripted end-to-end chat login is not possible through this API.

Checks with every run less often than the others and repeat their last result in between. Any check slower than slow_ms is degraded. A check that is not up is retried once, 5 seconds later, and only the retry counts, so a single slow or failed request does not become an incident.

The monitor also checks its own internet connection against a few unrelated hosts ([connectivity]). If none answer, every service is shown as unknown for that time instead of down, and unknown periods are left out of uptime numbers.

Individual check results (retention_days, default 7) feed the 24 hour bars and the 24h/7d uptime. Daily summaries and incidents (summary_days, default 400) feed the 30 day uptime and the month by month overview; days and months are UTC. Everything is kept in data/state.json.

Bot protection

Requests from outside the local network go through Anubis (shipped in the Docker image), a proof-of-work check that browsers pass automatically. Local clients ([anubis] local_networks, private ranges by default) get the page directly.

client ─▶ :8080 ─┬─ local ───────────────────────────▶ app
                 └─ other ─▶ Anubis 127.0.0.1:8923 ─▶ app 127.0.0.1:8081

Behind a reverse proxy the client address comes from X-Forwarded-For (or X-Real-IP if there is no X-Forwarded-For), but only when the connection comes from trusted_proxies (private ranges by default, or TRUSTED_PROXIES). The header is read from the right, so addresses a client puts in front itself are ignored. This relies on the proxy appending to or replacing X-Forwarded-For (nginx $proxy_add_x_forwarded_for, Traefik and Caddy do); a proxy that passes the client's header through unchanged lets clients claim a local address. If the proxy is not trusted, every visitor looks like the proxy, which is local.

  • Set REDIRECT_DOMAINS in [anubis.env] to your public hostname.
  • Anubis cookies are Secure, so outside access needs HTTPS (or set COOKIE_SECURE = "false").
  • Publish the port only to your reverse proxy or LAN. Some Docker setups (userland proxy) make every client appear as a local address.
  • The signing key lives in data/anubis.key, so passed checks survive restarts.
  • Remove the [anubis] section to serve everyone directly.

Run

cargo run --release
# or
docker compose up -d --build

Then open http://localhost:8080.

Environment:

  • CONFIG: config path, default config.toml; the built-in copy is used if the file is missing.
  • TRUSTED_PROXIES: comma separated networks or addresses whose X-Forwarded-For/X-Real-IP are believed, e.g. 172.16.0.0/12,192.168.0.10. Replaces [anubis] trusted_proxies; an empty value trusts no proxy.
  • MYTS_EMAIL, MYTS_PASSWORD: myTeamSpeak account for the login check (optional, see above). Only the password hash is sent.
  • RUST_LOG: log filter, default info.

Endpoints

  • / status page
  • /api/status current status and the last 24 hours, as JSON
  • /api/history daily summaries per check and all incidents, as JSON
  • /healthz liveness; 503 when no check round finished for 3 intervals