Back to Projects
Solo Project 2026

Tosly - Terms of Service Reader

Tosly is a Chrome extension that reads Terms of Service and Privacy Policies for you and flags the clauses that work against you: data selling, forced arbitration, auto-renewals, with the exact quote from the document. The extension extracts visible page text; a Go backend prompts an LLM with a structured-output schema, so flags are consistent and citable instead of a vague summary. Free, no account, source on GitHub.

The Story

  • April 2026: read Spotify's privacy policy after clicking 'Accept' on it for the hundredth time. They share listening data with advertising partners, and continuing to use the service is the opt-in. There's no way to refuse.
  • Realized I do this every signup, every checkout, every app update. Companies bet you won't read 30 pages of legalese. That bet always pays off.
  • Wanted something that read the document for me and quoted the bad parts back. Not a summary. Summaries strip out the evidence you need to act on.
  • Decided the right surface was a Chrome extension. The risk has to surface on the page where you're about to click. Anything that requires a separate app or workflow won't get used.
  • Built the Go backend first: a single /analyze endpoint, a TTL cache keyed by the page's domain, and Gemini behind an Analyzer interface so a second model could stand in later. The prompt is the product; the rest is plumbing.
  • Picked Plasmo for the extension because MV3 with Shadow DOM isolation and hot reload is the only practical way to iterate on UI that runs on every site.
  • Shipped end-to-end in a weekend. Then spent several resubmissions getting it through Chrome Web Store review, almost all of it about permission justifications and required support pages that aren't in the official MV3 docs.

The €10 Lesson

I bought a €10 VPS. A cheap box, nothing serious running on it, the kind of purchase you make in about ninety seconds without thinking twice.

Then it was suspended. The host operates under German law, and their terms prohibit hosting copyrighted material. That rule was not buried or hidden. It was sitting in the document I had accepted at checkout, and I had agreed to it in writing.

I had not read a word of it. I clicked through those terms exactly the way I click through every other terms page, and the box and the €10 went with it.

That is the part worth sitting with. I did not get caught out by fine print written to deceive me. I got caught out by ordinary print I never opened, in a document I had formally agreed to. Nobody tricked me. I just did not read it, and I have never once read one.

How Tosly changes that moment

Tosly exists because these documents are unreadable in practice, not because they are secret. It puts the analysis on the page you are about to accept, quotes the exact sentence that creates the risk, and ranks how badly it is likely to bite. Clicking a flag scrolls you to the original paragraph and highlights it, so the claim can be checked against the source rather than taken on trust.

It would not have caught my VPS. The rubric shipped with six categories, all of them about money or data, and none of them covered a clause that lets a host suspend a paid account over what you put on it. My own tool would have returned green on the sentence that killed my box.

So I added the seventh. Prohibited Use covers content or activity a provider forbids, and it names the consequence rather than the restriction, because losing the box with no refund is the part that hurts. The prompt states its category count in two places, so a test now holds the list and both counts together: adding an eighth without updating them fails the build instead of quietly teaching the model a contract the schema rejects. The gap cost €10 to find and one category to close.

Screenshot of the Tosly landing page

Video Demo

Key Features

  • Flags six clause families the prompt names outright: data selling, hidden fees, forced arbitration, auto-renewal, data-deletion rights, and third-party sharing. Every flag has to carry the verbatim sentence that triggered it, capped at 200 characters, so a reader can check the call against the document instead of trusting it.
  • Server-side cache keyed by domain with a 7-day TTL. Spotify's policy is analyzed once a week no matter how many people open it, which keeps LLM cost flat as usage grows rather than scaling with it.
  • Two permissions only: storage (the auto-scan preference and the last result per domain) and host_permissions (read visible page text). No identity, no browsing history, no cookies, no form data leave the browser.
  • Click any flagged quote in the result panel and the page scrolls to the original paragraph with a flashing highlight. Works on SPA-rendered DOMs where window.scrollTo silently no-ops.
  • Two providers behind one Analyzer interface: gemini-2.5-flash first, Groq's llama-3.3-70b as the free fallback. When the primary is rate-limited or out of credits the chain logs the failure and hands off, so the request still returns an answer.
  • Long policies are split into 3,000-word chunks and analyzed concurrently, then merged: the highest severity wins and flags dedupe by category. A sixty-page document comes back as one verdict rather than twelve partial ones.

Challenges & Solutions

The Challenge

Three things were harder than expected. First, getting a legal-text analyzer to produce consistent output: 'summarize this ToS' gives slop. The fix was a category rubric of fixed risk patterns, a severity scale, and a structured-output schema that requires an exact quote per flag. v1 of the prompt was unusable; v2 produces citable JSON on hostile input. Second, the Chrome Web Store review process. The first build worked locally in a weekend, but it took several resubmissions to get past the reviewer, almost all of it about permission justifications and required support and privacy URLs. Third, click-to-scroll on pages I don't own: finding a text fragment in an SPA-rendered DOM, scrolling it into the page's actual scroll container, and rendering a highlight that doesn't fight the host site's CSS.

What I Learned

  • Permission justifications in manifest.json get quoted back at you by the Chrome reviewer verbatim. Write them as sentences explaining why the feature needs the permission, not one-word labels. I dropped the tabs permission once I realized nothing actually needed it; the feature still worked.
  • The Web Store wants more than the extension. A working privacy URL and a working support URL on a real site are required. Added /privacy, /support, /support/success to the landing site mid-review because none of that is in the official MV3 docs.
  • Prompts beat code for legal text. The thing that makes Tosly trustworthy is a prompt that forces the model to (a) match a category from a fixed rubric, (b) return a severity, and (c) cite the source. None of that is novel ML; it's just refusing to ship a 'summarize this' prompt.
  • Cache aggressively when the input is public and identical for every user. Same policy URL means one LLM call per week, served to everyone. Cost stopped being a variable.
  • Shadow DOM isolation matters when your UI ships on every site on the internet. Plasmo's CSUI shadow DOM is the single biggest reason Tosly doesn't get repainted by the host page's CSS.

Impact & Growth

  • First product on the Chrome Web Store under my name. Went through the full submission, review, and listing cycle end-to-end.
  • Built a reusable prompt scaffold for structured output over hostile input (category rubric, severity, required exact quote) that I reach for any time I need an LLM to act on unstructured text.
  • Got a piece of generally useful DOM engineering out of it: click-a-quote, scroll-and-highlight on a page I don't control. Lifts cleanly to any annotation tool, doc reader, or 'find this string in a foreign page' use case.
  • Settled on a ship order: smallest privacy-preserving version first, support pages second, marketing site third. The slow steps are LLM cost and store review, both solvable.

Technologies Used

Chrome MV3PlasmoReactTypeScriptGoGeminiGroqDockerAstroTailwind CSS

Want to see more?

View all projects