Skip to content

Updates & Telemetry

Terminal window
sparkrun update

This upgrades sparkrun itself (if it was installed via uv) and then always refreshes your recipe registries from git. sparkrun setup update is the same command under its full name.

If sparkrun was installed some other way — pip, pipx, or an editable checkout — the self-upgrade step is skipped and only registries are updated.

sparkrun tracks three channels. With no flag, sparkrun update stays on your current channel; passing a flag switches to that channel and updates.

ChannelFlagSource
Stable--stablePyPI releases
Beta--betathe develop branch
Alpha--alpha (or --yolo)the develop-next branch
Terminal window
sparkrun update --beta # switch to beta and update
sparkrun update # stay on beta from now on
sparkrun update --stable # switch back

The channel also drives feature-flag defaults — a capability can be on by default for alpha while staying off for stable. You can preview another channel’s feature set without changing which code you run by setting features.channel in config.yaml.

sparkrun collects anonymous usage telemetry, and it is enabled by default. It carries no hostnames, addresses, credentials, or recipe names — see what is collected for the full list, including the one field that can identify your work.

For a single command, without touching config:

Terminal window
SPARKRUN_NO_TELEMETRY=1 sparkrun run my-recipe

Prefer this when only some of your runs are sensitive — the rest still contribute the signal the project relies on.

To opt out persistently:

Terminal window
sparkrun setup telemetry --disable # persist the opt-out
sparkrun setup telemetry # show the current setting
sparkrun setup telemetry --enable # turn it back on

The environment variable is the single per-process override and beats the persisted setting either way — a truthy value opts out, a falsy value forces it on. So SPARKRUN_NO_TELEMETRY=0 re-enables telemetry for one command even when it is disabled in config. The persisted setting lives at telemetry.enabled in ~/.config/sparkrun/config.yaml.

Four event types are emitted: run, benchmark, update, and setup_wizard. Each carries a random installation ID plus:

  • OS name, OS release, and CPU architecture.
  • The model identifier from the recipe — but only when it is a repo confirmed publicly readable on HuggingFace (see below) — plus a quantization/dtype summary.
  • Runtime, executor, and scheduler names; success/return code; duration; whether the run was solo or a dry run.
  • Cluster shape — node count, total GPU count, and accelerator vendor/model counts. Not hostnames or addresses.
  • How the recipe was referenced, classified into spark_arena / url / file / reference — not the recipe name, path, or URL.
  • Registry counts (total, enabled, non-default) — not registry names or URLs.
  • For updates: old/new version, channel, and whether the self-upgrade succeeded. For the wizard: which steps were opted into, skipped, or failed.

Hostnames, IP addresses, SSH usernames, recipe names and paths, registry names, and registry URLs are not sent. Emission is best-effort with a sub-second timeout, so it never delays or fails a command.

The model name is the one field that could describe your work, so it is filtered rather than sent as written. The raw value leaves your machine only when the repo is confirmed publicly readable on HuggingFace. Otherwise it is replaced with a coarse placeholder that records why it was withheld, without naming anything:

SentWhen
Qwen/Qwen3-1.7BThe repo is public — the signal the project actually wants
<hf-private>The repo is private or gated
<local-path>The model is a path to weights on local disk
<unknown-visibility>Visibility could not be established

Visibility comes from the Hub’s own private / gated flags, not from whether a lookup succeeded. That distinction matters: if you have an HF_TOKEN configured, your private repos resolve perfectly well, so “it fetched” would have been the wrong test.

The check fails closed. Offline, rate-limited, a typo’d repo id, or a run where model detection was skipped all yield <unknown-visibility> rather than the name — being unable to check is never treated as permission to send.

Local paths are classified without contacting the Hub at all.