EU made, and where your data lives
Where Nessie is built, where your data lives, and what you control when you host it yourself.
Who builds it, and under what law
Nessie is built by UnlikeOtherAI s.r.o., a company registered in the Czech Republic and operating under Czech law.
This page is a description of how the software is built and where it runs. It is not a compliance statement: Nessie holds no security certification, and nothing here should be read as one. What personal data is processed and on what basis is the subject of the privacy policy, which is a separate page.
Where the hosted product runs
Nessie Cloud runs on European hosting providers — Scaleway, Hetzner and others. We pick the best service each provider offers, so different parts of the product may run with different providers, all of them in Europe.
Anything more precise than that — a specific site, city or datacentre — is deliberately not stated here, because it would go out of date without anyone noticing.
Self-hosting puts the choice in your hands
Where a self-hosted instance runs is a deployment setting, not a product setting. The single-machine path is a Docker Compose stack you place on a machine you choose. In the Terraform tree for a managed cloud, the region is an input with no default at all: nothing there can be guessed, so nothing there has a default, and the database, the object-storage bucket and the services all follow the region you name.
Two structural details matter for the same question. The queue and the realtime transport are both PostgreSQL — there is no Redis and no message broker in the path, so there is no third system to place. And object storage is S3-compatible rather than tied to one vendor; the reference deployment runs MinIO in a container on a private network with no ingress of its own. The parts that have to live somewhere are the database, the object store and the two Node services, and they all live wherever you put them. How to stand that up is on the installation page.
What Nessie holds — and what it deliberately does not
Identity is not copied
With UnlikeOtherAI sign-in, the identity provider manages and stores human identity — authentication factors, profiles, organisation and team membership, and invitations. Nessie is a relying party, not a second identity system. This is a data decision as much as an architectural one: an identity Nessie never stores is one it cannot leak, lose or fail to delete.
What the users table actually keeps is the provider’s stable user identifier — the key everything else resolves through — the email address, a non-authoritative display-name and avatar mirror, and product-local fields (preferences, pronouns, a token version used to revoke sessions). The mirror exists only so a name and a picture can be rendered without a request per row, and it is re-synced from the provider’s verified claims at login, at team switch and at session refresh. Nothing manufactures a value: a person the provider has not named carries their email address until it does. A password hash exists only for the bootstrap owner of an install with no identity provider at all.
The same holds for structure. One provider organisation is one Nessie organisation, bound by the provider’s organisation id; a team with no upstream link answers 404 TEAM_NOT_LINKED rather than falling back to a local guess, and a live roster read goes to the identity provider and persists nothing.
Two mirrors have not gone yet, and it would be dishonest to imply otherwise. Organisation and team names are still stored locally alongside the binding ids, and local membership rows still authorise ordinary requests between synchronisations. Removing both is tracked, open work in the repository rather than something already finished.
Nothing commercial
The central billing service manages payments. Nessie stores no payment-provider customer, subscription, invoice, price, credit balance, top-up policy, payment consent, statement or cancellation state, and it does not calculate tariffs. It reads display-ready values and shows them.
Secrets
A configured vault is required to store a secret at all. There is deliberately no database fallback and no plaintext path: without one, saving, rotating and revoking a secret return an error rather than degrading to something less safe. Nessie’s own records for a secret hold no value, no ciphertext, no key and no vault token — only an opaque reference and its metadata — and vault paths are built from structural identifiers only, never from an organisation, team, project or person’s name.
Credentials Nessie must hold itself to act as an OAuth client — connector and mailbox credentials, webhook secrets, session refresh material, executor payloads — are sealed in purpose-bound AES-256-GCM envelopes under a versioned key ring that is a separate root from the session signing secret, with a documented rotation procedure.
There is also a rule about models: a secret may be known to exist and may be authorised for use, but secret material must not enter model context, prompts, tool arguments, tool results, memory, embeddings, search, logs or messages. An agent can be granted the ability to use a secret. It can never be granted the ability to reveal one.
Whether the underlying disks are encrypted is a property of the machine a deployment runs on. The repository does not establish volume-level encryption for the database and object-storage volumes, so this page does not claim it.
Tenant isolation
Nessie is multi-tenant by design: one database, in which every row belongs to exactly one organisation and every child table carries an organization_id. Isolation is enforced in two places — the scope clause every query carries, and real foreign keys on that column, which were added across forty-two constraints after an internal audit found the column present but unconstrained on dozens of tables.
A conformance suite holds that invariant in place at the HTTP boundary. It seeds a maximally privileged attacker — a full owner of their own organisation — and asserts that every canonical read of another organisation’s resources answers 403, 404 or empty, and every canonical mutation is rejected with the other organisation’s row untouched. It covers channels, approvals, the audit log, triggers, agents and their bindings, runs, projects, tasks, threads, connections, workflow installations, MCP instances, the knowledge base and alerts.
Being precise about the mechanism: this is application-level enforcement plus foreign keys, not PostgreSQL row-level security. There is no RLS policy and no database-side backstop, and the conformance suite runs against a faithful in-memory store rather than a live database, so it proves the scope clauses the code writes rather than the SQL a server executes. The repository says so in the suite’s own documentation, and so does this page.
The audit trail
Every consequential action is recorded: who acted and as what — a person, an agent, a service — what they did, to which resource, in which organisation, project, team or channel, whether it succeeded or was denied, why, and the request id, address and user agent it arrived with.
The log is append-only by design and there is no way to write to it from outside: POST /api/audit-log does not exist, and that is deliberate. Entries are chained — each one carries a SHA-256 digest of its own canonical fields plus the digest of the previous entry for that organisation — so a modified or removed row breaks the chain and is detectable. An organisation owner or admin can walk that chain on demand and is told where it first broke, if it did.
Two honest qualifications. The chain makes the log tamper-evident, not immune to tampering: there is no database-level write revocation or write-once storage behind it, so the guarantee is detection rather than prevention. And entries are retained indefinitely by default — configurable retention with export-before-delete is designed, not built. Entries are also barred from carrying secrets or full request bodies, and a fixed set of sensitive metadata field names is redacted on the way in.
The cost ledger
Every model invocation is recorded against the organisation, and against the user, team, project, channel, agent and run it came from, with the tokens it consumed and both the provider-reported cost and Nessie’s own estimate kept side by side. A metric a provider does not report is stored as null rather than guessed. Reports are readable by an organisation owner or admin.
This ledger is operational telemetry — what an organisation spent and on whose behalf. It is not an invoice and not a billing authority.
Third parties, when you use the hosted product
A hosted deployment talks to a small, fixed set of services. Model traffic and web search do not go to providers directly: both leave through one gateway operated by UnlikeOtherAI, which is where the upstream model and search providers sit. Sign-in and billing go to UnlikeOtherAI’s authenticator. The secret vault is self-hosted alongside the application. Server-minted video-call links default to the public Jitsi service.
Almost everything else is off until an operator configures it, and says so rather than failing quietly: connected Google, Microsoft and Slack accounts; agent email over Amazon SES, which a deployment runs in its own account with no intermediary; mobile and browser push; research and board-source connectors; cloud browsers. Two details are worth stating because they change where data goes:
- Connected mailboxes stay with your provider. Mail is read live from your provider, over IMAP and SMTP or the provider’s own sign-in; Nessie stores the sealed credential and nothing else about the account. A mailbox you connect to the Clients inbox is the exception: you are asking for its conversations to be kept with each customer, so they are copied in.
- Voice calls in the app do not pass through Nessie. The API brokers a credential and the client opens the audio connection itself. Dictation and telephone calls are different: their audio goes through Nessie to the speech and model providers that process it.
One inference detail follows the same principle: signed caller-identity headers are only sent to the configured chat host, so pointing embeddings at a different endpoint never hands that endpoint a delegation assertion about the person whose text is being embedded.
The hosted product uses several inference providers and picks between them for cost and quality. An organisation will be able to choose in Admin which providers its agents may use: Western only, European only, Chinese only, or no preference — the most efficient provider for each job. A self-hosted instance chooses its own.
Running it yourself changes all of this
Every third party above is a configuration choice on a self-hosted instance. Inference can be pointed at any OpenAI-compatible endpoint, including one on your own hardware. The connectors are off unless you turn them on. UnlikeOtherAI sign-in is the supported path, and a deployment with no identity provider at all runs as a single unbound organisation with a bootstrap owner.
That is the whole point of the arrangement: the software is the same, and the decisions about where it runs and what it may reach belong to whoever runs it. The installation page covers how.