NetRise Provenance — VS Code Extension
Supply-chain security for your dependencies, powered by
NetRise Provenance. As you open or edit a
supported manifest (Python out of the box; npm opt-in), every dependency gets an
inline icon, a squiggle, and a rich hover showing its risk,
advisories, and repo health. Optionally, an install-time firewall blocks
rejected packages before they ever touch disk.
How it works in one line: the extension is just the UI — it renders results
and runs installs. The actual analysis is done by the netrise binary,
which the extension downloads automatically the first time it needs it
(nothing to install by hand). All risk decisions come from that CLI; the
extension never guesses.
Contents
- Quick Start
- Install
- First-run setup
- What the icons mean
- Supported manifests & version resolution
- Install-time firewall
- Settings
- Policy
- Commands
- Troubleshooting
- Privacy & Telemetry
- The
netrise binary
- Releases & CI/CD
Quick Start
Get the .vsix from the repo's
Releases
page (latest at the top; file is under Assets).
Install it: code --install-extension netrise-provenance-<version>.vsix --force
(or Extensions panel → … → Install from VSIX…), then Ctrl+Shift+P →
Developer: Reload Window.
Pick your environment (if not prod): Ctrl+Shift+P → Provenance: Switch
Environment (Dev / Prod).
Sign in: Ctrl+Shift+P → Provenance: Sign In → create/copy an API key
in the browser → paste it back in VS Code.
Open a manifest (e.g. requirements.txt) → icons + hovers appear. The
first time, accept the one-click prompt to download the netrise binary.
Try the firewall: Ctrl+Shift+P → Provenance: Run Install Through
Firewall… → type a package → watch the Provenance Firewall output channel.
Requirements: VS Code 1.67.0+, a Provenance API key (step 4), and the
netrise binary (auto-downloaded; Windows / macOS Intel + Apple Silicon /
Linux x64 + arm64).
Install
Grab the latest .vsix from the
Releases
page (under Assets), then either:
- VS Code UI: Extensions panel (
Ctrl+Shift+X) → … menu → Install from
VSIX… → pick the file.
- Terminal:
code --install-extension netrise-provenance-<version>.vsix --force
Then reload: Ctrl+Shift+P → Developer: Reload Window.
Upgrading (same extension id netriseInc.netrise-provenance)? Install the newer
.vsix with --force and reload — your API key and settings are preserved.
Migrating from netrise.vsc-provenance-extension or netrise.provenance?
Uninstall the old extension, install this .vsix, and sign in again (secrets
are per extension id). Every push to main publishes a fresh .vsix
automatically (see Releases & CI/CD).
First-run setup (≈2 minutes)
1. Point it at the right environment (if needed)
By default the extension talks to https://provenance.netrise.io (prod). For
dev: Ctrl+Shift+P → Provenance: Switch Environment (Dev / Prod) (or set
provenance.apiUrl manually).
⚠ Your API key is tied to the environment it was created in. If icons all show
❓/⚡, the usual cause is a key/environment mismatch.
2. Sign in (paste your API key)
Ctrl+Shift+P → Provenance: Sign In → the Provenance API Keys page opens
in your browser → create a key, copy it (shown once), paste it back into the
prompt. The key is stored in VS Code SecretStorage — never in settings, never
logged. Sign out any time with Provenance: Sign Out.
3. Open a manifest and see results
Open any supported file (e.g.
requirements.txt). Within a few seconds each dependency line shows an icon and
(for non-clean packages) a squiggle. Hover a dependency for the full card. The
same findings appear in the Problems panel (Ctrl+Shift+M) and via
Ctrl+Shift+P → Provenance: Show Findings.
The first evaluation with no binary present prompts "the netrise binary was not
found. Download it now?" → Download. See The netrise binary.
What the icons mean
| Icon |
Squiggle |
Meaning |
| 🔴 |
red (Error) |
Rejected — matches a fail-on policy rule |
| ✅ + blue squiggle |
blue (Information) |
Advisory / repo-health note (default) — an indirect advisory (a contributor or transitive dep is flagged) or a repo-health signal (single-maintainer / deprecated / archived). None block at install, so they're informational. Set provenance.diagnostics.advisoryAsWarning: true to render indirect advisories as 🟡. |
| 🟡 |
yellow (Warning) |
Advisory — a warn-on rule fired for a direct advisory (or any advisory when advisoryAsWarning is on). |
| ℹ️ |
blue (Info) |
Allowlisted — would trigger, but approved in .provenance.yaml. |
| ✅ |
none |
Clean — no advisories (hover for repo health). |
| 🚧 |
— |
Unsupported — not pinned to an exact version and no lock file to resolve from (see below). |
| ❓ |
— |
Not found — package not indexed in Provenance. |
| ⚡ |
— |
Error — API / auth / network issue. |
Hover any dependency for: the policy rule that fired, advisories (linked to
their Provenance page), repo health (OpenSSF Scorecard, last commit, contributors,
bus factor, breached-credential signals), and "See in Provenance" links.
Prefer squiggles only? Set provenance.showInlineIcons: false to hide the
inline icons and keep just the squiggles + Problems entries.
Supported manifests & version resolution
The Provenance API scores a specific published release. But manifests often
declare ranges (^2.28, >=2.0, ~=4.2) that have no single version to look
up. So the extension reads the project's sibling lock/freeze file to find the
exact version that will actually be installed and evaluates that — the
manifest line then gets a real ✅ / ⚠️ / 🔴 verdict, and the hover notes "Version
X resolved from <lockfile>".
The acceptable fallback: no lock file, a dep missing from the lock, or a
VCS/local dep → the line stays 🚧 unsupported (exactly as an un-pinned dep).
Exact pins (requests==2.31.0) are always evaluated directly.
PyPI (always on)
Manifests: requirements.txt / requirements-*.txt / requirements.in /
requirements-*.in, pyproject.toml (plain PEP 621, Poetry, or uv), Pipfile.
Lock files (also render on their own): poetry.lock, uv.lock, pylock.toml
(PEP 751), Pipfile.lock, requirements.lock.
Range → lock resolution, first that exists wins:
pyproject.toml → poetry.lock, then uv.lock, then pylock.toml.
Pipfile → Pipfile.lock (both [packages] and [dev-packages]).
requirements.txt / requirements-*.txt / requirements.in (plain pip) →
the PEP 751 pylock.toml if present, else a pip-tools compiled
requirements file (requirements.lock, or a requirements.txt that is fully
==-pinned). Typical workflow: keep ranges in requirements.in, run
pip-compile, and the resulting pinned file is used to resolve.
Names are matched per PEP 503 (Flask / ruamel.yaml ≡ flask / ruamel-yaml).
npm (opt-in)
Manifests: package.json; lock files package-lock.json, yarn.lock,
pnpm-lock.yaml. Off by default — add "npm" to
provenance.enabledEcosystems to enable it (only against a backend that supports
npm; dev today, not prod). When enabled:
package.json ranges resolve from the sibling lockfile (priority
package-lock.json → yarn.lock → pnpm-lock.yaml).
- Lock files evaluate only the direct deps declared in
package.json (not the
whole transitive tree) to avoid flooding the API; the firewall still inspects
the full tree at install time.
- The firewall routes lock files unambiguously; for a bare
package.json it
picks the manager from the corepack "packageManager" field, else the sibling
lock file, else defaults to npm.
Install-time firewall
The edit-time icons cover manifests, but a pip install typed in a terminal
bypasses them. The firewall closes that gap: it runs your install through a
local proxy that evaluates each package before the bytes are downloaded, so a
rejected package never lands in your environment.
Ctrl+Shift+P → Provenance: Run Install Through Firewall…
- Enter what to install — e.g.
requests==2.31.0, or -r requirements.txt. (If
the cursor is on a dependency line, it's pre-filled. Also reachable from the 💡
lightbulb on a dependency, or as a Task: Terminal → Run Task… →
Provenance: Install via firewall.)
- Watch the Provenance Firewall output channel; blocked packages get an Error
diagnostic pinned to their manifest line and a summary toast.
The firewall always performs a real install of the non-blocked packages
(that's the only way to reliably catch a malicious wheel) and auto-routes to the
right tool: pip / pipenv (Pipfile) / poetry / uv (pyproject.toml /
lockfiles).
Which Python environment it installs into (pip / uv)
The firewall installs into the environment you're working in, trying these in
order (first that works):
provenance.firewall.pythonPath (your explicit override), then
- the interpreter selected in the Python extension (
ms-python.python), then
- a
.venv/ (or venv/) in the workspace root, then
- the
VIRTUAL_ENV env var, then
- the bare
pip on PATH.
pythonPath accepts either a python or a pip executable; we normalize both.
This controls where packages land, never whether they're inspected. (pipenv
and poetry manage their own virtualenvs, so this doesn't apply to them.)
Where each manager installs (and how to verify)
| You typed |
Manager |
Where |
Verify with |
attrs==1.2.3 (bare) |
pip |
the resolved interpreter (above) |
pip show attrs |
attrs==1.2.3 in a uv project |
uv (uv pip install) |
the project .venv |
uv pip show attrs |
attrs==1.2.3 in a Poetry project |
Poetry (poetry run pip install) |
the venv Poetry manages (under its cache by default, not a project .venv) |
poetry run pip show attrs |
-r requirements.txt |
pip |
the resolved interpreter |
pip list |
-r pyproject.toml / -r uv.lock |
uv (uv sync) |
the project .venv |
uv pip list |
-r pyproject.toml / -r poetry.lock |
Poetry (poetry install) |
the venv Poetry manages |
poetry show |
-r Pipfile |
pipenv (pipenv install --dev) |
the venv pipenv manages |
pipenv graph |
Single package in a uv / Poetry project installs only what you typed
(via uv pip install / poetry run pip install) without touching
pyproject.toml or the lockfile. Pass a manifest (-r pyproject.toml /
-r uv.lock / -r poetry.lock) to rebuild the whole env instead. We
deliberately avoid uv add / poetry add for a single package (they re-resolve
the entire project) — run those yourself after a package is vetted if you want
it recorded.
Poetry venv location: by default Poetry puts the venv under its cache, not
your project. Run poetry env info --path to find it, and use poetry run pip show <pkg> to confirm an install. poetry config virtualenvs.in-project true
switches to a project .venv.
Running the firewall directly from a terminal
If you've put netrise on your terminal PATH,
you can drive it yourself:
NETRISE_API_KEY=… netrise firewall --api-url <apiUrl> [--policy <path>] -- <install command>
Everything after -- is the real install command the firewall wraps. The API
key goes via the NETRISE_API_KEY env var, never on the command line. Notes:
--no-cache-dir (pip) / --no-cache (uv) matters — the firewall can only
inspect packages that are actually downloaded. The IDE command sets this.
--policy is optional; omit it for the binary's built-in defaults.
- Wrap any installer after
-- (-- uv pip install --no-cache <pkg>,
-- pip install -r requirements.txt, …). Run netrise firewall --help for all
flags.
Settings
Open Preferences → Settings (Ctrl+,) and search "provenance", or edit
settings.json. Every contributed setting, with its default:
| Setting |
Type |
Default |
What it does |
provenance.apiUrl |
string |
https://provenance.netrise.io |
Provenance API base URL. Override for dev (https://provenance-dev.netrise.io), on-prem, or staging. Your API key must belong to this environment. |
provenance.resolveVersionsFromLockfiles |
boolean |
true |
Resolve a manifest's ranges to the exact version in the sibling lock file so ranged lines get a real ✅/⚠️/🔴 verdict instead of 🚧 unsupported. Turn off to evaluate only what the manifest literally says (ranges stay 🚧; exact pins unaffected). |
provenance.enabledEcosystems |
string[] |
["pypi"] |
Which ecosystems are active. pypi is always on. Add "npm" to enable package.json / package-lock.json / yarn.lock / pnpm-lock.yaml parsing + firewall routing — off by default; enable only against a backend that supports npm. |
provenance.policy.path |
string |
.provenance.yaml |
Workspace-relative path to the policy file. Auto-detected if present. |
provenance.diagnostics.advisoryAsWarning |
boolean |
false |
Off: indirect-only advisories render ✅ green + blue squiggle + a hover note. On: legacy 🟡 yellow Warning. Direct advisories and rejects are unaffected. |
provenance.healthHoverEnabled |
boolean |
true |
Show the repo-health card on hover for clean packages. Reject/advisory/error hovers always show. |
provenance.showInlineIcons |
boolean |
true |
Show the inline status icons before each dependency. false keeps only squiggles + Problems entries. |
provenance.telemetry.enabled |
boolean |
false |
Opt-in anonymous usage telemetry — see Telemetry. Also gated by VS Code's global telemetry.telemetryLevel. No code/paths/names/URLs ever sent. |
provenance.experimental.allowManualApiKeyEntry |
boolean |
false |
Surface the Set API Key Manually… command. The recommended flow is browser-based Sign In; enable only for air-gapped/restricted setups. |
provenance.firewall.binaryPath |
string |
"" |
Absolute path to an existing netrise binary. Empty = search PATH, then the managed (downloaded) copy. |
provenance.firewall.pythonPath |
string |
"" |
Absolute path to the Python (or pip) executable the firewall installs into for pip/uv. Set = takes precedence over auto-detect. Empty = auto-detect (Python ext → workspace .venv/venv → VIRTUAL_ENV → bare pip). Controls where packages land, not whether they're inspected. |
provenance.firewall.policyFallback |
enum |
fail-open |
How the firewall behaves when the API is unreachable: fail-open (permit) or fail-closed (reject). Auth errors (401/403) always fail-closed. |
provenance.firewall.addBinaryToTerminalPath |
boolean |
false |
Add the managed netrise binary to the PATH of VS Code's integrated terminals so you can run netrise … directly. Affects only VS Code terminals (never your system PATH); takes effect in newly-opened terminals; applies only once the managed binary is downloaded. |
Workspace vs. User settings
When you open Preferences → Settings and search "provenance", the settings
editor shows two tabs — User and Workspace:
- User settings apply to every VS Code window on your machine. Good for
personal, machine-specific choices: your
firewall.binaryPath,
firewall.pythonPath, showInlineIcons, telemetry.enabled.
- Workspace settings apply to the current project only and are written to
.vscode/settings.json in the repo — so if you commit that file, your whole
team shares them. Good for project-wide choices: apiUrl (which environment the
project targets), policy.path, enabledEcosystems, firewall.policyFallback.
Precedence: Workspace overrides User for that folder. A common gotcha with
Switch Environment: if a project pins apiUrl at Workspace scope (via
.vscode/settings.json), that value wins over your User setting — the command
detects this and writes to the same scope so the switch actually takes effect.
Example project-shared .vscode/settings.json (commit this in your repo):
{
// Target the dev environment (use your dev API key when signing in).
"provenance.apiUrl": "https://provenance-dev.netrise.io",
// Policy file at the repo root (also the default).
"provenance.policy.path": ".provenance.yaml",
// If the firewall can't reach the API, block the install (stricter).
"provenance.firewall.policyFallback": "fail-closed"
}
Policy (optional)
Drop a .provenance.yaml in the workspace root to control what's rejected vs.
warned vs. allowlisted:
provenance:
fail-on:
advisory:
enabled: true
relationship: direct # direct advisories → 🔴 rejected
warn-on:
advisory:
enabled: true
relationship: indirect # indirect advisories → 🟡 advisory
allowlist:
packages:
- pkg:pypi/seleniumbase # approve a package (versionless matches any version)
All rules live under the top-level provenance: key. Changes are picked up on
save and applied to both the edit-time icons and the firewall. Health signals
(archived / single-maintainer / scorecard) show on hover but aren't
policy-gateable yet. The 💡 lightbulb on a flagged dependency offers
"Allowlist ", which appends it here for you.
Commands
Ctrl+Shift+P, then:
- Provenance: Sign In / Sign Out
- Provenance: Switch Environment (Dev / Prod)
- Provenance: Scan Workspace — re-evaluate every open manifest
- Provenance: Clear Cache — drop cached verdicts and re-scan
- Provenance: Show Findings — jump to any finding in the Problems panel
- Provenance: Run Install Through Firewall…
- Provenance: Download Firewall Binary…
Output channels
Ctrl+Shift+P → Output: Focus on Output View, then pick:
- Provenance — the main log: activation, config, what was parsed, every CLI
spawn, per-PURL verdicts. Start here.
- Provenance Firewall — one block per firewall run (
BEGIN/END, full
package-manager output, per-artifact breakdown, and a RESULT: summary line).
- Provenance Telemetry — only emits when telemetry is explicitly enabled.
Troubleshooting
- No icons / a "Sign in" note → run Provenance: Sign In and paste a key.
- Everything shows ❓ or ⚡ → wrong environment or key. Make sure
provenance.apiUrl matches where your key was created, then re-sign-in.
- A dep shows 🚧 unsupported → it's a range with no lock file to resolve from
(or a VCS/local dep). Add/commit a lock file, or pin an exact version. Confirm
provenance.resolveVersionsFromLockfiles is on.
- "netrise binary not found" → accept the download prompt, or set
provenance.firewall.binaryPath to an existing binary.
- Stale icons after a change → Provenance: Clear Cache.
- Firewall run says
child-failed → pip/uv/poetry itself failed (e.g. a
version that doesn't exist), not the firewall. Check its output in the
Provenance Firewall channel.
netrise in the terminal is the wrong version / not found after enabling
addBinaryToTerminalPath → something shadows it. Run Get-Command netrise
(PowerShell): an Alias in your $PROFILE or a different netrise earlier on
PATH wins; our injection only affects new integrated terminals.
Privacy
- Manifests are parsed locally; file contents are never uploaded.
- Only package URLs (PURLs) and repo URLs go to the Provenance API.
- The API key lives in SecretStorage only.
- Telemetry is off by default (below).
Telemetry
Telemetry is anonymous, opt-in, and off by default — and today it sends
nothing anywhere. There is no backend and no network call: when enabled, events
are written only to a local Provenance Telemetry output channel so you can see
exactly what would be sent. It records exactly two events, both counts/booleans
only:
| Event |
When |
Properties |
extension.activated |
activation |
hasApiKey (bool), ecosystems (count — never which), healthHover (bool) |
scan.completed |
a scan finishes |
manifests (count — never names/paths) |
No-PII guarantees: (1) double-gated on provenance.telemetry.enabled and
VS Code's global telemetry.telemetryLevel; (2) primitives only; (3) a defensive
filter drops any string that looks like a path / email / URL / PURL / free text.
The netrise binary
The extension shells out to a local netrise binary for the actual evaluation
(it's the source of truth — the extension makes no risk decisions itself). The
first time it needs the binary and can't find one, it prompts:
Provenance: the netrise binary was not found. Download it now? → Download
Download fetches the correct prebuilt archive for your platform, verifies
its SHA-256, extracts it, and caches it (namespaced by version) at:
…/globalStorage/netriseInc.netrise-provenance/netrise/<version>/netrise[.exe]
You do this once; later sessions reuse it. You can also trigger it manually with
Provenance: Download Firewall Binary…. The lookup order is:
provenance.firewall.binaryPath → system PATH → the managed (downloaded) copy.
Already have netrise? Point at it to skip the download:
"provenance.firewall.binaryPath": "C:\\path\\to\\netrise.exe".
Upgrades are automatic. The cache path is namespaced by version and the
extension pins the version it expects; a new build simply re-downloads (you click
Download again). No stale-binary footgun.
Reclaim disk / re-test the download by deleting the whole netrise/ folder
(it holds only downloaded binaries; the extension re-fetches what it needs):
# Windows (PowerShell)
Remove-Item -Recurse -Force "$env:APPDATA\Code\User\globalStorage\netriseInc.netrise-provenance\netrise"
# macOS
rm -rf "$HOME/Library/Application Support/Code/User/globalStorage/netriseInc.netrise-provenance/netrise"
# Linux
rm -rf "$HOME/.config/Code/User/globalStorage/netriseInc.netrise-provenance/netrise"
Using VS Code Insiders? Replace Code with Code - Insiders in the path.
Running netrise from a terminal
The managed binary is not on your system PATH by default (it's invoked by
absolute path). To run it from a terminal:
- Opt in (recommended): set
provenance.firewall.addBinaryToTerminalPath: true. This prepends the managed binary's dir onto the PATH of VS Code's
integrated terminals only. Open a new terminal afterward. Applies only
once the binary is downloaded.
- Call it by full path (no setting), e.g.
& "$env:APPDATA\Code\User\globalStorage\netriseInc.netrise-provenance\netrise\<version>\netrise.exe" --help.
- Install the CLI yourself from the
provenance-cli releases,
put it on your
PATH, and point provenance.firewall.binaryPath at it so the
IDE and terminal share one binary.
Where things live (at a glance)
| Thing |
Location |
| The extension |
VS Code's extensions dir (~/.vscode/extensions/…). Contains no binaries — only JS + assets. |
The managed netrise binary |
…/globalStorage/netriseInc.netrise-provenance/netrise/<version>/ |
| Your API key |
VS Code SecretStorage (OS keychain-backed; never in settings/logs) |
| Verdict cache |
VS Code workspace state (per-workspace). Cleared with Provenance: Clear Cache. |
| Packages the firewall installs |
Your Python environment (the resolved venv / interpreter) — NOT the extension |
| Policy |
<workspace>/.provenance.yaml (you author it; optional) |
Releases & CI/CD
Two GitHub Actions workflows in .github/workflows/:
- CI (
ci.yml) — on every pull request to main: npm ci, compile +
lint + full test suite (headless VS Code via xvfb), and a vsce package smoke
test. The merge gate; it doesn't bump the version or release.
- Build & Release (
release.yml) — on every push to main (after a PR
merges): bumps the PATCH version, rolls the CHANGELOG (promotes ## [Unreleased]
to a new ## [x.y.z] - DATE section and seeds a fresh empty one), compiles +
lints + tests, packages the .vsix, commits the bump/tag with [skip ci], and
publishes a GitHub Release with the .vsix attached and the CHANGELOG
section as the notes.
So the patch version rises automatically on every merge, and the matching .vsix
is one click away on the
Releases page.
Writing the changelog: add user-facing notes under ## [Unreleased] in
CHANGELOG.md (grouped Added / Changed / Fixed /
Removed / Security) as you work; the release job files them under the new
version automatically.
One-time setup: in Settings → Actions → General → Workflow permissions
select Read and write permissions. If main is protected, add a token with
contents: write as the RELEASE_TOKEN secret (the workflow prefers it, falling
back to GITHUB_TOKEN). Nothing in CI publishes to the Visual Studio
Marketplace or Open VSX — download netrise-provenance-<version>.vsix from
Releases and upload it under publisher netriseInc. See
docs/PUBLISHING.md.