◆ NFRGate / User Guide
📘 Everything in one place

The NFRGate user guide.

Static analysis (Python/Java/Go) plus LLM semantic assessment, with confidence-based routing to gate CI/CD merges. What you get, how to install it, and how to use every surface.

Features Installation Configuration CI gate Overrides API & CLI Dashboard Notifications IDE integration Links

What you get

Twelve features spanning the full loop: catch NFR gaps before merge, route them to the right reviewer, and keep the whole platform observable.

🔍

Static analysis

Rule-based checks for logging, metrics, tracing, and reliability NFRs on every changed Python, Java, or Go file.

🤖

LLM semantic assessment

Ticket text and diff context judged against the rubric for criteria no static rule can catch on its own.

🚦

Confidence-based routing

High-confidence failures on medium/high/critical rules auto-block; everything else is flagged for human review — nothing is silently dropped.

💬

CI gate comments

One continuously-updated PR/MR comment with collapsible sections and direct links to each rule's docs page.

🗂️

Overrides API + audit trail

Every human override of a routed decision is recorded and queryable, via CLI or HTTP.

📊

Findings dashboard

Coverage trend, most-flagged criteria, and AI-vs-human agreement rate, backed by the findings store.

🔔

Slack / Jira notifications

Blocking failures and tier0 human-review escalations pushed straight to your team's tools.

🧩

IDE integration

A real language server (nfrgate-lsp) plus a VS Code extension surface the same diagnostics inline, before a PR ever opens.

📖

Public rule reference

Every rule code has its own page: what it checks, why, and exactly how, per language.

🩺

Self-observability

Structured JSON logs, OpenTelemetry traces/metrics, and a /healthz liveness probe — nfrgate observes itself the same way it grades other services.

🐳

Multi-arch Docker images

linux/amd64 and linux/arm64, published to GHCR on every release.

🗃️

Versioned DB migrations

Alembic-managed schema for the findings store — never hand-run SQL against a live database.

Installation

Every install path ships the same static analyzers and LLM assessment core; extras add the optional API/dashboard, IDE, and telemetry surfaces.

pip install nfrgate                # static analysis + LLM assessment
pip install "nfrgate[api]"         # + the overrides API and findings dashboard (FastAPI/uvicorn)
pip install "nfrgate[lsp]"         # + the nfrgate-lsp language server for IDE integration
pip install "nfrgate[otel]"        # + OpenTelemetry trace/metric export
pip install "nfrgate[dev,api,lsp]" # local development (test suite + all extras)

Or run the packaged API + dashboard via Docker (multi-arch: linux/amd64, linux/arm64):

docker pull ghcr.io/parab-rohit/rubric:latest
docker run --rm -p 8000:8000 -e NFR_GATE_API_KEY=<a-real-secret> ghcr.io/parab-rohit/rubric:latest

Pin a specific released version instead of latest by tag, e.g. ghcr.io/parab-rohit/rubric:0.2.0 — see GitHub Releases for what's available.

Configuration

Every piece of deployment-specific config is an environment variable — no source edits needed to point at different infrastructure.

Env varDefaultPurpose
NFR_GATE_DATABASE_URLpostgresql://nfr_gate:nfr_gate_dev@localhost:5432/nfr_gatePostgres connection for the findings store
NFR_GATE_SERVICE_TIERS_PATHconfig/service_tiers.yamlService-criticality tier registry; missing file defaults every service to the most conservative tier (tier0)
NFR_GATE_LLM_BACKENDollamaollama (local model, no API key) or anthropic (hosted, needs ANTHROPIC_API_KEY)
NFR_GATE_LLM_MODELbackend's own defaultOverride the model name for either backend
NFR_GATE_OLLAMA_BASE_URLhttp://localhost:11434Where to reach Ollama
NFR_GATE_API_KEY(none — required to start the API)Shared bearer-token secret the overrides API and dashboard endpoints check, and the CLI sends
NFR_GATE_API_URLhttp://localhost:8000Where the nfrgate-override CLI reaches the API
NFR_GATE_API_PORT8000Port nfrgate-api binds to
NFR_GATE_SLACK_WEBHOOK_URL(unset — disabled)Slack incoming-webhook URL for blocking-failure/escalation notifications
NFR_GATE_JIRA_BASE_URL / _EMAIL / _API_TOKEN / _PROJECT_KEY(unset — disabled)All four required together to enable Jira ticket creation on the same triggers as Slack
OTEL_EXPORTER_OTLP_ENDPOINT(unset — tracing/metrics are no-ops)Where nfrgate exports its own traces/metrics; needs pip install "nfrgate[otel]"
NFR_GATE_LOG_LEVELINFOLevel for nfrgate's own structured JSON logs (to stderr)

