See what your code does to your users.

Unfault brings user and system-level context to your code as you write it, the same way a linter flags a typo, but for the things that actually affect people.

Python, Go, Rust, TypeScript. Runs locally. MIT licensed.

~/projects/api
❯ unfault review
Worth looking out for
🟡 main.py:12 · The Slow Death
FastAPI app `app` has no request timeout middleware
A downstream dependency slows down and your service holds threads/connections until it saturates and dies.
Tradeoff
↳ Simplicity               no timeout means less code and fewer configuration decisions.
↳ Systemic Availability  a single slow dependency can exhaust the thread pool and take down the entire service.
🟡 main.py:36 · The Retry Storm
HTTP call via `httpx.AsyncClient` has no retry policy
During an outage your service retries failures instantly, preventing the downstream service from ever recovering.
Tradeoff
↳ Local Availability       retries transparently mask transient failures from the caller.
↳ Systemic Metastability synchronized retries with no backoff create thunderstorms that prevent downstream recovery.
app-a  ·  python  ·  fastapi  ·  1 file   [ parse 5ms  engine 0ms  fetch 7849ms ]

The gap between your code and its effect on users is usually invisible.

A missing timeout. A retry with no backoff. An error handler that swallows the exception. None of these look wrong in a diff. Unfault reads your code the way your users experience it and tells you where the two don't match.

unfault review

Named.
Located.
Explained.

Every finding has a name ("The Retry Storm"), a file and line, and the tradeoff spelled out: what you gain by leaving it as-is, and what your users risk if you do.

~/services/payments
❯ unfault review
Worth looking out for
🟡  payments.py:88 · The Retry Storm
HTTP call via `httpx.AsyncClient` has no retry policy
↳ puts Payment Success Rate SLO at risk (99.5%)
During an outage your service retries failures instantly,
preventing the downstream service from ever recovering.
Tradeoff
↳ Local Availability       retries mask transient failures
↳ Systemic Metastability no backoff creates thunderstorms
payments  ·  python  ·  fastapi  ·  3 files   [ parse 12ms  engine 0ms ]
unfault graph

See who
else uses
what you're
changing.

unfault graph impact <file> walks the actual dependency graph, direct and transitive. Before you change something central, you know what else is leaning on it.

~/services/api
❯ unfault graph impact src/db/connection.py
Analyzing impact of src/db/connection.py...
Direct    (4)  src/models/user.py
               src/models/order.py
               src/models/payment.py
               src/session.py
Transitive (11)  src/api/routes/...
❯ unfault graph critical --limit 3
1. src/db/connection.py    15 dependents
2. src/auth/tokens.py      12 dependents
3. src/config.py           9 dependents
CI/CD · AI agents

Pipes well.
Exits clean.

Exit 0 means clean. Exit 5 means findings. SARIF for GitHub Code Scanning. JSON for agents. Wire it wherever you like.

.github/workflows/review.yml
- name: Review
run: unfault review --output sarif > results.sarif
- uses: github/codeql-action/upload-sarif@v3
AGENTS.md
# Before committing:
unfault review --output json
# Address high-severity findings first.
# Before touching a file:
unfault graph impact <file>
A Note from the Builder
I've shipped code that took down production because of a missing timeout. The kind of thing that's obvious in hindsight and invisible on the day. I built Unfault so that particular class of obvious mistake stops making it through review. It won't catch everything. It catches the things that have a name.
— Sylvain
Privacy

Local.
Auditable.
MIT.

Parsing, analysis, graph traversal: all on your machine. Source code never leaves. The rules are in the repo. You can read exactly what it checks and why.

Read docs