MailSignals

Self-open suppression

It does not turn green when you look at your own Sent folder.

This is the hardest problem in the product and the one that justifies not having a Gmail scope. When you open a sent message, your Gmail fetches the tracking pixel through the same proxy a recipient would use. Same user agent, same IP, same referrer. Nothing in the request distinguishes "they read it" from "you looked at Sent".

  1. +0.6 s Your own view Pixel loaded Your own Gmail loaded the image while showing the copy in your Sent folder. Who: You.
  2. +9 min Your own view Pixel loaded You were viewing this message in Gmail at the time, so this is your own open. Who: You.
  3. +2 h Opened (proxied) Pixel loaded Gmail loaded the image through its proxy while showing the message. Gmail hides the reader's device and location. Who: Gmail hides who read it. The recipient — or you, if you opened this on a device without MailSignals.
Two of your own views, withdrawn, and one genuine proxied open. The third entry is honest about the one case that cannot be told apart from the request alone.

The problem every tracker has

Without a fix, every sender watches their own checkmarks turn green seconds after opening Sent. It is the most common complaint about every tracker on the market, and it destroys trust in every other number the product shows.

Commercial trackers solve it with the Gmail API: one has a patented mechanism that replaces the copy in your Sent folder with a pixel-free copy. MailSignals has no API access, so it needs a different idea.

The insight

We cannot tell the two apart from the proxied request. But we know two things the request does not say: the extension is running in your browser and can see what is on screen, and the message was registered from your own network. The first covers the proxied fetch; the second covers the direct one.

The content script reports a view only for messages you sent, only while the message is expanded and the tab is visible, on load, on every expansion, on visibility change and on a 20-second heartbeat. Reports are batched, and each lookup is scoped to your account, including the tokens the extension supplies, so nobody can suppress anyone else's opens by claiming to be looking at them.

Five paths, because the race has several outcomes

The pixel and the view signal race, and either can win; and your Gmail fetches the pixel two different ways.

  1. The view signal lands first

    The tracking endpoint reads your recent view signals before classifying. Rule self-view-correlation fires and the hit is judged yours on the spot, at confidence 0.97. Nothing to undo.

  2. The pixel lands first

    The common case: Gmail requests the image while rendering, before the extension's report arrives. The open is already counted, so the views endpoint, after storing the signal, withdraws every counted open within 20 seconds of it. Without this pass every sender sees their checkmarks turn green a second after opening Sent.

  3. Both in flight at once

    The pixel handler reads view signals before it commits the event, so a signal arriving during that write is invisible to it, and equally invisible to the views endpoint, which finds no event yet to reclassify. Both halves miss each other. The Firestore trigger runs after the event is committed, which makes it the one place that can see both; it re-checks, withdraws the open, and returns before any rollup or alert sees it.

  4. Your Sent copy rendered directly

    Gmail web sometimes fetches the pixel directly, not through the proxy, when it renders the copy you just composed. Rule self-sent-copy catches it by comparing the hit's salted network hash with the one stored at registration. No view signal is involved, so it works before the extension's first report.

  5. Neither: you say so

    Reading your own sent mail on a phone with no extension produces a proxied self-open that nothing can correlate. "That open was me" in the dashboard (by event, or all proxied opens at once) or in the Gmail sidebar reclassifies it, keeps the original verdict for audit, and can be undone with "That was not me after all". Each claim is counted; at two, the dashboard explains that you read your sent mail on a device without MailSignals and points at Gmail's "Ask before displaying external images" setting, the only thing that removes the ambiguity at the source.

Withdrawal rebuilds, it does not subtract

Withdrawing an open changes the event's classification to "your own view", then rebuilds the open buckets and the first and last open times from every open event as it will be after the transaction. Rebuilding replaced add-and-subtract bookkeeping, which drifted the moment two reclassifications raced. The original verdict is preserved, which makes the change auditable and the correction reversible.

The selector re-evaluates what counts at your current sensitivity, so only opens that count today are candidates.

The honest gap

Where self-opens are and are not caught automatically
Where you read your sent mailWhat happens
Desktop Gmail in Chrome or Edge with the extensionWithdrawn automatically, by whichever of the four automatic paths applies
Gmail web on another browser or profile without the extension, on the same networkCaught only when Gmail fetches directly with your network hash; a proxied fetch is counted until you claim it
The Gmail mobile appCounted as a proxied open until you claim it. Set images to "Ask before displaying external images" on the phone and the fetch never happens
A recipient on your own network reading in Gmail web with direct image loadingJudged as your own. The documented cost of the network rule

Where it lives

extension/src/gmail/messages.ts
Reporting views from Gmail: expansion, visibility, the heartbeat.
functions/src/api/views.ts
The endpoint, ownership scoping, the backward pass.
functions/src/tracking/reclassify.ts
Withdraw and restore; rebuilding the counters from events.
functions/src/triggers.ts
The backstop that runs after the event is committed.
shared/src/classifier.ts
Rules self-sent-copy and self-view-correlation.

Related: the guide on self-opens, for any tracker · the rule table.

Run it in your own project.

One Firebase project, two subdomains, one deploy script. About an hour the first time, and no subscription afterwards.