Nikasha
Proof, not prose.
Nikasha (निकष, touchstone: the stone used to assay gold) checks the factual claims in a vulnerability report against the source code at the exact version the report names. It runs offline against a git repository, is deterministic (the same report and the same commit give byte-identical JSON, with no LLM involved), and returns an evidence-backed verdict in which every finding carries the evidence behind it (the repository, commit, path and lines where there is code, and the git command with output hashes where one was run), plus neutral questions to send back to the reporter. It judges claims, never people: there is no "AI detection" anywhere in it.
Status: first release (
0.1.0). Everything shown on this page is a real run against the bundled vulnlab demo (a fictional C library with a deliberately introduced heap overflow), not against real reports. The early real-world gate (M3.5) has not run yet, so Nikasha has no accuracy numbers on real reports, and this page makes no such claim.
The five verdicts
| Verdict | Meaning | Exit code |
|---|---|---|
| REPRODUCED | The proof of concept ran in the sandbox and produced the claimed crash signature. nikasha check has no --repro option yet, so a check run cannot reach this verdict today; the sandbox itself (nikasha repro, M5) is verified in CI. |
0 |
| GROUNDED | A high grounding score and no substantial refutation anywhere. | 0 |
| MIXED | Evidence on both sides, or findings that fit a different version than the report names (a wrong version header is a question, never an accusation). | 10 |
| UNGROUNDED | The hardest verdict to reach: a low score and a core file, symbol, quoted line, snippet or option that never existed in the repository's history, corroborated by a second independent group of evidence (or strong refutations from three independent groups). | 20 |
| INSUFFICIENT | Too little to check: no resolvable version, fewer than two checkable claims with no trace, snippet, patch or PoC, or too little total evidence weight. | 30 |
Errors exit 1. --fail-on VERDICT moves the non-zero line for CI.
Quickstart
You need Python 3.11 or newer and git. Install from a checkout with uv or pipx:
git clone https://github.com/rakshit-737/nikasha && cd nikasha
uv tool install . # or: pipx install . (or: uv sync && uv run nikasha ...)
nikasha doctor # Python, git, cache directory, container engines; no network
Optional extras add the integrations: web (local web UI), mcp (MCP server), llm
(Anthropic and OpenAI clients for --llm) and bench (NikashaBench plots), for example
uv tool install '.[web,mcp]'.
Build the demo repository. scripts/build_vulnlab.py replays a fixed commit plan into a
new bare repository with git fast-import, so the five tags (v1.0.0 to v1.3.0) land on
the same commit SHAs on every machine (examples/vulnlab/README.md):
python scripts/build_vulnlab.py ~/vulnlab.git
Then check the fixture reports against it, entirely offline:
nikasha check examples/reports/fabricated_hdr_overflow.md --repo ~/vulnlab.git # UNGROUNDED, exit 20
nikasha check examples/reports/genuine_hdr_overflow.md --repo ~/vulnlab.git --explain # GROUNDED, with the ledger
nikasha check examples/reports/mixed_wrong_version.md --repo ~/vulnlab.git --format markdown -o reply.md
nikasha check examples/reports/genuine_hdr_overflow.md --repo ~/vulnlab.git --format html -o report.html
nikasha check examples/reports/genuine_hdr_overflow.md --repo ~/vulnlab.git --format json -o result.json
nikasha explain result.json
--format takes terminal (default), json, markdown or html. The Markdown reply
stays under GitHub's comment limit and escapes everything that came from the report or the
code. The HTML report is one self-contained file that makes no network request, enforced by
a Content-Security-Policy that pins its single inline script by hash, with light, dark and
print themes and a JSON download. --explain appends the log-odds ledger behind the score;
nikasha explain prints the ledger from a saved JSON result. --quiet prints only the
verdict line and --ascii avoids non-ASCII symbols on legacy consoles. A https://
repository is cloned only with --online; a local path never touches the network.
The other two demo fixtures: GROUNDED and MIXED
nikasha extract: every claim the report makes, before any check runs
Other commands: nikasha extract (the claims, with --json), nikasha index,
nikasha timeline SYMBOL (in which releases a symbol is defined, with "did you mean"
suggestions), nikasha trace (a stack trace against the code), nikasha lint (check a
draft report before submitting it, no verdict), nikasha cve (a CVE JSON 5.x record),
nikasha repro and nikasha recipes (sandboxed reproduction), nikasha bench,
nikasha mcp, nikasha serve (local web UI), and the read-only fetchers nikasha h1 and
nikasha gh-advisories. nikasha --help lists them all.
What it checks
The pipeline is ingest → extract → resolve → code intelligence → checks → fusion → render
(ADR 0001). Intake reads Markdown, text and HTML with an
exact map back to the source; extraction produces 12 claim kinds; resolution pins the
report to a tag or commit and never guesses silently; tree-sitter parses C, C++, Python,
JavaScript, TypeScript/TSX, Go, Rust, Java, PHP and Ruby; and nine trace formats are parsed
(ASan, UBSan, valgrind, gdb, Python, Java, Go, Rust, Node), each tested against real
captured output. The 19 deterministic checks, C01 to C18 and C21, are catalogued with their
strengths in docs/checks.md, generated from the code:
| Group | Checks | The question each answers about the code at the resolved commit |
|---|---|---|
| version | C01, C16 | Does the named version resolve to a tag or commit? Does the claimed affected range fit the core symbol's timeline across releases? |
| locus | C02, C03, C14 | Do the cited file, symbol and option exist here, and if not, did they ever exist in the history? |
| lines | C04, C05 | Is the line number inside the file, and inside the function it is said to be in? |
| code quotes | C06, C07 | Does the quoted line match the file? Where does the quoted snippet come from (winnowing fingerprints)? |
| trace | C08, C09, C10 | Do the frames fit the code, can each caller reach each callee in the call graph, and which release fits the trace best? |
| trace meta | C11 | Is the sanitizer output consistent with itself: PIDs, frame numbering, addresses, region arithmetic, SUMMARY line, access size? |
| patch | C12 | Does the proposed diff apply at this commit, or was it already applied? |
| refs, meta, behavior | C15, C17, C18 | Do cited commits, links, CVE and CWE records check out? Does the CVSS vector recompute to the stated score? Does the named function really call the API? |
| info | C13, C21 | Which later commits touch the reported locus? What is the report missing (version, PoC, trace, location)? |
Two more checks sit outside that count. C19 (reproduction) scores a sandboxed PoC run
and produces nothing without one; nikasha check does not start the sandbox yet. C20 (LLM_REVIEW) is on disk but inert unless you name a model
with --llm or in nikasha.toml: it may only tilt a score, because its strength is capped
at |0.5| in lr_defaults.yaml, below every verdict threshold, and a model that cannot be
reached yields an error at strength 0, never a refutation.
How the verdict is computed
Every check emits evidence with a natural-log likelihood ratio taken from
lr_defaults.yaml (no float literal is ever used as a strength),
and within each evidence group the findings are ranked by strength and weighted 1, ½, ¼, …,
so ten correlated findings cannot outweigh a few independent ones; the damped sum is the
log-odds behind the 0–100 grounding score. An ordered verdict ladder (SPEC §14.3,
fuse/verdict.py) then decides, first match wins: a
reproduced crash; too little to check; UNGROUNDED only for a low score plus a
never-existed refutation corroborated across groups; a version mismatch capped at MIXED;
GROUNDED for a high score with no substantial refutation; otherwise MIXED or INSUFFICIENT.
Before any of that, the refutation gate of ADR 0003
lets only claims the reporter attributed to the project, and did not negate, be refuted at
all: a finding about the reporter's own PoC code or a third-party API is still shown, with
the strength it would have had, but counts for nothing.
Principles
- Evidence, not AI detection. Wording targets claims, never people.
- Deterministic core. Byte-identical JSON for identical inputs; the LLM is optional, off by default and never decisive.
- Confidential by default. Offline unless
--online, no telemetry, cloud models only withllm.allow_cloud = truein a config file. - Conservative about "fabricated". The target is at most 1% false UNGROUNDED on genuine reports; when in doubt, MIXED or INSUFFICIENT, and ask.
- PoCs are hostile. They run only in the sandbox, never on the host.
- Every line is explainable down to a repository, commit, path, lines and command.
- Input is an attack. Report text, repositories and traces are treated as hostile (XSS, ReDoS, path traversal, git config tricks, prompt injection).
- Useful to reporters too (
nikasha lint).
When principles conflict, safety wins: P4 first, then P5, then P3.
Where this stands
A static "does this function exist?" check alone is not enough. slopcheck measured six static existence checks (file, line range, symbol, snippet, commit and tagged version) on 557 publicly disclosed curl reports, every one scored against HEAD rather than the version it named, and published a careful negative result: the checks flagged confirmed, genuine reports about as often as the reports they were meant to catch. Nikasha treats that as the baseline to beat and targets its measured failure modes (version pinning, claim scoping, negation, line binding, structural trace and patch checks), but whether that is enough is an open question until the M3.5 gate runs. Its results will be published whatever they are, and thresholds are never adjusted to pass it.
| Milestone | Status |
|---|---|
| M0 Bootstrap · M1 Models, intake, extraction · M2 Resolution and code intelligence · M3 Checks, fusion, CLI outputs · M4 HTML report | done (measurements in PROGRESS.md and ADR 0004) |
| M3.5 Early real-world gate (curl corpus vs. slopcheck) | not started: needs network access and a check of HackerOne's terms for the disclosed-report endpoint before any report is fetched |
| M5 Sandbox reproduction | sandbox CI green with the real recipe image; curl/sqlite/libxml2 recipes unverified end-to-end |
| M6 NikashaBench and calibration | machinery done; the real-report splits wait on M3.5 corpus access |
| M7 Integrations | done (a GitHub Action, nikasha lint, the MCP server, the web UI, the optional model layer; network paths tested with stubs only) |
| M8 Launch polish and v0.1.0 | tooling done, not published: a release is a public, irreversible action that waits for the maintainer's approval |
| M9 Stretch | not started |
More
CONTRIBUTING.md: setup, standards, and the guides for adding a check, a trace format, a reproduction recipe or a language.SECURITY.md: private reporting and what is in scope.SUPPORT.mdfor help.docs/checks.md: the checks catalogue.docs/action.md: the GitHub Action.docs/adr/: the decisions, including the rename (SPEC.mdstill says "Pramaan"), dependencies, repository access and the fusion decisions.PROGRESS.mdandCHANGELOG.md: what is done, with numbers.
License
Code is licensed under Apache-2.0; documentation under
CC-BY-4.0. See REUSE.toml for per-file details.
Release files for nikasha 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nikasha-0.1.0.tar.gz | 923.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nikasha-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.6 MB
Release files / nikasha-0.1.0.tar.gz
| Download URL | nikasha-0.1.0.tar.gz |
|---|---|
| Size | 923.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
af9d080e433265bdd35daffdf42dce9d18bdad780c2f7ca718cd8f4b410209d9
|
|
BLAKE2b-256 checksum How to use checksums |
5aad3419e976b65fc122a92afd24b05c1b598e43bd4641a2aacc0160fcd4b7e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / nikasha-0.1.0-py3-none-any.whl
| Download URL | nikasha-0.1.0-py3-none-any.whl |
|---|---|
| Size | 653.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0f326f56ea053ef3688ae2685dc5a542b4f1bad9dfa82f5e335fd501ed9c4b11
|
|
BLAKE2b-256 checksum How to use checksums |
0e03ebc1f78987ff2b25a9cc2a9f91ed27b33b5ad1b7ef47275b7bc25f51d6a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log