The dvsentry.py command-line client
dvsentry.py is one file, Python 3.9+, standard library only, so any CI runner or laptop can use it. Download it from DVSentry:
curl -fsSo dvsentry.py https://app.dvsentry.com/downloads/dvsentry.py
python3 dvsentry.py --version
It has two halves:
- API commands (
scan,wait,gate,findings,cancel,repositories,whoami) call the/v1API. - Local commands (
local,install-hook) scan a folder on your machine offline and upload nothing.
Configuration
Configuration comes from the environment only, so the token never appears in a process list or a CI log:
| Variable | Value |
|---|---|
DVSENTRY_URL | https://app.dvsentry.com (plain http:// only for localhost) |
DVSENTRY_TOKEN | A personal access or service account token |
DVSENTRY_TOKEN_FILE | Or a file holding the token |
Use a token with only the scopes the job needs (API and tokens).
Commands
| Command | Calls | Scopes |
|---|---|---|
whoami | GET /v1/auth/me | any |
repositories [--limit N --offset N] | GET /v1/repositories | repositories:read |
scan --repository-id ID --scanner S [--scanner S2 ...] | POST /v1/security-reports, waits, then gates | scans:trigger, scans:read, gates:read |
wait REPORT_ID | Polls the report, then gates | scans:read, gates:read |
gate REPORT_ID | GET /v1/security-reports/{id}/ci-result | gates:read |
findings REPORT_ID [--severity high] [--limit --offset] | GET /v1/security-reports/{id}/findings | findings:read |
cancel REPORT_ID | POST /v1/security-reports/{id}/cancel | scans:trigger |
local [PATH] | Nothing (offline) | none |
install-hook | Nothing (writes .git/hooks/pre-commit) | none |
Every command takes --json to print the API's JSON instead of text.
scan options
| Option | Default | Meaning |
|---|---|---|
--repository-id | required | The DVSentry repository id (repositories lists them) |
--scanner | required, repeatable | Scanner ids, for example semgrep-local, gitleaks, osv-dependencies, trivy-filesystem, syft-sbom |
--ref | the repository's default | Branch, tag or commit; pass the CI commit SHA |
--path | . | Path inside the repository; repeat for several |
--environment | development | development, staging or production (policies can differ per environment) |
--strategy | full | full or incremental |
--name | CI scan | Report name |
--scan-timeout | 600 | The scanners' own time budget in seconds (the server raises it to each scanner's floor) |
--idempotency-key | a new UUID | Pin it to the CI build id so a retried step returns the same scan |
--no-wait | off | Queue the scan and exit 0 |
--timeout | 1800 | Seconds to wait for the scan |
--poll-interval | 10 | Seconds between checks |
A server-side --scanner smart / full profile is planned (it needs a new API field) and not on main yet; list scanners explicitly for now.
Exit codes
| Code | Meaning |
|---|---|
| 0 | The policy gate passed (or --no-wait) |
| 1 | The policy gate failed |
| 2 | The scan did not finish: error, cancelled, or still running at --timeout |
| 3 | Usage or configuration error |
| 4 | The API refused the request or could not be reached |
The POST is retried safely thanks to the idempotency key; reads are retried on 429/502/503/504. Redirects are never followed, so the token cannot be carried to another host.
Example
$ export DVSENTRY_URL=https://app.dvsentry.com DVSENTRY_TOKEN_FILE=~/.dvsentry-token
$ python3 dvsentry.py repositories
3f0c5a1e-... payments https://github.com/acme/payments
1 of 1 repositories
$ python3 dvsentry.py scan --repository-id 3f0c5a1e-... \
--scanner semgrep-local --scanner gitleaks --scanner osv-dependencies \
--ref "$(git rev-parse HEAD)" --environment production
queued report 9a1e...
report 9a1e...: running (40%)
report 9a1e...: completed (100%)
FAIL: 1 policy rule(s) were violated.
- Production release bar: 3 active finding(s) meet or exceed the HIGH threshold.
$ echo $?
1
$ python3 dvsentry.py findings 9a1e... --severity high
Local scan: nothing uploaded
local runs the scanners already installed on your machine, offline, and prints what they found. It makes no network call, needs no URL or token, strips DVSENTRY_* variables from the scanners' environment, and never prints a secret's value.
python3 dvsentry.py local . # every installed scanner
python3 dvsentry.py local --scanner gitleaks . # one scanner
python3 dvsentry.py local --semgrep-config rules/ . # Semgrep only ever uses rules you point it at
python3 dvsentry.py local --staged . # only what is staged for commit (secrets)
python3 dvsentry.py local --fail-on critical --json . # machine-readable, fail only on critical
| Scanner | Runs | Offline because |
|---|---|---|
| gitleaks | detect --no-git (protect --staged with --staged), --redact | it never calls out |
| trivy | fs --skip-db-update --offline-scan --scanners vuln,misconfig | it uses the cached database only; with no database it is reported as failed |
| semgrep | scan --metrics off --disable-version-check --config <yours> | only with --semgrep-config; registry rules are never fetched |
A scanner that is not installed, times out (--scanner-timeout, 600 s) or fails is listed as skipped with the reason. Exit codes: 0 nothing at or above --fail-on (default high), 1 something is, 2 no scanner ran, 3 usage error.
Pre-commit hook
python3 dvsentry.py install-hook # writes .git/hooks/pre-commit running `local --staged`
python3 dvsentry.py install-hook --fail-on medium --force
git commit --no-verify # skip it once
It refuses to replace a hook it did not write unless --force.
IDE extension
The VS Code extension (extensions/vscode/ in the repository) shows the open findings of the repository you have open, and runs dvsentry.py local. It is read only: it can never start a scan or change a finding.
- Set
dvsentry.serverUrl(defaulthttps://app.dvsentry.com). - Run DVSentry: Sign in. The editor shows an eight-letter code and opens
/ide/activate. - In the browser, check the code, pick the organization and approve (with your MFA code if you use MFA).
The editor receives a token with the single scope ide:read, for one organization, for 30 days, kept in VS Code's SecretStorage and listed under Settings → Personal access tokens. Build and install it from the folder:
cd extensions/vscode && npx @vscode/vsce package && code --install-extension dvsentry-0.1.0.vsix
The extension is not published to the VS Code Marketplace; JetBrains IDEs are not supported.
Something missing or unclear? Tell us with Feedback in the app.