Сделано в ЕС и где хранятся ваши данные
Где создаётся Nessie, где хранятся ваши данные и что вы контролируете, если размещаете Nessie у себя.
Кто его создаёт и по какому праву
Nessie создаёт UnlikeOtherAI s.r.o. — компания, зарегистрированная в Чешской Республике и работающая по чешскому праву.
На этой странице описано, как устроено программное обеспечение и где оно работает. Это не заявление о соответствии требованиям: у Nessie нет сертификатов безопасности, и ничто здесь не следует понимать как такое заявление. Какие персональные данные обрабатываются и на каком основании — тема политики конфиденциальности, это отдельная страница.
Где работает облачная версия
Nessie Cloud работает у европейских хостинг-провайдеров — Scaleway, Hetzner и других. Мы выбираем лучший сервис каждого провайдера, поэтому разные части продукта могут работать у разных провайдеров, но все они находятся в Европе.
Более точные сведения — конкретная площадка, город или дата-центр — здесь намеренно не указаны: они устарели бы незаметно для всех.
Свой хостинг — выбор за вами
Где работает ваш собственный экземпляр — это параметр развёртывания, а не параметр продукта. Вариант для одной машины — стек Docker Compose, который вы размещаете на выбранной вами машине. В дереве Terraform для управляемого облака регион задаётся входным параметром, у которого вообще нет значения по умолчанию: ничего тут нельзя угадать, поэтому ничего и не задано заранее, а база данных, бакет объектного хранилища и сервисы следуют региону, который вы укажете.
Для того же вопроса важны ещё две особенности устройства. Очередь и транспорт реального времени — это PostgreSQL: ни Redis, ни брокера сообщений на этом пути нет, так что размещать третью систему не нужно. А объектное хранилище совместимо с S3 и не привязано к одному поставщику; эталонное развёртывание запускает MinIO в контейнере в закрытой сети без собственного входящего доступа. Всё, что должно где-то находиться, — база данных, объектное хранилище и два сервиса на Node, — находится там, куда вы их поставите. Как это развернуть, описано на странице установки.
Что Nessie хранит — и что намеренно не хранит
Идентификация не копируется
При входе через UnlikeOtherAI личность человека ведёт и хранит поставщик идентификации: факторы аутентификации, профили, членство в организациях и командах, приглашения. Nessie — доверяющая сторона, а не вторая система идентификации. Это решение и о данных, и об архитектуре: личность, которую Nessie не хранит, она не может ни потерять, ни допустить её утечку, ни забыть удалить.
В таблице users на самом деле хранятся: стабильный идентификатор пользователя у поставщика — ключ, через который разрешается всё остальное, — адрес электронной почты, неавторитетное зеркало отображаемого имени и аватара и локальные поля продукта (настройки, местоимения, версия токена для отзыва сессий). Зеркало нужно лишь для того, чтобы показывать имя и картинку без отдельного запроса на каждую строку; оно заново синхронизируется с проверенными утверждениями поставщика при входе, при переключении команды и при обновлении сессии. Значения не подставляются «из воздуха»: пока поставщик не назвал человека по имени, вместо имени показывается его адрес электронной почты. Хеш пароля существует только у начального владельца экземпляра, в котором вообще нет поставщика идентификации.
То же относится и к структуре. Одна организация у поставщика — это одна организация в Nessie, привязанная по идентификатору организации у поставщика; команда без связи с поставщиком отвечает 404 TEAM_NOT_LINKED, а не подставляет локальную догадку, и актуальный список участников запрашивается у поставщика идентификации и нигде не сохраняется.
Два зеркала пока остались, и было бы нечестно делать вид, что это не так. Названия организаций и команд по-прежнему хранятся локально рядом с идентификаторами привязки, а локальные записи о членстве по-прежнему дают права обычным запросам между синхронизациями. Удаление обоих — открытая задача в репозитории, а не уже сделанная работа.
Ничего коммерческого
Платежами управляет центральный платёжный сервис. Nessie не хранит ни клиента платёжного провайдера, ни подписку, ни счёт, ни цену, ни баланс кредитов, ни правила пополнения, ни согласие на платёж, ни выписку, ни состояние отмены и не рассчитывает тарифы. Она получает готовые к показу значения и показывает их.
Секреты
Чтобы сохранить секрет, нужно настроенное хранилище секретов. Резервного варианта в базе данных и пути с открытым текстом намеренно нет: без хранилища сохранение, ротация и отзыв секрета возвращают ошибку, а не переходят на менее безопасный способ. Собственные записи Nessie о секрете не содержат ни значения, ни шифртекста, ни ключа, ни токена хранилища — только непрозрачную ссылку и метаданные, — а пути в хранилище строятся только из структурных идентификаторов и никогда из названия организации, команды, проекта или имени человека.
Учётные данные, которые Nessie обязана хранить сама, чтобы действовать как клиент OAuth, — данные коннекторов и почтовых ящиков, секреты вебхуков, материалы для обновления сессий, данные исполнителей — запечатаны в конвертах AES-256-GCM, привязанных к назначению, под версионируемой связкой ключей. Это отдельный корень по отношению к секрету подписи сессий, и для неё описана процедура ротации.
Есть и правило для моделей: о существовании секрета можно знать, использование секрета можно разрешить, но сам секретный материал не должен попадать ни в контекст модели, ни в промпты, ни в аргументы и результаты инструментов, ни в память, ни во встраивания, ни в поиск, ни в журналы, ни в сообщения. Агенту можно дать право использовать секрет. Право раскрыть его агенту нельзя дать никогда.
Зашифрованы ли сами диски, зависит от машины, на которой работает развёртывание. Репозиторий не обеспечивает шифрование на уровне томов для базы данных и объектного хранилища, поэтому эта страница не утверждает, что оно есть.
Изоляция тенантов
Nessie изначально мультитенантная: одна база данных, в которой каждая строка принадлежит ровно одной организации, а в каждой дочерней таблице есть organization_id. Изоляция обеспечивается в двух местах — условием области, которое несёт каждый запрос, и настоящими внешними ключами по этому столбцу; их добавили в виде сорока двух ограничений после того, как внутренний аудит обнаружил, что столбец есть, но в десятках таблиц на него не наложено ограничение.
Набор проверок на соответствие удерживает этот инвариант на границе HTTP. Он создаёт максимально привилегированного злоумышленника — полноправного владельца собственной организации — и проверяет, что любое каноническое чтение ресурсов другой организации отвечает 403, 404 или пустым результатом, а любое каноническое изменение отклоняется, и строка другой организации остаётся нетронутой. Он охватывает каналы, согласования, журнал аудита, триггеры, агентов и их привязки, запуски, проекты, задачи, ветки, подключения, установки рабочих процессов, экземпляры MCP, базу знаний и оповещения.
Сформулируем механизм точно: это защита на уровне приложения плюс внешние ключи, а не защита на уровне строк PostgreSQL (row-level security). Политик RLS нет, страховки на стороне базы данных тоже нет, а набор проверок на соответствие работает на точной копии хранилища в памяти, а не на живой базе данных, поэтому он доказывает корректность условий области, которые пишет код, но не SQL, который выполняет сервер. Об этом сказано в документации самого набора проверок в репозитории — и на этой странице.
Журнал аудита
Записывается каждое значимое действие: кто действовал и в какой роли — человек, агент или сервис, — что он сделал, с каким ресурсом, в какой организации, проекте, команде или канале, удалось ли действие или было отклонено, почему, а также идентификатор запроса, адрес и user agent, с которыми оно пришло.
Журнал по замыслу доступен только для дозаписи, и записать в него что-либо извне нельзя: POST /api/audit-log не существует, и так сделано намеренно. Записи связаны в цепочку — каждая содержит дайджест SHA-256 собственных канонических полей и дайджест предыдущей записи этой организации, — поэтому изменённая или удалённая строка рвёт цепочку, и это можно обнаружить. Владелец или администратор организации может в любой момент пройти по цепочке и узнать, где она впервые прервалась, если это произошло.
Две честные оговорки. Цепочка делает журнал защищённым от скрытой подделки, но не неуязвимым для неё: за ней нет ни отзыва прав записи на уровне базы данных, ни хранилища с однократной записью, поэтому гарантируется обнаружение, а не предотвращение. И записи по умолчанию хранятся бессрочно — настраиваемое хранение с экспортом перед удалением спроектировано, но не создано. Кроме того, записям запрещено содержать секреты и тела запросов целиком, а фиксированный набор названий чувствительных полей метаданных при записи скрывается.
Журнал расходов
Каждое обращение к модели записывается на организацию и на пользователя, команду, проект, канал, агента и запуск, откуда оно пришло, вместе с израсходованными токенами; стоимость, сообщённая провайдером, и собственная оценка Nessie хранятся рядом. Показатель, который провайдер не сообщает, хранится как null, а не угадывается. Отчёты доступны владельцу или администратору организации.
Этот журнал — операционная телеметрия: сколько потратила организация и по чьему поручению. Это не счёт и не платёжный документ.
Третьи стороны при использовании облачной версии
Облачное развёртывание обращается к небольшому, фиксированному набору сервисов. Трафик моделей и веб-поиск идут не напрямую к провайдерам: оба выходят через один шлюз, которым управляет UnlikeOtherAI, — именно там находятся провайдеры моделей и поиска. Вход и оплата идут через сервис аутентификации UnlikeOtherAI. Хранилище секретов размещено на собственных мощностях рядом с приложением. Генерируемые сервером ссылки на видеозвонки по умолчанию ведут на публичный сервис Jitsi.
Почти всё остальное отключено, пока оператор это не настроит, и сообщает об этом, а не отказывает молча: подключённые аккаунты Google, Microsoft и Slack; почта агентов через Amazon SES, которую развёртывание запускает в собственном аккаунте без посредников; push-уведомления на мобильных устройствах и в браузере; коннекторы для исследований и источников досок; облачные браузеры. Две детали стоит назвать, потому что они меняют то, куда уходят данные:
- Подключённые почтовые ящики остаются у вашего провайдера. Почта читается у вашего провайдера в реальном времени — через IMAP и SMTP или через собственный вход провайдера; Nessie хранит учётные данные в запечатанном виде и больше ничего об аккаунте. Исключение — ящик, который вы подключаете ко входящим раздела «Клиенты»: вы просите хранить его переписку вместе с каждым клиентом, поэтому она копируется в Nessie.
- Голосовые звонки в приложении не проходят через Nessie. API выдаёт учётные данные, а клиент сам открывает аудиосоединение. Диктовка и телефонные звонки устроены иначе: их звук проходит через Nessie к провайдерам распознавания речи и моделей, которые его обрабатывают.
Ещё одна деталь работы с моделями следует тому же принципу: подписанные заголовки с идентификацией вызывающей стороны отправляются только настроенному хосту чата, поэтому если направить встраивания на другой адрес, этот адрес не получит утверждение о делегировании от имени человека, чей текст встраивается.
Размещённый продукт использует нескольких провайдеров инференса и выбирает между ними по стоимости и качеству. Организация сможет выбрать в Admin, каких провайдеров могут использовать её агенты: только западных, только европейских, только китайских или без предпочтений — тогда для каждой задачи берётся самый эффективный провайдер. Собственный экземпляр выбирает провайдеров сам.
Если запустить всё у себя, всё это меняется
Каждая из перечисленных выше третьих сторон — вопрос настройки в собственном экземпляре. Вывод моделей можно направить на любой OpenAI-совместимый адрес, в том числе на ваше собственное оборудование. Коннекторы выключены, пока вы их не включите. Поддерживаемый путь — вход через UnlikeOtherAI, а развёртывание вообще без поставщика идентификации работает как одна непривязанная организация с начальным владельцем.
В этом и смысл такого устройства: программное обеспечение одно и то же, а решения о том, где оно работает и до чего может дотянуться, принадлежат тому, кто его запускает. Как это сделать, описано на странице установки.