| summary | Email open tracking in gog (Gmail + Cloudflare Worker) | ||
|---|---|---|---|
| read_when |
|
Goal: track email opens for gog gmail send via a tiny tracking pixel served from a Cloudflare Worker.
High-level:
gog gmail send --trackinjects a 1×1 image URL into the HTML body.- The Worker receives the request, stores an “open” row in D1, and returns a transparent pixel.
gog gmail track opens …queries the Worker and prints opens.
Abuse controls:
- Repeated opens for the same tracking id, IP, and user-agent are deduplicated within a one-hour window.
- Each IP can record at most 100 opens per hour; excess requests still receive the transparent pixel but are not inserted into D1.
Privacy note:
- Tracking is inherently sensitive. Treat this as instrumentation you opt into per email.
- The Worker stores recipient email, subject hash, sent/open timestamps, IP, user-agent, bot classification, and coarse geo from Cloudflare request metadata when available.
- The deployed Worker includes a daily cron trigger that deletes open rows older than 90 days.
- Admin
/opensqueries default to 100 rows and are capped at 500 rows per request.
Create per-account tracking config + keys:
gog gmail track setup --worker-url https://gog-email-tracker.<acct>.workers.devThis writes a local config file containing:
worker_url(base URL)- the active tracking key version
- per-account tracking/admin keys are stored in your keychain/keyring (not in the JSON file)
Optional: auto-provision + deploy with wrangler:
gog gmail track setup --worker-url https://gog-email-tracker.<acct>.workers.dev --deployFlags:
--worker-name: defaultgog-email-tracker-<account>.--db-name: default to worker name.--worker-dir: defaultinternal/tracking/worker.
Re-run gog gmail track setup any time to re-print the current TRACKING_KEY / ADMIN_KEY values (it’s idempotent unless you pass explicit --tracking-key / --admin-key).
From repo root:
cd internal/tracking/worker
pnpm installProvision secrets (use values printed by gog gmail track setup):
pnpm exec wrangler secret put TRACKING_KEY
pnpm exec wrangler secret put TRACKING_KEY_V1
pnpm exec wrangler secret put TRACKING_CURRENT_KEY_VERSION
pnpm exec wrangler secret put ADMIN_KEYCreate D1:
pnpm exec wrangler d1 create gog-email-trackerUpdate wrangler.toml to reference the D1 database_id, then migrate and deploy:
pnpm exec wrangler d1 execute gog-email-tracker --file schema.sql --remote
pnpm exec wrangler deploywrangler.toml includes a daily cron trigger for retention cleanup. After deploy, Cloudflare calls the Worker once per day and the Worker deletes open rows older than 90 days.
Rotate the pixel encryption key without invalidating old tracking ids:
gog gmail track key rotateThe command generates the next key version, deploys all active TRACKING_KEY_V<N> secrets plus TRACKING_CURRENT_KEY_VERSION, then stores the new current version in local config. Legacy unversioned tracking ids still decrypt through the stored TRACKING_KEY fallback.
For local-only testing:
gog gmail track key rotate --no-deployDo not send newly tracked mail after --no-deploy until the Worker has the matching versioned secret, or new pixels will not decrypt.
Tracked email constraints:
- Exactly one recipient (
--to; no cc/bcc). - HTML body required (
--body-html).
Optional per-recipient sends:
gog gmail send \
--to [email protected],[email protected] \
--subject "Hello" \
--body-html "<p>Hi!</p>" \
--track \
--track-split--track-split sends separate messages per recipient (no CC/BCC; each message has a unique tracking id).
Example:
gog gmail send \
--to [email protected] \
--subject "Hello" \
--body-html "<p>Hi!</p>" \
--trackBy tracking id:
gog gmail track opens <tracking_id>By recipient:
gog gmail track opens --to [email protected]Status:
gog gmail track statusrequired: --worker-url: rungog gmail track setup --worker-url …first (or pass--worker-urlagain).401/403on/opens: admin key mismatch; redeploy secrets and re-runtrack setupif needed.- New tracked messages do not show opens after key rotation: verify the Worker has
TRACKING_KEY_V<N>for the current localgmail track statusversion andTRACKING_CURRENT_KEY_VERSIONmatches it. - No opens recorded:
- ensure the HTML body contains the injected pixel (view “original” in your mail client).
- some clients block images by default; “open” only happens after images load.