Last updated 20 October 2026

Privacy

nodes is one person's app, in closed beta. This page says exactly what leaves your device, what does not, who else touches it, and how to stop the parts that are optional. It is written to be read, not to be survived — if something here is unclear, that is a bug in the page.

The short version

What stays on your device

On desktop and mobile, your whole collection lives in a SQLite file on the machine, one per signed-in account. Studying, editing, importing, the knowledge graph and the scheduler all run locally and work offline. A few things never leave at all:

What goes to the cloud when you sync

Sync is manual on desktop and mobile — it happens when you press the button. The web client at /app/ is different: it has no local database, so it reads and writes the cloud copy directly, and reviews you do there are saved as you go.

What is uploaded, all of it under your account:

That last one deserves the detail: the image store is readable by anyone who knows the 256-bit path, and the path carries no filename, no deck name and no account id. Only your own account can read the map that produces those paths. It is not a secret vault — it is a store where finding your image requires already knowing its exact contents.

Diagnostics during the beta

The app had no way to see a crash. A tester whose screen went red produced nothing at all, which is a poor way to run a beta, so the app now reports two kinds of thing. Both are covered by a single switch, which starts on, and which the app offers to turn off the first time you open it.

Usage counts

Seven event names, and no more — a name the app has not declared is rejected by the database. Each event carries numbers, flags and fixed labels; never text you typed:

Events are queued on your device and uploaded at launch and after a sync, so nothing is sent while you are offline. The queue holds 500 at most and drops anything older than 30 days.

Crash reports

When something throws, the app can send the error message, the stack trace, a short fixed label for where it happened, the app version and the platform. IP address, username and device name are explicitly switched off, and no performance tracing is collected.

"Can" is doing real work in that sentence. Crash reporting only installs itself in a build made with a reporting key, and no build script in the app's public repository supplies one — including the one that produces the beta downloads. So unless you were handed a specially-built copy, the crash reporter is not merely switched off on your machine: it was never installed, and no crash report has ever left it. Usage counts are separate and are not affected by this.

Two things the app's own wording gets slightly wrong

"Anonymous" is not quite right. Reports and events carry your account id, so that "this tester keeps hitting the same crash" is answerable. They carry no name, no email and no address — but they are linked to your account, and the honest word for that is pseudonymous, not anonymous. The wording inside the app will be corrected to match this page.

"Cards never leave your device" is a promise about intent, plus a ceiling. No event or crash report is designed to carry card text, and the fixed event list above is the whole of what is sent. But a parser that chokes on a malformed card can quote part of it in its own error message. So every free-text field in a crash report is cut at 240 characters before it is sent. That turns "a whole note ends up in an error report" into "a fragment might" — which is a real difference, and less than an absolute promise. You should read it as: card content is never sent deliberately, and cannot be sent wholesale.

Turning it off

Settings → Diagnostics → the first switch. Usage events stop being recorded the moment you flip it, and anything already queued stays on your device unsent. Crash reporting stops the next time you open the app, because it installs itself before the first screen is drawn.

Diagnostics are desktop and mobile only. The web client at /app/ collects neither crashes nor usage events.

The AI features, and your own key

Card chat, generating cards from text, and "more angles" on an existing card send text to Google's Gemini API. That text is what the feature is about: the passage you pasted, or the card you asked about. These features do nothing at all until you enter your own Gemini API key in Settings, and the key is stored only on your device. If you never enter one, nothing is ever sent to Google by the app. With Nodes Plus they can also run on AI credits instead, which goes to Anthropic, not Google (below).

When you make cards from a passage you selected in a Library book, that passage is sent exactly as any pasted text would be, and only the passage: never the rest of the book, its file name or the page it came from. The book itself still never leaves your device.

When you make cards from a region you drag over a figure, table or scanned page, the images you select in a book are sent too: a picture of just that rectangle, rendered on your device (at most 3 MB), together with the text printed within about half an inch of it, if the page has any. Nothing else of the page and none of the rest of the book goes. The model is asked to say in words what the picture shows and to keep it on the cards that use it. Those cards then carry the picture as an ordinary card image, so it is stored with them and synced like any other card image (see Card images above); the book still is not. The generator's record keeps the model's text and a count of images shown, never the picture itself.

When a card you send to "Improve card" (More angles, Split card or Rewrite card) carries images, those images are sent to Gemini too, alongside the card's text — up to four per card, each under 3 MB, read from your device. The model is asked to describe what each image shows in the new cards and to keep the picture on them. Images are never stored anywhere for this: the generator's record described above keeps the model's text and a count of how many images it was shown, never the images themselves.

"Improve card" also has an optional box for saying why the card is not sticking for you. If you write in it, that note is sent to Gemini with the card, to steer what the new cards work on. It is never stored: the generator's record keeps only whether a note was given, never what it said.

What Google does with that text is governed by Google's terms for the key you are using, not by this page.

With Nodes Plus, the card generator's fact check runs on a larger model in the cloud: Clef, on Cloudflare Workers AI. For each card the generator writes, it sends the card's question, its answer and the passage the model quoted for it — not the rest of your text — and gets back a single score. Our server passes the text through and logs none of it. To enforce the daily limit it records, per day, how many cards you checked and how many tokens that used, never the text. Without Plus, nothing is sent; the fact check on your own computer is used instead, if you downloaded it. What Cloudflare does with the text is governed by Cloudflare's terms for Workers AI.

Nodes Plus also includes AI credits, so the AI features work without a Gemini key of your own. When a feature runs on those credits (and not on your own key, which you can choose between in Preferences), the same text it would have sent to Gemini goes instead to Anthropic's Claude API, through our server: the passage or card you are working on, a card's images or a region you selected in a book, and any note you add. Anthropic processes it for us under its data processing terms, which cover the transfer to the United States. Our server passes the text through and logs none of it. To keep each subscriber within their monthly allowance it records, per day, how many calls you made, how many tokens they used and what they cost, never the text.

The tour on the front page

The front page holds a working copy of the app you can click through. It keeps a count of how far visitors get: how many opened each of the three tours, how many cards they answered, and how many reached the end. Those are daily totals and nothing else — no cookie, no identifier, no IP address, no browser fingerprint, and no record of any individual visit. The counts cannot say whether two of them came from the same person, and neither can we. It is our own counter on our own domain; there is no third-party analytics script on the page.

The invite form on the front page

"Request an invite" stores the email address you type and, if you opened one of those three tours first, which one — and nothing else. No name, no cookie, no record of what you did on the page. It is used once, to send you an invite. The key the page uses can add a row to that list and cannot read it back.

Who processes it, and where

There is no advertising network, no tracking pixel, no session recorder, and no data broker anywhere in that list.

Deleting things

A card or deck — delete it in the app. On the next sync it is removed from the cloud as well, not merely hidden.

Your account and everything in it — there is no self-service delete button yet; it is on the list. Until it exists, ask and it will be done by hand: the account and every row keyed to it in the database, which is your collection, your history and your usage events. Crash reports live in a second system and are deleted separately, in the same request. Write to privacy@nodes.cards.

One thing that deletion does not reach, because it cannot: the deduplicated image bytes described above. The map from your card's image to its bytes is yours and goes with the account. The bytes stay in the shared store, now unreferenced — they sit under a path that is a hash of the file itself, so they are unreachable without already having the file, and removing them would break every other account that happens to hold the same image.

Deleting the app without deleting the account leaves the cloud copy in place — which is the point of a cloud copy, and also why the request above exists.

Changes

This is a beta and this page will change with it. It lives in the app's public repository, so every edit to it is a dated, readable commit rather than a silent replacement. The date at the top is the last one.