Nodaro ドキュメント
ドキュメントノードリファレンスモデルAI エージェント(MCP)開発者向けセルフホスティングリサーチ

アーキテクチャ

運用者とコントリビューター向けに、Nodaro の仕組みを説明します。エディター、Fastify API、BullMQ ワーカー、Redis、ストレージ、3 種類の認証方式、エディションを取り上げます。

Nodaro は、REST ファーストの AI ワークフローエンジンです。ユーザーはブラウザーのエディターで、画像や動画の生成、動画の合成、テキストモデル、オーディオといった AI ノードをつないで、グラフを作ります。グラフは Supabase の Postgres に保存されます。グラフを実行すると、Fastify の API がグラフを並べ替え、各ノードの処理を Redis 上のキューのワーカーに渡します。生成はモデルプロバイダーが行い、結果は S3 互換ストレージに保存されます。

同じコードで Community、Business、Cloud の 3 つのエディションを提供し、3 種類のアクセストークンを受け付けます。このページは、Nodaro を運用する方と、Nodaro のコードを変更するコントリビューターに向けたものです。

システムの全体像

ノードのジョブエディターとアプリブラウザーSDK、CLI、MCPAPI トークンまたは OAuthAPIFastify、/v1SupabasePostgres とログインRedisBullMQ のキューオーケストレーターグラフを実行ワーカーメディアとレンダリングストレージS3 互換
クライアントは API を呼び出します。API はデータを Supabase に保存し、実行を Redis のキューに入れます。オーケストレーターは各ワークフローのグラフをたどり、ノードのジョブをワーカーに渡します。ワーカーはモデルプロバイダーを呼び出し、結果をストレージに保存します。

リポジトリ

公開リポジトリ github.com/nodaroai/app.nodaro.ai は、npm workspaces を使った 1 つのモノレポで、次のフォルダーで構成されています。

フォルダー内容
backend/Fastify の API、BullMQ のワーカー、オーケストレーターです。Node.js 22、TypeScript で書かれています。
frontend/ブラウザーで動くアプリです。エディター、アプリランナー、管理パネルが含まれます。React 19、React Router 7、React Flow を使っています。
packages/共有コード、公開している npm パッケージ @nodaro/shared、@nodaro/sdk、@nodaro/cli、そして Remotion の動画コンポジションです。
supabase/データベースのスキーマです。前方向にだけ適用する SQL マイグレーションとして管理しています。
docs/製品のドキュメントです。設計メモも含まれます。
tools/運用者向けのスクリプトです。キーの生成、コントラクトプローブ、バックアップとリストアがあります。
examples/サンプルです。たとえば、SSH 経由でサーバーを更新する GitHub Actions のワークフローがあります。

フロントエンド

フロントエンドは、Vite で作られたシングルページアプリです。Docker イメージでは、Web サーバーの Caddy がこれを静的ファイルとして配信します。1 つのバンドルに、次の 4 つのプロダクトが入っています。

  • エディター:React Flow のキャンバス、ノードの系統ごとの設定パネル、そして編集中にブラウザーでワークフローを実行するグラフ実行エンジンです。
  • アプリランナー:公開したワークフローを、入力と結果を並べたシンプルなフォームとして表示します。
  • OAuth 同意画面:ユーザーがサードパーティのアプリや MCP クライアントを承認する画面で、/oauth/authorize にあります。
  • 管理パネル:Business エディションと Cloud エディションにだけあります。

サーバーの状態は React Query、UI の状態は Zustand、キャンバスの状態は React Flow 自身のストアで管理します。

API

API は、Node.js 22 上で動く Fastify サーバーで、TypeScript で書かれています。ルートは Fastify のプラグインで、すべてのエンドポイントがリクエストとレスポンスのスキーマを宣言します。同じスキーマで各リクエストを検証し、/v1/openapi.json の OpenAPI 3.1 ドキュメントも生成します。GET /v1/nodes は、その環境で実行できるノードタイプの一覧を返します。

すべてのルートの前に、1 つの認証フックが実行されます。認証を参照してください。

プロセスとキュー

バックエンドは、1 つのイメージに含まれる 5 つの Node.js プロセスとして提供されます。

プロセス役割
API サーバーHTTP API です。
メディアワーカーキューからノードごとのジョブを受け取り、モデルプロバイダーを呼び出して、結果をストレージにアップロードします。画像や動画の生成、ffmpeg による処理、オーディオなど、40 種類以上のジョブを扱います。処理の大半がネットワーク待ちなので、同時実行数のデフォルトは 50 です。
レンダーワーカーコンポジション、モーショングラフィックス、Lottie オーバーレイ、3D タイトル、コンポジットを描画する Remotion のレンダラーです。ヘッドレス Chrome を動かすため、CPU が処理のボトルネックになります。同時実行数のデフォルトは 2 です。
オーケストレーターワークフロー全体を実行します。実行を読み込み、そのグラフを並べ替えて、レベルごとに実行します。同時実行数のデフォルトは 20 です。
パイプラインワーカーストーリー → 動画(Story → Video)のパイプラインです。すべてのエディションに含まれますが、動作するのは Nodaro Cloud だけで、それ以外ではすぐに終了します。

Docker では、1 つの起動スクリプトがすべてのプロセスを立ち上げ、API の前段には Caddy が立ちます。スケールアウトした環境では、それぞれのプロセスを別々のコンテナで動かせます。プロセス同士の連携は、Redis 上の BullMQ のキューと、Postgres 上の実行レコードだけを通じて行われます。スケーリングを参照してください。

ワークフローの実行の流れ

ユーザーがエディターで実行をクリックすると、実行の経路は 2 つに分かれます。

  1. ブラウザー:編集中は、エディター自身がグラフを実行し、ノードごとに API のエンドポイントを 1 回呼び出します。各ノードが終わるとすぐに、その結果が表示されます。
  2. サーバー:Webhook、スケジュール、API からのトリガーによる実行、アプリの実行、明示的なサーバー実行は、オーケストレーターに送られます。オーケストレーターは進行状況を実行のレコードに保存するので、どのクライアントからでも進行状況を追えます。

オーケストレーターは、次のように動きます。

  1. ワークフローのノードと接続を読み込みます。
  2. グラフを並べ替えて、レベルに分けます。1 つのレベルには、入力がすべて揃ったノードが入ります。
  3. 各レベルのノードを並列に実行します。並列数の上限は、ユーザー自身の並列数と、サーバーの上限 MAX_CONCURRENT_NODES_PER_EXECUTION です。
  4. 各ノードを、次の 3 つの方法のいずれかで実行します。
方法実行場所例理由
キュー経由メディアワーカーまたはレンダーワーカーのジョブ画像生成(Generate Image)、動画生成(Generate Video)、テキストから音声(Text to Speech)、動画を結合(Combine Videos)、動画レンダリング(Render Video)時間のかかる処理や外部への呼び出しです。バックプレッシャーのため、オーケストレーターから切り離します
直接 HTTPAPI ルートへの内部呼び出しプロンプト(Prompt)、モーショングラフィックス(Motion Graphics)、プロンプトヘルパー、SNS への投稿ノードすぐに終わるテキストモデルの呼び出しで、キューのオーバーヘッドがかかりません
インラインオーケストレーターの中テキストを結合(Combine Text)、テキストを分割(Split Text)、コンポジット(Composite)外部への呼び出しがない、純粋なロジックです
  1. ノードが終わるたびに、その出力を依存するノードに渡し、ノードごとに 1 回のデータベース書き込みで実行の進行状況を保存します。

実行が止まる形は 2 つあります。キャンセル済みになった実行は、すぐに止まります。実行中のノードは破棄され、すでに請求されたクレジットは返還されません。停止処理中の実行は、現在のレベルを最後まで実行し、次のレベルに進む前に止まります。

上限:各ノードは最長 90 分、実行全体は最長 120 分かけられます。独自の持ち時間を宣言するノードには、代わりにその持ち時間が与えられ、実行全体の上限も同じだけ延びます。現在これに当たるのは EDL 適用(Apply EDL)で、持ち時間はレンダリングする編集の規模から決まります。実行中にオーケストレーターが再起動した場合、持ち時間を持つノードは、そのワーカーがまだ動いていれば、2 回実行されずに再開されます。サブワークフロー(Sub-Workflow)は、循環を検出しながら、最大 5 階層まで再帰的に実行されます。

ストレージとデータベース

  • メディア:生成されたすべての画像、動画、オーディオファイルは S3 互換ストレージに保存され、キーで参照されます。Nodaro が、保存したメディアを自分から削除することはありません。
  • データベース:Supabase の Postgres には、プロフィール、プロジェクト、ワークフロー、実行とその進行状況、ジョブ、メディアのレコード、クレジットの取引、OAuth アプリとそのトークン、個人用 API トークン、暗号化された SNS の接続情報が保存されます。すべてのテーブルで行レベルセキュリティが有効なので、各ユーザーが自分の行だけを扱えるよう、データベース自体が制限します。
  • マイグレーション:スキーマは supabase/migrations/ にあり、ファイル名の順に、前方向にだけ適用されます。
  • Redis:キューと、小さな共有キャッシュを保持します。Redis のジョブの状態は一時的なものです。バックアップとリストアを参照してください。

