An evidence-first security gate for CI/CD. ProofGate runs your code, dependency, container, infrastructure, cloud, and AI-logic checks on every change, normalizes them into one format, and then does the thing nothing else does: it refuses to block a build on any finding it cannot prove. Every AI-discovered bug is verified line-by-line against the real code so it physically cannot hallucinate past the gate; every finding β scanner or AI β must survive an adversarial challenge; problems are linked across layers into attack chains; and every keep/drop decision is written to a ledger, so the tool's precision is a measured number, not a marketing claim.
Built with Python Β· FastAPI Β· GitHub Actions Β· Docker, plus a pluggable LLM backend. The analysis engine is pure standard library β the demo runs with no install and no API keys.
Two things are true in security engineering right now:
- Teams already have scanners (SAST, dependency, container, IaC, cloud) β and they drown in the output: five tools, five formats, thousands of "HIGH/MEDIUM" lines, most of them false positives nobody triages.
- Everyone is bolting AI onto security tools β and nobody trusts the AI in the merge path, because it hallucinates: it will confidently cite a line of code that doesn't exist.
ProofGate is not another scanner and not another AI bot. It's the verification and trust layer that sits after everything else and makes the combined output safe to gate a build on. That's the defensible idea:
Treat every finding β from a scanner or an AI β as a claim to be proven, not a fact to be trusted. Only claims that survive proof are allowed to block a build.
That single principle is why ProofGate stays unique even in a market full of scanners and AI assistants:
| The pain everyone has | Existing tools | ProofGate's answer |
|---|---|---|
| AI security tools hallucinate β can't gate merges | β so the AI stays advisory-only | β a mechanical citation check (no model) confirms every AI-cited line exists in the diff, or drops it |
| Scanners are noisy β alerts get ignored | tuned rules per tool | β an adversarial verification pass refutes what it can't defend, across all tools at once |
| "Port open in prod" β but which code caused it? | separate dashboards | β cross-layer correlation links live cloud β the IaC line β the code bug |
Logic bugs (IDOR, refund abuse, is_admin) sail through |
β scanners can't see intent | β an AI reviewer that must cite the line and write a working exploit |
| "How do I know it isn't just noise?" | π€· | β a trust ledger with a measured noise-reduction figure |
The category is real β it's what commercial ASPM platforms (Snyk, Wiz, Apiiro) charge for. ProofGate's twist β proof-carrying findings β is the piece that makes an AI-in-the-loop gate trustworthy, which is the genuinely unsolved problem.
Shipping software is like sending a dish out of a kitchen. Most tools are single-purpose inspectors β one tastes the recipe, one checks the ingredients, one looks at the packaging β and each hands you its own noisy report. None tells you whether the dish is actually safe to serve.
ProofGate is the head inspector: it runs all the specialists at once, rewrites their reports in one plain language, throws out any alarm that can't be defended, refuses to take the clever AI inspector's word without checking the evidence, connects the dots across the whole kitchen, ranks by real danger, and gives one answer β serve or don't serve β while recording why it kept or dropped every item.
Left (writing code) to right (running in the cloud):
| # | Layer | Inspects | Tool | Example finding |
|---|---|---|---|---|
| 1 | CODE_LOGIC | intent & ownership bugs in a PR | AI reviewer | IDOR: any user reads any order by id |
| 2 | SAST | your source code | Bandit, Semgrep | SQL injection, eval() on user input |
| 3 | SCA | open-source dependencies | Trivy | a library version with a known CVE |
| 4 | CONTAINER | the Docker image | Trivy | image runs as root |
| 5 | IAC | your cloud blueprint | Checkov | security group open to 0.0.0.0/0 |
| 6 | CLOUD | your live AWS account | Prowler | that same port, open in production |
ProofGate reuses the best open-source scanners rather than reinventing them β the value is the proof, correlation, and gate layer on top.
The engine is pure standard library and ships with realistic sample scanner outputs and a sample pull request:
# full gate across all six layers on the bundled targets
python3 -m proofgate.cli scan
# β prints the decision + findings, writes reports/report.{html,md,json}
# or:
make demo && open reports/report.htmlYou'll see ~25 findings across all six layers, cross-layer attack chains (including a code bug linked to an over-permissive cloud IAM policy), a few findings refuted or floored as noise, and the build BLOCKED because critical issues exist.
pip install -r requirements.txt
make serve # dashboard β http://localhost:8000
docker compose up --build # same, in Dockergit diff main...HEAD | python3 -m proofgate.cli scan --diff - --no-scanners
python3 -m proofgate.cli scan --fail-on CRITICAL --fail # CI-style gate (exit 1 on block)This is where ProofGate is meant to live. It sits after your scanners run and before a merge/deploy is allowed, as the single decision point.
developer opens PR ββ
βΌ
βββββββββββββ CI runs in parallel βββββββββββββ
β Bandit Semgrep Trivy Checkov Prowler β β scanners write JSON to reports/raw/
βββββββββββββββββββββ¬βββββββββββββββββββββββββββ
βΌ
ProofGate gate β collect β prove β correlate β score β gate
ββ posts inline PR comments (code-logic findings)
ββ uploads report.html / .json artifact
ββ exit 0 = allow merge Β· exit 1 = block merge
ProofGate consumes two inputs and needs neither at once:
--raw-dir <dir>β a folder of<tool>.jsonscanner outputs (any subset; missing tools are simply skipped).--diff <file|->β the pull-request diff, for the AI code reviewer.
So the pipeline pattern is: run your scanners β point ProofGate at their output β let its exit code gate the build.
.github/workflows/proofgate.yml already implements the full pattern: independent scanner
jobs (sast, sca-container, iac) each write to reports/raw/ and never fail the build
on their own; a final gate job merges the artifacts and runs ProofGate as the single
decision point. To adopt it in a real repo:
- Copy
proofgate/,integrations/,requirements.txt, and.github/workflows/proofgate.ymlinto your repository. - (Optional) Add an
ANTHROPIC_API_KEYrepository secret to enable the real LLM reviewer. Without it, ProofGate runs offline and never breaks CI. - Turn the gate from advisory to blocking by setting
FAIL_ON_CRITICAL: "true"(orPROOFGATE_FAIL_BUILD: "true") in the workflow env, then add thegatejob to your branch-protection required status checks (Settings β Branches β Branch protection).
The included gate job seeds bundled fixtures if a scanner is unavailable, so the pipeline
is demonstrable even before you wire in live scanners.
stages: [scan, gate]
sast:
stage: scan
script:
- pip install bandit semgrep
- mkdir -p reports/raw
- bandit -r apps -f json -o reports/raw/bandit.json || true
- semgrep --config auto . --json -o reports/raw/semgrep.json || true
artifacts: { paths: [reports/raw] }
proofgate-gate:
stage: gate
script:
- pip install -r requirements.txt || pip install requests
- python -m proofgate.cli scan --raw-dir reports/raw --fail-on CRITICAL --fail
artifacts: { when: always, paths: [reports/report.html, reports/report.json] }stage('Security Gate') {
steps {
sh 'python3 -m proofgate.cli scan --raw-dir reports/raw --diff pr.diff --fail-on CRITICAL --fail'
}
post { always { archiveArtifacts artifacts: 'reports/report.*' } }
}The contract is just the exit code: 0 = allow, 1 = block. Everything else (PR
comments, artifacts) is optional decoration.
Run the FastAPI app (uvicorn server.app:app) and point a GitHub webhook at POST /webhook.
It verifies the HMAC signature (GITHUB_WEBHOOK_SECRET), reviews each PR as it opens, and
posts inline comments. Good for orgs that want one central service instead of per-repo CI
steps.
- Advisory mode first. Deploy with the gate non-blocking (
--failoff). It comments and reports but never blocks. Watch the ledger's noise-reduction figure for a week. - Tune the floors. Raise
PROOFGATE_MIN_SEVERITY/PROOFGATE_MIN_CONFIDENCE/PROOFGATE_MAX_RISKuntil the retained set is signal, not noise. - Flip to blocking on CRITICAL only.
--fail-on CRITICAL. Widen toHIGHonce the team trusts it. - Split the cadence. Run the diff-scoped AI review + fast scanners on every PR (seconds), and the full cloud/live scan (Prowler) on a schedule (nightly), because live-cloud scanning needs real credentials and is slower.
| Concern | How ProofGate handles it |
|---|---|
| A scanner crashes | Every parser is defensive β a bad/missing file yields no findings, not a failed gate |
| Secrets in CI | API keys via CI secrets/env only; the webhook verifies an HMAC signature; nothing is logged |
| Cost / latency | Offline mode is free and instant; the LLM runs only on the diff, not the whole repo; effort is tunable |
| Auditability | The ledger records every keep/drop with a reason β exportable as JSON for compliance |
| False positives | Layered defense: citation check β verification β confidence floor β severity floor β gate |
| Native GitHub UI | report.json is emitted today; SARIF export for the GitHub Security tab is the next step (see roadmap) |
Everything above runs on a deterministic offline backend. Set one variable and the same pipeline calls a real model for detection, verification, and triage:
cp .env.example .env
export ANTHROPIC_API_KEY=... # or PROOFGATE_LLM_API_KEY
python3 -m proofgate.cli scan # engine mode flips "offline" β "llm"The LLM layer is provider-pluggable: the engine talks only to an abstract LLMBackend, and
the reference backend uses structured (schema-validated) outputs, adaptive thinking, and a
configurable effort level. Swapping providers is one new file, not a pipeline change.
scanner JSON + PR diff
β
1. COLLECT every source β one unified Finding sources/*.py
2. CITE-CHECK AI citations must exist in the diff, or drop citations.py β can't hallucinate
3. VERIFY a panel of skeptics tries to REFUTE each finding verify.py β cuts noise
4. CORRELATE link findings across layers into attack chains correlate.py
5. SCORE 0β100 real-world risk (internet-facing? admin?) risk.py
6. TRIAGE plain-English explanation + concrete fix triage.py
7. REPORT report.html / .md / .json report.py
8. GATE block the build if anything provable is bad report.py:gate()
9. LEDGER record every keep/drop β provable precision ledger.py
Cheap, certain filters (citation check, verification) run first, so ProofGate never wastes effort ranking a finding it's about to discard.
ProofGate/
βββ proofgate/ # β
the engine (pure standard library)
β βββ models.py # the unified Finding schema
β βββ sources/ # scanner adapters + the AI reviewer
β βββ llm/ # pluggable backends (offline + Anthropic)
β βββ citations.py # β
mechanical anti-hallucination guard
β βββ verify.py # β
adversarial verification + gates
β βββ correlate.py risk.py triage.py # chains Β· risk Β· plain-English
β βββ ledger.py # β
trust ledger (provable precision)
β βββ report.py pipeline.py cli.py # report+gate Β· orchestration Β· CLI
βββ server/ # FastAPI dashboard + JSON API + GitHub webhook
βββ integrations/ # GitHub REST client + Actions entrypoint
βββ .github/workflows/ # the CI pipeline (scanners β one gate)
βββ apps/ infra/ samples/ # INTENTIONALLY VULNERABLE scan targets
βββ fixtures/ # sample scanner outputs + sample PR (demo data)
βββ tests/ # 22 tests, fully offline
make install-dev && make test # 22 tests, no keys requiredAll optional; every value has a safe default. See .env.example.
| Variable | Default | Purpose |
|---|---|---|
ANTHROPIC_API_KEY / PROOFGATE_LLM_API_KEY |
(unset β offline) | enable the real model |
PROOFGATE_MODEL |
claude-opus-4-8 |
model id |
PROOFGATE_EFFORT |
high |
reasoning effort |
PROOFGATE_VERIFY / PROOFGATE_VERIFY_VOTES |
true / 3 |
adversarial pass + skeptics per finding |
PROOFGATE_MIN_CONFIDENCE |
0.7 |
drop code-review findings below this |
PROOFGATE_MIN_SEVERITY |
MEDIUM |
severity floor for reporting |
PROOFGATE_FAIL_ON / PROOFGATE_MAX_RISK |
HIGH / 75 |
what blocks the build |
GITHUB_TOKEN / GITHUB_WEBHOOK_SECRET |
(unset) | post PR comments / verify webhooks |
- The AI reviewer reviews the diff, not the whole repo β a flaw that only emerges from unchanged code far away can be missed.
- Correlation joins on shared concepts, not exact resource identities, so it can occasionally over-link; ARN/tag matching is the next iteration.
- Offline mode is a deterministic stand-in for demos, not real analysis.
- It's a second pair of eyes that complements β not replaces β scanners and human review.
- The
apps/,infra/, andsamples/targets are intentionally insecure teaching material. Never deploy them.
Resource-identity correlation (exact ARN/tag matching) Β· SARIF export β native GitHub Security tab Β· persistent ledger with a precision trend line Β· auto-generated fix PRs Β· a suppression/allowlist workflow with expiry for accepted risks.
Python 3.10+ Β· standard-library engine Β· FastAPI (dashboard + API) Β· GitHub REST API & Actions Β· Docker / Compose Β· pytest Β· pluggable LLM backend (reference implementation on the Anthropic Messages API: structured outputs, adaptive thinking, effort).