Last updated 21 August 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.

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

The invite form on the front page

"Request an invite" stores the email address you type, and nothing else — no name, no cookie, no analytics on the submission. 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.