CLI for Virgil — self-hosted security audit with the triage built in. Real scanners + clustering + LLM priority queue + code-grounded chat. Installs as `virgil` on your PATH.
Project description
virgil
Terminal client for Virgil — self-hosted security audit with the triage built in. Real scanners + clustering + LLM priority queue + code-grounded chat.
The CLI is a thin shell over a running Virgil API. It bundles your working directory, submits a scan, streams progress, and prints the ranked findings with CI-friendly exit codes.
Install
virgil is a CLI tool, so the right installer is pipx — it puts the
binary on your $PATH and isolates the dependencies from your system Python:
pipx install virgilhq
Don't have pipx yet? Either install it once, or fall back to user-mode pip:
# macOS
brew install pipx && pipx ensurepath
# Linux / other
python3 -m pip install --user pipx && pipx ensurepath
# Fallback if you don't want pipx at all:
python3 -m pip install --user virgilhq
The PyPI package is virgilhq (the bare virgil name was already taken).
The command on your $PATH is still just virgil.
Standalone binaries (no Python required)
Each GitHub Release ships single-file binaries built with PyInstaller, for folks who don't want a Python toolchain in the picture (and for the VS Code extension, which downloads one on first launch). Grab the right one for your machine from Releases:
| File | For |
|---|---|
virgil-macos-arm64 |
macOS, Apple Silicon |
virgil-macos-x86_64 |
macOS, Intel |
virgil-linux-x86_64 |
Linux x86_64 |
virgil-windows-x86_64.exe |
Windows |
# macOS / Linux
chmod +x virgil-macos-arm64
./virgil-macos-arm64 --version
macOS Gatekeeper. The binaries are not (yet) Apple-notarized, so the first launch may be blocked with "cannot be opened because the developer cannot be verified." Either right-click → Open once, or strip the quarantine attribute:
xattr -d com.apple.quarantine ./virgil-macos-arm64
Notarization is planned; for now this is a one-time step per download.
Why pipx and not
pip install? Modern Python distributions (Homebrew Python, Debian/Ubuntu's python3, etc.) mark the system interpreter as externally managed per PEP 668 — a barepip installerrors out.pipxquietly handles the venv for you, which is what you want for CLI tools anyway: each one gets its own isolated environment so avirgilupgrade can't break some other tool.
By default the CLI talks to the hosted instance at https://virgilhq.app —
no extra setup. Just virgil scan . and you're in.
To self-host instead, run docker compose up from the
main repo and point the CLI
at it:
virgil config set api_url=http://localhost:8000
virgil config set web_url=http://localhost:3000
Usage
# Scan and land on triage (counts → ranked clusters → next-steps hint).
virgil scan . # local directory
virgil scan # …also defaults to the cwd
virgil scan github.com/OWASP/NodeGoat # remote, bare host
virgil scan OWASP/NodeGoat # remote, GitHub shorthand
virgil scan https://github.com/OWASP/NodeGoat # remote, full URL
# Land on a different surface after the scan finishes.
virgil scan . --show report # exec narrative
virgil scan . --show surface # languages / frameworks / IaC profile
virgil scan . --show ask_virgil # drop into the chat REPL pre-flighted
# PR mode — only flag findings on lines changed between two SHAs.
virgil scan . --base-sha abc1234 --head-sha def5678
# Don't wait for the scan to finish; print the audit ID and return.
virgil scan . --no-wait
# After a scan, drill in:
virgil clusters <audit-id> # every cluster, sorted by severity
virgil clusters <audit-id> --sev high # filter
virgil cluster <audit-id> <key> # one cluster in detail (prefix match ok)
virgil findings <audit-id> # raw findings table
virgil chat <audit-id> # interactive Q&A grounded in this audit
virgil chat <audit-id> -m "what's the worst finding?" # one-shot
virgil open <audit-id> # launch the web app on the triage tab
virgil open <audit-id> --page chat # …or chat / findings / report / attack-surface
virgil status <audit-id>
# Reports in any supported format.
virgil report <audit-id> --format md
virgil report <audit-id> --format sarif -o findings.sarif
virgil report <audit-id> --format json
virgil report <audit-id> --format pdf
Config
Persistent settings live in ~/.config/virgil/config.json:
virgil config show
virgil config set api_url=https://virgil.example.com/api
virgil config set web_url=https://virgil.example.com
virgil config set default_fail_on=high
virgil config set default_post_scan_view=ask_virgil # triage | report | surface | ask_virgil
virgil config unset default_fail_on
virgil config path
Resolution order for each setting: env var → config file → built-in default.
CI integration
virgil scan . --fail-on critical # exits 1 on any Critical
virgil scan . --fail-on high # exits 1 on Critical or High
virgil scan . --fail-on never # always exits 0
Exit codes:
| Code | Meaning |
|---|---|
0 |
scan finished, no findings exceeded --fail-on |
1 |
scan finished, findings exceed the configured threshold |
2 |
the audit itself failed (clone error, scanner crash, etc.) |
3 |
could not reach the Virgil API |
Environment
| Variable | Default | What it does |
|---|---|---|
VIRGIL_API |
https://virgilhq.app/api |
API base URL. |
VIRGIL_WEB |
https://virgilhq.app |
Web app base URL used by virgil open. |
VIRGIL_FAIL_ON |
critical |
Default --fail-on threshold for virgil scan. |
VIRGIL_SHOW |
triage |
Default --show surface after virgil scan (triage / report / surface / ask_virgil). |
VIRGIL_CONFIG_DIR |
~/.config/virgil |
Override the config directory. |
What the output looks like
$ virgil scan .
bundle /work/myrepo → zip → submit
┌─ [ virgil ] ───────────────────────────────────────────────────────────────┐
│ audit_id c9b1… │
│ source scan.zip │
│ state succeeded phase=completed │
└────────────────────────────────────────────────────────────────────────────┘
CRIT HIGH MED LOW INFO KEV unreach
2 7 14 6 3 1 19
╭─ [ fix.this_week() · ranked ] ─────────────────────────────────────────────╮
│ #01 [ CRIT ] Hard-coded AWS access key in source ×3 │
│ Critical credential exposure with CISA-KEV-adjacent risk profile… │
│ #02 [ HIGH ] SQL injection via raw query helper ×12 │
│ 12 callsites share src/db/query.py — fix the helper, not callsites. │
╰────────────────────────────────────────────────────────────────────────────╯
License
Apache-2.0. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file virgilhq-0.4.1.tar.gz.
File metadata
- Download URL: virgilhq-0.4.1.tar.gz
- Upload date:
- Size: 21.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c86b82146a7d25b5bcc7b7124aa00c620f5230566fe527b36f13a70bed7cb77f
|
|
| MD5 |
339b78ec2193750338d895dc73f45c89
|
|
| BLAKE2b-256 |
d94a62c1b37cfbc67e17afe74d829222cdb1a47c2174d10a834b421ef298ce5a
|
Provenance
The following attestation bundles were made for virgilhq-0.4.1.tar.gz:
Publisher:
publish-cli.yml on ayaanmaliksgithub/virgil
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
virgilhq-0.4.1.tar.gz -
Subject digest:
c86b82146a7d25b5bcc7b7124aa00c620f5230566fe527b36f13a70bed7cb77f - Sigstore transparency entry: 1634524743
- Sigstore integration time:
-
Permalink:
ayaanmaliksgithub/virgil@6ddc0d6fd4fe7609a3fb167fdafcbac446c658a5 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/ayaanmaliksgithub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-cli.yml@6ddc0d6fd4fe7609a3fb167fdafcbac446c658a5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file virgilhq-0.4.1-py3-none-any.whl.
File metadata
- Download URL: virgilhq-0.4.1-py3-none-any.whl
- Upload date:
- Size: 23.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2e37da0454d7845e1502a6e01f7c30676b4990d74a967f8ee2b4931f967c866
|
|
| MD5 |
0440d07e139d41c6497d574c395794bd
|
|
| BLAKE2b-256 |
d7fa85bd663729257d3c948a7f03aa3ebbace8a71bb6c0e79a370576f818497d
|
Provenance
The following attestation bundles were made for virgilhq-0.4.1-py3-none-any.whl:
Publisher:
publish-cli.yml on ayaanmaliksgithub/virgil
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
virgilhq-0.4.1-py3-none-any.whl -
Subject digest:
a2e37da0454d7845e1502a6e01f7c30676b4990d74a967f8ee2b4931f967c866 - Sigstore transparency entry: 1634524811
- Sigstore integration time:
-
Permalink:
ayaanmaliksgithub/virgil@6ddc0d6fd4fe7609a3fb167fdafcbac446c658a5 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/ayaanmaliksgithub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-cli.yml@6ddc0d6fd4fe7609a3fb167fdafcbac446c658a5 -
Trigger Event:
release
-
Statement type: