nessie

欧州製。データの保管場所

Nessieの開発拠点、データの保管場所、そして自社でホスティングする場合にご自身で管理できる範囲について説明します。

誰が、どの法律のもとで作っているか

Nessieは、チェコ共和国に登記され、チェコ法のもとで事業を行うUnlikeOtherAI s.r.o.が開発しています。

このページは、ソフトウェアがどのように作られ、どこで動いているかを説明するものです。コンプライアンスに関する表明ではありません。Nessieはいかなるセキュリティ認証も取得しておらず、ここに書かれた内容を認証の主張として受け取らないでください。どのような個人データを、どのような根拠で処理するかについては、別ページのプライバシーポリシーに記載しています。

ホスト版が動いている場所

Nessie Cloudは、ScalewayやHetznerなど、欧州のホスティングプロバイダー上で動いています。各プロバイダーが提供する最適なサービスを選んでいるため、製品の部分ごとに異なるプロバイダーで動くことがありますが、いずれも欧州域内です。

これより詳しい情報、つまり特定の拠点、都市、データセンターは、あえてここには書いていません。誰も気づかないうちに古くなってしまうためです。

セルフホストなら、選ぶのはあなたです

セルフホストしたインスタンスをどこで動かすかは、製品の設定ではなく、デプロイの設定です。1台構成の場合はDocker Composeのスタックで、お好きなマシンに置きます。マネージドクラウド向けのTerraformツリーでは、リージョンは既定値のない入力です。推測できるものは何もないので、既定値もありません。データベース、オブジェクトストレージのバケット、各サービスはすべて、指定したリージョンに従います。

同じ問いに関わる構造上の点が2つあります。1つめは、キューとリアルタイム転送がどちらもPostgreSQLであることです。Redisもメッセージブローカーも経路にないため、配置を考えるべき第三のシステムはありません。2つめは、オブジェクトストレージが特定のベンダーに縛られないS3互換であることです。リファレンス構成では、外部からの入口を持たないプライベートネットワーク上のコンテナでMinIOを動かしています。どこかに置く必要があるのは、データベース、オブジェクトストア、2つのNodeサービスで、すべてあなたが置いた場所で動きます。構築方法はインストールのページで説明しています。

Nessieが保持するもの、そしてあえて保持しないもの

IDは複製しません

UnlikeOtherAIログインを使う場合、認証要素、プロフィール、組織とチームのメンバーシップ、招待といった人のIDは、IDプロバイダーが管理・保存します。Nessieは依存当事者(リライングパーティ)であり、2つめのIDシステムではありません。これはアーキテクチャの判断であると同時に、データの判断でもあります。Nessieが保存しないIDは、Nessieから漏れることも、失われることも、削除し損ねることもありません。

usersテーブルが実際に保持するのは、プロバイダーが発行する安定したユーザー識別子(他のすべてがこれをキーに解決されます)、メールアドレス、正本ではない表示名とアバターのミラー、そして製品ローカルの項目(設定、代名詞、セッションを失効させるためのトークンバージョン)です。ミラーは、行ごとにリクエストしなくても名前と画像を表示できるようにするためだけに存在し、ログイン時、チーム切り替え時、セッション更新時に、プロバイダーが検証済みのクレームから再同期されます。値を作り出すことはありません。プロバイダーが名前を持っていない人は、名前が届くまでメールアドレスで表示されます。パスワードハッシュが存在するのは、IDプロバイダーがまったくないインストールのブートストラップオーナーに限られます。

構造についても同じです。プロバイダーの組織1つがNessieの組織1つに対応し、プロバイダーの組織IDで紐づけられます。上流とのリンクがないチームは、ローカルで推測して代用するのではなく404 TEAM_NOT_LINKEDを返します。メンバー一覧の取得はIDプロバイダーに対して直接行われ、何も保存されません。

2つのミラーはまだ解消できていません。そうでないかのように書くのは不誠実でしょう。組織名とチーム名は、紐づけ用のIDと並んでまだローカルに保存されており、同期の合間には、ローカルのメンバーシップ行が通常のリクエストの認可を担っています。この2つをなくす作業は、すでに完了したものではなく、リポジトリで管理されている未完了の作業です。

商用データは持ちません

支払いは中央の課金サービスが管理します。Nessieは、決済プロバイダーの顧客、サブスクリプション、請求書、価格、クレジット残高、チャージのポリシー、支払い同意、明細、解約の状態を一切保存せず、料金体系の計算も行いません。表示用に整えられた値を読み取って、そのまま表示するだけです。

シークレット

シークレットを保存するには、設定済みのボールトが必須です。データベースへのフォールバックも、平文で保存する経路も、意図的に用意していません。ボールトがなければ、シークレットの保存、ローテーション、失効はより安全性の低い方法に切り替わることなく、エラーを返します。Nessie自身のシークレット関連レコードには、値も、暗号文も、鍵も、ボールトのトークンも含まれず、不透明な参照とそのメタデータだけがあります。ボールト内のパスは構造上の識別子だけで組み立てられ、組織、チーム、プロジェクト、個人の名前は使われません。

OAuthクライアントとして動作するためにNessie自身が保持しなければならない認証情報(コネクタやメールボックスの認証情報、Webhookのシークレット、セッション更新用の素材、エグゼキューターのペイロード)は、用途ごとに分けたAES-256-GCMのエンベロープで封印されます。鍵はバージョン管理されたキーリングで管理され、セッション署名用シークレットとは別のルートに置かれ、ローテーション手順も文書化されています。

モデルに関するルールもあります。シークレットは、存在を知らせることも、使用を認可することもできますが、シークレットそのものをモデルのコンテキスト、プロンプト、ツールの引数や結果、メモリ、埋め込み、検索、ログ、メッセージに入れてはなりません。エージェントにシークレットを使う権限を与えることはできますが、中身を明らかにする権限を与えることはできません。

基盤となるディスクが暗号化されているかどうかは、デプロイを動かすマシンの性質によります。リポジトリはデータベースやオブジェクトストレージのボリューム単位の暗号化を保証していないため、このページでも暗号化されているとは述べません。

テナントの分離

Nessieは設計上マルチテナントです。データベースは1つで、すべての行がちょうど1つの組織に属し、すべての子テーブルがorganization_idを持ちます。分離は2か所で担保されています。すべてのクエリが持つスコープ句と、その列に張られた実際の外部キーです。外部キーは、社内監査でこの列が数十のテーブルに存在するのに制約がかかっていないことが判明したのを受けて、42の制約として追加されました。

適合性テストスイートが、この不変条件をHTTPの境界で守っています。スイートは、自分の組織の完全なオーナーという最大権限を持つ攻撃者を用意し、他の組織のリソースに対するあらゆる標準的な読み取りが403、404、または空で返ること、そしてあらゆる標準的な変更が拒否され、相手組織の行が変わらないことを確認します。対象は、チャンネル、承認、監査ログ、トリガー、エージェントとそのバインディング、実行、プロジェクト、タスク、スレッド、接続、ワークフローのインストール、MCPインスタンス、ナレッジベース、アラートです。

仕組みを正確に言うと、これはアプリケーションレベルの制御と外部キーによるもので、PostgreSQLの行レベルセキュリティではありません。RLSポリシーはなく、データベース側の最後の砦もありません。また、適合性スイートは稼働中のデータベースではなく忠実なインメモリストアに対して実行されるため、証明されるのはコードが書くスコープ句であって、サーバーが実行するSQLではありません。リポジトリのスイート自身のドキュメントにもそう書かれており、このページも同じです。

監査証跡

影響のあるすべての操作が記録されます。誰が、どの立場で(人、エージェント、サービス)、何を、どのリソースに対して、どの組織・プロジェクト・チーム・チャンネルで行ったか。成功したのか拒否されたのか、その理由、そしてリクエストID、アドレス、ユーザーエージェントです。

ログは設計上追記専用で、外部から書き込む方法はありません。POST /api/audit-logは存在せず、これは意図したことです。エントリはチェーンされています。各エントリには、自身の正規化された項目のSHA-256ダイジェストと、その組織の直前のエントリのダイジェストが含まれるため、行が変更または削除されるとチェーンが途切れ、検出できます。組織のオーナーと管理者は、いつでもこのチェーンをたどって検証でき、途切れていれば最初に途切れた場所が示されます。

