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 baseline | UI baseline scan | |
|---|---|---|
| Purpose | Block 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 create | vulkro baseline or the Export for CI button in the console. | "Set as baseline" on any saved scan in the UI. |
| How to use | vulkro scan --baseline <file> or vulkro scan --ratchet. | The Compare scans tab; defaults the "from" side to the baseline scan. |
| Best for | CI, version control, PR gating, audit handoff. | Local triage, release-to-release deltas, exploratory diffs. |
| Survives repo move | Yes - 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
- Scan a project from the console (or run
vulkro scan . --savefrom the terminal - both populate the same DB). - 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. - Keep developing. Run more scans.
- 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. - 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:
- In
vulkro serve, open the scan you want to freeze as the team baseline (typically the latest clean release). - Open the Export dropdown on the scan detail view.
- Choose Export for CI. The browser downloads
.vulkro-baseline.json- a JSON array of findings identical in shape to whatvulkro baselineproduces. - Drop it at the repo root and commit it.
- CI runs
vulkro scan . --ratchetagainst 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.
Related
vulkro scan- the--baselineand--ratchetflags.vulkro diff- saved-scan-to-saved-scan deltas (a third comparison flavour, scoped to the SQLite history).vulkro serve- launches the console where UI baselines live.- Desktop console - Compare scans tab, Export menu.
- CI/CD integration - copy-paste recipes.