Telemetry and the install heartbeat
Vulkro scans run entirely on your machine: your code, your findings, and your scan history never leave it. The only thing the binary sends on its own is a best-effort anonymous install heartbeat, and this page documents exactly what it contains and how to turn it off.
The heartbeat is now the only automatic outbound call in the binary.
The scanner does not check for updates while it works. Every other call
Vulkro can make is one you start yourself: vulkro update, vulkro triage, vulkro probe --base-url, vulkro rules add / rules update,
--post-to, --validate-secrets, an opt-in live CVE lookup
(VULKRO_CVE_LIVE=1), and the installer.
The heartbeat
Vulkro sends an anonymous heartbeat at most once per 24 hours (debounced
by a timestamp file under ~/.vulkro). It
runs on a background thread, never blocks or delays a scan, never changes
the exit code, and fails silently if the network is unavailable. It exists
so we can count active installs and retention.
What triggers it
The heartbeat is fired by the account check, so it can run on any command
that goes through that check: vulkro scan, serve, discover, prove,
mcp, inspect, report, formats, respond, lsp, cra-bundle, and
the rest of the scanning surface. It fires at most once per 24 hours across
all of them, not once per command.
The local and recovery commands skip the account check entirely and therefore never send it:
machine-id, buy, activate, license-status, login, logout,
account, usage, init, install-hook, guard, gate, intel,
bench, scan-port, slopcheck, and explain --risk-model.
So vulkro gate and vulkro guard (the hook and pre-commit path) make no
outbound request at all, whatever your environment.
The heartbeat fields
The heartbeat is content-free by design. Its core is six usage keys:
| Field | What it is |
|---|---|
| product | Which Vulkro binary sent it. |
| install_id | A random id minted once and stored in ~/.vulkro/install_id. Anonymous; not your machine fingerprint. |
| version | The scanner version. |
| os | An OS string. |
| timestamp | When the heartbeat was sent. |
| scans-since-last | One aggregate integer: scans since the last heartbeat. |
After a scan the heartbeat also carries languages: a short,
comma-separated set of the lowercased language slugs the scan detected
(for example go,java,javascript), deduplicated and capped. It is a set
of language names only, never a file path, filename, source code, or a
per-language count. With the device token below, that is up to eight
fields in total.
It never sends your machine_id, any file path, any project or repository
name, or any finding body. Your scan history stays on your machine;
vulkro usage reads it locally and never triggers the
heartbeat.
When you are signed in
If you have paired this machine with an account (see
Accounts and licensing), the heartbeat
additionally carries your device token (never the machine_id) so the
server can refresh your subscription lease. The lease-refresh call sends
only the device token. A refreshed lease is verified and applied silently.
If the server reports the license revoked or expired, nothing is stored
and the cached lease simply lapses at its signed expiry. When you are not
signed in, the body is the anonymous fields above (the six usage keys, plus
languages after a scan) and carries no device token.
Turning it off
| Variable | Effect |
|---|---|
VULKRO_NO_HEARTBEAT=1 | Disable the heartbeat (any non-empty value) while leaving the rest of online licensing untouched. |
VULKRO_OFFLINE=1 | The air-gap switch. Disables the heartbeat along with every other outbound network call, guaranteeing zero egress. |
VULKRO_HEARTBEAT_URL | Override the heartbeat endpoint (defaults to https://api.vulkro.com/v1/heartbeat). Useful for self-hosting or testing. |
The release-feed version check
Vulkro used to ask the release feed for the latest version on every
invocation and print a one-line "update available" notice at the end of
the command. That check has been removed. A plain vulkro scan makes
no release-feed request and can print no update notice.
The version check now runs in exactly one place: when you run vulkro update, which is an explicit user action. There it is
forced (it ignores the 24-hour cache) and, if a newer binary exists, it
offers to install it.
| Variable | Effect |
|---|---|
VULKRO_NO_UPDATE_CHECK=1 | Still supported, and still suppresses the version check. Because nothing else checks any more, this now only affects vulkro update; --no-version-check on that command does the same thing. |
VULKRO_OFFLINE=1 | Disables it along with every other outbound call. |
The release feed itself is unchanged and still published: install.sh
reads it to resolve which binary "latest" means. Only the binary's
automatic call to it is gone.
Related
vulkro usage- your local scan-history report (no network).- Accounts and licensing - the lease refresh.
- Offline mode - running with zero egress.
- Network reference - every endpoint Vulkro can call.