Skip to content

fix: notify per anonymous viewer, not once per tracked link#15

Merged
TrueLineCollective merged 1 commit into
mainfrom
fix/anon-open-notifications
Jul 2, 2026
Merged

fix: notify per anonymous viewer, not once per tracked link#15
TrueLineCollective merged 1 commit into
mainfrom
fix/anon-open-notifications

Conversation

@TrueLineCollective

Copy link
Copy Markdown
Owner

The bug

On a tracked link without email verification, every anonymous viewer was bucketed under the constant string "anon". recordOpen decides "first open" per viewer, and /api/track only notifies on a first open, so after the very first anonymous open the owner's notification never fired again, no matter how many different people opened the link. "Email me when a viewer opens it" effectively meant "email me about the first viewer, ever."

The fix

Give each browser a stable id via a first-party sentou_vid cookie and key anonymous viewers as anon:<id>:

  • The viewer page reads the cookie; if absent it mints an id (a server component can't set cookies, so the tracking script persists it client-side, path=/, ~1yr, SameSite=Lax) and passes it into the server-signed tracking token.
  • recordOpen's existing per-viewer first-open logic then does the right thing with no change: distinct browsers are distinct viewers (one notification each), while a refresh by the same browser reuses the id and dedupes.
  • Verified viewers are unchanged (keyed by their verified email).
  • The id is a dedup bucket, not an identity or a security token, so a client-set cookie is acceptable; tampering only affects that viewer's own dedup.
  • displayViewer in the analytics page renders anon:<id> as anon-<id> so distinct anonymous viewers read as distinct rows.

Chosen behavior (per product decision): one notification per distinct browser, and a refresh by the same person does not re-notify.

Verification

  • npm run typecheck clean
  • npm run lint clean
  • npm test 245/245 (added 3: trackingContext keys distinct browsers distinctly and ignores the id for a verified viewer; recordOpen treats two browsers as two first-opens and a repeat as not-first; the viewer page persists the cookie)
  • npm run build exit 0
  • Live-smoked the cookie round-trip on a dev server: a request with no cookie mints a fresh id, a request carrying a sentou_vid cookie reuses that exact id (so a refresh dedupes), and a subsequent no-cookie request gets a different id (distinct visitors stay distinct).

For a tracked link without email verification, every anonymous viewer was
keyed as the constant "anon". recordOpen decides "first open" per viewer, so
after the very first open the owner's notification never fired again, no
matter how many different people opened the link.

Give each browser a stable id via a first-party sentou_vid cookie and key
anonymous viewers as "anon:<id>". The viewer page mints the id when the
cookie is absent (a server component cannot set cookies, so the tracking
script persists it client-side) and passes it into the tracking token.
recordOpen's existing per-viewer logic then notifies once per distinct
browser while a refresh by the same browser still dedupes; verified viewers
keep being keyed by email. The id is a dedup bucket, not an identity, so a
client-set cookie is fine. displayViewer renders "anon:<id>" as anon-<id>.

Tests: trackingContext keys distinct browsers distinctly and ignores the id
for a verified viewer; recordOpen treats two browsers as two first opens and
a repeat as not-first; the viewer page persists the cookie. Live-smoked the
cookie round-trip: no cookie mints a fresh id, a sent cookie is reused
(refresh dedupes), and a new visitor gets a different id.
@TrueLineCollective TrueLineCollective merged commit a98afbf into main Jul 2, 2026
6 checks passed
@TrueLineCollective TrueLineCollective deleted the fix/anon-open-notifications branch July 2, 2026 02:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant