Skip to main content

Baselines explained

TL;DR

Vulkro has two things called "baseline" and they do not share storage. The CLI baseline is a JSON file you commit to your repo so CI fails only on new findings. The UI baseline scan is a flag on a stored scan inside vulkro serve that powers the in-browser compare-scans view. Use the CLI baseline for PR gating; use the UI baseline for interactive triage. A new "Export for CI" button in the desktop console bridges the two - capture state visually, drop the JSON into CI.

At a glance

CLI baselineUI baseline scan
PurposeBlock PRs on new findings; long-tail tech debt is tolerated.Anchor an in-browser "what changed since X?" comparison.
Where it lives.vulkro-baseline.json in your repo.A flag on a row in the vulkro serve SQLite DB.
How to createvulkro baseline or the Export for CI button in the console."Set as baseline" on any saved scan in the UI.
How to usevulkro scan --baseline <file> or vulkro scan --ratchet.The Compare scans tab; defaults the "from" side to the baseline scan.
Best forCI, version control, PR gating, audit handoff.Local triage, release-to-release deltas, exploratory diffs.
Survives repo moveYes - it's a file checked into the repo.No - tied to the local vulkro-desktop.db on one machine.

Workflow A - CI gating with the CLI baseline

The classic "ratchet" pattern. The baseline encodes findings that already exist on main; CI only blocks new ones added by the PR.

# 1. On a known-good commit (e.g. main, post-release), capture the baseline.
git checkout main
vulkro baseline # writes ./.vulkro-baseline.json

# 2. Commit it.
git add .vulkro-baseline.json
git commit -m "chore: refresh vulkro baseline"

# 3. In CI, gate on new findings only.
vulkro scan . --ratchet # exits 1 only when a Critical/High
# finding is missing from the baseline

--ratchet is shorthand for --baseline .vulkro-baseline.json with the exit-code policy switched to "fail on additions only". Both forms emit the full finding list in the requested --format; only the gate semantics differ.

When findings legitimately accumulate (a refactor, a new framework, a deliberate tech-debt batch), refresh the file:

vulkro baseline --out .vulkro-baseline.json
git add .vulkro-baseline.json && git commit -m "chore: refresh baseline"

Treat baseline refreshes as reviewable PRs - every line removed from the baseline is a future regression you'll catch automatically, every line added is a finding the team agreed to tolerate.

Workflow B - Interactive triage with the UI baseline scan

For local exploration, not CI:

vulkro serve # opens http://127.0.0.1:8723
  1. Scan a project from the console (or run vulkro scan . --save from the terminal - both populate the same DB).
  2. Open that scan's detail view and pick Set as baseline. The row is marked in vulkro-desktop.db; only one baseline per project at a time.
  3. Keep developing. Run more scans.
  4. Open the Compare scans tab. The picker (CompareScansPicker) pre-fills the "from" side with the baseline scan. You see added / fixed / unchanged findings, grouped by severity.
  5. Clear it later from the same menu (or via POST /api/scans/clear-baseline/:project_id) - the underlying scan stays, only the flag is dropped.

This baseline never leaves the machine and never affects exit codes. It exists purely to anchor the UI's compare view.

Workflow C - Bridge: "Export for CI"

You triage in the UI, you gate in CI. The Export-for-CI button lets the same artifact serve both:

  1. In vulkro serve, open the scan you want to freeze as the team baseline (typically the latest clean release).
  2. Open the Export dropdown on the scan detail view.
  3. Choose Export for CI. The browser downloads .vulkro-baseline.json - a JSON array of findings identical in shape to what vulkro baseline produces.
  4. Drop it at the repo root and commit it.
  5. CI runs vulkro scan . --ratchet against it, exactly as in Workflow A.

Use this when the visual triage flow is faster than the CLI for picking "what good looks like" - for example, after a release sprint where you've already triaged dozens of findings in the UI.

FAQ

Can I have both at the same time?

Yes. They don't see each other. A repo can ship a .vulkro-baseline.json for CI while every developer also marks UI baselines on their local vulkro serve. The CLI never reads from vulkro-desktop.db; the UI never reads from .vulkro-baseline.json.

Which baseline do I delete first if I want to start over?

For CI: delete .vulkro-baseline.json from the repo (and re-run vulkro baseline). For the UI: clear the flag from the scan menu - the scan itself is preserved. Deleting one has no effect on the other.

Does the CLI baseline include triage / suppressions?

No. .vulkro-baseline.json is a raw list of SecurityFindings, matched on next run by stable finding_key. Inline suppressions (# vulkro-disable-next-line) and the vulkro-suppress.yaml file are the supported ways to silence findings permanently; the baseline is for tolerating quantity, suppressions are for tolerating individual items.

Do the two baselines share any data?

Only via the Export for CI button - that's the one path that takes a UI scan and produces a file the CLI can ratchet against. There is no inverse import (the CLI doesn't push into the desktop DB).

Does --baseline and --ratchet differ?

--baseline <file> compares against the file and reports the diff but keeps the default Critical/High exit-code policy. --ratchet does the same comparison but flips the gate to "only added Critical/High findings fail the build". Use --baseline when you want diagnostics; use --ratchet when you want a gate.