Privacy & network
What your server sends, and what it never does
A precise account of every connection your tofa server makes, what data is in it, what stays on your box, and how to turn things off.
Your tofa server is yours. It runs on your hardware, your media stays on your disks, and the cloud's job is limited: sign-in, remote access, metadata, and keeping your watch state in sync across your devices. This page lists every connection the server makes and exactly what is in it, so you can verify our claims instead of trusting them. Everything here matches the shipped code, and you can confirm most of it yourself with a packet capture.
The short version#
- Your media files, filenames, folder paths, and library listings never leave your server. There is no path in the code that uploads them.
- Playback telemetry (who watched what, when, with what errors) stays on your box. It is visible to you as the server owner and is not collected by us.
- The server talks to one cloud endpoint,
api.tofa.tv, and every request it makes is signed with a key that only your server holds. - Streaming goes directly from your server to your devices. When a direct connection is impossible, an encrypted relay forwards the bytes without inspecting them.
- One optional report exists (a daily playback problem digest), and it is off by default.
- A fresh install reports twice that it exists, so we can see where installs break. It carries the platform and version and nothing else, and you can turn it off before first start.
Every connection, in detail#
Heartbeat, every 25 seconds#
The server checks in with the cloud so your apps can find it. The heartbeat carries connectivity and version facts only: your server's local IP, public IP, port, tofa version, platform, update channel, and any custom access URLs you configured yourself. No usernames, no titles, no watch data.
Metadata lookups#
When the scanner finds new files it asks our metadata service to identify them. What is sent is the cleaned-up title guess the scanner derived (for example a name and a year), plus the library's language and country settings. The raw filename and the file path are never transmitted. Adult-content matching is only requested for libraries where you explicitly enabled it.
Watch state and watchlist sync#
So you can pause on the TV and resume on your phone, the server syncs watch positions to your account. Each record is a numeric catalog ID, a season and episode number where relevant, a playback position, and a completed flag. Titles, filenames, and paths are not part of the payload.
Preferences sync#
Your app settings (subtitle preferences, playback defaults, and similar) sync as a small settings document tied to your account, so a fresh sign-in on a new device feels like yours.
Update checks, every 2 hours#
The server asks whether a newer build exists for its platform and channel, and reports whether an update it applied succeeded or failed (version numbers and a status, nothing else). There is no crash reporting, no stack traces, and no log upload.
Install report, twice in a server's life#
A newly installed server sends two short messages: one as it starts up, before it brings up its database, and one once it is running and accepting connections. Each carries how it was installed (Docker, Windows, a Linux package, and so on), the operating system and processor type, the tofa version, and the update channel. That is the whole payload. Your IP address is not stored, and nothing about your machine name, your paths, or your library is in it.
The reason the first message is sent before the database starts is that this is where a broken install usually dies. A server that reports a start and never reports being ready is one we know failed, on a platform we can name, without anyone having to file a ticket.
Only fresh installs report. A server that updates to a newer version does not send these again, and neither does a restart. The messages are identified by your server's own key rather than by you, though that key becomes linked to your account if you go on to connect the server, so this is pseudonymous rather than anonymous and we would rather say so plainly.
To switch it off, set TOFA_INSTALL_PING=0 in the server's environment before
you start it for the first time.
The one opt-in report: playback problem digest#
If you turn on playback_digest_enabled (it is off by default), the server
sends us one summary per day covering up to ten problem playback sessions:
timestamp, username, title, client, and a one-line description of the problem
(a fatal error or heavy rebuffering). This exists so we can fix playback bugs
for testers who want to share that data. While the setting is off, nothing of
this kind ever leaves the server.
Remote access and UPnP#
For remote streaming your server needs a reachable port. tofa tries, in order: a UPnP port mapping request to your router, then PCP, then NAT-PMP. This is a single TCP port mapping for the server's port, renewed every 30 minutes. It is a request to your own router, not an announcement to the network.
Local discovery on your home network#
So your apps can find the server instantly on your own network, the server
advertises itself over mDNS (the same Bonjour mechanism printers use) as
_tofa._tcp.local.. The advertisement never leaves your local network and
carries connection facts only: the server's display name, its version, its
port, whether it serves TLS, its local address, and its server ID. No account
data, no tokens, and nothing about your library.
You can turn it off in Admin, Settings (the lan_discovery_enabled setting);
it stops advertising immediately, no restart needed. If you deploy with
environment variables instead, TOFA_LAN_DISCOVERY=false keeps it off
regardless of the setting.
Things tofa deliberately does not do on your LAN:
- No SSDP or DLNA advertising. Your server does not announce itself over SSDP, and there is no DLNA media server in tofa.
- One listening port (33333 by default), nothing else.
To turn automatic port mapping off entirely, set a manual external port in
Admin, Settings, Remote Access (the manual_external_port setting). That
tells tofa you forward the port yourself, and the UPnP, PCP, and NAT-PMP code
never runs.
If neither a mapped nor a forwarded port is reachable, streaming falls back to our relay. The relay is a pipe: it forwards the encrypted bytes between your device and your server and does not decrypt or inspect what you watch.
Permissions on your machine#
- On macOS, the menu bar app runs the server as your user, not as root. The optional "Run at Boot" mode installs a system service with one admin prompt, and even then the server process runs under your account. The terminal installer works the same way: it asks for your password to register the service, then runs the server as you, and it refuses to install if it can't tell which user that should be.
- On Linux, the installer asks for your password because it creates a
dedicated unprivileged
tofasystem user, installs to/opt/tofa, and registers a systemd service that runs as that user, locked down with systemd's sandboxing (no new privileges, read-only system, writable only in its own folders). The server never runs as root. - On Windows, the installer needs admin once to install; the server itself then runs in your session as your user, unelevated.
- In Docker, the container starts as root only long enough to match file
ownership to the
PUID/PGIDyou set, then permanently drops to that unprivileged user before the server starts. It never touches the ownership of your media.
Verify it yourself#
We like testers who check. The server's outbound surface is small enough to
audit in an afternoon: put a proxy or packet capture in front of it and you
should see exactly the traffic described above, all to api.tofa.tv, plus
your configured media apps talking to the server directly. If you find
anything that does not match this page, tell us. That is a bug in the code or
in this document, and we will fix whichever is wrong.