Docs
One file, plain steps. Vigilance is the product. The command is vigi.
Install it, point it at a folder, and let it watch. Everything
below needs the free activation or a Pro license first, except the install itself.
Start Here
Install it, license it, and get a first answer.
Install
Pick your package manager. It pulls the right file and checks the hash before it runs.
brew install vigihq/vigi/vigi
scoop bucket add vigi https://github.com/vigihq/scoop-vigi scoop install vigi
No package manager? Download the file and check it on the verify page.
Other package managers and tools
Every one installs the same signed binary.
brew install vigihq/vigi/vigi
scoop bucket add vigi https://github.com/vigihq/scoop-vigi scoop install vigi
npm install -g vigihq
pip install vigihq
docker run --rm -v "$PWD:/scan" ghcr.io/vigihq/vigi /scan
- uses: vigihq/vigi-action@v1
with:
path: ./dist
Get a License
For the free tier, run the line below. It registers this install with us and keeps the key it gets back.
Every run afterwards posts a signed report: the hash of each file it scanned, the capabilities found in it, and a few structural hints. Never your code, never a path, never a file name.
A free run has to reach us, so a machine with no internet needs Pro.
vigi activate
For Pro, buy it on the plans page. A signed file arrives by email. Install it on each machine:
vigi license install vigilance.lic
The same file works everywhere: it lists no machines and counts no seats. It lives in a folder only an administrator can write. If vigi asks, run that line with sudo. To see which tier you are on and when a Pro licence runs out, run:
vigi license status
Go from Free to Pro
One command moves a Free install to Pro. On a Free install it opens checkout in your browser.
Pay, then install the licence file that arrives by email. Run the command again. It downloads the Pro binary, checks the hash against the signed release, and swaps it in.
vigi upgrade
Pro is a separate binary with no network code, so it opens no connection at all. Free and Pro are both on the Verify page, each with its own hash.
First Run
Point it at a folder. The first run remembers what is there. Every run after that tells you what changed.
vigi ./the-folder
It Keeps Watching (Schedule)
It asks where this is running, then schedules itself. It uses whatever your computer already uses to run jobs. Nothing of ours sits running in the background. It re-runs the same check on the schedule you pick, as often as every 15 minutes.
vigi setup
To take all of it back out, run:
vigi setup --remove
Everyday Use
What a run tells you, and how to keep an eye on many machines.
See It Is Alive
A quiet guard and a dead guard look the same. This one line says which one you have. It shows when each folder was last checked, any check that is overdue, and any record that was changed. It reads what the checks already wrote, so it opens no network and needs no setup.
vigi status
It shows the guard is awake and found nothing. It does not say the machine is clean. A program that owns the machine can fake what this reads. The copy that survives that lives off the machine, in the receipts folder your fleet reads.
Watch a Fleet, with No Server
Every machine writes one signed line into a folder you own. Nothing calls home, and no cloud key ever sits on a machine you are watching. Your own sync client moves the folder.
On the machine you manage from, mint the fleet key once. Name the folder every machine will write into:
vigi team init --to /path/to/receipts
Print the one line your machines join with. Ship it through Intune, Jamf, a GPO or your image:
vigi team token --to /path/to/receipts
On each machine, run that line. It makes a key for that machine and sets the schedule:
vigi enroll --team <token> --to /path/to/receipts
Read the whole fleet from the folder. Write one page you can send or open from a USB stick:
vigi fleet ./receipts --report fleet.html
The page opens with three numbers: how many machines are enrolled, how many reported inside their own window, and how many did not.
A machine that goes quiet reads as loud as one that gained a capability, because a silent machine is the case an attacker wants.
Machines checking in on schedule fold to a count, so the page lists only what needs a decision. --all lists every machine. --export writes the CSV an audit asks for by name.
A machine cannot retire itself or wave away its own bad news. These two lines carry the team key. That key never goes on a watched machine:
vigi team retire <machine> --to /path/to/receipts vigi team ack <machine> --to /path/to/receipts
Receipts name machines by a short hash and never carry a watched path. Point the folder at append-only storage. Then it is evidence, not a convenience.
What It Looks For
It reads every file. It only calls out the ones that do something new.
- A new file that can run commands, download, or read your saved passwords.
- A file that can suddenly do more than before.
- A file that started connecting somewhere new.
- A change buried inside an archive, an installer, or a package.
- An edit to a file your AI assistant follows.
- A file that hides the name of what it loads inside an encoding.
- A package that starts fetching a dependency from outside the registry.
- A build file that runs a data file.
- A change to its own records.
Check an Update Before You Install It
Give it the version you run and the new one. It flags the file that gained a new capability, before the update ever runs.
vigi diff --old ./installed --new ./update
Get a Page You Can Send (HTML Report)
Add --report to any check and it writes one plain HTML file. Black text on
white, so it prints, and it opens in a mail client that strips half of what it is given.
Send it to a vendor, attach it to a ticket, hand it to an auditor.
vigi ./dist --report report.html
The same file holds the machine-readable record, in the page source. One file is both the page a person reads and the data a dashboard parses, so you never keep two in step.
The scheduled check and the git hooks take it too. That is where it earns its keep. Those are the runs nobody is watching, and the page is what you open afterwards.
vigi watch /opt/app --report /var/log/vigi/app.html
Wire It In
Builds, pipelines, commits and your own rules.
Check a Build Against What Went into It
A first run on a folder has nothing to compare against, so it gives no verdict. A build does not have that problem, because it knows what went in. Name the inputs, then ask which file in the output none of them explain.
vigi record --step source --dir ./src --out source.json vigi record --step artifact --dir ./dist --out artifact.json vigi check --target artifact.json --cause source.json
It answers on the first run. No earlier release, no history, nothing to set up first. A file that arrives in your build with no input to account for is the thing this names.
Exit Codes and Flags
Vigilance returns one exit code per run. Wire your pipeline to these.
| Code | Meaning |
|---|---|
| 0 | Clean. Nothing changed and no denied capability. |
| 1 | A compare found a change. |
| 2 | A usage error. Check the command. |
| 3 | First run. No baseline yet, so there is nothing to compare. |
| 4 | Another run held the folder. Nothing was read, so this is not a pass. |
| 5 | A denied capability is present. |
Output flags. --report writes an HTML page. --sarif writes SARIF for code scanning. --json writes JSON. --deny fails the run on a named capability.
Send Findings to Your Dashboard (SARIF)
Add --sarif to a check and it writes SARIF 2.1.0, the format the code-scanning
dashboards read. The findings show where your other scanners report. You learn no new screen.
vigi hook check --sarif > vigi.sarif
In GitHub, pass that file to the code-scanning upload step. Each finding then shows in the Security tab, on its line.
vigi diff --old prev --new ./dist --sarif > vigi.sarif
The check still exits on its own code, so a pipeline can gate on the result and file the SARIF at the same time.
Guard Every Commit (Git Hook)
Run one command in a repo and vigi checks each commit from then on. A commit that plants a file with a new capability stops with a nonzero exit. A clean commit passes and says nothing.
vigi hook git
It writes into the repo's own hooks and marks its lines. vigi hook git --remove
removes them and leaves any hook you already had. If your team runs the
pre-commit tool, add vigi to .pre-commit-config.yaml. Every
checkout then gets the same guard.
Show It on Your Repo (Badge)
Put a badge on a project that vigi checks. It shows
and links back
here. Paste this into a README:
[](https://vigihq.com)
Set Your Own Rule (--Deny)
Name a capability that must never appear in what you ship. A file holding it raises an alarm and the run exits 5, so a pipeline stops.
vigi ./dist --deny network-shell --deny exec:scripts/**
This is your rule, not our verdict. Vigilance ships no list of bad capabilities and decides
nothing about which ones are acceptable in your product. Run vigi --deny ?
for the 28 capabilities you can name, and add :some/path/** to scope a rule to part
of the tree.
It can only ever add an alarm. There is no way to silence one, and there is no --allow.
It also asks a different question from every other check: not what a file gained, but whether the capability is there at all.
So it works on a first run, against a folder nothing has ever scanned, which is every run on a throwaway build agent.
Nothing is discovered. Every rule is an argument you type, or a file you name with
--deny-from. We never hunt for a config file. Anyone who can write that
folder can also empty it.
Reference
The rest.
Profile a Fresh Install
Point it at the one copy you have. It lists everything inside that can act, so you skip building an SBOM and reading it yourself. It never alarms here, because there is nothing to compare against.
vigi profile ./installed
At the top it shows the files worth reading first. A file that runs during an install and can also reach out. A file that can read saved credentials and can also send bytes out.
Across 6,294 files from eleven packages nobody attacked, one file held either combination. 29 of the 109 recorded attacks held one.
This part needs no history, so it answers on a folder nothing has ever scanned.
It says what a file can do, never what it is. An honest installer that downloads its own payload holds the same combination, so the line is a reason to read the file and nothing more.
During development, run this against your own build. Point your SAST tools and code review at whatever holds the most capability first.
Follow New Releases (RSS)
Every release goes into a feed. Poll it with whatever you already run and you learn a new version is out without opening the website.
https://vigihq.com/dl/latest/releases.xml
There is a JSON copy at /dl/latest/releases.json for a script, and a
signature at /dl/latest/releases.xml.sig. Each entry states a version, carries
that release's changelog lines, and points at that release's signed manifest.
The binary never fetches this, on any tier. Pro opens no connection at all.
You fetch the files. Check each binary against the hash the signed manifest lists. Then drop the manifest and the binary into each machine's update folder.
The next scheduled run picks it up offline. That is how an air-gapped fleet updates on its own schedule.
Verify Your Download
Every release lists the sha256 of each file. The installer verifies it for you. To verify by hand, put SHA256SUMS beside the files and run:
shasum -a 256 -c SHA256SUMS
The Verify page has every hash and a full bill of materials.
Free and Paid
Two plans. Both have all features. Free costs nothing and covers up to 50 machines. It runs online, so it needs the network. Pro is one flat price for the whole company. The price follows company size: $999 a month up to 500 machines, $2,999 up to 2,000, and $9,999 up to 10,000. Above 10,000 machines we quote per company. Each band is flat on any number of machines inside it. Pro runs fully offline. See Pricing. What Free sends is in the privacy policy.
Talk to Us
A question, a pilot, or a bigger fleet? Send a note. It reaches a person.