Security & data protection

Where your data lives, what protects it, and how you get it back out.

Specific to the point of naming algorithms. Everything below describes what the running system actually does — where a limit exists it is stated rather than left for you to discover.

Where it runs

HostingDedicated hardware in the EU, operated by us. Not a managed cloud database, not a hosted vector service.
StoreOne PostgreSQL 17 database with pgvector. Your messages, your facts and your embeddings are rows in it — there is no second system holding a copy of your data.
EgressModel calls, in two directions: chat calls go to the provider you configure, on your own key. Extraction — turning messages into facts — calls OpenRouter on our account, which is why it is on the subprocessor list below. Plus the Slack connection if you choose to connect one. Nothing else leaves the machine.
Fonts, analyticsNone loaded from anyone. No third-party scripts, no analytics, no tag manager. Typefaces are served from this box, so visiting this page does not tell a US company you did.

What is encrypted

Message bodiesAES-256-GCM Every stored message and the provider's complete original event, each with its own 96-bit random nonce.
Per-person keysAES-256 Each person in your workspace has their own data key. It is stored wrapped under a master key, never in the clear.
Master keyoutside the database Delivered to the service as a systemd credential, never a row and never in a dump. A stolen database decrypts to nothing.
Backupsage / X25519 Encrypted to a recipient whose private key is not on the machine. The backup script refuses to finish if any file was left in the clear.
Provider keysAES-256-GCM Your model key and Slack token are encrypted under the master key before they touch the database.

The master key is generated once and never regenerated. If it is absent the service refuses to start rather than minting a new one — a fresh key would encrypt new rows under something no backup contains while silently orphaning every existing row. That failure has been reproduced deliberately and is now a hard error.

Isolation between customers

Enforced by PostgreSQL row-level security, not by application code remembering to add a WHERE clause. Every table that holds tenant data carries a policy keyed on the tenant, and a query issued without a tenant context returns nothing rather than everything — the failure mode is empty, not leaked. Four tables are deliberately outside it — the ones that have to be read before anyone knows which tenant is asking, such as the API key being presented — and there the protection is that the application role holds no access to them at all.

This is tested, and the test earned its place: an early version applied RLS to the parent tables but not to their partitions, so a cross-tenant read was possible through the partition directly. It was found by a test written to prove the opposite, and fixed: a policy now attaches to every partition and an event trigger extends it to partitions created later. A dedicated script verifies the coverage; it runs on demand rather than in the every-run suite, and we would rather say that than round it up.

Physical separation — your own database rather than a row in a shared one — is available as a separately-operated deployment: ask us. PostgreSQL cannot query across databases at all, which is what makes that isolation absolute at the query layer.

Deletion you can prove

Erasing a person destroys their key. The ciphertext may remain in a backup taken last week — and it is permanently unreadable there too, which is the part a DELETE statement cannot honestly promise. The audit record of the erasure survives, so you can show it happened.

GET /v1/subjectsEveryone the system holds data about.
GET /v1/subject/{id}/exportEverything held about one person, in full, as data rather than a support ticket.
DELETE /v1/subject/{id}Crypto-shredded erasure. Requires an explicit confirmation parameter.

Ordinary corrections are not deletions: when a fact changes, the old one is retired with a pointer to what replaced it and stays visible as history. Erasure is the separate, irreversible path.

How your key is handled

An API key is stored only as a SHA-256 hash. We cannot show you a key you have lost, because we do not have it — only revoke it and mint another. Keys can be scoped, so a key that only needs to read cannot write or spend.

The dashboard used to ask for your key on every visit and keep it in browser storage in between. It no longer does. You hand it over once and it is exchanged for a session cookie marked HttpOnly, which means scripts running on the page cannot read it — including any that should not be there. That session inherits the key's scopes exactly, and revoking the key ends every session made from it on the next request rather than at the next expiry.

Being precise, because this is the kind of sentence products usually blur: that session is not an account. It proves someone held a working key at one moment. There are no per-person customer logins yet, so anyone holding a key can sign in, and every change is attributed by the name the person types rather than by an identity we verified.

Who else touches your data

Your model providerOnly what a call sends. You bring your own key, so the relationship and the data-processing terms are directly between you and them.
OpenRouterRoutes model calls, including the extraction we run at our own cost.
SlackOnly if you connect a workspace. Socket Mode, so there is no public webhook endpoint to attack.
CloudflareDNS and the tunnel that puts this site online. The box has no inbound ports open to the internet.
StripeSubscription billing. Card details go to Stripe, never to us.
TailscaleHow the operator reaches the host machine for maintenance. It is not in your request path — your traffic reaches us through Cloudflare, and Tailscale is installed on the host only, not on the container that holds your data. It coordinates the connection and holds device names and public keys; the connection itself is end-to-end encrypted and it cannot read what crosses it, including on the relays it falls back to. Listed anyway, because it is a route into the machine your data sits on, and a page that lists who touches your data should not require you to notice an omission.

What this does not protect against

Encryption at rest defends against a stolen disk, a leaked dump and a restored backup. It does not defend against someone with root on the running host, because the running service must be able to decrypt in order to work. Anyone who tells you otherwise is selling you the same architecture with better adjectives.

Also true today, and being worked on rather than hidden: this is a single node with no high-availability pair, and backups are encrypted but still held on the same machine. If that matters to your procurement, ask us where it stands before you buy rather than after.