Who is responsible
Tellwor is run by Mykola Semeniuk, individual entrepreneur (ФОП Семенюк Микола Миколайович), registered in Ukraine under 3802604951, at vul. Olhy Kobylianskoi 7, Kamianka, Chernivtsi district, Chernivtsi oblast, 60427, Ukraine. That is the data controller for everything described here, and the party you have any argument with.
Write to mykola@tellwor.app. A person reads it. There is no data protection officer — we are not required to appoint one and have not appointed one.
The short version
- We ask Google who you are. We never see a password, so we never store one.
- Everything you capture — the text, the notes, the voice memos, the files, the scripts — is stored so that we can show it back to you. That is the only thing we do with it.
- No AI touches any of it. Nothing is sent to a model, nothing is transcribed, nothing is generated, nothing is embedded. Search is a Postgres full-text index over your own words.
- No analytics, no tracking pixels, no advertising, no profiling, no data sold to anyone.
- Four companies are involved: Google for sign-in, Paddle for payment, Cloudflare for media storage, Hetzner for the server. That is the entire list.
- Taking your data out is free, on every plan, in a format that is not ours.
- Nothing you captured is ever deleted for not paying.
This website
The page you are reading is a static file. It sets no cookies, writes nothing into your browser, and asks nothing of any other company — no fonts, no analytics, no tag manager, no embeds. There is nothing here to consent to, which is why you were not asked.
The home page runs one script of its own, served from this domain: it is what lets you pick up the cards in the header and move them, and what plays the drawings as you scroll. It watches the pointer and the scroll position in order to draw, and it reports none of it anywhere — there is nothing on the other end to report to. Turn JavaScript off and the same page renders, still and complete. Ask your system for reduced motion and it does not run at all.
Reading it tells us what any web server is told: your IP address, the page you asked for, your browser's user agent, and the time. https://tellwor.app is served by Hetzner, on the same machine as the app, and that host keeps its own access log — 30 days.
The app at https://app.tellwor.app is a different matter and is what the rest of this document is about.
What the product stores
Signing in
Sign-in is Google and nothing else. When you sign in, Google gives us four things and we keep them: your Google subject id (a stable opaque number that identifies the account even if you change your address), your email address in lower case, your display name, and the URL of your Google profile picture. We ask Google for exactly the scopes openid email profile and for nothing else, and no other Google service is called anywhere in the product.
Two honest notes. Google also tells us whether your address is verified; we check that and do not store it. And the profile picture URL is stored and handed back to your own browser, but nothing in the app currently draws it — it is held for an avatar that does not exist yet. If that has not changed by the next revision of this page, it should stop being stored.
Your IP address when you sign up
The IP address you first created the account from is stored on your account row. It has one use: refusing more than a few new accounts from the same address in twenty-four hours. It is never shown to anyone, and it is not used to work out where you are.
It is kept for the life of the account even though the check never looks back further than a day. That is longer than the purpose needs. It is written down here rather than quietly left out.
Sessions and cookies
Signing in writes one row and sets one cookie. The cookie, cm_session, is thirty-two random bytes and carries no information about you at all: it is a pointer, and the database row is the truth. It is httpOnly, SameSite=Lax, marked Secure in production, and expires thirty days after you sign in.
The row holds a SHA-256 hash of that token — never the token itself, so a copy of the table is not a set of working logins — along with your account id, the times it was created, last used and expires, the IP address you signed in from, and your browser's user agent string.
The IP and the user agent are recorded and then never read: there is no session list, no "you are signed in on three devices" screen, nothing in the product that looks at them. They are collected without a live purpose, which is the hardest kind of collection to justify, and the honest fix is to stop writing them or to build the screen.
One more cookie exists for ten minutes during sign-in: cm_oauth_state, twenty-four random bytes, deleted when Google hands you back. It stops somebody else starting a sign-in in your browser. There are no other cookies, and none of them is used for analytics, because there are no analytics.
Everything you capture
This is the product, so it is stored in full: your text, the note you wrote about why you saved something, links, the original filename, the file type, its size in bytes, its dimensions, how long a clip runs, where the card sits on the canvas, whether it is pinned, which group it belongs to, and when it was created. A search index is built by the database from your note, your text, the filename and the URL — Postgres full-text, over your own words, on our own server. No model, no embedding, no outside call.
A voice note is stored as the audio you recorded, next to its measured duration. The duration is read out of the file's own header. The audio is never transcribed— not by us, not by anybody, and nothing in the product could.
Stories are stored as a title, a one-line direction, and the whole script as a structured document. Around them sit the placements — which memory is anchored to which section, block or phrase — the links you draw between objects, and the names of your groups. If you used folder import, those group names came from the folder names on your own disk, and folder names from a personal machine can carry more than you meant them to.
Media
Uploaded files are kept byte for byte as they arrived, plus two things generated from them: a 640-pixel WebP thumbnail, and a still frame for a video. The canvas renders the thumbnails; the original is only fetched when you ask for it.
Because the original is never rewritten, whatever metadata your file already carried is still in it — EXIF, which on a phone photo usually means the camera, the timestamp, and the GPS coordinates of where it was taken. Only the generated thumbnail is re-encoded, and re-encoding is what strips that. So the location data is in our storage and in every export you download. We do not read it and nothing in the product looks at it, but it is there, and you should know that before you drag in a folder of holiday photographs.
Your plan
Your plan, its status, when the current period ends, and the two ids Paddle uses for you. Those live in our own database rather than being asked of Paddle on every request, so your workspace keeps working when a payment provider has a bad afternoon. They are written only by Paddle's signed webhook. We also store how many bytes of media you are using and what your ceiling is.
Keys and share links
An API key — the tw_ kind, for MCP or for capturing from your phone — is stored as a name you chose, a SHA-256 hash of the key, its first nine characters in clear so you can tell two apart, and the times it was made, last used and revoked. The key itself is shown once and is not recoverable.
A share link is a 32-byte random token, a mode, and the times it was made and revoked. What that link does is in Three things worth knowing, below.
In your browser, and nowhere else
The app remembers your theme, your interface language, where you had scrolled in each video and which zoom level you were at. All of that is in your browser's local storage and none of it is sent to the server.
Logs and backups
The server writes an ordinary web access log: client IP, method, the path asked for, the status, and headers with cookies and authorization redacted. One detail matters — a request for one of your files has the file's path in it, so the log line contains your account id and the original filename.
The API also prints its own operational log, mostly billing events with opaque ids in them. In two places that log can contain an email address: when a payment event arrives for an address that matches no account, and when a call to Paddle fails and the reply is printed. Neither is deliberate and both should be narrowed to an id.
The deployment carries a nightly database dump: the whole database, gzipped, kept on the server and copied to the same Cloudflare bucket the media lives in. That dump is everything — every account row, every session row with its IP and user agent, every note, every script.
Whether it is actually running is a nightly dump at 03:17 UTC, kept on the server; an off-machine copy is pending a private bucket. The script and the timer are both in the repository, but the timer is armed by hand on the live machine and the installer stops short of arming it, because it has no bucket credentials. That is a fact about a server rather than about code, so it is written as one instead of being asserted in the present tense.
Media files are not in that dump. They live in the Cloudflare bucket and we copy them nowhere else, so what stands behind them is whatever durability Cloudflare gives a bucket — we have not configured object versioning or a lifecycle rule, and a file deleted from the bucket is gone. We are not going to put a redundancy figure here that we cannot stand behind.
Why we are allowed to
If the GDPR applies to you, these are the grounds we rely on.
- Performing our contract
- Your account, your sessions, everything you capture, your media, your plan state, and sending your email address and name to Paddle so it can bill you. Without these there is no service to give you. Article 6(1)(b).
- Legitimate interests
- The sign-up IP check, the rate limits, the access and operational logs, and the nightly backup. The interest is keeping the service standing up and recoverable; the data is minimal and none of it is used to build a picture of you. Article 6(1)(f). You can object — see below.
- Legal obligation
- Paddle is the seller of record, so the invoice and tax records the law requires are Paddle's to keep, not ours. Article 6(1)(c), and mostly not ours to rely on.
- Consent
- Not relied on for anything. There is no analytics, no marketing mail and no optional processing, so there is nothing to ask you for and nothing to withdraw.
We do not ask you for special-category data and the product has no field for any. You may of course capture something that falls into one — a photograph of a protest, a note about your health — and if you do, it is stored the same way everything else is, at your direction, under the contract.
Who else it reaches
- Sign-in. Exchanging the code and asking who you are means Google necessarily learns that this account signs in to Tellwor, and when. We send Google nothing but our own client credentials and the sign-in code, and we ask for nothing beyond
openid email profile. No other Google service is used. - Paddle
- Payment, as Merchant of Record. Paddle receives your email address and display name to create its customer record, the price you are buying, and our own account id attached to the transaction. Nothing about your memories, scripts or media is ever sent, and no card field exists anywhere in our code — checkout is Paddle's own page. What comes back is a signed message saying what you bought; the addresses, tax rates and invoice lines it also sends are deliberately not copied into our database.
- Cloudflare
- Object storage (R2), in region {{R2_REGION}}. Every original, every thumbnail, every voice note, and the nightly database dump. The bucket holding them is private: it has no public address, and nothing in it can be read without our server first deciding that you may. Once it has, the file itself is fetched from Cloudflare rather than through us, so Cloudflare sees every media fetch — and the link it hands out stops working within the hour.
- Hetzner
- The server. One machine, in one datacentre, at Helsinki, Finland. It runs the API, the database that holds every row described above, and the logs. Everything not in Cloudflare is on that disk.
- Let's Encrypt
- The HTTPS certificate. It learns a hostname. No personal data.
- Nobody else
- There is no analytics service, no error reporter, no tag manager, no CDN script and no font host anywhere in the product. The server itself reaches exactly three places: Google, to ask who you are; Paddle, to ask about a subscription; and the Cloudflare bucket, which it signs a request to on every upload, every thumbnail it makes, every export it packs and every delete. It never fetches a URL you saved, so saving a link tells the linked site nothing at all — and nothing in the product could give a saved link a title or a picture, because nobody ever looks at the page.
Google, Paddle and Cloudflare are global companies and will process some of this outside the country you live in, under their own transfer terms. The server itself, and therefore the database, is in Helsinki, Finland. Ukraine, where we are, is not in the European Economic Area.
How long it is kept
Some of these are longer than we would like. They are what the code actually does today, which is the only thing worth writing down.
- Your account row
- Until you delete it. There is a button — Settings, Your data,Delete everything — and it removes the row, the memories and the files as you wait. Nothing expires on its own. See Your rights.
- Session rows
- The cookie and the session expire thirty days after sign-in, and signing out deletes the row at once. But expired rows are not currently swept, so a row that stopped working — with its IP address and user agent — stays in the database. We are not going to write "thirty days" here while that is true.
- The session cookie
- Thirty days, or until you sign out.
- The sign-in cookie
- Ten minutes, then deleted.
- API keys
- Until you revoke one. A revoked key keeps its row on purpose, so that a key which stopped working can still be explained to you.
- Share links
- Until you revoke one. They do not expire on their own and the row is never deleted.
- What you captured
- Until you delete it. Nothing is ever deleted for age, for inactivity or for non-payment.
- Media files
- Deleted together with the object they belong to — original, thumbnail and voice note in one go. Otherwise kept.
- Rate-limit counters
- Minutes at most, in memory only, never written to disk, and forgotten entirely when the server restarts.
- Web access log
- Thirty days, and at most about a hundred megabytes.
- API operational log
- 14 days. Nothing in our deployment configures a retention period for it, which is a gap and not a policy.
- Nightly database dumps
- Seven nights on the server, on the nights they run — see Logs and backups, which is honest about whether the timer is armed. The copies in Cloudflare are not pruned by anything we run — {{R2_BACKUP_LIFECYCLE}}.
- Local storage in your browser
- Until you clear it. It never reaches us.
Where a dump was taken before you deleted something, that copy of it is inside the dump and goes when the dump does.
Three things worth knowing
A share link is the whole key
A workspace link hands one video — its title, its direction, its full script, and every memory placed in it — to anyone who has the URL. No sign-in, no account, no name. It is the only unauthenticated door in the product and it exists so you can hand a video to an editor without giving them your memory.
It does not expire. We do not record who opened it. If it is forwarded, it works for whoever it was forwarded to, forever, until you revoke it in the app. This is the one way private material leaves your account by design, and the decision is entirely yours.
Media is protected by its address, not by a login
Files are served from a public address with an unguessable path — your account id, a random object id, and the file's own name. Anyone holding that URL can fetch the file whether or not they have an account, and the URL has no expiry. Two links you send to different people can be seen to have come from the same account, and the filename travels inside the URL, which is also what puts it in the access log.
This is deliberate: share links have to work, and a canvas showing fifty images cannot sign fifty URLs first. But it means we cannot honestly describe your media as access controlled, so we are not going to.
The backups sit in the same bucket as the media
The nightly database dump is written into the same Cloudflare bucket the media is served from, under a separate backup/ prefix. That prefix is not linked from anywhere and its filenames are not published. It is not separately access controlled: whether it can be reached the way a media file can depends on the bucket's public-access configuration, which is the whole media bucket is public; database dumps are never written to it.
Your rights
If the GDPR applies to you, you have the rights below. If it does not, ask anyway — we do not intend to run two standards.
Getting a copy, and taking it elsewhere
Free on every plan, and never gated. The export is one zip: every row your account owns as a single JSON file, and every file you uploaded as a file. It is your material in the shape it was stored, with no interpretation and no ordering of ours — which is what makes it portable. Nothing about it checks what you pay, deliberately: someone whose plan just lapsed is already unable to add anything, and being unable to leave as well would make the memory a hostage.
It is in Settings, under Your data — Download everything. The same address answers directly if you would rather have it that way:https://app.tellwor.app/api/account/export.zip, open to any signed-in account. If anything about it goes wrong, write to mykola@tellwor.app and we will send you the file.
Pro's script export is a different product and is not this. It sells shape — a script laid out for a prompter, a brief for an editor, a CSV tying each word to the memory anchored on it. Your own data, unshaped, is free whether you pay us or not.
Correcting something
Your name and picture come from Google and refresh when you sign in, so fix them there. Everything else you can edit in the app. Anything you cannot, write to us.
Deleting your account
It is in Settings, under Your data — Delete everything. Two presses, and the second one says exactly what it does. Nothing is queued and nothing is flagged: your account row, every memory, every video, every key and every file you uploaded are removed as you wait, and the tab you did it in is signed out because there is nothing left to sign in to.
A paid plan is cancelled as part of it, immediately, before anything is deleted. If that cancellation fails — our payment provider having a bad day — the deletion stops and tells you, rather than leaving you with a card still being charged for an account you cannot reach. Download your archive first if you want to keep any of it; the two controls sit next to each other for that reason.
mykola@tellwor.app still works, and always will, for anyone who cannot reach the button — a blocked account, a lost sign-in. We will finish within thirty days, usually much sooner, and confirm when it is done.
Blocking an account is a separate thing and is not deletion: it removes nothing at all. But it ends your sessions, and the address above is behind a session like everything else, so a blocked account cannot fetch its own zip. If you are blocked and want your data, the contact address is the route and we will produce it for you.
The rest
- Restriction and objection. You can ask us to stop relying on legitimate interests for the sign-up IP, the logs or the backups. Tell us and we will answer with what we can actually do rather than a form letter.
- No automated decisions. Nothing here profiles you, scores you or decides anything about you automatically. The sign-up rate limit counts addresses in a twenty-four-hour window and is not a judgement about a person.
- Complaining. You can complain to the data protection authority in the country you live in. We would rather you wrote to us first, but you do not have to.
We do not charge for any of this, and we will not ask you to prove who you are beyond signing in from the account in question.
Security
What is actually in place:
- Everything travels over HTTPS.
- No password exists to be stolen — sign-in is Google's, and the session cookie is
httpOnlyso a script cannot read it. - Session tokens and API keys are stored as SHA-256 hashes. A copy of those tables is not a set of working credentials.
- Every payment message from Paddle is checked against a signature over the raw body with a five-minute window before anything is believed.
- Every request for your data is scoped to your account on the server, not in the browser. A missing video and another person's video return the same answer, so nothing leaks by being denied differently.
What is not in place is in Three things worth knowing. We are one small operation running one server; we have no certifications and we are not going to imply any.
If you find something wrong, write to mykola@tellwor.app. We would much rather hear it from you.
Changes and contact
When this document changes, the date at the top changes with it. There is no mailing list and the product sends no email, so this page is where a change is announced. If a change is significant, the previous version's date will still tell you which one you agreed to.
Questions, requests, corrections, or the thing you noticed that this page gets wrong: mykola@tellwor.app, or by post to Mykola Semeniuk, individual entrepreneur (ФОП Семенюк Микола Миколайович), vul. Olhy Kobylianskoi 7, Kamianka, Chernivtsi district, Chernivtsi oblast, 60427, Ukraine.