ee-database sync client

schemaデータベース向けのオープンソースの適用クライアントです。ご自身のPostgreSQLに到達できる任意のマシンで実行し、一度ペアリングすれば、そのデータベースをenrichmentと同期した状態に保ちます。スナップショットからブートストラップし、単一のアウトバウンドWebSocket経由でライブの差分フィードを適用します。接続文字列がそのマシンから外に出ることはありません。

Entity Enricherserver · outboxee-databaseあなたのマシンお客様のデータベースPostgres · MySQL · SQLitebatch · リース 120秒apply — 単一トランザクションcommitack 次のウィンドウが即座にプッシュされます

すべてのステートメントはリビジョンガード付きなので、再配信されたバッチは同じ行に収束します。SQLエラーが発生するとバッチはロールバックされて停止し、問題のあるデルタが暗黙的にスキップされることはありません。

クライアントが取得するのは 操作ではなく状態 です。各デルタは、変更されたエンティティの現在の全行を、冪等な INSERT … ON CONFLICT … DO UPDATE として持ち込むため、バッチを取りこぼしてもターゲットは収束します。

なぜ同期クライアントなのか?

database sync は、n8n、Make.com、MCP、生の webhook、REST デルタフィードなど、いくつかの方法で利用できます。sync クライアントは完全に自動化された方法であり、構築の手間も情報漏洩のリスクも最小限です。

構築するワークフローはゼロ

n8nシナリオもcronもグルーコードも不要です。一度ペアリングすればスナップショットからブートストラップし、その後は到着するたびにすべてのデルタを適用します。

DSN がマシンから出ることはありません

接続文字列はコマンドラインで渡すか、ローカルに mode-600 で保存されます。Entity Enricher に送信されることは決してありません。クライアントは外向きにのみ接続します。

設計上、リプレイセーフです

各デルタは冪等でリビジョンガード付きのアップサートです。バッチの途中でクライアントが停止しても、リースの期限切れ後にバッチが再配信され、再適用によって同じ行に収束します。

失敗時は必ず隔離し、黙って見過ごすことはありません

SQLエラーが発生するとバッチはロールバックされ、失敗したデルタが報告されます。サーバーはそのエンリッチメントのバッチ全体を隔離し、それを除いてキューを再送信するため、クライアントは接続を維持したまま適用を続けます。1件の不正な行が後続のすべてを滞らせることはなく、隔離された処理は対処するまで一覧に表示され続けます。

クイックスタート

まずschema上にデータベースを登録し、次にクライアントをペアリングして、データベースに到達できるマシンで実行してください。

  1. 1

    データベースを登録

    Database Sync ページで、ミラーリングしたいスキーマにデータベースを登録し、そのデータベースキーを確認します。全体の仕組みについては Database Sync を参照してください。この手順で、クライアントが適用する対象データベースの方言を宣言します。

  2. 2

    クライアントをインストール

    これをターミナルに貼り付けてください。スクリプトはインストール前にcosign署名を検証します。

    curl -fsSL https://entityenricher.ai/install-eedatabase.sh | sh

    Windows: iwr -useb https://entityenricher.ai/install-eedatabase.ps1 | iex。または、リリースから署名済みバイナリをダウンロードするか、ソースからビルドします(Go ≥ 1.23): go build -o ee-database .

    ソースと署名付きリリースは TOT-Concept/ee-database(MIT)にあります。

  3. 3

    ブラウザでペアリング

    ee-database pairを実行します。ブラウザのタブが/database/connectで開き、短いコードが表示されます——それを確認し、このクライアントが同期するデータベースを選択してください。

    ee-database pair --server https://entityenricher.ai
    
    Open this URL in your browser to confirm pairing:
       https://entityenricher.ai/database/connect?code=7QX-KP2
    
      Code: 7QX-KP2
    
    Waiting for confirmation...

    トークンを使いたい場合は、Database Sync ページ(Sync client → Pair a client)で発行し、直接渡してください: ee-database pair --server … <refresh-token>

    このフローで唯一の決定事項です。このマシンがどの登録済みデータベースを同期するかを選びます。ペアリングを行うと、そのデータベースの以前の認証情報が置き換えられるため、古いクライアントは停止します。
  4. 4

    データベースに到達できるマシンで実行します

    初回実行時、クライアントは.sqlスナップショットを取得して適用し、その後接続してデルタをストリーミングします。--save-dsnは接続文字列をローカルに保存するため、以降の実行では引数が不要になります。

    各実行では、ログインのプロビジョニング権限(データベース作成、DDL、DML)も自己チェックし、結果を Sync クライアントカードに報告します。そのため、差分の適用が失敗する前に、権限の不足を確認できます。リンクされたスキーマがまだ公開されていない場合、クライアントは接続したまま待機します。最初の公開でフィードが自動的に開始され、再起動は不要です。

    ee-database run --dsn "postgres://user:pass@localhost:5432/mydb" --save-dsn

    「Next to」とはネットワーク的に近接していることを意味し、データベースサーバー上にある必要はありません:DSN に到達できるマシンやコンテナであれば動作します — 通常 TLS を強制するクラウドマネージドの PostgreSQL(Azure、OVHcloud、AWS RDS…)も含みます:…/mydb?sslmode=require

    実行中のクライアントから報告される内容です。接続されているかどうかと、プロビジョニング権限のセルフチェック結果が表示されます。

マネージド同期ホスト

1 台のマシンに複数のデータベースが同居する場合は、sync host がペアリングの手順を 1 段上のレベルに引き上げます。マシンを一度ペアリングすれば、そこに割り当てたデータベース同期はすべて自動的に取得・プロビジョニングされ、同期が維持されます。新しい同期を登録するために、あらためてターミナルを開く必要はありません。クライアント 1.5.0 以降が必要です。1.5.0 以降はマシンごとではなくサーバーごとにペアリングするため、1 台のホストで複数の Entity Enricher インスタンスを並行して扱えます。

  1. 1

    ホストを登録

    Database Sync ページでツールバーの Sync hosts ボタンをクリックし、そのマシン名でホストを追加します。1 回限りのペアリングトークンが一度だけ表示され、コピー & ペーストできる host pair コマンドとセットアップ手順に埋め込まれています。

  2. 2

    マシンを一度ペアリングします

    データベースサーバーに到達できるマシンでコマンドを実行してください。--dsnサーバーを指定する基本接続文字列で、データベース名は含みません — 割り当てられた各同期がそこから独自のデータベースを導出します。すべてのDSNと同様に、ローカルでモード600で保存され、Entity Enricher に送信されることはありません。

    ee-database host pair --server https://entityenricher.ai \
      --dsn "postgres://user:pass@host:5432/" <token>

    ペアリングでは、ログインのプロビジョニング権限(データベース作成、DDL、DML)を自己チェックし、権限が不足している場合はすぐに失敗します。最小権限のログインを希望されますか?--admin-dsn を追加すると、プロビジョニングは代わりに管理接続を通じて、不足している各ロールとデータベースを作成します。管理 DSN はプロビジョニング時のみ使用され、保存されることはありません。

    ホストのペアリングはマシンごとに1回だけです。その後そのホストに割り当てる登録は、マシンに再度触れることなく作成・同期されます。
  3. 3

    これを実行してから、UIから同期を割り当てます

    ee-database host run

    ホストは1つのコントロールプレーンWebSocketを保持し、UIで行われた割り当てに反応します。データベースの登録時、または後からデータベースの概要タブでホストを選択してください。割り当てられた各同期はクレームされ、データベースがなければ作成され(同期名をスネークケース化したもの。ホストのconfig.json内のdatabase_namesで同期ごとに上書き可能)、その後は下記の通常のループで同期されます。

    他のクライアントとすでにペアリングされているデータベースは報告されてスキップされ、乗っ取られることはありません。UI でホストを取り消すと、そのマシンは即座に切断され、取得したデータベースごとの資格情報もすべて無効になります。割り当てとすでに同期済みのデータは残るため、再ペアリングされたホストは古いホストが停止した箇所から再開します。

配信の仕組み: リースとack

デルタは、データベースごとに厳密な FIFO アウトボックスを通じて Entity Enricher から送出されます。サーバーは表示ウィンドウを 120 秒間リースし、1 つのバッチとしてプッシュします。クライアントはバッチ全体を単一トランザクションで適用し、ack を返します。これによりカーソルが前進し、次のウィンドウが即座にトリガーされます。バッチの途中で停止したクライアントは、リースの期限切れとサーバー側の再プッシュによってカバーされるため、データの損失や二重コミットは発生しません。

スナップショット = ゼロからのデルタ

ブートストラップと定常状態は同一のコードパスを共有します。データベースがすでにシードされている場合は、--skip-bootstrap でブートストラップをスキップできます。

リビジョンガード付き

各ステートメントは _sync_revision を持つため、順序が前後しても、古い行が新しい行を上書きすることは決してありません。

失敗時は隔離します

SQLエラーが発生するとバッチはロールバックされ、問題のあるステートメント全体とともに、失敗したデルタが報告されます。サーバーはそのエンリッチメントのバッチを隔離し、それを除いてキューを再送信します。クライアントは残りの適用を続けます。デルタを特定できない失敗の場合のみ、非ゼロで終了します。

各ウィンドウが書き込む内容

適用されたウィンドウはすべて、書き込んだ内容の形状をテーブル単位で報告します。そのため、夜間の再エンリッチメントの規模を見積もる際に、すでに ACK 済みで消えたデルタについてログを掘り返す必要はありません。

applying 12 delta(s) (10831 .. 10842) in one transaction
applied 12 delta(s) in 84ms — 38 statement(s): mushroom 4 upserts,
  mushroom_common_names 12 upserts + 4 prunes, mushroom_human_uses 14 upserts + 4 prunes
acked up to delta 10842

upsert 1 件は 1 行に相当するため、カウントは行数です。prune は、新しいペイロードが参照しなくなった子行または中間テーブルの行を削除する、リビジョン保護付きの単一の DELETE です。子レコードはその場で調整され、消去して再挿入されることはありません。--verbose を付けると、デルタごとに 1 行ずつ、そのエンティティタイプ・実時間・個別の形状が出力されます。

データベースとダイアレクト

対象の方言は Entity Enricher でのスキーマ・データベース登録によって決まります。クライアントはサーバーが生成した SQL をそのまま適用します。提供開始時の方言は PostgreSQL で、MySQL / MariaDBSQL ServerOracle のレンダラーは対応予定です(MySQL ドライバーは同梱済みです)。複数ステートメントの適用はドライバーごとに処理されます(pgx のシンプルプロトコル、MySQL の multiStatements)。

必要なデータベース権限(PostgreSQL)

対象のデータベースが既に存在する場合、ログインに必要な権限はデータベースへの CONNECT と、対象スキーマへの USAGE + CREATE だけです。(PostgreSQL 15 以降、public はデフォルトで全ユーザーに CREATE を付与しなくなりました。)

それ以外はすべて所有権から導かれます。クライアント自身がレプリカテーブルを作成するため、そのテーブルを所有することになり、所有権があればデータ差分に必要な読み書きも可能になります。所有権は省略できません。フィードには、PostgreSQL がテーブル所有者に限定しているマイグレーション文(ALTER TABLE …CREATE INDEX …)も含まれており、どのような権限の組み合わせでもこれを代替することはできません

レプリカのテーブルが既に別の所有者の下に存在する場合、ログインは新規テーブルを作成できるため実行時の権限プリフライトは通過しますが、最初のマイグレーションデルタが失敗します。権限を追加するのではなく、ALTER TABLE … OWNER TO <login> で所有権を移すか、所有ロールへのメンバーシップをログインに付与してください。

差分が隔離される場合

データベースが拒否するステートメント(多くは、新しいユニークインデックスの下で既存の重複が生じることが原因です)があっても、フィードは停止しません。バッチはロールバックされ、クライアントは失敗したデルタを問題のあるステートメント全体とともに報告し(切り詰められることはありません)、サーバーはそのエンリッチメントのバッチを隔離してキューを再送信します。クライアントは後続のすべてを適用し続けます。

隔離された処理は、対応するまで Database Sync ページの Quarantine タブに残ります。データベース側で原因を修正して再投入する(古いステートメントを再実行するのではなく、現在の状態からエンティティを再射影します)か、その行がすでに不要であれば破棄してください。

ブートストラップの失敗は別です。スナップショットは単一のトランザクションであるため部分的に適用されることはなく、クライアントはそれをペアリングのプロファイルディレクトリにsnapshot-failed.sqlとして保存します(モード0600、試行のたびに置き換えられ、次回の成功時に削除されます)。そのためpsql -fで内容を確認したり再実行したりできます。

セキュリティ

送信のみ

クライアントは :443/wss 経由で WebSocket を開始します。データベースホストがインバウンド接続を受け付けることは決してありません。開放するポートも、設定するイングレスもありません。

1つの認証情報、1つのデータベース、1つのクライアント

認証情報は1つの database sync に紐付けられます。再度ペアリングすると認証情報がローテーションされ、以前のライブ接続は即座に切断されます。

短命なアクセストークン

