Your mouse was counted. Your keyboard was not.
macOS hands you an event tap that silently drops every keystroke, reports success, and lets your permission checklist show a green tick over a keyboard you cannot see.
A writer spent an afternoon writing. The app told them they had been idle.
Not "we're not sure". Not "no activity detected, still counting". Idle — the state that pauses your timer and asks whether you would like to resume. They had been typing continuously for hours, on the machine, in the foreground, into a document that got longer the whole time.
To count input without recording it, we run a listen-only CGEventTap on a background thread. It
sees that an event happened and increments a counter; it never sees content, and it is enabled only
while a session is actually running.
The problem is this: CGEventTapCreate returns a perfectly valid tap without Input Monitoring
permission. It does not return NULL. It does not set an error. It hands you a live tap that
delivers mouse events all day long — moves, clicks, scrolls, everything — and drops every single
keydown in silence.
So the question our code was asking:
/// Whether the listen-only input tap is running at all.
///
/// This is NOT "input counting works" and must never be read as such.
pub fn input_tap_active() -> bool {
MONITOR_ACTIVE.load(Ordering::Relaxed)
}
...has a true answer that means almost nothing. The tap is alive. Events are arriving. Something is definitely being counted. It just isn't the keyboard.
Everything we could observe said the feature was working. Every observation was of the half that was working.
As the exact opposite of the truth
Follow the chain. A writer types for three hours and generates almost no mouse events — that is what writing is. The tap reports near-zero input. The heartbeat reads sustained near-zero input as absence. The app concludes the member is away and pauses their tracking.
The person doing the most focused work on the team got the strongest accusation of doing none.
And the permission checklist — the screen whose entire job is to tell you when something is misconfigured — showed a green tick, because it asked whether the tap was active and the tap was active. The one surface built to catch this was the surface most confidently reporting that everything was fine.
The fix is almost embarrassingly small. One flag, set from inside the callback the first time a real keyboard event arrives:
/// Whether a KEYBOARD event has actually reached us since launch — the only self-evident proof
/// that keystrokes are being counted. Positive evidence only: `false` means "not proven yet",
/// which is also what a member who genuinely hasn't typed looks like, so a caller must never
/// turn a bare `false` into an accusation.
pub fn keyboard_counting_confirmed() -> bool {
KEYBOARD_EVENT_SEEN.load(Ordering::Relaxed)
}
One real keydown is unforgeable proof that keyboard counting works, whatever the permission system claims. No API can fake it, no stale grant can survive it, and it stays correct on the day Apple changes how any of this is reported — because it is not a report. It is the thing itself.
The comment carries the other half of the design, and it is the half people skip. false is not
evidence of anything. It means "not proven yet", and someone who simply hasn't typed since launch
looks exactly the same as someone whose permission is broken. So the flag is allowed to clear a
warning and never to raise one. The two-part test — ask the system, and check whether events are
actually arriving — decides what we show; a bare false decides nothing at all.
Every platform has an API that answers the question you asked instead of the question you meant. This one is unusually cruel about it, because the successful-looking path and the broken path are byte-identical until you look at which events arrive.
Three things we now do differently, all cheap:
- Instrument the outcome, not the setup. "Did the subsystem initialise" is a proxy. "Has real data arrived" is the measurement. Prefer the measurement wherever a flag can stand in for it.
- Treat asymmetric evidence asymmetrically. Some signals can only ever confirm. Write down which direction a flag is allowed to be read in, next to the flag, or somebody will read it the other way inside a month.
- Ask what the failure looks like from the user's chair. Ours arrived as an accusation of idleness aimed at the person concentrating hardest. That is not a rounding error in a metric; that is the product calling someone a liar because it could not see their hands.
The tap was working. That was never in question. It was the answer to a question nobody had asked.
Questions, corrections, or a war story of your own? Write to hello@worktrackhq.com — a person reads every message.