認証

API は 3 種類のベアラートークンを受け付け、その形式で見分けます。

トークン取得元誰として動作するか
eyJ…(JWT)エディターでの Supabase のログインセッションログイン中のユーザー
ndr_app_<64 hex>ユーザーが承認した OAuth アプリアプリを承認したユーザー。付与されたスコープの範囲に限られます
ndr_<64 hex>個人用 API トークントークンの所有者

1 つの環境の中のプロセス同士も、INTERNAL_ORCHESTRATOR_SECRET で認証し合います。この値は定数時間で比較されます。Web サーバーは、外部からのリクエストに含まれるこのシークレットのヘッダーを削除します。

API は、リクエストごとに次の順でチェックします。

  1. 公開ルート(/v1/openapi.json や Webhook の受信など)は、認証をスキップします。
  2. 内部シークレット:プロセス間の呼び出しに使います。
  3. OAuth アプリのトークン:取り消されておらず、期限切れでもないことが必要です。
  4. Supabase のセッショントークン:ユーザーのロールをプロフィールから読み取り、5 分間キャッシュします。
  5. それ以外は 401 を返します。

個人用 API トークンは、そのトークンを受け付けるルートがチェックします。これらのルートは、レート制限も適用し、トークンで扱えるワークフローを、そのトークンに設定されたものに限定します。認証と OAuth アプリを参照してください。

コード上のエディション

1 つのコードベースで、3 つのエディションすべてを提供します。

  • Community(EDITION=community、デフォルト):セルフホスティングで運用し、管理パネル、クレジットの台帳、請求はありません。新規登録したユーザーは、全員が一般ユーザーになります。
  • Business(EDITION=business):管理パネルとユーザー管理が加わります。セルフホスティングで運用し、請求がない点は同じです。
  • Cloud(EDITION=cloud):クレジット、請求、管理者が設定するクレジット価格が加わります。app.nodaro.ai を動かしているエディションで、セルフホスティング向けではありません。

ルートは、エディションの名前ではなく、エディションが持つ機能をチェックします。管理用のルートは管理パネルのあるエディションでだけ動作し、クレジットのコードはクレジットのあるエディションでだけ動作します。このチェックは、ルートのほかのどの処理よりも先に行われます。ブラウザーのアプリは、ビルド時に VITE_EDITION からエディションを読み取ります。エディションとサーフェスプロファイルを参照してください。

ステータスとストリーミング

ジョブと実行のステータスは、2〜5 秒ごとのポーリングで取得します。ステータスは秒単位で変わるものであり、ポーリングはデプロイが簡単だからです。ストリーミング出力(テキストモデルからのテキスト、ワークフローの実行、パイプライン)には、Server-Sent Events を使います。そのため、Nodaro の前段に置くリバースプロキシでは、レスポンスをバッファリングしてはいけません。

技術選定の理由

  • Fastify:TypeScript の型を第一級でサポートし、スキーマに基づく検証を備えています。プラグインによって、各ルートを独立したスコープに保てます。
  • BullMQ:Redis 上で動く成熟したキューで、リトライ、バックオフ、同時実行数の制御を備えています。同じ Node.js のスタックで動きます。
  • REST:ルートごとに OAuth のスコープを設定しやすく、OpenAPI ドキュメントを生成でき、サードパーティのクライアントにとってもシンプルなモデルです。
  • Supabase:Postgres、ログイン、リアルタイム機能、行レベルセキュリティを 1 つのサービスで提供します。行レベルセキュリティのおかげで、独自の認可コードの多くが不要になります。
  • React Flow:ノードグラフ向けの実績あるキャンバスで、Nodaro はこれを大幅に拡張して使っています。
  • Remotion:宣言的な React のコンポジションで、サーバーでもエディターのプレビューと同じ結果にレンダリングされます。
  • 単一のプロバイダーレイヤー:モデルの呼び出しはすべて 1 つの抽象化レイヤーを通るため、機能のコードを変えずに、モデルを別のプロバイダーに移せます。

よくある質問

最終更新

目次