Integrations
Ways to run paranoid in a workflow: the GitHub Action, a pre-commit hook, and Claude Code (a Stop hook and an MCP server). All of them run the same binary you use locally.
GitHub Action
The composite action at alainrk/paranoid/action@main
runs verify on the pull request range, uploads the
report as an artifact, appends it to the job summary, optionally
posts a sticky PR comment, and sets the check status from the
verdict. Full inputs, outputs, and the SARIF upload example are
on the Exit codes & CI page.
- uses: alainrk/paranoid/action@main
with:
sandbox: none # or auto, when the runner has docker
claims: summary.md # optional
pre-commit
This repository ships .pre-commit-hooks.yaml with
one hook, paranoid-verify. Add it to a target
project's .pre-commit-config.yaml:
repos:
- repo: https://github.com/alainrk/paranoid
rev: main # pin a tag or commit
hooks:
- id: paranoid-verify
Then, in the target project:
pre-commit install --hook-type pre-commit --hook-type pre-push
pre-commit run paranoid-verify --all-files # try it once by hand
What the hook runs
paranoid verify --sandbox none --offline --base HEAD
- --sandbox none: no clean-room re-run of the test suite. A container build on every commit is too slow for a pre-commit hook. The sandbox rules are reported as skipped instead. Run
paranoid verifyby hand, or in CI, for the full clean-room check. - --offline: no registry lookups. A pre-commit hook should not need the network. DEP001 to DEP004 are reported as skipped.
- --base HEAD: compare against the last commit.
--headstays at its default, the working tree. - --format text (the default) is used as is. Add
args: [--fail-under, "80"](or any otherverifyflag) in the target project's own hook entry to raise the bar, since pre-commit appendsargsafterentry.
The commit range at each stage
stages: [pre-commit, pre-push] runs the same
command at both points. --base HEAD covers
different changes at each point.
-
At the
pre-commitstage, HEAD is the last real commit and the working tree holds whatever is about to be committed. paranoid diffs HEAD against the working tree, not against the git index, so unstaged changes present at commit time are included. -
At the
pre-pushstage, the working tree is normally clean, so--base HEADfinds nothing to check. This stage catches changes thatgit commit --no-verifyskipped. It does not audit the commits about to be pushed. To audit them, runparanoid verify --base origin/mainby hand, or use the GitHub Action.
To raise the bar, add args: [--fail-under, "80"] (or
any other verify flag) to the hook entry in the
target project's own .pre-commit-config.yaml.
pre-commit appends args after entry.
The golang hook language
pre-commit's golang language builds the hook's own
repository with go install ./... inside an isolated
Go environment it manages itself. Since this repo has a
go.mod at the root and its command lives at
cmd/paranoid, that install produces a binary named
paranoid (Go names the binary after the directory
holding package main), which lands on
PATH inside pre-commit's environment and matches
the hook's entry line. No manual install step or
wrapper script is needed. A Go toolchain is required, and
pre-commit's golang language can provide one.
There is no GitLab CI component or merge-request comment integration.
Claude Code
A settings.json hook config and an MCP server. The hook is configuration only and needs no extra Go code. Full docs, the settings snippet, and the security notes: integrations/claude-code/README.md.
Stop hook and PreToolUse variant
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "paranoid verify --sandbox none --offline --format text --fail-under 80 --strict 1>&2"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git commit *)",
"command": "paranoid verify --sandbox none --offline --format text --fail-under 80 --strict 1>&2"
}
]
}
]
}
}
Copy the hooks block into .claude/settings.json
(committed, shared) or .claude/settings.local.json
(personal). Claude Code's Stop hook contract blocks the session
only on exit code 2, with the command's stderr fed back as the
reason; the trailing 1>&2 redirects
verify's report (stdout by default) to stderr so
Claude receives it, and --strict maps a warn
verdict onto exit 2 too, since a plain exit 1 does not block.
The PreToolUse variant uses Claude Code's if field
to match the Bash tool's command text
(Bash(git commit *)). This scopes a hook to one
command; matcher alone only matches the tool name.
MCP server
paranoid mcp runs a Model Context Protocol server
over stdio: newline-delimited JSON-RPC 2.0, implemented on the
standard library with no SDK dependency. It implements
initialize, tools/list, and
tools/call for one tool, verify
(arguments: path, base,
head, claims, session,
offline, sandbox), which runs the same
pipeline as paranoid verify and returns the JSON
report as the tool result. To add it to Claude Code as an MCP
server:
{
"mcpServers": {
"paranoid": {
"command": "paranoid",
"args": ["mcp"]
}
}
}
Trust boundary
Both integrations depend on the binary they run
and the files that configure them. If the agent under audit can
edit the installed paranoid binary,
.paranoid-baseline.json, or the hook configuration,
it can make paranoid say pass. Install paranoid outside the
repository the agent can write to, and point the hook at that
installed binary on PATH, never a repo-local build.
SF006 watches .paranoid-baseline.json and hook
configuration files for this reason. It does not prevent the
edit. It reports the edit as a finding in the next run.