Skip to main content
Each tracker version is a fixed file at its own URL. Pin the version and its integrity hash in your snippet, and the browser runs exactly those bytes or nothing. A released version never changes: a fix ships as a new version, and you upgrade by changing the URL and the hash.

Releases

The source of truth is packages/tracker/releases.json. The file itself is committed as packages/tracker/releases/1.0.0.js.
Snippet for 1.0.0
Check the served file against its hash. The output must equal the part after sha384-:

Release policy

  • Immutable. packages/tracker/releases/<version>.js is never edited. The build refuses to overwrite a release whose bytes would change, and a releases.json entry is written once and never updated.
  • Fixes ship as new versions. This includes security fixes. Earlier versions stay online at their URLs.
  • Cached for a year. Files under /v/ are served with Cache-Control: public, max-age=31536000, immutable and Access-Control-Allow-Origin: *.
  • One URL shape. Releases are served at /v/<major>.<minor>.<patch>/l.js. The tunnel route for t. allows only digits in the three parts, so a pre-release version such as 1.1.0-beta.1 would not be reachable.
  • Upgrades are deliberate. Nothing changes in your pages until you change the version and the hash yourself.
The integrity hash makes the browser refuse a modified file. It does not stop anyone from removing the script tag or embedding a copy without the integrity attribute.

How a release is built

1

Change the tracker source

The tracker is src/tracker/index.ts and src/tracker/pow.ts in vendor/umami. Changes to Umami live as patches in patches/umami. After editing vendor/umami, write them back:
2

Bump the version

Set the new version in packages/tracker/package.json. The build uses this version, and the tag must match it.
3

Build

scripts/build-tracker.mjs runs Umami’s Rollup config (rollup.tracker.config.js) through pnpm@12.3.4, with the collect host and path from the lumenstat field of package.json (https://t.lumenstat.com and /e) built in. It then writes:It prints the size, the hash and a ready snippet.
4

Commit and let CI check it

CI rebuilds the tracker from a clean checkout and compares it byte for byte with the committed file. See the table below.
5

Tag the release

The tag triggers .github/workflows/release-tracker.yml, which publishes the GitHub release and the npm package.
6

Deploy

deploy/deploy.sh checks the build again and copies every release into Umami’s public folder, so the new version is served at its URL. See Self-hosting.

How a release is checked

node scripts/build-tracker.mjs --check rebuilds the version in packages/tracker/package.json and compares it byte for byte with packages/tracker/releases/<version>.js. It writes nothing. It exits with an error when the bytes differ or when the release file does not exist. scripts/stage-tracker-releases.sh copies every packages/tracker/releases/<version>.js to vendor/umami/public/v/<version>/l.js, which the Umami image serves. scripts/sync-umami.sh runs it too.
--check covers the current version only. Earlier releases are served from their committed files and pinned by their hashes in releases.json. They are not rebuilt, because their source is an earlier commit.
The GitHub release for each version is named “Tracker <version>” and carries the size, the Umami base, the URL, the integrity hash, a snippet and the curl command above.

npm package

@lumenstat/tracker contains dist/l.js, releases.json and the README. Its exports are . and ./l.js (the tracker) and ./releases.json. dist/l.js is copied from releases/<version>.js when the package is packed, so it has the same bytes and hash as the hosted file. The release workflow publishes it with npm provenance, so the package links to the commit and workflow run that built it.
The first npm publish of @lumenstat/tracker is pending (Milestone 1). Until then, use the hosted file.
To serve the file from your own domain, see Self-host the tracker. The copy still sends events to https://t.lumenstat.com unless you set data-host-url.

How projects learn about upgrades

  • Setup page. The snippet on your project’s Setup page uses the version the operator pins with TRACKER_VERSION. Its integrity hash is computed from the bytes the server serves. Snippets you already embedded keep their version.
  • GitHub releases. Each version gets a release from the tag tracker-v<version> with its URL, hash and check command. Watch the repository’s releases to be notified.
  • releases.json. The full list of versions, in the repository and in the npm package.
  • Changelog. Tracker releases are listed in the changelog.
  • Security fixes. The security policy says enrolled projects are told to upgrade when a tracker fix ships.
Planned: upgrade notices in the dashboard. The project Settings page lists them as planned for the next milestones.
Last modified on November 14, 2026