Privacy
Tetris runs locally. The compression pipeline never touches the network. The one thing that does leave your machine is a small heartbeat — exactly what’s in it is documented below, and the code that sends it is linked so you can read it yourself.
What never leaves your machine
- Your code. File contents, diffs, edits, scratch buffers — none of it.
- Your prompts. Nothing you type to the assistant is uploaded anywhere.
- Tool-call payloads. The thing tetris compresses. Processed in-memory; never written to disk except your own ledger; never transmitted.
- Tool names, file paths, filenames, repo names. Tetris doesn’t log them server-side. The ledger on your disk records compression stats, not identifiers.
- Hostname, username, raw IP. The heartbeat does not include any of these. Source IP is visible to our edge for standard request routing. From the IP we derive a single coarse value — the 2-letter ISO country code — via an edge country header, and store that on your install row. The raw IP itself is never written to a row, never joined, never logged after the request ends. See “What we store on the install row” below for the full list.
What the heartbeat contains
Exactly these fields, and nothing else:
| Field | Value | Why |
|---|---|---|
install_id |
Random UUID generated once at first run, stored in ~/.tetris/install_id. |
Counts unique installs. Not tied to your identity. |
machine_id_hash |
SHA-256 of a salted, OS-provided machine UID. One-way; never reversible. | Enforces the license device count without knowing which machine you’re on. |
binary_version |
e.g. 0.0.23 |
So we know which versions are in the field when we ship fixes. |
platform |
Target triple, e.g. aarch64-apple-darwin. |
OS + architecture only. Five possible values. |
compression_count (optional) |
Lifetime count of compressions from your local ledger. | So the community stats page has a real denominator. |
tokens_saved_mtd (optional) |
Tokens saved this month, from your local ledger. | License gate: the free plan caps savings, Pro lifts the cap. |
active_assistants (optional) |
Array of assistant names wired on this install, e.g. ["claude-code", "codex"]. |
So we know which assistants to prioritize for fixes. |
strategies_used (optional) |
Array of pipeline-stage names that ran since the last beat. | So we know which compression stages earn their keep. |
That’s the whole payload. No file content, no prompt text, no tool names, no diffs, no filenames, no repo paths, no hostname, no username.
When the heartbeat fires
- First run: once, at the end of
tetris onboard. Marks the install as active. - Steady state: rate-limited to once every six hours. The next tool call after the window elapses triggers it in a detached background task; the hook itself doesn’t wait.
- License refresh:
tetris logincontacts the license server to validate or refresh your JWT. Same payload shape.
That’s it for the CLI. No per-compression callback, no continuous stream, no CLI analytics SDK.
What we store on the install row
Every heartbeat upserts a single install row keyed by the device and assistant. The full set of fields we keep:
device_id— yourinstall_id.license_jti— the JWT ID of your license, so we can count devices against the cap.assistant—"claude-code","codex", …triple— target triple from the heartbeat payload (OS + arch).cli_version— binary version from the heartbeat payload.last_seen_at— timestamp of the most recent heartbeat. Drives the “active in last 30 days” count on the landing page.country— ISO-3166 alpha-2 country code (e.g."US","DE","IN") from the edge country header. Anonymizers (Tor, “XX”) are dropped.region— ISO-3166-2 subdivision when our edge provides it; otherwise absent. Coarser than a city; we never store finer.
Notably absent: raw IP, user-agent string, ASN, latitude/longitude, city, postal code, browser fingerprint. We do not derive these and we do not store them.
What we do server-side
Heartbeats land in the install record described above. An hourly job aggregates
them into the public stats cache (total installs, tokens saved, rough active-assistant
mix, count of distinct active countries, top-10 countries by install count) which is what the
numbers on the landing page read from. Raw heartbeat log lines are retained for 90 days for
debugging and then deleted; the installs rows themselves persist while your install
is active and are aged out 30 days after the last heartbeat.
We do not sell this data. We do not share it with ad networks. We do not have an ad network.
What runs in the browser
The public website loads Google Analytics through Google tag to count aggregate page views and product interactions such as install clicks, copy-command clicks, outbound links, form-submit intent, scroll depth, and section views. We do not send source code, prompts, file paths, license IDs, install IDs, form contents, email addresses, account identifiers, or device identifiers to Google Analytics.
The homepage also fetches aggregate community stats from our own public endpoint. That response contains public totals only and does not identify a browser.
Source-code receipts
If any of the above is vague, read the code. The relevant client files:
crates/tetris-core/src/license/heartbeat.rs— the full payload struct, the HTTP client, and the 10-second timeout.crates/tetris-cli/src/cmd/compress.rs— where the rate-limited heartbeat is fired on the hook path (in a detachedtokio::spawn, not awaited).crates/tetris-cli/src/cmd/onboard.rs— the one-shot first-run beacon.crates/tetris-core/src/compress/— the compression pipeline. No network I/O in here by construction.
Running fully offline
Compression works with the network unplugged. The license has a 72-hour offline grace
— after that, compression pauses (tool calls are passed through unchanged) until the heartbeat
reaches the server again. Enterprise customers on the air-gap plan get a self-hostable license
server; point TETRIS_API_URL at it and no request ever leaves your network.
Opting out of telemetry
Set TETRIS_TELEMETRY=0 in your shell environment before running any tetris command.
Heartbeats stop firing; the license check still needs to reach the server to validate but
sends only the minimum install_id + machine_id_hash + version.
Deleting your data
Run tetris uninstall, then rm -rf ~/.tetris/. That removes your
install_id locally. The heartbeat stops arriving; the install row is marked
inactive after 30 days and aged out of aggregates. There’s no other identifier tying a
row to you — we never knew who you were.
If you want us to hard-delete a specific install_id immediately, email
privacy@tetris.codes with the ID (you can read it from
~/.tetris/install_id) and we’ll run the deletion within 30 days of receipt.
Questions
Privacy questions: privacy@tetris.codes. General support: help@tetris.codes. If you find something in the code that contradicts anything on this page, that’s a bug — tell us and we’ll fix the code or fix the page.