This directory is the entire server side of photo-tagger's anonymous usage telemetry: a small Cloudflare Worker that records one Analytics Engine data point per run. There is no third-party analytics service. It is kept in the repo so anyone can read exactly what the collector does.
What the client sends, when, and how to turn it off is documented in the
Telemetry section of the main README. The client side lives in
src/photo_tagger/telemetry.py.
| File | Purpose |
|---|---|
worker.js |
The Worker: validates a POST beacon, writes one AE data point. |
wrangler.toml |
Deploy config (Worker name, AE binding, custom domain route). |
queries.sql |
Starter SQL answering each question the telemetry exists to ask. |
dashboard.py |
A marimo dashboard that runs those queries and charts them. |
One data point per event, in one shared column layout (see the map in queries.sql):
run(end of a tagging run): the fields inbuild_payload: app version, interface (cli/gui), provider, model, output language, UI language, file types (extensions only, e.g.cr3,jpg), CPU arch, OS, OS release, Python version, CPU/GPU model, core count, RAM in GB, batch size, run duration, outcome counts (successes, failures, cache hits, retry recoveries, workers, tokens, model time, dry-run flag), coarse failure buckets (e.g.timeout:3), and a random install id.crash(an unhandled exception): the same platform/hardware facts plus the exception type and its code location inside photo-tagger (module:function:line). Never the error message, which could embed paths.
Older clients still send schema v1; the Worker accepts both, and v1 rows simply have empty values in the newer columns.
It does not store IP addresses, set cookies, or read anything back; the endpoint is write-only
and replies 204 No Content. Strings are length-clamped so a malformed client cannot bloat a point.
Prerequisites: a Cloudflare account, tagger.photo as a zone on it, and
Wrangler installed.
cd telemetry
export CLOUDFLARE_ACCOUNT_ID=... # or run `wrangler login`
wrangler deployWrangler creates the photo_tagger_telemetry Analytics Engine dataset on first write and provisions
the DNS record and TLS certificate for telemetry.tagger.photo.
Use the
Analytics Engine SQL API
(or the Cloudflare dashboard SQL console) with the statements in queries.sql. They
cover most-used models, median batch size, CLI vs GUI split, duration by CPU arch, OS distribution,
and active installs over time.
dashboard.py is a marimo notebook that runs those queries and
turns them into sections: adoption (KPI tiles, daily active-installs trend, app/Python
versions), usage (models, providers, CLI vs GUI, batch sizes, file formats, token appetite),
hardware & performance (CPU/GPU models, RAM, core counts, OS, duration by architecture),
reliability & crashes (success and cache-hit rates, crash trend, crashes by version, a crash
signature table), languages, and a free-form SQL console. It reads the SQL API directly and
never writes.
It is a self-contained PEP 723 script, so
uv resolves its dependencies into a throwaway venv. It needs two
environment variables: the same CLOUDFLARE_ACCOUNT_ID as the deploy step, and a
Cloudflare API token scoped to Account
Analytics: Read (read-only, nothing else):
export CLOUDFLARE_ACCOUNT_ID=...
export CLOUDFLARE_API_TOKEN=...
uvx marimo run --sandbox telemetry/dashboard.py # read-only app; use `marimo edit` to tinkerIf the variables are missing the dashboard opens to setup instructions instead of failing. A window control (last 7/30/90 days) and an interface filter (all/CLI/GUI) scope every chart, and each chart has a table view so no value is gated behind color or a hover.