Ctrl AI
Privacy
This page describes what the code does. The code is the source of truth: where a sentence here and the repository disagree, the repository is right and this page is the bug.
The short version
- What you paste is never stored. Not to a database, a file, a log or a cache. It goes to the model that reads it and nowhere else.
- What can be stored is what you ratified, and only when you publish it. The decisions, not the prose they came from.
- We can technically read a published context and we do not. Not zero-knowledge, not end-to-end. Section five explains why that is a constraint rather than a preference.
1. What you paste
You ask your assistant for an honest audit of what it thinks it knows about you, and you paste its answer here. That text is posted to our server over HTTPS, held in memory for the length of the request, and sent to the model that reads it.
It is not written to a database, a file, a log or a cache. Our logs carry durations, token counts, model ids and outcomes. When something throws, only the error’s type is recorded and a fixed sentence is returned, because an error’s own message can quote the text that caused it.
Total pasted text is capped at 400KB, enforced in your browser and again on the server.
What the reading produces — your score, the sections, the cards you decide on — lives in the browser tab and nowhere else. It is not written to local storage or a cookie. Close the tab without publishing and nothing about you exists on our side.
2. Where the pasted text goes
One model, and only one: deepseek/deepseek-v4-flash-0731. There is no tier to choose. It is reached through OpenRouter, which routes the request to a provider serving that model. Other than the host our own code runs on, which every request passes through by definition, that is the only place your pasted text goes.
Zero data retention has to be requested for the OpenRouter account whose key is deployed, and the routing policy for that specific model checked, because a provider sitting behind an aggregator can have its own retention terms. The code cannot tell you whether that has been done. [TO CONFIRM: whether zero data retention is enabled on the deployed OpenRouter key, and what the routing policy for deepseek/deepseek-v4-flash-0731 says about the provider actually serving it]
3. What is stored, and only when you publish
Nothing is stored until you press publish. Publishing writes one row: your ratified decisions, encrypted. Alongside the ciphertext, in the clear, sit the things a list needs to render without decrypting anything — a version name that is a hash of the ratified state, how many items it holds, how many were ratified, and when.
Every publish writes a new version rather than replacing the last one. The older rows are the trajectory: what you retired, what came back, what you said you would do. If you are signed in and publish again with nothing changed, no row is written at all.
If you publish without an account, the row is written unowned. Your typed address is stored in the clear so the claim link can be sent to it, together with a SHA-256 of a secret held only by the browser that published — enough for that browser to see what it just made, and enough for nothing else. Typing an address is not proof of holding it, so the context becomes yours only when you open the link we send.
4. Everything in the database
This is the whole list, table by table. Anything not named here is not stored.
- People. An id, your email address in the clear, and two timestamps. There is no password and no profile.
- Sign-in and claim links. The SHA-256 hash of the token only, the address it was sent to, which of the two kinds it is, and its expiry. The token itself is never stored.
- Contexts. The encrypted corpus, its version hash, item and ratified counts, timestamps — and, until it is claimed, the pending address, a claim hash and a claim expiry.
- Connector credentials. The SHA-256 hash of each token, the label you gave that AI, and when it was created, last used and revoked. A revoked row is marked rather than deleted, so old receipts can still name the connection.
- Receipts. In the clear: the time, which tool was called, the context version, how many items were disclosed and withheld, and the names of the scopes requested and denied. Sealed: the purpose the AI declared, and the items it received.
- Usage counters. A bucket name, a count, an expiry. See section eight — the bucket holds no address.
- OAuth clients. The id, the name and the redirect URLs of an application that registered itself to connect. That is the app’s data, not yours.
- OAuth codes and tokens. SHA-256 hashes only, bound to you, to one client, to one redirect and to one audience, with their scopes and expiries.
- Saved audits. A table left from the memory-export flow that has been removed. Nothing in the product writes to it any more. Rows saved under the old flow are still sealed and are still attached to an address when it is proved.
So: email addresses are stored in the clear. Every credential — sign-in links, connector tokens, OAuth codes and tokens — exists only as a SHA-256 hash, which means a database taken whole yields nothing anybody can present.
5. What the encryption promises, and what it does not
Every stored record gets its own random 256-bit key and is sealed with AES-256-GCM. That key is itself encrypted under a master key held in our environment. Steal the database and not the environment and the rows are noise. Hold both — us, on any normal Tuesday — and they can be read.
That is a constraint, not a preference. A model fetching your context holds no passphrase and runs no JavaScript, and a server that cannot decrypt cannot compile the slice it is asked for. The column that holds the sealed key on a published context is NOT NULL, so the limit sits in the schema rather than in a promise.
We can technically read a published context and we do not. Not zero-knowledge, not end-to-end. A sign-in link restores your access from an inbox, which means something recoverable has to exist on our side. Saying otherwise would be exactly the confident-and-wrong claim this product exists to hunt.
There is a second mode in the code, where the key is derived in your browser from a passphrase we never receive and we genuinely cannot read the record. It belonged to the removed memory-export flow, no screen offers it today, and it cannot apply to a context an AI is meant to fetch.
6. What a connected AI receives
Never a dump. A connected assistant asks for context for one declared task and receives a compiled projection: what you are working on now, your ranked priorities, what you have retired, how you want to be worked with, what you said it gets wrong, and which scopes it asked for and did not get.
- Only what you ratified. Candidates you never ruled on are not served. Items you marked never-serve are never served.
- Scoped by policy, not by the request. An AI may ask for a scope; it cannot grant itself one. Today one standing policy covers every connection: work, personal and family are allowed, finance and health are not, and anything above ordinary sensitivity is withheld.
- No model runs in the compiler. It is a filter and a sort over typed state, so the same corpus and the same request always select the same items. Selection that cannot be reproduced cannot be receipted.
- Whole sections are dropped to fit a budget, never truncated mid-sentence.
It does not receive your email address, your receipts, your earlier versions, the items you rejected, or anything you did not ratify.
The second tool an AI can call proposes changes. It stores nothing at all — the review queue that would hold a proposal is not built — and it says so in its own reply rather than telling the model something is waiting for you.
7. Receipts
Every fetch writes a receipt before anything is handed over. If the receipt cannot be written, nothing is disclosed and the AI is told why. A disclosure with no record would be the one thing this product cannot afford.
The structural half — time, tool, version, counts, scope names — is stored in the clear, because on its own it identifies nobody. The purpose the model declared and the list of items it received are sealed with the same key as everything else, because they are a record of what you were doing. You can read them on your settings screen.
8. Counting spend without counting people
Reading a reply costs us a fraction of a cent, so the limits exist to stop a script rather than to ration you: 20 calls per caller per hour and 5,000 a day across everybody, per task.
The counter holds no identifier. Your address is HMAC’d with our record key before it ever reaches a row, so the table says how much was spent and never who was here. The counters expire with their window and are swept. If the database cannot be reached the check fails open — the product keeps working rather than stopping to save a few dollars.
9. Cookies
We set no advertising cookies and do no cross-site profiling. The fonts are served from our own domain rather than a CDN, so the typography reaches nobody. Google Analytics is the one third party a page contacts, and it is in the list below.
- Session. Set when you redeem a sign-in link. HTTP-only, SameSite=Lax, 60 days. It carries a person id and its own expiry, authenticated by an HMAC — there is no session table to leak.
- Claims. Names the contexts this browser published but nobody has yet claimed. 30 days, the same window as the rows it points at.
- Connection flow. Two short-lived cookies while an AI is being connected, so the approval survives a sign-in in the middle of it.
- Google Analytics. Sets its own cookies, in production builds only. See the next section.
10. Who else handles it
- OpenRouter — reads the reply you paste, and routes it to the provider serving the model. The only place pasted text goes.
- Neon — the PostgreSQL database holding everything in section four.
- Resend — sends the sign-in and claim emails. Receives your address and the contents of those messages. Nothing about the message body is logged by us.
- Vercel — hosts the site and runs every request, so it handles all traffic in transit and keeps the platform’s own request logs. [TO CONFIRM: Vercel’s log retention on the plan this is deployed on]
- Google Analytics — page views only, in production builds only. No custom events, no funnel, no interaction data. It cannot see pasted text: the reply lives in a text box and a POST body, never in a URL.
Ctrl AI will never sell your context, advertise with it, or train shared models on it. That is a commitment rather than a mechanism, and it is the one line on this page you have to take on trust — which is why the rest of it is written to be checkable.
Database in AWS us-east-2 (Ohio). Hosting on Vercel's default region for the deployment. [TO CONFIRM: the transfer basis for readers in the UK and EU, given where it runs]
11. How long things are kept
- An unclaimed context is deleted 30 days after it was published. A row nobody has ever proved they own is one where an address was typed and never verified.
- A claimed context and its history are kept until you ask for them to be deleted. There is no automatic expiry.
- Sign-in and claim links expire in 30 minutes and work once.
- Usage counters expire with their hour or their day.
- OAuth — an authorization code lasts a minute, an access token an hour, a refresh token 30 days.
- Revoked credentials keep their row, marked revoked, so receipts can still say which connection fetched what.
12. Seeing it, taking it back, deleting it
Seeing it. Your settings screen shows your current context in full, every version behind it, which AIs hold a key, and every fetch they have made. That is everything stored about you that is not a hash of something you hold.
Taking it back. Revoking a connection is immediate and one AI at a time — the next fetch on that credential fails. Note what revoking cannot do: context already handed to an assistant is in that provider’s conversation and its logs, and no action here reaches it.
Deleting it. There is no self-serve delete button today, for a context or for an account. Saying otherwise would be the easiest sentence on this page to write and the first one a reader could catch us on. Ask, and we will delete the rows: [TO CONFIRM: the address people write to for deletion and access requests]
Taking a copy. The settings screen hands your whole corpus back to your own browser, along with the compiled block you can paste into any assistant by hand. There is no one-click export file.
[TO CONFIRM: how the company answers UK/EU data subject requests — the legal basis, the response window, and the supervisory authority a complaint goes to]
13. Children, and shared accounts
This is not a service for children and is not directed at them. [TO CONFIRM: the minimum age the company sets, and for which countries]
Shared accounts are handled deliberately rather than forbidden, because households share accounts and assistants rank that as their own first source of bad personalization. If other people use the account you audited, some of what your assistant believes about “you” is about them. Marking that is one of the decisions the product exists to let you make — and when you do, the instruction that reaches your assistant is to assume a different person when the writing changes, and the more cautious reader where safety is involved.
14. Changes, and who to ask
This page changes when the code changes, and the date at the top moves with it. There is no archive of earlier versions yet.
Ctrl AI, Inc. is responsible for the processing described here, registered at [TO CONFIRM: the registered address, as it appears on the incorporation record]. Privacy questions go to [TO CONFIRM: the contact address for privacy questions].