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:

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:

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:

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

*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