Network egress
This page lists every outbound network call the Vulkro scanner can make. It is the page to hand your security team. The short version:
- There is no content telemetry, no analytics, and no crash reporting anywhere in the binary. Vulkro never sends your code, file paths, or findings to us. The only usage data that ever leaves the machine is the account entitlement refresh, which carries up to eight fields: product, install id, version, operating system, timestamp, and a scan counter, plus a device token while you are signed in and a short set of language slugs after a scan.
- Exactly one call is automatic: the once-a-day entitlement refresh
while you are signed in. Every other call happens only when you run a
specific command or flag. In particular, the scanner does not check for
updates while it works: the release-feed check runs only inside
vulkro update. VULKRO_OFFLINE=1disables all of them. With it set, the scanner makes zero outbound connections; pair it with an installed license file, which satisfies the account requirement without any network access.
Egress inventory
| Call | Trigger | Destination | Payload | Default | VULKRO_OFFLINE=1 |
|---|---|---|---|---|---|
| Account sign-in | vulkro login (explicit) | The Vulkro license service | Sign-in exchange that returns a signed lease; never code, paths, or findings | Off (explicit command) | Refused; use a license file instead |
| Entitlement refresh | Background, about once a day while signed in | The Vulkro license service | Up to eight usage fields: product, install id, version, OS, timestamp, scan counter, plus a device token while signed in and a language slug-set after a scan | On when signed in | Disabled; an installed license file satisfies the account requirement |
| Release-feed version check | vulkro update only (explicit); suppressed by --no-version-check or VULKRO_NO_UPDATE_CHECK=1 | dist.vulkro.com/releases/latest.json | GET; User-Agent: vulkro/<version>; no body | Off (explicit command) | Refused (the update is refused first) |
| Dependency CVE lookup | vulkro scan with VULKRO_CVE_LIVE=1, when a parsed manifest is found | api.osv.dev/v1/querybatch | POST JSON: {name, version, ecosystem} per dependency | Off (opt-in) | Disabled |
| CVE bundle download | vulkro update (no --bundle) | dist.vulkro.com/cve/... (override: VULKRO_CDN_BASE_URL) | GET; signature + sha256 verified before apply | Off (explicit) | Refused (exit 2) |
| Installer fetch | Interactive vulkro update after you confirm | dist.vulkro.com/install.sh | GET via curl | bash | Off (TTY + consent) | The update is refused first, so this never runs |
| Secret validation | vulkro scan --validate-secrets | Stripe, GitHub, SendGrid, Slack, OpenAI APIs | GET/POST with Authorization: Bearer <the discovered secret> | Off (opt-in) | Refused (exit 2) |
| Notifications | vulkro notify / vulkro scan --post-to <...> | Your webhook (Slack/Teams/Jira) or events.pagerduty.com | POST JSON scan summary (counts, top findings) | Off (opt-in) | notify refused (exit 2); --post-to skipped with a warning |
| Rule-pack registry | vulkro rules add / rules update | dist.vulkro.com/rules/... (override: VULKRO_RULES_REGISTRY_URL) | GET; sha256 verified | Off (explicit) | Refused (exit 2) |
| Active probe | vulkro probe --base-url <URL> | The target URL you supply | Crafted attack requests (see probe) | Off (explicit) | Refused (exit 2) |
| Test webhook | "Test webhook" button in vulkro serve | Your configured webhook URL | POST a fixed test payload | Off (manual click) | Refused (error in the UI) |
Notes:
- Destinations are fixed or yours. The
dist.vulkro.comcalls and the license-service calls are the only ones that reach Vulkro infrastructure, and all of them are off by default except the entitlement refresh. The probe and webhook destinations are URLs you supply. - Signed and verified. Every artifact Vulkro downloads (CVE bundle, rule
packs) is signature- and/or sha256-verified against trust roots
pinned in the binary before it is used. Mirroring via
VULKRO_CDN_BASE_URLdoes not change the trust model.
Listeners (not egress)
These accept inbound connections on loopback; they do not send data out. They
are not gated by VULKRO_OFFLINE because their purpose is to serve a local UI.
vulkro servebinds127.0.0.1:8723(the desktop console).vulkro mcp serve --port <P>binds127.0.0.1:<port>(SSE transport; the default MCP transport is stdio, no socket).vulkro lspspeaks over stdio (no socket).
Verifying zero egress
VULKRO_OFFLINE=1 is the single switch. To prove it under OS-level network
blocking:
# Linux: run in a network namespace with no interfaces.
unshare -rn /bin/bash -c 'VULKRO_OFFLINE=1 vulkro scan .'
# macOS / any platform: observe syscalls and confirm no connect() to a
# non-loopback address.
sudo dtruss -t connect -f env VULKRO_OFFLINE=1 vulkro scan . # macOS
strace -f -e trace=connect env VULKRO_OFFLINE=1 vulkro scan . # Linux
The scan should complete normally and you should see no connect() to any
non-loopback address. If you observe one, it is a bug: please report it.
The egress inventory above is also enforced in our own CI: a source-level guard
test fails the build if any new code path makes an outbound call (reqwest, raw
TCP, or raw TLS) without being covered by a VULKRO_OFFLINE gate. The inventory
cannot silently grow.
Related
- Offline mode - the command-by-command behaviour table.
- Telemetry - the heartbeat fields in detail.
- Accounts and licensing - sign-in and the lease.
- Bundle format and Signing & trust roots.