You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Right now the project has no idea how many EmDash installations exist or which versions they run. npm downloads don't answer this — they count CI runs and mirrors, and a site that was deployed six months ago and never reinstalled is invisible. Knowing the version distribution matters practically: it tells you when it's safe to drop a compatibility path, whether people actually upgrade after a security release, and roughly how big the blast radius of a breaking change is.
WordPress solves this without any tracking pixel: every site pings api.wordpress.org for update checks, and the "active installations" numbers are derived from those requests. The mechanism is honest — the request that benefits the user (update awareness) is the same request that produces the count. But WordPress sends the site URL along with it, which is the part I don't want to copy.
#1939 (the update notice from #1889) already builds the carrier: a server-side check that hits registry.npmjs.org/emdash/latest at most once per day, deferred via after(), cached in the options table. The proposal here is the natural next step:
Point the daily check at a project-owned endpoint instead of npm. Something like version.emdash.dev/check?version=0.28.1&adapter=cloudflare. It answers with the latest version (same contract as today) and counts the request as a side effect.
What gets sent: the running EmDash version, the adapter (cloudflare/node), and a random instance UUID generated once and stored in the options table — derived from nothing, resettable by deleting one row. That's the whole payload. No domain, no URL, no locale, no user counts, no content stats. The receiving side would be a small Worker writing data points to Workers Analytics Engine, which doesn't retain IPs unless you explicitly write them (we wouldn't).
The UUID is what turns "requests per day" into real numbers: unique installs over 7/30 days, version distribution, upgrade latency after a release. Astro and Next.js do the same thing for their CLI telemetry.
Opt-out that doesn't punish you:telemetry: false in the config (or EMDASH_TELEMETRY=0). Opted-out sites keep the update check — they just query registry.npmjs.org directly, exactly like #1939 does today. Losing update awareness as the price for opting out would be the WordPress dark pattern, and it's avoidable here.
The remaining pieces would be a docs page listing exactly what is sent (and a release-notes mention in whatever version introduces it), and eventually a public stats page — transparency is what makes this kind of thing acceptable in an open-source project, and the aggregate numbers are interesting for the community anyway.
Is this something you'd want at all? Telemetry is the most sensitive feature category in open source, even in this minimal form, and it's your call — I don't want to build it speculatively.
The endpoint has to be project-owned (a Worker + an Analytics Engine dataset on your account, or wherever emdash.dev infra lives). Happy to build the Worker and the sender side if you're up for hosting it.
Implementation-wise the sender is a small diff on top of #1939 — the once-per-day guard, the after() deferral, and the options-table cache all already exist there.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Right now the project has no idea how many EmDash installations exist or which versions they run. npm downloads don't answer this — they count CI runs and mirrors, and a site that was deployed six months ago and never reinstalled is invisible. Knowing the version distribution matters practically: it tells you when it's safe to drop a compatibility path, whether people actually upgrade after a security release, and roughly how big the blast radius of a breaking change is.
WordPress solves this without any tracking pixel: every site pings
api.wordpress.orgfor update checks, and the "active installations" numbers are derived from those requests. The mechanism is honest — the request that benefits the user (update awareness) is the same request that produces the count. But WordPress sends the site URL along with it, which is the part I don't want to copy.#1939 (the update notice from #1889) already builds the carrier: a server-side check that hits
registry.npmjs.org/emdash/latestat most once per day, deferred viaafter(), cached in the options table. The proposal here is the natural next step:Point the daily check at a project-owned endpoint instead of npm. Something like
version.emdash.dev/check?version=0.28.1&adapter=cloudflare. It answers with the latest version (same contract as today) and counts the request as a side effect.What gets sent: the running EmDash version, the adapter (
cloudflare/node), and a random instance UUID generated once and stored in the options table — derived from nothing, resettable by deleting one row. That's the whole payload. No domain, no URL, no locale, no user counts, no content stats. The receiving side would be a small Worker writing data points to Workers Analytics Engine, which doesn't retain IPs unless you explicitly write them (we wouldn't).The UUID is what turns "requests per day" into real numbers: unique installs over 7/30 days, version distribution, upgrade latency after a release. Astro and Next.js do the same thing for their CLI telemetry.
Opt-out that doesn't punish you:
telemetry: falsein the config (orEMDASH_TELEMETRY=0). Opted-out sites keep the update check — they just queryregistry.npmjs.orgdirectly, exactly like #1939 does today. Losing update awareness as the price for opting out would be the WordPress dark pattern, and it's avoidable here.The remaining pieces would be a docs page listing exactly what is sent (and a release-notes mention in whatever version introduces it), and eventually a public stats page — transparency is what makes this kind of thing acceptable in an open-source project, and the aggregate numbers are interesting for the community anyway.
Two things only you can answer, @ascorbic:
Implementation-wise the sender is a small diff on top of #1939 — the once-per-day guard, the
after()deferral, and the options-table cache all already exist there.All reactions