Security and privacy
Where your data lives, who can do what and how that is checked, what is encrypted, how you sign in and what that protects, which addresses we refuse to fetch, what we log, and what the assistant can and cannot see.
Your data is yours, and it is separated
Every row that belongs to a workspace carries its workspace id, and every query the product makes is scoped by it. That is not a convention we try to follow -- data access goes through helpers that take the workspace as an argument, so a query that forgets it does not compile. There is a test that builds two workspaces and fails if either can reach the other's rows.
Who can do what, and how that is checked
Everyone you invite has a role -- owner, admin, member or viewer -- and Workspaces and your account says what each one may do. What belongs here is the part that is easy to get wrong and impossible to check from outside: an interface that hides something is not a check. A disabled button, a tab that is not drawn, a menu item missing from a sidebar -- that is convenience. Underneath, every action the product takes is an address that can be reached directly, whatever was on the screen at the time.
So each one reaches an authorisation decision on our servers before it does anything, and each one that changes something writes who did it to the audit log. Neither rule is left to discipline, because rules like those decay one commit at a time: two tests read our own source code and fail if a server action or an endpoint can be reached without an authorisation decision, or if something that changes your data does not record who did it. They read the code rather than run it, so the rule reaches work nobody has written yet.
The endpoints that are deliberately open -- a widget answering a visitor, a webhook a service posts to -- are named in the first of those tests with the reason written beside each one, so "is this one meant to be public?" is answered in one place rather than re-decided by whoever reads the file next.
Credentials are encrypted and not readable
Every secret you give us -- a WordPress application password, a Webflow token, a CRM key, your own Anthropic key -- is encrypted with AES-256-GCM before it is stored, and it is never sent back to the browser. Not to you either. To change one you replace it; there is nothing to read.
API keys we issue work the other way round: we store only a hash, so the key itself is shown once at creation and never again. Revoking one takes the hash with it, so a revoked key cannot be verified at all rather than being refused by a check.
Some connections have no secret at all. Where a service supports OAuth -- Webflow, for one -- you press Connect, approve the access on their site, and we receive a key scoped to what you approved. We never see that service's password, and you can withdraw the access from their side without touching anything here.
How you sign in, and what that protects
Four ways: a password, a passkey, a link emailed to you, and Google. Workspaces and your account covers each one; two things belong here because they are security properties rather than conveniences.
A passkey cannot be phished. It is bound to this domain, so a copy of our sign-in page on an address that merely resembles ours has nothing to ask for -- the browser will not offer a key that does not belong there. Add one in Profile; your password keeps working alongside it, because a passkey that is your only credential turns a lost phone into a lost account.
Changing or resetting a password ends every other session. Not just the next sign-in: the sessions already open elsewhere are revoked at the same moment. If you changed it because you thought somebody else was inside, that is the part that does the work.
New accounts verify their address before they can be used. It is one link in one mailbox, and it is what stops somebody registering an address that is not theirs and still being signed in when the real owner arrives and recovers it.
Losing every device that holds a passkey is the one case with no self-service answer. Recovering an account is reviewed by a person against what the account actually is -- the plan, the invoices, the workspaces -- because an automatic way to remove somebody's strongest credential is a way in. The request never reveals whether an address has an account here.
Addresses we refuse to fetch
Several features fetch a URL you give us: the web crawler, adding a document by URL, verifying that links still resolve, and sending a webhook. All of them go through one rule -- the machine at the other end has to be a public one -- and the rule is applied to the address the connection actually reaches, not to the one that was typed.
That distinction is the whole of it. A hostname is checked, then resolved, and the resolved address is pinned to the connection, so a name that answers differently the second time cannot be used to reach somewhere else. Redirects are followed by hand and re-checked at every hop, because an address that passes the check and then answers "go here instead" would otherwise walk straight past it.
In practice: private and internal addresses are refused, and so is a public address that redirects to one. If you are crawling something on your own network it will be refused, and that is not a bug -- our servers are not on your network, so they could not have read it anyway.
What the assistant can see
An assistant answers from the knowledge base it is attached to and nothing else. It cannot read your other knowledge bases, your conversations, your leads, or anything about your workspace. If a passage is not in the content you gave it, it is not in the answer.
The widget also sends a small amount of context about the page it is on -- the URL, the title, the headings -- so the answer can be relevant to what the visitor is reading. You control this: page excerpts, selected text and headings can each be switched off per widget, and personal data in the page can be redacted before it is sent.
Which sites may ask is a separate question, and the answer is yours: a widget only answers on the domains you list, so lifting the snippet onto another site does not bring your knowledge base with it. Widgets and installation covers how to set them.
What we log, and what we do not
We record what an assistant was asked and what it answered, because that is the conversation you came to read. We record who changed what in your workspace, in the audit log.
We do not store visitor IP addresses. Where an address is needed -- for rate limiting, or so an audit entry can be tied to a location -- we store a hash of it. You can still filter the audit log by an address, because the filter hashes what you type before it compares, but the log itself is not a record of where anyone works from.
Bug reports capture the page and recent activity to make a report useful, and everything that looks like a credential is removed before the report is stored -- by name and by shape, so a token in a field nobody thought to exclude is caught anyway.
Files
Uploaded files live in object storage, never in the database, under a key prefixed with your workspace id. Deleting a document removes the file. Deleting a workspace removes every file it ever uploaded, not only its rows -- the objects go first, so a storage failure cannot leave them orphaned.
Retention
A deleted workspace is recoverable for 180 days and then permanently purged. A deleted document, chunk or conversation is removed immediately. Analytics are kept for the window your plan includes.
Sub-processors
Model inference goes to Anthropic and OpenAI. Payments go to Lemon Squeezy, who are the merchant of record. Email goes to Resend. On the Bring Your Own plan the model inference goes to your own provider accounts instead, under your agreement with them.