正直に述べるべき留保が2つあります。チェーンによってログは改ざんを検知できるものになりますが、改ざんされないわけではありません。データベースレベルの書き込み権限の剥奪や、一度書いたら変更できないストレージはその背後にないため、保証されるのは防止ではなく検知です。また、エントリは既定では無期限に保持されます。削除前のエクスポートを伴う保持期間の設定は、設計済みですが、まだ実装されていません。なお、エントリにはシークレットやリクエスト本文の全体を含めることはできず、機微なメタデータの項目名は、固定の一覧に従って、記録される際にマスクされます。

コスト台帳

すべてのモデル呼び出しは、組織に対して、またその発生元であるユーザー、チーム、プロジェクト、チャンネル、エージェント、実行に対して記録されます。消費したトークン数と、プロバイダーが報告したコストおよびNessie自身の見積もりが並べて保存されます。プロバイダーが報告しない指標は、推測せずnullとして保存されます。レポートは組織のオーナーと管理者が閲覧できます。

この台帳は運用上のテレメトリー、つまり組織が何に、誰の代わりにいくら使ったかの記録です。請求書ではなく、課金の正本でもありません。

ホスト版を使う場合の第三者

ホスト版のデプロイが通信する先は、限られた固定のサービスだけです。モデルへの通信とWeb検索は、プロバイダーに直接は送られません。どちらもUnlikeOtherAIが運用する1つのゲートウェイを通って外に出ており、その先に上流のモデルプロバイダーと検索プロバイダーがあります。ログインと課金はUnlikeOtherAIの認証基盤に送られます。シークレットのボールトは、アプリケーションと並べてセルフホストされています。サーバーが発行するビデオ通話のリンクは、既定では公開のJitsiサービスを使います。

それ以外のほぼすべては、運用者が設定するまで無効であり、静かに失敗するのではなく、そのことを明示します。具体的には、連携したGoogle、Microsoft、Slackのアカウント、Amazon SESによるエージェントのメール(デプロイが自分のアカウントで、仲介者なしに運用します)、モバイルとブラウザのプッシュ通知、リサーチやボードソースのコネクタ、クラウドブラウザです。データの行き先を変えるため、特に明記しておきたい点が2つあります。

  • 連携したメールボックスは、お使いのプロバイダーに残ります。メールは、IMAPとSMTP、またはプロバイダー自身のサインインを通じて、プロバイダーからリアルタイムで読み取られます。Nessieが保存するのは、封印した認証情報だけで、アカウントに関するそれ以外の情報は保存しません。Clientsの受信トレイに接続するメールボックスは例外です。各顧客とともにやり取りを保管するよう依頼することになるため、会話はコピーされます。
  • アプリ内の音声通話はNessieを経由しません。APIは認証情報を仲介するだけで、音声接続はクライアントが自分で開きます。音声入力と電話は別です。その音声はNessieを経由して、処理を行う音声認識プロバイダーとモデルプロバイダーに送られます。

推論についても同じ考え方の細部があります。呼び出し元のIDを示す署名付きヘッダーは、設定されたチャットホストにのみ送られます。そのため、埋め込みの送信先を別のエンドポイントにしても、そのエンドポイントに、テキストが埋め込まれる本人についての委任アサーションが渡ることはありません。

ホスト版は複数の推論プロバイダーを使い、コストと品質に応じて使い分けます。組織は管理画面で、エージェントが使えるプロバイダーを選べるようになる予定です。欧米のみ、欧州のみ、中国のみ、または指定なしのいずれかで、指定なしの場合はジョブごとに最も効率的なプロバイダーが使われます。セルフホスト版では、ご自身で選びます。

自分で運用すれば、これらすべてが変わります

上に挙げた第三者は、セルフホストしたインスタンスではどれも設定で選べます。推論は、自社のハードウェア上のものを含め、OpenAI互換のどのエンドポイントにも向けられます。コネクタは、有効にしない限り無効です。UnlikeOtherAIログインが正式にサポートされた方法ですが、IDプロバイダーをまったく使わないデプロイは、ブートストラップオーナーを持つ、どこにも紐づかない1つの組織として動作します。

この仕組みの要点はそこにあります。ソフトウェアは同じで、どこで動かし、何に接続させるかを決めるのは、それを運用する人です。方法はインストールのページをご覧ください。