← All articles
Jun 26, 20266 min read

Local-First by Default: A Browser Extension That Never Phones Home

  • Browser Extensions
  • Privacy
  • Local-First
  • Engineering
Local-First by Default: A Browser Extension That Never Phones Home

There's a particular category of browser extensions that has quietly become the norm: tools that add a thin UI layer on top of a web app, then funnel your activity through a third-party backend to make that UI work. Your conversation history, your prompts, your thinking — routed through a server you don't control because the feature "needs" it. Or, more often, because the developer built the simplest thing and syncing state to a backend is the simplest thing.

I built NorthLab Folders the other way around. It adds a real folder sidebar inside claude.ai and chatgpt.com, and the folder structure never leaves your browser. No account creation. No analytics. No tracking. The only outbound call the extension makes is a license validation to Lemon Squeezy when you activate a Pro key.

Building it that way was a choice, not an accident — and it came with real tradeoffs that are worth being honest about.

What "local-first" actually meant in practice

Claude has no native folders for individual chats. The closest thing is Projects, which cap at five on the free plan and can't nest — one project per chat, no pinning individual conversations. If you want to keep a folder of quick experiments separate from client work separate from personal use, you're currently out of luck on the native UI. That gap is why the extension exists.

The question I had to answer early: where does the folder structure live?

The answer shaped everything else. I put it in the browser's local extension storage — the key-value store that Manifest V3 extensions get access to per-device. Each folder is a name and a list of chat IDs. That's it. No message text, no conversation content, no prompts. The extension never reads what's inside a conversation for any purpose other than rendering the sidebar in your browser or generating a local export you initiate.

When you export a chat as Markdown or JSON — or bulk-export a folder as a ZIP of Markdown files — that content is read in-page, assembled in memory, and written to a file on your machine. At no point does it pass through a server. The export is entirely local.

The honest tradeoff: no sync

The thing you give up with this model is cross-device sync. Your folders on your work Mac don't appear on your personal laptop. If you reinstall your browser, your folders are gone unless you exported them.

I could have added a sync layer. A small backend, user accounts, row-level security — it's not a hard problem. But the moment you have a backend, you have a policy question: what are you storing, for how long, and who has access? And more importantly, you now have a surface that can be breached, subpoenaed, or quietly expanded. You have a commitment to keep.

The no-sync limitation is real and I mention it plainly in the product. Some people hit it immediately and decide it's a dealbreaker. That's a fair call. But I'd rather be honest about a constraint than paper over it with a backend that introduces a different class of risk.

What I've found is that most people's use case is single-device anyway — a work machine where they do their AI-assisted work — and the tradeoff is invisible to them. For the people who need sync, they know, and now they also know this isn't the right tool for them.

Defaults are the product

There's a version of this extension where I could have shipped "optional" cloud sync — off by default, but available. That framing sounds reasonable but I think it's a cop-out. Features you ship become features users enable. Once a backend exists, the pressure to use it — to justify the maintenance cost, to build engagement features, to add analytics that tell you how the product is being used — becomes structural. The default eventually shifts.

I wrote previously about the idea of "Honest Staleness" — the discipline of not presenting cached data as live just because it's easier to ship. The same instinct is at work here. The default should match the actual architecture. If the folder data lives locally, the UI should make that clear, not obscure it behind account language that implies a cloud service you don't have.

The privacy property of NorthLab Folders isn't a marketing claim. It falls out directly from the architecture: there's no backend, so there's nothing to leak. You don't have to trust my privacy policy because there's no server for the policy to govern.

The one call we do make

I want to be precise about this: the extension does make one outbound network request, and it's to api.lemonsqueezy.com.

Lemon Squeezy is the merchant of record for Pro licenses. When you activate a Pro key, the extension verifies it against their API. That's a license check, not usage telemetry — it doesn't send any information about your conversations or folder contents. It sends the license key you provided and gets back a validity response.

If you never upgrade to Pro and stay on the free tier (three folders), that call never happens either.

I'm flagging this not because it's a gotcha — license validation is a completely standard and expected thing for paid software — but because "never phones home" should be accurate, and accurate means accounting for every request. I wrote a guide on what the extension can and can't access if you want the full breakdown of what's in scope.

Building on Manifest V3

The Chrome Web Store build runs as a Manifest V3 extension, which has a different security posture than V2 — service workers instead of persistent background pages, stricter CSP, more limited host permissions. On macOS, there's also a signed and notarized Safari web extension distributed as a direct DMG download, because Safari requires it and I wanted parity across browsers.

MV3's restrictions actually make it easier to reason about what an extension can do. The permission model forces you to be explicit about what you're requesting access to, which aligns with the local-first approach. I'm not requesting permissions I don't need, which means users (and reviewers) can verify that.

What this is really about

Most of the decisions I made building NorthLab Folders were about defaults. Not the most powerful possible feature set, but the right default behavior for a tool that touches something as personal as your AI conversation history.

The default should be: your data stays with you. If you want to change that, you should be able to, but it should require active opt-in, not passive acceptance of a backend you didn't notice.

Building without a backend isn't always the right call. There are plenty of products where sync and collaboration require shared infrastructure and the privacy tradeoff is worth it. But for a personal organizer that sits inside your AI chat client, local-first is the correct default. The question was whether I was willing to accept the constraints that came with it.

I was. The extension works better for being honest about what it is.

WRITTEN BY

Shahzaib Muhammad Akram

Senior Frontend EngineerCyberjaya, Malaysia