I audited my own note app's network traffic. Here's what I found.

22 August 2026

Neoma's central claim is that your notes never leave your device. For the people it's built for. Researchers under ethics approval, anyone holding privileged or confidential material, that isn't a preference. It's the reason they're allowed to install it.

A claim carrying that much weight should be tested rather than asserted in marketing copy. So I tested it, found two things I didn't like, and fixed them.

Here's the method, so you can run it against any app you rely on. Including mine.

The method

Three layers, cheapest first.

1. Find every call site. Search the source for the primitives that can reach the network:

fetch(  ·  XMLHttpRequest  ·  WebSocket  ·  EventSource  ·  sendBeacon

2. Find every external reference. Absolute URLs in source and markup, plus anything in index.html pulling from a CDN. Fonts are the usual offender, a single Google Fonts stylesheet sends every visitor's IP to a third party before the app does anything.

3. Watch it run. Static analysis tells you what the code says. Only execution tells you what it does. Drive the app with Playwright, record every request, and fail if any leaves the origin.

What I found in Neoma

The first layer came back clean, which honestly surprised me:

checked result
fetch / XHR / WebSocket / EventSource / sendBeacon call sites 0
Analytics or telemetry packages (of 56 dependencies) 0
External fonts or CDN assets 0 KaTeX's 20 font files are bundled
Off-origin requests on load 0
Off-origin requests while editing 0

No account system, no sync server, nothing to phone home to.

Then layer three found the part that mattered.

The mistake that nearly gave me a clean bill

My first runtime test put a remote image in a note and checked whether it was fetched. It wasn't. I nearly wrote that up as a pass.

It was wrong. The test typed the markdown into the editor, and in edit mode markdown is never rendered to HTML. No <img> element ever existed. No request could possibly have been made.

I had proved nothing and very nearly published it as proof of something.

Re-running in reading mode reversed the result completely:

FINDING: remote images in notes ARE fetched. This is a tracking vector.
FINDING: YouTube embed contacted 9 URLs across 3 hosts:
  www.youtube-nocookie.com
  i.ytimg.com
  www.gstatic.com

This is how privacy audits produce comfortable answers. Not by lying, by testing the wrong state and stopping once the result is the one you hoped for. If you audit anything, make the test fail first. A test that has never gone red is decoration.

Why a remote image matters more than it sounds

![](https://example.com/pic.png) in a note looks harmless. It is a beacon.

Whoever controls that address learns when the note was opened, and from which IP. If the note was written by someone else. Sent to you, shared in a team vault, downloaded from somewhere, that's a disclosure you never agreed to.

It's the email tracking pixel, in a document you thought was local.

For most apps that's a minor privacy wart. For one being installed because a research office signed off on it, it undermines the whole basis of the approval.

What I changed

Remote content is blocked by default.

The naive fix is to remove the <img> after rendering. That does not work, and finding out why was the useful part: the browser begins fetching an image the moment it parses the tag. By the time your code runs, the request is already in flight. You've hidden the image and leaked the beacon.

The working fix rewrites the HTML inside an inert DOMParser document, where nothing loads, before it ever reaches the live DOM. The URL is stripped and kept as text, so you can read where it pointed without requesting it.

And a Content Security Policy, so it isn't just a matter of my code behaving:

connect-src 'self' ipc: http://ipc.localhost tauri: asset: blob: data:
script-src 'self'
form-action 'none'
object-src 'none'

No remote origin appears in connect-src. The application cannot upload a note. Not by fetch, not by XHR, not by WebSocket, not by sendBeacon. That is enforced by the platform, and it holds even if my code is compromised.

That's the difference worth caring about: "doesn't" is a promise, "can't" is a property.

Run it yourself

Don't take my word for any of this.

  1. Install Neoma
  2. nettop -p Neoma on macOS, or Wireshark anywhere
  3. Use it properly. Create a vault, write, link, render, search
  4. Watch nothing leave

Then put ![](https://example.com/x.png) in a note, render it, and confirm no request is made until you enable remote content in Settings.

The audit itself is e2e/network.spec.ts in the repository. It runs on every change and fails the build if anything leaves the origin.

The part I can't automate away

If Neoma ever starts transmitting something, this page, the privacy policy and that test would all have to change in a public commit. There's no version of it that happens quietly.

That's not a technical guarantee. It's just the shape of the thing: the claim is falsifiable, the test is public, and breaking it leaves a trace.


Neoma is free for personal use, on macOS, Windows, Linux, iOS and the web. Notes are plain Markdown files in a folder you choose. There is no account, and there will never be a Neoma server holding your notes. If sync is ever built, it will be strictly peer-to-peer.