The eyehands free-tier heartbeat: what we send, what we don't, and how to opt out
by Fireal Software · ~6 min read
eyehands 1.6.0 added an anonymous heartbeat that fires every 6 hours from free-tier installs. This post exists because telemetry should be disclosed in plain English, and because “anonymous” means nothing if I don’t tell you exactly what’s in the payload.
Why there’s telemetry at all
Up through 1.5, only paid users showed up in any usage stats — the existing /api/updates/eyehands/check call carries a license key, so licensed installs were counted via the update check. Free users were invisible to me. I had no idea if eyehands had 10 free users or 10,000.
This matters because eyehands is a solo project and I’m allocating my time based on what people actually use. If 90% of users are on the free tier, that’s useful signal for deciding where to spend effort. “Nobody uses the free tier” and “the free tier is 90% of my user base” lead to different roadmaps.
But I also don’t want to screw people over with surveillance that pretends to be telemetry. So here’s the full disclosure.
What gets sent
Every 6 hours (once per eyehands process), free-tier installs POST to https://portal.fireal.dev/api/updates/eyehands/ping with this JSON:
{
"installId": "sha256-hash-of-local-random-token",
"installToken": "local-random-token-hex",
"deviceId": "sha256-hash-of-hostname-and-SystemRoot",
"version": "1.6.0"
}
installId / installToken
On first run, eyehands generates 32 random bytes (secrets.token_bytes(32)) and writes them to .install_token in the data directory. The hex of that token is sent as installToken; its SHA-256 is sent as installId.
On the server side, I store only sha256(token) — the plaintext token is verified once and discarded. This means:
- Two pings from the same install match on
installId(I can count unique installs) - A compromised server database can’t impersonate the install, because the plaintext token only lives on the client
- I have no way to connect an
installIdto a specific user, machine name, or network address — it’s just 32 random bytes
deviceId
This is SHA-256(hostname + SystemRoot). Example: if your machine is DESKTOP-ABC with SystemRoot=C:\Windows, the deviceId is sha256("DESKTOP-ABC" + "C:\\Windows").
The purpose is to detect “same machine, different installs” — if you have eyehands installed in two different Python environments on the same PC, they’ll hash to the same deviceId. This lets me count installs per unique machine as a rough deduplication.
The hostname is pre-hashed before it leaves your machine. I never see DESKTOP-ABC; I only see its SHA-256.
version
The literal version string ("1.6.0"). This tells me which versions are in active use so I can decide when to drop support for old ones.
What is NOT sent
I want to be explicit because “anonymous telemetry” often smuggles in more than it claims:
- No IP address is stored. The HTTP layer sees your IP on the incoming connection, but I don’t log it to the database. Only the
installIdhash is persisted. - No operating system version, CPU, GPU, monitor count, or any hardware info. Just the
versionof eyehands itself. - No usage data. I don’t know how many times you’ve called
/clickvs/find. I don’t know what endpoints you use. I don’t know if you use the Chrome extension. - No error messages, stack traces, or crash reports. If eyehands crashes, you’ll see it in your terminal. I won’t.
- No file paths, window titles, screenshots, or any screen content. This would be appalling and I have genuinely no interest in that data.
- No license keys or email addresses. Paid installs don’t use this path at all — they’re counted through the existing
/api/updates/eyehands/checkcall, which carries the license key as auth. - No user input. I don’t see your keystrokes, mouse clicks, or typed text. eyehands is a desktop automation tool; it has access to all of that, and I’ve been very careful to make sure none of it touches the network.
How to opt out
Three ways, any of which disables the heartbeat completely:
1. CLI flag
eyehands --no-telemetry
This sets a module-level flag at startup. No token is generated. No ping is sent. No file is written.
2. Marker file
Create a file named .no-telemetry in the eyehands data directory:
- Pip-installed:
%APPDATA%\eyehands\.no-telemetry - Source checkout:
<repo>/.no-telemetry
The file can be empty. Just its existence is enough. eyehands checks for it before generating the install token, so opting out via this path never touches the filesystem for telemetry purposes at all.
3. Don’t connect to the internet
If portal.fireal.dev is unreachable, the ping fails silently and eyehands continues normally. This is the default behavior for offline use.
The bandwidth cost
The ping is ~200 bytes up, ~20 bytes down. Once every 6 hours. Approximate annual bandwidth: 280KB. That’s effectively nothing.
Licensed installs also ping once every 6 hours via the separate update-check path, so there’s no net difference in frequency between free and paid tiers — just in which endpoint gets hit and what auth it carries.
Why I’m writing this
Writing a privacy post for a product of this size is unusual. But eyehands is “a tool that has access to your screen and your keyboard”. The trust bar is necessarily higher than for a normal CLI utility. If I’m going to send anything to my server, I want users to know exactly what’s in the packet, and I want opting out to be trivial.
If you still don’t want the heartbeat, use --no-telemetry or drop the .no-telemetry file. The tool will work identically.
If you have questions, concerns, or want to see the actual code that does this: _free_checkin() in eyehands/server.py. It’s ~30 lines and comments out the whole flow.
Install
pip install eyehands
eyehands --no-telemetry # opts out from the first run
Links
- eyehands repo: https://github.com/shameindemgg/eyehands
- Source of
_free_checkin: https://github.com/shameindemgg/eyehands/blob/master/eyehands/server.py
*If anything in this post doesn't match what the code actually does, please open an issue. I take "transparency in telemetry" seriously enough that a mismatch between this post and the source is a bug.*
Give Claude eyes and hands on Windows
eyehands is a local HTTP server for screen capture, mouse control, and keyboard input. Open source with a Pro tier.
Try eyehands