Baseline and suppressions
On a repository that has never run paranoid, verify
reports every finding in the range, not only the latest change.
A baseline snapshots those findings so only new ones fail CI.
.paranoid-baseline.json is the only file paranoid
reads besides command-line flags. It never changes which rules
run and never tunes a threshold. It only records which findings
were already reviewed and carried forward, the same job a
linter's baseline file does elsewhere. It is data, not
configuration.
Adopt paranoid on an existing repo
Run this once, from the repo root:
paranoid baseline --base <the oldest point you care about>
This runs the exact same checks as verify and
writes every finding it sees to .paranoid-baseline.json,
with no reason and no expiry. Commit that file.
From then on, verify auto-loads it: any finding
that still matches one of its entries moves out of the report's
findings and into a suppressed section instead, and
the score ignores it. New findings, the ones an agent introduces
after adoption, still fail the run exactly as before. Nothing is
hidden: the suppressed section always lists what
was suppressed and why, in every format, even when it is empty.
A baseline entry is matched by rule ID, file, and a fingerprint of the finding's evidence text, not by line number, so it survives unrelated edits elsewhere in the file. It does not survive the evidence text itself changing, or the finding moving to a different file.
Flags that control it
| Flag | Meaning |
|---|---|
| --baseline <file> | Load this file instead of the auto-detected .paranoid-baseline.json at the repo root |
| --no-baseline | Ignore any baseline file, including the auto-detected one |
A baseline file that exists but cannot be parsed is always a
hard error (exit 3), whether it was auto-detected or named
explicitly. Only a genuinely missing file at the auto-detected
path is silently treated as "nothing to suppress"; a missing
file named explicitly by --baseline is still an
error, since you asked for that file by name.
Hand-suppressing one finding
You can also hand-edit .paranoid-baseline.json to
suppress one specific finding you have reviewed and accept,
instead of baselining everything. Every entry may carry a
reason and an expires date (RFC 3339,
for example 2026-12-01).
{
"baseline_version": 1,
"tool_version": "v0.1.0",
"created_at": "2026-08-20T10:00:00Z",
"entries": [
{
"rule_id": "DEP003",
"file": "package.json",
"fingerprint": "a1b2c3...",
"reason": "internal package, low downloads expected",
"expires": "2026-12-01"
}
]
}
paranoid baseline never sets reason or
expires itself, so if you set expires
by hand you must also set reason, non-empty. A
permanent hand-added suppression (no expires) is
not required to have one, though it is good practice to always
say why.
Once expires passes, that entry stops suppressing:
the finding comes back into the report and the score, and the
report still lists the expired entry so you see it happened.
Re-running paranoid baseline writes a fresh, full
snapshot from scratch and discards any reason or expiry you
added by hand. Treat it as a reset, not a merge.
What the report shows
The report's suppressed block has a count
(findings actually removed from scoring) and a list of
entries: every baseline entry that matched a finding
this run, whether it suppressed it or had already expired. An
expired match is exactly as visible as an active one, distinguished
by one field, and its finding is simultaneously back in
findings and counted in the score. See
Report & schema for the
exact JSON shape.
A .paranoid.toml [[disable]] entry can
also add entries to the same suppressed block; each
entry's source field says which one matched it,
"baseline" or "policy", and text/markdown
output labels them suppressed-by-baseline and
suppressed-by-policy so the two are never confused.
See Team policy file.
baseline command flags
paranoid baseline [path] accepts the same
--base, --head, --offline,
--sandbox, --sandbox-image,
--sandbox-timeout, and --compare-base
flags as verify, so a baseline is generated from
exactly the range you would otherwise verify. It always writes
a full snapshot to .paranoid-baseline.json at the
repo root; there is no output path flag. It exits 0 on success
and 3 on any error; it has no score of its own, so nothing maps
to exit 1 or 2.
Using this in CI
Generate and commit .paranoid-baseline.json once,
locally, before turning on CI enforcement or the GitHub Action.
The Action needs no extra input for this: verify
auto-detects the committed file at the repo root the same way it
does outside CI. See
Exit codes & CI.