DEV Community

Kamal
Kamal

Posted on

LeaseCheck: a lease reader that runs on your laptop, with rule updates you can verify

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

Almost nobody reads a lease properly. It's a stack of dense paragraphs written by the other side, you get it with a pen in your hand and a landlord waiting, and the one clause that mattered only shows up months later, when it costs you money.

LeaseCheck is a small tool that reads the lease alongside you before you sign. You paste in the text (or click Load sample lease), and it hands the lease back as a marked-up page. Clauses worth a second look are highlighted the way you'd do it with a highlighter on paper, and each one gets a short note: what the clause means in plain English, and what to ask the landlord about it.

The notes come in three levels, so you can tell at a glance what matters most:

  • Push back (red): clauses that deserve a real conversation before you sign.
  • Look closer (amber): clauses that may be perfectly fine, but you should understand them first.
  • Good to know (quiet blue-grey): lower-stakes limits worth being aware of.

The whole thing runs on your own laptop. There's no account, nothing is uploaded, and it doesn't need a GPU. I also ran it with Wi-Fi switched off, and it worked exactly the same.

What happens when you press Check lease

  1. You paste a lease. The box says numbered clauses work best, because that's how the text gets split.
  2. LeaseCheck splits the text into clauses and runs a set of rules over each one, looking for risky patterns.
  3. For every clause that gets flagged, a small AI model running on your own machine writes a two-sentence explanation in plain English.
  4. You get the marked-up lease on the left and a findings list on the right. You can filter by level, search the findings, and expand any of them. Click a finding and the lease scrolls to its clause. Click a clause and its finding opens.
  5. A Before you sign tab pulls out the three most important questions to raise and a ready-to-send draft email to the landlord. You can also download all the notes as a file.

One clause, start to finish

Here's what the tool does with a single line from my sample lease:

  • The clause: "Landlord may enter the unit at any time without notice to inspect or show the property."
  • Flagged as: Push back, "Landlord entry with little or no notice".
  • The note, written by Gemma: "The landlord can enter your unit whenever they want, without you being notified beforehand."
  • The question to ask: "Can entry require at least 24-48 hours notice except in emergencies?"

That's the whole product in miniature: find the clause, say what it means, and give the tenant a sentence they can actually say out loud.

Who would pay for this?

Tenants, small landlords, and anyone helping a friend or relative make sense of a lease. A lease is full of names, income details and ID numbers, so people have good reason not to paste one into a random website, and that's where running locally helps.

The model itself is free and runs on your machine, so it isn't what you'd charge for. What does go out of date is the rules. Tenancy law differs from place to place and changes over time, so the thing worth paying for is signed, region-specific rule packs that stay current. The signing is what would let a customer trust that a pack really came from its publisher and is the current one (more on that in the TUF section). All of this is a hypothesis, though. I haven't tested pricing with anyone yet.

Demo

LeaseCheck is built to run on your own computer, so there's no hosted version to click. Instead, here's a walk-through with screenshots from my own machine. The quick start is in the Code section below if you'd like to try it yourself.

Figure 1. The start page. The left side explains the idea: a sample marked-up clause, then three plain facts. Everything runs on this computer, the notes are written by gemma3:4b on your own processor, and the rule pack is signed and checked with TUF. The right side is a text box for the lease, a "Load sample lease" shortcut, a checkbox to turn the model's explanations on or off, and the Check lease button. The green chip at the top says the rules were verified, and there's a dark mode toggle.

Figure 2. Results for the sample lease. The lease sits on the left with flagged clauses highlighted. On the right, the panel reports 10 of 11 clauses flagged: 5 Push back, 4 Look closer and 1 Good to know, with filters and a search box above the list. The small print under the panel reads "Explained on this computer with gemma3:4b at about 13 tokens per second. Nothing was uploaded."

Figure 3. The same analysis with Wi-Fi turned off. The Windows quick-settings panel is open in the corner and the Wi-Fi tile is switched off. The check still ran and gave the same findings, because nothing in the analysis needs the network.

Figure 4. A shorter paste: just the first five clauses. All five get flagged (3 Push back, 2 Look closer), and the panel adjusts to fit.

What I tested: the sample is a synthetic 11-clause lease that I wrote to exercise the rules. LeaseCheck flags 10 of the 11 and correctly leaves the "no pets" clause alone. I have not run it on a real lease yet.

Code

Repo: https://github.com/Phantom9869/Lease-check

