@devguardch/cli
v0.1.1
Published
devguard evidence courier — run collectors and push their output into devguard Evidence records from CI.
Downloads
198
Readme
@devguardch/cli
Continuous evidence for devguard: run your collectors (npm audit, Trivy, Terraform plans, any command) on a schedule and push their output into a devguard Evidence record. The Evidence stays fresh instead of relying on a screenshot someone uploaded once a year.
Install
npx @devguardch/cli --help # no install
npm install -g @devguardch/cli # global install, provides `devguard`Standalone Linux binaries (x64/arm64, no Node required) are attached to each GitHub release, with a SHA256SUMS file to verify downloads. They embed a slimmed-down Node runtime (no ICU, npm or inspector), roughly half the size of a stock Node build, and each has an .xz-compressed variant next to it for constrained hosts (xz -d once after download).
Quickstart
- Create an Evidence in devguard and link it to your controls in-app. Note its number (e.g. EV-42).
- Create an API key under Settings → Tokens. A devguard API key carries the full permissions of your account in every organization you belong to, so treat it as a high-value secret in CI (store it as a masked secret, rotate it if exposed).
devguard login(or setDEVGUARD_API_KEYin CI). Login lists your organizations, lets you pick a default for the url (--org <id>skips the picker), and stores it alongside the key.- Commit a
devguard.yml— rundevguard initfor an interactive wizard that detects your project and writes it, or by hand:
organization: <your organization id> # optional if login stored a default; --org overrides both
collectors:
- name: npm-audit # becomes the file's identity, each push replaces the last
command: npm audit --json
evidence: 42 # target Evidence shortId; must already exist
- name: trivy-fs
command: trivy fs --format sarif --output trivy.sarif .
output: trivy.sarif
evidence: 57
expiresAt: 7d # freshness window (default 30d, 'never' to opt out)- Push:
devguard evidence pushEach collector's upload supersedes its previous file, so the Evidence always holds exactly one current artifact per collector. If a pushed file passes its expiresAt without being refreshed, devguard flags it on the deadlines view.
Commands
| Command | What it does |
| --- | --- |
| devguard init | interactive wizard: writes a devguard.yml (inline login, organization and evidence pickers) |
| devguard login | verify an API key and store it for the target url (~/.devguard/config.json) |
| devguard scan --evidence 42 | run an installed vulnerability scanner (trivy/osv-scanner/grype/npm audit) and push its report |
| devguard evidence validate | read-only preflight: config, auth, every Evidence resolves |
| devguard evidence push | run collectors, then secret-scan, upload and supersede |
| devguard evidence push --dry-run | the whole pipeline as a rehearsal: collectors run and secrets block, nothing uploads |
Configuration reference
Collector fields: name (slug, required), command plus optional output (default stdout; a file path otherwise), evidence (shortId, required), expiresAt (duration or never, default 30d), timeout (default 10m), allowExitCodes (default [0], set [0, 1] for scanners that exit 1 on findings), allowSecrets (default false). Recipe form: uses: scan with optional scanner:.
Connection settings (highest wins): --url/--key flags, DEVGUARD_API_URL/DEVGUARD_API_KEY, the login keystore, then https://app.devguard.ch. Point the CLI at a local instance with DEVGUARD_API_URL=http://localhost:3000.
Organization (highest wins): the --org flag, organization: in devguard.yml, then the default stored by devguard login for the resolved url.
Add .devguard/ to your .gitignore. It holds staged artifacts.
Secret scanning
Before uploading, every artifact is checked against a built-in pattern pack (cloud keys, private keys, tokens, high-entropy assignments). Findings block the push with a masked report. If a collector's output legitimately contains matches (e.g. a pentest report), set allowSecrets: true on that collector. It is reviewed in the PR that adds it, unlike a global flag.
Developing and testing locally (without publishing)
Three ways to run the CLI from the monorepo, from fastest iteration to closest to a real install:
# 1. straight from source, no build step (edits take effect immediately)
npx tsx packages/cli/src/cli.ts --help
# 2. the built bundle, exactly what npm ships
npm run build --workspace=packages/cli
node packages/cli/dist/cli.mjs evidence push
# 3. globally as `devguard`, via the real publish artifact
npm run build --workspace=packages/cli
npm pack --workspace=packages/cli # produces devguardch-cli-<version>.tgz
npm install -g ./devguardch-cli-*.tgz
devguard --versionOption 3 also exercises the files whitelist and the prepack build hook (npm pack runs prepack, and so does npm publish), so run it once before any release.
To test against a local devguard stack, the target is just configuration:
# one-time: create an API key in the local app under Settings → Tokens
devguard login --url http://localhost:3000Then point a scratch devguard.yml at any Evidence in your local organization and push with DEVGUARD_API_URL=http://localhost:3000. The keystore stores keys per URL, so local and production logins coexist. A quick way to verify the supersede behavior: push twice and check the Evidence detail page shows exactly one file with the "Automated" badge both times.
CI example (GitHub Actions)
name: evidence
on:
schedule: [{ cron: '17 3 * * *' }]
workflow_dispatch:
jobs:
push-evidence:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npx @devguardch/cli evidence push
env:
DEVGUARD_API_KEY: ${{ secrets.DEVGUARD_API_KEY }}