365日のリフレッシュトークン(mode-600 で保存)は、WebSocket を認証する15分間のアクセストークンと交換されます。UI から取り消すと、稼働中のクライアントは約1秒以内に切断されます。

ホストのペアリングキーは不透明なシークレットです

マネージドホストはJWTではなく短い eeh_… キーでペアリングします。サーバーはそのハッシュのみを保持し、キーに有効期限はなく、UIでホストを取り消すことによってのみ無効になります。

ペアリングごとに 1 プロセス

プロファイル単位のロックにより、同じペアリングを2つのプロセスが同時に実行することを防ぎます。そうしないと、互いのWebSocketセッションを繰り返し追い出し合うことになります。

権限は限定しつつ、自身のテーブルは所有する

クライアントは、同期対象のスキーマに限定した専用ロールで実行してください。そうすればトークンが漏えいしても他には影響しません。ただし、レプリカテーブルはそのロール自身に作成させ、所有者にしてください。マイグレーションのステートメントが必要とするのは権限付与ではなく所有権です。

CLI リファレンス

コマンド機能内容
ee-database pair --server URLブラウザで確認するデバイスコードペアリングです。同期するデータベースを選択してください。
ee-database pair --server URL <token>Database Sync ページで発行されたトークンでペアリングします (ヘッドレス環境にも対応)。
ee-database run --dsn DSN [--save-dsn] [--skip-bootstrap]スナップショットからブートストラップし(スキップされない限り)、接続してデルタを適用します。
ee-database run … --create-missing対象のデータベースが存在しない場合は、DSN 自身の認証情報を使って先に作成します(postgres には CREATEDB、mysql には CREATE 権限が必要です)。
ee-database run … --create-missing --admin-dsn DSN対象 DSN が指定するすべてのものを、管理者接続でブートストラップします。存在しないロール/ユーザー(DSN のパスワードを使用)と、それが所有するデータベースを作成します。これにより対象 DSN には作成権限が不要になり、管理者 DSN は一切保存されません。
ee-database run --allペアリング済みのすべてのデータベースを1つのプロセスから並行して同期します(それぞれに保存済みの DSN が必要です)。
ee-database run … --verboseウィンドウ単位の集計だけでなく、各デルタの書き込み形状と実時間をログに記録します。host run でも指定できます。
ee-database host pair --server URL --dsn BASE_DSN [--admin-dsn DSN] <token>このマシンを管理対象の同期ホストとして一度だけペアリングします。ベース DSN はデータベースサーバーを指定するもので (データベース名は含みません)、マシンの外に出ることはありません。トークンは Sync hosts ダイアログ (Database Sync ページのツールバーにある Sync hosts ボタン) から取得します。ペアリングはサーバーごとに 1 つで、複数の Entity Enricher サーバーに対して並行してペアリングできます。
ee-database host run [--server URL]管理モード: このホストに割り当てられたすべてのデータベース同期を取得し、存在しない場合は作成して、自動的に同期状態を維持します。データベースごとのペアリングは不要で、ペアリング済みのすべてのサーバーを一度に対象とします (--server を指定すると 1 台に限定されます)。他のクライアントとすでにペアリングされているデータベースは報告されるだけで、引き継がれることはありません。
ee-database host status / host disconnect [--server URL]このマシンのホストペアリングを表示または削除します。サーバー側での取り消しは「同期ホスト」カードから行います。
ee-database statusペアリング状態、サーバー URL、およびペアリング済みデータベースを表示します。
ee-database disconnect1つのペアリングのローカル認証情報を削除します。サーバー側での取り消しは UI から行います。
ee-database version印刷版。

認証情報は mode-600 で、ペアリング済みのデータベースごとに 1 つのプロファイルとして ~/.config/ee-database/profiles/ に保存されます。ペアリングはデータベースごとに一度だけ行い、複数をペアリングしている場合は --database NAME で 1 つを選択します。自動化を一切使いたくない場合は、同じフィードを素の REST として利用できます。GET /api/databases//changes を呼び出し、続けて POST /api/databases//ack を呼び出してください。詳しくは Database Sync をご覧ください。

オープンソース

クライアントは MIT ライセンスで、公開リポジトリに存在するため、誰でも自分のデータベースに対して何が実行されるのかを正確に監査できます。

ソース: github.com/TOT-Concept/ee-database

リリース: github.com/TOT-Concept/ee-database/releases — 各バイナリは公開前に cosign で署名されています。

インストーラーを監査する: curl -fsSL https://entityenricher.ai/install-eedatabase.sh | less