To run it yourself you need Python 3.10 or newer, plus Ollama with gemma3:4b pulled. On Windows PowerShell:

python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
python -m tools.tufrepo
python -m http.server 8000 --directory repo
Enter fullscreen mode Exit fullscreen mode

Then, in a second terminal with the venv activated, run streamlit run app.py. The tools.tufrepo step generates your own signing keys (kept out of git) and publishes the signed rule pack that http.server serves. python -m tools.attack_demo runs the attack scenarios described below. The repo includes samples/sample_lease.txt, so you can try it straight away.

How I Built It

1. Rules decide what gets flagged

LeaseCheck has 11 rules, written in YAML. They cover entry without notice, automatic renewal, early-termination fees, non-refundable fees, deposit deductions, blanket repair duties, liability waivers, rent changes at the landlord's discretion, late fees, joint and several liability, and subletting limits. Here's one of them:

- id: entry_without_notice
  title: Landlord entry with little or no notice
  severity: high
  patterns:
    - 'enter\b.{0,80}\b(at any time|without (prior |advance )?notice)'
  why: The landlord may be able to come in without warning.
  ask: Can entry require at least 24-48 hours notice except in emergencies?
  localize: Check the minimum entry-notice period in your region.
Enter fullscreen mode Exit fullscreen mode

Each rule has regex patterns, a severity, a plain-English reason, a question to ask, and a localize note. That last one points to the number you'd want to check for your area, like a notice period or a deposit cap, once the rule has been verified for a place.

The most important design decision was what not to do. I didn't ask the model to read the whole lease and decide what's risky. A small local model working through thousands of words is slow and inconsistent, and it can skip a clause without telling you. Rules are instant, repeatable, and easy to audit: you can see exactly why any clause was flagged.

2. Gemma 3 4B explains, locally

The model's job is deliberately narrow. For each flagged clause, it gets the rule title and the clause text, and nothing else. It's asked to reply with JSON holding a short plain-English explanation, two sentences at most, using only facts stated in the clause. Temperature is low, the output length is capped and the context window is fixed, which keeps it fast and predictable.

Through Ollama, gemma3:4b generated 12.6 to 13.9 tokens per second across my runs. That's on a Ryzen 7 7730U laptop with 16 GB of RAM and no dedicated GPU, with Wi-Fi both on and off.

Since it only ever sees short clauses, a 4B model is enough. I'll be honest about what it adds, too. Its explanations are accurate, but they mostly restate the clause in plainer words. The rules do the detecting, and the model makes the result readable for someone with no legal background. If the model isn't running, the app says so and falls back to each rule's own explanation, so the tool still works.

3. Rule updates are verified with TUF

This is the part of the project I care most about, so I'll explain it from the beginning.

The problem. The rule pack is the one file that decides whether LeaseCheck says "this looks fine." If someone tampered with it, the tool could reassure a tenant about a bad lease. And the rules can't be frozen, because tenancy law changes, so they have to be updatable. That puts LeaseCheck in the same position as any software that updates itself: how does the app know that a file that arrived from somewhere else is the genuine, current one?

Why not just HTTPS and a checksum? Those are a start, but they leave real gaps:

  • HTTPS protects the connection, not the machine at the other end. If the server or a mirror is compromised, it serves the attacker's file over a perfectly valid connection.
  • A checksum published next to the file doesn't help either, because anyone who can replace the file can replace the checksum too.
  • Neither tells you whether the file is the latest one. An attacker can hand you an older, genuine, signed version (a rollback), or keep showing you the same old files forever so you never see the update (a freeze).

What TUF is. TUF, The Update Framework, is an open specification for distributing updates securely, designed around exactly those failures. I used python-tuf 7.0.1, the Python reference implementation. The core idea is that no single key and no single file is trusted with everything. Trust is split across four roles, each with its own key and its own job, and each signs a small JSON metadata file:

  • root: says which keys are allowed to sign for each of the other roles. It ships with the app, so it's the starting point of trust, and everything else is checked against it.
  • targets: lists the actual files being distributed (here, the rule pack) along with each file's exact length and hash. This is the file that says "this precise file is the rules."
  • snapshot: records which version of the targets metadata is current, so a client can't be handed a mix of old and new metadata.
  • timestamp: a tiny file that's re-signed often and expires quickly. It points to the current snapshot, and it's the freshness signal that says "this is current."

