Read your code like water.
Torbido takes source code and gives back a turbidity reading: one number for how cloudy the code is, and a list of the exact lines making it that way. It is one ES module with no dependencies.
Introduction
Models write working code quickly and leave the same mistakes behind: SQL built from strings, keys in config files, auth that was going to be added later. Torbido looks for those patterns line by line.
- The lab reads pasted code or a public GitHub repo.
- The module runs in a browser, in Deno and in Node 18 or newer.
- Nothing is sent anywhere unless you call
fromGitHub, which only talks to GitHub.
Quick start
Import the module from this site and hand it your files. Each file is a path and its text.
import { read } from "https://torbido.vercel.app/lib/torbido.js"; const report = read([ { path: "api/users.ts", text: source } ]); console.log(report.ntu, report.band.name); report.findings.forEach(f => console.log(f.file, f.line, f.title));
# Keep a copy next to your project and import it locally
curl -O https://torbido.vercel.app/lib/torbido.js
mv torbido.js torbido.mjs
node -e 'import("./torbido.mjs").then(t => console.log(t.read("const a = eval(b)").ntu))'
<script type="module"> import { read } from "https://torbido.vercel.app/lib/torbido.js"; document.title = read(document.querySelector("#code").textContent).ntu + " NTU"; </script>
Try it
This box runs the real module in your browser.
How a reading works
Every line of every file is tested against the rule list. Generated folders, lockfiles and minified bundles are skipped. package.json gets its own check against the advisory list. Auth routes get one extra file-level check for a rate limit.
Secrets found in a line are masked before they reach the report, so a report is safe to paste into an issue.
Turbidity (NTU)
NTU is the unit water labs use for cloudiness. Here it is a weighted sum of findings. The first finding under a rule counts in full, and each repeat of the same rule counts for 60% of the one before, so one noisy pattern cannot bury everything else. Clean code reads 0.4, because no water is perfectly clear.
| Severity | Weight |
|---|---|
| Critical | 46 |
| High | 24 |
| Medium | 9 |
| Low | 2 |
Bands
| Band | NTU | Reading |
|---|
Secchi depth
A Secchi disk is a black and white plate lowered into water until it disappears. Torbido's version is the number of lines you can read, in file order, before the first critical or high finding. If there is none, it equals the total lines read.
Clarity is 100 / (1 + ntu / 40), shown as a percentage.
The lab
The lab page does the same reading with a form on top. Add ?repo=owner/name to the address to start a reading straight away, or ?sample=1 for the sample project.
Rules
This table is built from the module, so it always matches what runs.
| Rule | Severity | Finds | Reference |
|---|
Checks outside the line rules
| Rule | Severity | Finds |
|---|---|---|
TB-407 | Medium | Login or auth route with no rate limiting in the file |
TB-601 | Low | Dependency pinned to latest or * |
TB-602 | By advisory | Dependency below a fixed version in the advisory list |
Advisory list
A short list of well-known advisories, not a replacement for npm audit.
| Package | Fixed in | Advisory | Problem |
|---|
API
read(files)
Takes an array of { path, text }, or a single string. Returns a report. Synchronous.
turbidity(findings) and clarity(ntu)
Recompute the number for any subset of findings, which is how the homepage filter works.
digest(report)
Async. SHA-256 of the sorted rule:file:line entries. The same findings always give the same digest.
fromGitHub(repo, { limit, onProgress })
Fetches up to limit text files (default 80) from a public repository. Config and server paths are read first.
import { read, fromGitHub, digest } from "https://torbido.vercel.app/lib/torbido.js"; const repo = await fromGitHub("owner/name", { limit: 40 }); const report = read(repo.files); console.log(report.ntu, await digest(report));
Report format
| Field | Type | Meaning |
|---|---|---|
ntu | number | Turbidity reading |
band | object | name, note, max |
clarity | number | 0 to 100 |
secchi | number | Lines read before the first serious finding |
linesRead, filesRead | number | Coverage |
counts | object | Findings per severity |
perFile | array | path, lines, findings, ntu |
findings | array | rule, severity, cat, cwe, title, file, line, text, why, fix |
Fail a build
Save the module next to this script. The build stops when there is anything critical, or the reading is above your limit.
import { readFileSync, readdirSync } from "node:fs"; import { join } from "node:path"; import { read, TEXT_FILE } from "./torbido.mjs"; const LIMIT = 25; const files = readdirSync("src", { recursive: true }) .map(p => join("src", String(p))) .filter(p => TEXT_FILE.test(p)) .map(path => ({ path, text: readFileSync(path, "utf8") })); const r = read(files); r.findings.forEach(f => console.log(f.severity, f.file + ":" + f.line, f.rule)); console.log(r.ntu + " NTU, " + r.band.name); process.exit(r.counts.critical || r.ntu > LIMIT ? 1 : 0);
Sediment proof
A digest proves what a report said. To record it, send a memo transaction from your own wallet with the digest as the text. Torbido never touches a wallet, so this step is yours.
Limits
- Rules work on single lines, so a bad value passed through three variables can be missed.
- There is no understanding of your business logic. A missing permission check looks like normal code.
- Patterns can fire on harmless lines. Read the line before you change it.
- The advisory list is short on purpose. Run your package manager's audit as well.
- Only the first 80 files of a repository are read, with config and server code first.
Agent skills
The Skills page has six plain SKILL.md files that teach an agent the same habits: full reading, secret review, wallet sweep, dependency sweep, injection trace and a release gate.
Token
The coin of the project is $TORBIDO. The scanner works the same with or without it.
FAQ
Why water?
Torbido is the Italian word for murky water, and most generated code starts out that way.
Can it read private repositories?
Not through the lab. Paste the file, or run the module on your own machine.
Does it fix the code?
It tells you what to change on each line. The change is yours.