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 的一个组织,通过提供方的组织 ID 绑定;没有上游关联的团队会返回 404 TEAM_NOT_LINKED,而不是退回到本地猜测,实时成员名册的读取会直接请求身份提供方,且不保存任何内容。

还有两份镜像尚未移除,在这一点上暗示相反的说法都是不诚实的。组织和团队的名称仍与绑定 ID 一起保存在本地,本地的成员关系记录在两次同步之间仍用于授权普通请求。移除这两项是代码仓库中已登记、尚未完成的工作,而不是已经完成的事。

不存任何商业数据

付款由中央计费服务管理。Nessie 不存储支付服务商的客户、订阅、发票、价格、额度余额、充值策略、付款同意、对账单或取消状态,也不计算资费。它只读取已准备好用于展示的数值并显示出来。

密钥与机密

必须配置密钥库,才能存储任何机密。我们有意不提供数据库回退方案,也没有明文路径:没有密钥库时,保存、轮换和吊销机密都会返回错误,而不是降级为更不安全的方式。Nessie 自己关于某个机密的记录不含值、不含密文、不含密钥,也不含密钥库令牌,只有一个不透明的引用及其元数据;密钥库路径只用结构性标识符构建,绝不使用组织、团队、项目或个人的名称。

Nessie 为了充当 OAuth 客户端而必须自行持有的凭据,包括连接器和邮箱凭据、Webhook 密钥、会话刷新材料和执行器载荷,都会密封在按用途绑定的 AES-256-GCM 信封中,由带版本的密钥环保护。该密钥环与会话签名密钥是相互独立的根,并有文档化的轮换流程。

此外还有一条针对模型的规则:系统可以知道某个机密存在,也可以授权使用它,但机密内容不得进入模型上下文、提示词、工具参数、工具结果、记忆、嵌入向量、搜索、日志或消息。智能体可以被授予使用某个机密的能力,但永远不会被授予查看它的能力。

底层磁盘是否加密,取决于部署所在的机器。代码仓库并未确立对数据库和对象存储卷的卷级加密,因此本页不作此声明。

租户隔离

Nessie 在设计上是多租户的:只有一个数据库,其中每一行都恰好属于一个组织,每个子表都带有 organization_id。隔离在两处实施:每个查询都带有的范围条件,以及建立在该列上的真实外键。内部审计发现该列存在却在几十张表上没有约束,之后我们通过四十二个约束补上了这些外键。

一套一致性测试在 HTTP 边界上守住这一不变量。它会模拟一个权限最大的攻击者,即自己所在组织的完整所有者,并断言:对其他组织资源的每一次规范读取,都返回 403、404 或空结果;每一次规范变更都会被拒绝,且其他组织的数据行不受影响。覆盖范围包括频道、审批、审计日志、触发器、智能体及其绑定、运行、项目、任务、会话线程、连接、工作流安装、MCP 实例、知识库和提醒。

关于机制,需要说得准确:这是应用层的强制隔离加外键,而不是 PostgreSQL 的行级安全。这里没有 RLS 策略,也没有数据库层面的兜底;一致性测试运行在一个忠实模拟的内存存储上,而不是真实数据库,因此它证明的是代码所写的范围条件,而不是服务器实际执行的 SQL。代码仓库在测试套件自己的文档中这样说明,本页也这样说明。

审计追踪

每一项有实际影响的操作都会被记录:谁执行的、以什么身份(个人、智能体还是服务)、做了什么、针对哪个资源、在哪个组织、项目、团队或频道、成功还是被拒绝、原因,以及请求 ID、来源地址和用户代理。

日志在设计上只能追加,也无法从外部写入:POST /api/audit-log 并不存在,这是有意为之。各条记录首尾相连,每条都带有自身规范字段的 SHA-256 摘要,加上该组织上一条记录的摘要,因此被修改或删除的记录会使链条断裂,并且可以被检测到。组织所有者或管理员可以随时校验这条链,若链条在某处断裂,会被告知最早断在哪里。

有两点需要如实说明。这条链让日志可发现篡改,而不是不可被篡改:日志背后没有数据库层面的写入撤销,也没有一次写入的存储,所以保证的是发现,而不是阻止。此外,记录默认无限期保留,可配置的保留期限以及删除前导出的功能是设计中的,尚未构建。记录也禁止包含机密或完整的请求正文,一组固定的敏感元数据字段名在写入时会被脱敏。

费用账本

每一次模型调用都会记录在组织名下,并关联到产生它的用户、团队、项目、频道、智能体和运行,同时记录消耗的 token 数,以及服务商报告的费用和 Nessie 自己的估算,两者并列保存。服务商没有报告的指标会存为 null,而不是被猜测。报表可由组织所有者或管理员查看。

这份账本是运营遥测数据:记录一个组织花了多少,是代谁花的。它不是发票,也不是计费依据。

使用托管版时涉及的第三方

托管部署只与一小组固定的服务通信。模型流量和网络搜索不会直接发往服务商,两者都通过 UnlikeOtherAI 运营的同一个网关发出,上游的模型和搜索服务商位于该网关之后。登录和计费发往 UnlikeOtherAI 的认证服务。密钥库与应用一起自行托管。由服务器生成的视频通话链接默认使用公共的 Jitsi 服务。

几乎所有其他内容在运维人员配置之前都是关闭的,并且会明确提示,而不是悄悄失败:已连接的 Google、Microsoft 和 Slack 账户;通过 Amazon SES 收发的智能体电子邮件(部署在自己的账户中运行,没有中间方);移动端和浏览器推送;研究和看板来源连接器;云端浏览器。有两点值得说明,因为它们会改变数据的去向:

  • 已连接的邮箱仍留在你的服务商处。邮件通过 IMAP 和 SMTP 或服务商自己的登录方式,从你的服务商处实时读取;Nessie 只存储密封后的凭据,不存储该账户的其他任何信息。你连接到 Clients 收件箱的邮箱是例外:你要求把它的对话与每位客户保存在一起,所以这些邮件会被复制进来。
  • 应用内的语音通话不经过 Nessie。API 只负责代为获取凭据,音频连接由客户端自行建立。听写和电话通话则不同:它们的音频会经由 Nessie 送往处理它的语音和模型服务商。

还有一个推理方面的细节遵循同样的原则:带签名的调用方身份标头只会发送到已配置的聊天主机,所以即使把嵌入向量指向另一个端点,该端点也不会收到关于被嵌入文本所属之人的委托断言。

托管版产品使用多家推理服务商,并在成本和质量之间进行选择。组织将能够在 Admin 中选择其智能体可以使用哪些服务商:仅限西方、仅限欧洲、仅限中国,或不设偏好——为每项工作选用效率最高的服务商。自行托管的实例则自行选择。

自己运行,上述一切都会改变

上面提到的每一个第三方,在自行托管实例上都是配置选项。推理可以指向任何兼容 OpenAI 的端点,包括你自己硬件上的端点。连接器除非你开启,否则保持关闭。UnlikeOtherAI 登录是受支持的方式;完全没有身份提供方的部署,则作为单个未绑定的组织运行,并带有一个初始所有者。

这正是这种安排的意义所在:软件是同一套,而在哪里运行、可以访问什么,由运行它的人决定。具体做法请见安装页面。