What the app does on every update. The checks run in a fixed order, and each file vouches for the next one:

  1. Start from the root that shipped with the app. This is the only thing trusted up front.
  2. Fetch timestamp. Check its signature against the key listed in root, check it hasn't expired, and check its version isn't older than one already seen.
  3. Fetch snapshot. Check it matches what the timestamp says it should be, plus its own signature and expiry.
  4. Fetch targets. Check it against the snapshot in the same way.
  5. Look up the rule pack in targets, download it, and check its length and hash against the recorded values.
  6. Only if every step passes does the app use the new rules. Otherwise it keeps the last verified rules and says so.

python-tuf's client library performs these checks, and the error names in the table below come straight from it.

The attack demo. To see whether this holds up, I wrote a script that serves doctored repositories from a local malicious mirror and records the app's verdict on each:

Attack What the attacker is after Client verdict Reason
Honest repo Nothing (the baseline) accepted verified
Rule pack altered on the mirror Quietly change the rules so bad clauses stop being flagged refused LengthOrHashMismatchError
Rollback to older signed rules Replay genuine but outdated rules refused BadVersionNumberError
Stale metadata past its expiry Freeze the client on old rules forever refused ExpiredMetadataError
Forged timestamp, attacker's key Impersonate the publisher refused UnsignedMetadataError

What you see in the app. The start page shows a green "Rules verified by TUF" chip, and the footer line reads "Rule pack v1 verified by TUF (metadata valid until 2026-10-25)", with a Check for rule updates link beside it. That date matters most, because laws change. If it passes without a fresh signature, the app should tell you "rules not verified fresh" instead of quietly using old ones. When an update is refused, the app falls back to the last verified rules and shows an orange status chip.

What TUF does and doesn't give me. TUF proves the rules are untampered and current. It does not prove they are correct, and a real signature on a wrong rule is still a wrong rule. My demo also takes a shortcut: all the signing keys live on one machine. A real deployment would keep the root and targets keys offline on hardware, require a threshold on root (for example, two of three keyholders must sign), and use delegations, which let you hand one region's rules to a trusted person without giving them keys to everything. I skipped delegations here.

4. The interface

The shell is Streamlit. The review panel, with the highlighted lease on one side and the filterable findings on the other, is a self-contained HTML and JavaScript component that makes no network calls. That's why selecting, filtering and searching feel instant and work offline. All lease and model text is escaped before it reaches the page. The panel is keyboard accessible and has a dark mode, and I checked the layout in a real browser at desktop and phone widths.

Limits, plainly

  • The 11 rules are a starter set. They have not been checked against any jurisdiction's law, and every finding says so until a source is added.
  • The demo's TUF keys all sit on one machine and I skipped delegations (see the TUF section above for what a real setup would look like).
  • TUF proves the rules are untampered and fresh. It does not prove they are correct.
  • This is not legal advice, and a result with no flags does not mean a lease is safe.
  • It has only been tried on the sample lease, not on real leases or real users.

Next: verify the rules for one jurisdiction with citations, get a real person to try it, and benchmark other local models for explanation quality.

A bit of AI was used to help build this project. Prior work credited: python-tuf, Streamlit, Ollama, Gemma, PyYAML.

Why Does Open Innovation Matter?

Four things made this project possible that a closed API wouldn't have.

The lease never leaves the machine. A lease holds names, income and ID details. With a local open-weight model, the analysis happens on the tenant's own laptop. With a closed API, every lease would travel to a server the tenant doesn't control.

It works offline and costs nothing per check. I ran the full analysis with Wi-Fi off, at the same speed (Figure 3). For a small tool aimed at individuals, having no per-call cost is what makes a low price workable.

Everything is inspectable. The rules are plain YAML, so you can see why anything was flagged. TUF is an open specification with an open-source Python client, so the update checks are code you can read rather than a promise from a vendor. And because the rules are signed and shipped separately, the part that has to keep changing stays apart from the model.

No lock-in. The model is a single setting. Anything Ollama can run can replace Gemma, and nobody can change its price or terms underneath the tool.

There's one place a closed model might do better: explanation quality. I didn't benchmark against one, so I can't claim open wins there. What I can show is that a 4B open model on a laptop CPU, paired with deterministic rules, is enough to make a lease readable while keeping it private.

Prize Categories

  • Best Use of Gemma: Gemma 3 4B runs locally through Ollama and writes the plain-English explanation for every flagged clause.

Top comments (0)