Liffy — self-hosted AI code review

self-hosted · working end to end

liffy

Code review that has actually read your codebase. Not the file. The codebase.

01 · pull request
02 · retrieve
03 · review
04 · verdict
setup-windows.bat pull request #58
121:: generate a signing secret
122call :gen_hex 32 SECRET
123
124:: write it into backend\.env
125set "L=%L:JWT_SECRET_KEY==JWT_SECRET_KEY=%SECRET%%"
critical logic error

126echo %L%>> backend\.env
request changes comments 8 tokens read 25,043 elapsed 2m 07s

// how it knew where to look

Nobody told it those two files were related

The Windows script and the macOS script share almost no words. Searching for text would never have connected them. Liffy connects them because, once, it read the whole project and stored what each piece means.

01 · your repo
02 · split
03 · embed
04 · recall
indexing lucenity0/Liffy 412 files
backend/app/services/rag_service.py12 chunks
backend/app/services/chunker.py9 chunks
backend/app/config.py4 chunks
setup-macos.sh6 chunks
setup-windows.bat5 chunks
┌ chunk 04
  def write_secret(env_path, secret):
    replace the JWT_SECRET_KEY line in place
└ cut at the function boundary, not at line 40
query “how does this project write the signing secret into .env?”
indexed files 412 chunks 4,062 runs once, then only on change

// what just happened

It found the bug by reading a different file

Nothing in that Windows script is wrong on its own terms. It is only wrong next to the macOS script, which does the same job correctly — and Liffy pulled that file up on its own, because it had read the whole project and knew the two belonged together.

That bug was already merged and sitting in the main branch. Nobody had noticed.

0comments
0tokens read
0elapsed
0already in main

// why not just ask an ai

Same model. One of them has read your code.

Every AI review tool sends the diff to a model. The only question that matters is what else it sends. Flip the switch.

what everyone else ships

generic 0 files retrieved

// receipts

Liffy reviews its own codebase

The first repository we connected was this one. What follows is not a mock-up — it is Liffy's own review screen, carrying real reviews out of a running instance: the comments it left, the lines it left them on, and one run that failed.

lucenity0/Liffy 4 of 13 reviews
completed comment claude-opus-5 409,804 tokens 101 files +9,148 −1,570 3m 01s

Review summary

A very large PR: a five-theme ladder with a custom-theme editor, a chrome/rail restructure replacing the top bar, a tabbed review workspace, new analytics endpoints, review history on GET /repos, and a GitHub pull-request picker. The backend additions are well-scoped to the caller and the aggregate queries correctly avoid join fan-out. Documentation and tests are unusually thorough. A handful of small issues below: one dead prop expression, one theme-store update that does not notify subscribers, and a render-phase DOM mutation in the theme editor.

0critical
3warning
1info

it posted this to the pull request

Every figure above is read out of a running instance's database — four of the thirteen reviews on it. The failed run is included on purpose: it is the same instance, on a day the model provider was not installed.

// start to finish

You do step one. It does the rest.

01

Connect a repo

Sign in with GitHub. Liffy never sees your password, and you can revoke its access from your GitHub settings whenever you like.

02

It reads everything

Split at function and class boundaries, not arbitrary line counts, then indexed by meaning — so it finds code that is about the same thing.

03

A PR arrives

A webhook fires, the job goes on a queue, the review runs in the background. A forty-file change blocks nothing.

04

You score it

Rate any comment up or down. Those ratings roll into a score for the whole system — a review nobody scores cannot improve.

// run it

Your repos. Your keys. Your machine.

Liffy is not a service you sign up for — there is no account and no upload. You run it, and it talks to whichever model you point it at. Set that to a local one and nothing leaves the building.

quickstart
$ git clone https://github.com/lucenity0/Liffy.git
$ cd Liffy
$ cp .env.example backend/.env   # add your keys, or don't
$ docker compose up
Ollama free · no account Runs on your machine. Nothing leaves it.
Gemini free tier A free Google account is the whole cost.
Claude Code One you are probably already paying for.
Anthropic An API key. What the demo above ran on.

Embeddings run locally by default and never need a key, whichever model you pick. With Ollama nothing leaves your machine at all; the three hosted options send the diff and the retrieved context to that provider, under their terms. Exactly what gets sent →

// where it stands

All fifteen steps run today

Liffy is a student project built in the open. This is the real state of it, not a roadmap written to look finished.

Working

  • GitHub sign-in and repo connection
  • Whole-codebase indexing at function boundaries
  • Webhook-triggered reviews on every push
  • Retrieval-grounded, line-anchored comments
  • Background queue and a dashboard to watch it
  • Four interchangeable model providers
  • Rating comments up or down
  • A weekly job that scores reviews against those ratings
  • Posting reviews back to the PR itself
  • Indexing languages beyond Python
  • Settings in the app, so nobody edits a .env by hand
  • Subscription models — bring a Claude or Codex plan
  • Filter and sort the review list, and pick a PR from it
  • Per-model performance analytics
  • Five themes and a customiser for them
  • A help page that files its own bug reports

Not built

  • Teams and organisation accounts
  • Reviewing anything that is not a GitHub pull request
  • Your future contributions

// the long version

Every step, drawn out in full

Fifteen numbered steps from sign-in to score, written for someone who does not write software. It is the piece to send to anyone who asks what you have been building.