Using the CI gate

Analyzes the diff on a PR/MR, posts one continuously-updated comment, and fails the job on high-confidence blocking violations. Both examples below are complete, runnable job definitions — not just the invocation line.

GitHub Actions

name: NFRGate

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: read
  pull-requests: write   # needed to post/update the PR comment

jobs:
  nfrgate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # full history -- pr_gate.py diffs base...head, a shallow clone can't resolve an arbitrary base SHA

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - run: pip install nfrgate

      - name: Run NFRGate
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: python -m nfr_gate.ci.pr_gate

Reads GITHUB_TOKEN, GITHUB_REPOSITORY, and GITHUB_EVENT_PATH from the standard Actions environment — the three env vars above and GitHub's own automatic ones are everything it needs, no further wiring. This exact shape (installing from a repo checkout instead of PyPI) is what .github/workflows/nfrgate-ci.yml in this repo runs for real, dogfooding it on this project's own PRs.

GitLab CI

nfrgate:
  stage: test
  image: python:3.12-slim
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  variables:
    GIT_DEPTH: 0   # full history, same reason as the GitHub Actions example
  script:
    - pip install nfrgate
    - python -m nfr_gate.ci.mr_gate

Reads GitLab's built-in CI_* predefined variables (CI_PROJECT_DIR, CI_MERGE_REQUEST_IID, CI_MERGE_REQUEST_DIFF_BASE_SHA, CI_PROJECT_PATH, CI_API_V4_URL, CI_PROJECT_ID, etc.) automatically — nothing to map by hand. It still needs a token with permission to post MR notes: add a GITLAB_TOKEN CI/CD variable (Settings → CI/CD → Variables, masked + protected — a project or group access token with api scope works) if the job's own CI_JOB_TOKEN isn't allowed to post notes on your instance; GitLab injects CI/CD variables into the job automatically, so nothing in the YAML above needs to reference it explicitly.

Both examples only need the static-analysis path to work out of the box. The LLM-assessment half defaults to a local Ollama model (NFR_GATE_OLLAMA_BASE_URL, http://localhost:11434 by default) — unreachable from a hosted runner unless you also set NFR_GATE_LLM_BACKEND=anthropic and ANTHROPIC_API_KEY, or run on a self-hosted runner with Ollama on it. Static analysis, routing, and blocking all work correctly either way; only the LLM-only/LLM-supplement findings are skipped without one of those two.

Overrides API & CLI

Every routed decision a human overrides is recorded in an auditable log.

nfrgate-api                                        # or: uvicorn nfr_gate.api.app:app
nfrgate-override list --source-ref <ref>
nfrgate-override apply --source-ref <ref> --rule-code T1 --reviewer-id you@example.com --reason "..."

Requires NFR_GATE_API_KEY to be set on the server; the CLI sends it via NFR_GATE_API_URL. GET /healthz is an unauthenticated liveness probe independent of database reachability — point a load balancer's health check there. The API serves plain HTTP; put a TLS-terminating reverse proxy in front of it for any non-localhost deployment.

Findings dashboard

Coverage trend, most-flagged criteria, and AI-vs-human agreement rate — on the same running API, once it's deployed:

GET /dashboard

Open it in a browser, paste in your NFR_GATE_API_KEY, and click Connect — the key is kept in that browser's local storage only, never sent anywhere but your own API.

Notifications

Best-effort Slack and/or Jira notifications fire on blocking failures and on tier0 (always-human-review) failing results — never on a routine pass. Configure one, both, or neither; an unconfigured backend is silently skipped rather than an error. See the NFR_GATE_SLACK_WEBHOOK_URL / NFR_GATE_JIRA_* rows above.

IDE integration

The exact same rule engine that gates PRs in CI, surfaced as inline diagnostics before a PR ever opens.

pip install "nfrgate[lsp]"
nfrgate-lsp   # stdio transport — point any generic LSP client at it (Neovim, Emacs' eglot, ...)

A minimal VS Code extension lives in editors/vscode/ in the repo — see its README for setup (npm install, then F5 to launch an Extension Development Host).