post://a-grammar-checker-that-never-phones-home

A grammar checker that never phones home

read: 6 min words: 1,095
A grammar checker that never phones home
toc://sections
outline

    The business model of a writing assistant requires reading what you write. That is not a bug in the design, it is the design. Your draft is the input, a model marks it up, and the value comes back. The consequence is that every email, every contract clause, and every half-finished thought passes through someone else's servers, and you accept it because the alternative is worse writing.

    OpenGrammar started from the observation that a large share of the useful checking does not need a model at all. Repo: swadhinbiswas/opengrammar · Site: opengrammer.eu.cc

    Two engines, and knowing which one to use

    The heart of the project is a dual-engine design. A local engine runs a regular-expression rule set against a 156,000-word offline dictionary entirely in the browser. It catches misspellings, passive voice, and weak phrasing with no network call, no latency, and nothing to bill. A separate, optional engine routes to a language model for the things that genuinely need context: rewriting a sentence for tone, or fixing a subtle agreement error that depends on the whole paragraph.

    The interesting design decision is the seam between them. The AI is synchronized with the local checker, which means it never spends a token re-checking a typo the dictionary already caught. That sounds like a small optimization and it is actually the thing that makes the dual-engine approach feel coherent instead of like two products stapled together. Each engine gets the work it is best at. The dictionary handles the mechanical layer for free, and the model handles the layer where a generic rule cannot help.

    <svg viewBox="0 0 900 260" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="A scan moving down lines of text and flagging a correction">
      <g fill="currentColor" opacity="0.22">
        <rect x="80" y="60" width="520" height="14" rx="7"/>
        <rect x="80" y="100" width="600" height="14" rx="7"/>
        <rect x="80" y="140" width="430" height="14" rx="7"/>
        <rect x="80" y="180" width="560" height="14" rx="7"/>
      </g>
      <rect x="80" y="52" width="96" height="30" rx="8" fill="#f9e2af" opacity="0.35">
        <animate attributeName="x" values="80;584;80" dur="5s" repeatCount="indefinite"/>
      </rect>
      <g stroke="#a6e3a1" stroke-width="4" fill="none" stroke-linecap="round">
        <path d="M700,126 L716,142 L748,104">
          <animate attributeName="opacity" values="0.2;1;0.2" dur="2.2s" repeatCount="indefinite"/>
        </path>
      </g>
      <text x="700" y="180" font-family="ui-monospace, monospace" font-size="13" fill="currentColor" opacity="0.6">fixed</text>
    </svg>
    

    Privacy as a property of the architecture

    Most privacy claims are a policy about data the service already collected. OpenGrammar tries to make the claim structural instead.

    There is no database. The API key you paste for your own model stays in browser storage and never reaches the backend. Text that does go for AI correction passes through stateless edge functions and is not retained, because there is nowhere to retain it. The public edge deployments on Cloudflare, Vercel, and Netlify exist so you can use them without hosting anything, and the self-hosted path exists so you do not have to trust even those.

    Bring your own key is the piece that makes the economics work. You can point the router at OpenAI, Groq, Together, OpenRouter, or a local model through Ollama, and you pay only for what you use. The project is not trying to operate the model. It is trying to do the cheap layer itself and hand off the expensive layer without taking a cut.

    The shape of the backend

    The backend is a single service with a small REST surface, which is what lets it run almost anywhere.

    Method Endpoint What it does
    GET / status dashboard and engine version
    GET /health health check
    POST /analyze grammar analysis
    POST /autocomplete context-aware completion
    GET /providers which providers are configured

    It listens on port 8787 by default, reads a PORT and provider keys from the environment, and ships as a multi-architecture Docker image. Because it runs on Node, it also runs where Node runs, which is how it ends up on an old laptop, a Raspberry Pi, or an Android phone through Termux. The extension points at a server URL, so switching from a public edge endpoint to one running on your desk is a settings change.

    The features that go past spellcheck

    Two features are worth calling out because they show where the product is aiming.

    Smart context awareness detects whether you are writing a casual post, a technical document, or a formal email and scales its strictness to match. A rule that should fire on a contract should not fire on a Slack message, and hardcoding one strictness level is what makes grammar tools annoying. Alongside it, a dynamic writing score rates text from 0 to 100 across correctness, readability, engagement, and sentence delivery, which turns a list of complaints into something closer to feedback.

    The extension injects into the editors people actually use: Gmail, Google Docs, Notion, and Reddit. That integration work is unglamorous and it is the difference between a demo and a tool someone leaves installed.

    The local engine is not a toy

    It would be easy to treat the offline checker as the consolation prize and the model as the real product. The rule set is what earns its keep. A 156,000-word dictionary catches the misspellings that make writing look careless, and the regular-expression rules catch the structural habits that a spellchecker ignores: passive constructions, hedging, weak verbs, sentences that say nothing. Each rule is a documented pattern in the repository rather than a black box, which means a contributor who knows grammar but not machine learning can improve the product by editing a file.

    That openness is the reason the local layer can be genuinely competitive rather than a fallback. If a rule is wrong, you can see why and fix it, and the fix does not require a training run or a release from someone else. For a project aimed at unseating a subscription product, the ability to accept a grammar fix from a language enthusiast is a feature, not a nice-to-have.

    What I took from it

    Grammar checking is a good test case for a broader argument. The default assumption now is that any language task needs a hosted model, and the privacy-preserving version is assumed to be worse. OpenGrammar makes the case that the layered version is often better, because the majority of the value is deterministic pattern matching that is faster, free, and private, and the model is the upgrade rather than the baseline. Getting the seam right between the two is the whole product.

    It is Apache 2.0 licensed, and the repository holds the engine, the server, and the extension.

    Reach me at swadhinbiswas.cse@gmail.com or on GitHub and LinkedIn.

    react://a-grammar-checker-that-never-phones-home
    comments://a-grammar-checker-that-never-phones-home

    No comments yet.