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
- Your cards go to the cloud when you sync. That is what sync is. They sit in a database only your account can read.
- They do not go anywhere else — not to diagnostics, not to any analytics product, not to a model, unless you use the AI features and supply your own key.
- During the beta the app reports crashes and usage counts — which features you opened, how many cards were in a session — tied to your account. One switch in Settings turns it off.
- Nothing here is sold, shared with advertisers, or used to train anything. There is no ad network and no third-party tracker on this site or in the app.
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:
- The original media files from an
.apkgimport, and the.apkgyou exported. - The 112 MB embedding model, and the act of embedding a card: the text is turned into a vector on your own CPU, never by a service.
- The card generator's fact checker, if you download it (286 MB, on desktop): it compares each generated card with the passage it came from on your own CPU, never by a service. Both models are downloaded from our own server while you are signed in. The request checks your session with Supabase, and Cloudflare keeps ordinary web-server logs of it, as for the rest of this site. It carries no card text.
- The PDFs you add to the Library on desktop. Nodes copies each one into its own folder on your machine, one folder per account, and reads it from there. The book itself, your place in it and your search words are never uploaded or synced, so another device will not have it. Removing a book deletes that copy and leaves your original file alone.
- For each card you make from a Library book, the page and the passage it came from, so the card can open the book there again. This stays on the device with the book and is never uploaded, synced or included in diagnostics; your cards still sync, without it.
- Your diagnostics preference, your Gemini API key if you set one, and the interface settings kept in the OS preference store.
- What each generated card was written from. When you generate cards, the passage the model quoted for each one, and the whole text you gave it, are kept in the local database. That includes the cards you did not keep. It is there so we can check how often the generator writes something the text does not say, on real material rather than on test text. This record is never uploaded, with or without diagnostics. With Nodes Plus, each card's quote is also sent once to the cloud fact check described under the AI features; our server keeps no copy.
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:
- Decks and cards — the front and back of every card as HTML, its tags, and the original Anki note it came from if it was imported. This is your card content. It goes up because it has to for the same collection to appear on another device.
- Your review history — every answer, its rating and timestamp, plus exam attempts.
- Scheduling and settings — per-deck FSRS parameters, learning steps, limits, easy-day configuration.
- Card embeddings — the vectors computed on your device. A vector is derived from card text and is not readable as text, but treat it as card-derived data, because it is.
- Deletion records — when you delete a card or deck, a small record of the deletion is synced so the deletion reaches your other devices, then the row is removed on the server.
- Card images, if you have any. A downscaled, re-encoded copy is uploaded for display on the web client. The private part is the map from an image's name to where its bytes are; the bytes themselves sit in a shared store under a path derived from a SHA-256 of the file, which is what lets the same image be stored once for everyone who has it.
- A record of what the card generator produced, if you
use it. For each card the AI proposed — including the ones you
unticked and the ones it withheld from you — a row saying which version
of the pipeline wrote it, which model answered, what the quality checks
measured, and what you then did about it: kept, edited, rescued, or
dropped. It is how we can tell whether a change to the generator made
the cards better or worse, which is not answerable from a card alone.
Two parts of this go further than the rest of this page, so they are worth stating plainly. It includes the model's own text for cards you rejected — text you never saved and that never became a card — because the point is to compare what was offered against what you were willing to keep. And where you edited a card before keeping it, it includes what the model originally wrote, so the edit is visible as an edit. The same goes for a card the generator tidied with ✨ Format before showing it to you: if you kept the tidied version, the record includes the untidied text it started from. And a card the checks withheld that you rescued and kept unchanged is recorded with its text too, although the saved card holds the same words: it is the most telling row a run produces, and the record outlives the card if you delete it later. If you turn diagnostics off in Settings, none of that text is uploaded; the version numbers and the measurements still are, and they contain nothing you typed.
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:
app_opened— app version and platform.study_session_completed— which pacing mode, how many cards, how long, whether you finished.import_finished/import_failed— the format, how many cards, how long, and for a failure, which stage and the class name of the error.generation_run— how far a card-generation run got and how many claims and cards were proposed, kept and accepted, as counts.sync_finished— whether it worked, how many rows moved, how long.feature_used— one label from a fixed list: the graph view, card chat, close-up, undo, backlog burn-down, more angles, the shortcut sheet.
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
- Supabase — the account, the synced collection, the usage events, the invite list and the tour counts above. Hosted in the EU (Ireland). Every table is protected by row-level security, so a signed-in client can only read its own rows.
- Stripe — payments for Nodes Plus, once it is on sale: your name, email, billing address and card details, which Stripe holds. We keep only Stripe's customer id and your subscription status.
- Sentry — crash reports only, and only from a build carrying a reporting key, which as of the date above no published build does. The account it would report to is on Sentry's EU service.
- Google (Gemini API) — only the text you pass to an AI feature, the images of a card you ask it to improve, a region you select in a book and any note you add to it, only if you have entered a key.
- Cloudflare Workers AI — Nodes Plus only: the question, answer and quoted passage of each generated card, for the cloud fact check described above.
- Anthropic (Claude API) — Nodes Plus only, and only for an AI feature run on Plus credits: the same text, images and note that feature would otherwise send to Gemini.
- Cloudflare Pages — serves this site and the web client, runs the first-party tour counter described above, and serves the two on-device models to a signed-in app. Ordinary web-server logs, and no analytics script from anyone else.
- Google Fonts — this page and the front page load two typefaces from Google's font CDN, which means your browser makes a request there and Google sees the IP address it came from. That is the only third-party request either page makes.
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.