コンテンツにスキップ
OmniRoute source

Radar Free-Model Catalog (日本語)

以下のステータスは、この OSS リリースで実装されている内容と、その後の Radar ワークストリームを区別するものです。これはコードレベルのステータスであり、特定のホステッド環境や 外部連携が現在利用可能であることを保証するものではありません。

領域 このリリースでのステータス
署名付きカタログクライアント RADAR_ENABLED の背後で実装済みです。個別のオプトイン、Ed25519 検証、ローカルで暗号化された設定とキャッシュ、永続的な表示・有効化オーバーライド、元に戻せるトゥームストーン、スケジューラー、ダッシュボードを備えています。
コントリビューターの有効化 ダッシュボードはサーバーでホストされる GitHub の申請フローにリンクし、既存の omr_… キーを受け付けます。コントリビューター資格は非公開サービスによって判定されます。OSS クライアントには GitHub トークンやキー発行ロジックは含まれません。
サポーターキーによる有効化 実装済みです。未加工のキーは検証され、保存時に暗号化され、読み取り時にはマスクされ、サーバー側の同期によってのみ送信されます。キーを変更または消去すると、権限に依存する 4 つのフィードキャッシュがすべて無効化されます。
紹介リンク 個別に署名され、1 時間ごとに更新されるフィードとして実装済みです。固定リンクはコミュニティティアですぐに利用できます。期間限定キャンペーンは引き続きライブティアのデータです。
サポーター向けオファー 個別に署名されたライブ専用フィードおよびダッシュボードページとして実装済みです。クライアントは閉じたベネフィットスキーマを再検証し、最後に正常だったキャッシュを保持し、期限切れのエントリを除外し、パートナーオファーであることを明示します。
インテリジェンスとサポーター認識 Radar 独自の ELO、カタログの鮮度とトレンドに関する客観的情報、検証済みのローカルサポーターバッジ、ダッシュボードページ、ローカル専用の CLI ステータス/同期コマンドを備えた、厳格に署名されたライブ専用フィードとして実装済みです。
決済とトランザクションメール OSS クライアントには実装されていません。購入、寄付、領収書の確認、復旧、メール配信は非公開サービスが担います。ホステッド環境で利用できるかどうかは、引き続きその監督下でのデプロイとプロバイダー設定に依存します。
リサーチエージェントのワークストリーム このクライアントリリースには含まれません。キュレーション済みフィードの内容は引き続きサーバー側のデータです。OmniRoute のインストール環境で自律型リサーチエージェントが実行されることはありません。

汎用アナウンスリーダーは、Radar 機能フラグとは独立しています。ダッシュボードのホームおよび 変更履歴ビューアーは、単純な GET を使用して、リポジトリの公開 news.json を NEWS_JSON_URL (src/shared/utils/releaseNotes.ts) から取得します。Radar の設定、プロンプト、プロバイダー 構成、使用記録、ローカルの非表示状態は一切送信されません。

news.json は、parseNewsPayload() で実装された閉じた v2 スキーマを使用します。

  • schemaVersion: 2 と、上限が設定された items[] コレクション。
  • 安定した一意のアナウンス id 値。
  • 明示的な active フィールドと ISO 形式の publishedAt フィールド。
  • 必須の英語文面と、任意のローカライズされた文面。
  • 認証情報を必要としない任意の HTTPS リンクと、許可リストに登録されたアイコン。
  • 有効な項目を新しい順に選択し、ロケールがない場合は英語へフォールバックし、ID ごとにローカルで非表示化。

古いフォークが変更履歴ビューを壊すことなく移行できるように、パーサーは以前の単一形式 { active, title, message, ... } を一時的に受け入れます。無効なフィードは何も作用しません。Radar のリリース エントリは active: false の状態で提供されます。これを true に変更することは、マージおよびデプロイ後に 別途実施するリリース作業であり、RADAR_ENABLED や独立したフィード同期のオプトインには影響しません。


フラグ: RADAR_ENABLED(デフォルトはオフ)

Section titled “フラグ: RADAR_ENABLED(デフォルトはオフ)”

Radar は、RADAR_ENABLED 機能フラグによってエンドツーエンドで制御されます (src/shared/constants/featureFlagDefinitions.ts、カテゴリ policies、 defaultValue: "false")。

フラグがオフの場合、この機能面は存在しません。

  • ローカルのモデル状態の読み書きを含むすべての /api/radar/* エンドポイントは、 Radar モジュールに触れる前に 404 を返します。
  • ダッシュボード画面 (/dashboard/radar、/dashboard/radar/setup、 /dashboard/radar/combos、/dashboard/radar/offers、/dashboard/radar/intel) は notFound() をレンダリングします。
  • getRadarCatalog() (src/lib/radar/index.ts) は、変更されていないベースラインを返します。 エントリ数も値も同一で、すべてのエントリに origin: "baseline" が付与され、フィードキャッシュを 読み取ることはありません。
  • Radar のネットワーク呼び出しは一切行われません。各同期モジュールは、fetch に触れる前に { status: "disabled" } を返します。

これは厳格な上位ゲートです。フラグをオンにしても、_画面_が利用可能になるだけであり、 それ以上のことは何も起こりません。データのアップロード、バックグラウンド同期の開始、ルーティングや モデル選択の変更は行われません。以下の独立したオプトインを参照してください。


データ同期は別個のオプトイン — プライバシーに関する約束

Section titled “データ同期は別個のオプトイン — プライバシーに関する約束”

RADAR_ENABLED をオンにしても、UI が利用可能になるだけです。フィードの同期には、 radar_settings.opt_in に保存される、もう一つの独立したオプトインが必要です (src/lib/db/radar.ts、マイグレーション 136_radar_cache_settings.sql)。 syncRadar() は、ネットワーク呼び出しを行う前にフラグとオプトインの両方を確認します。

フラグがオフ → { status: "disabled" } — ネットワーク呼び出しなし
オプトインが偽 → { status: "opt_out" } — ネットワーク呼び出しなし

両方がオンの場合、同期経路は次のとおりです。

  1. x-omniroute-radar-schema: 2 と、任意の Authorization: Bearer &lt;supporter key&gt; ヘッダー(以下を参照)を使用して、 GET <feed base URL>/v1/catalog/latest を実行します。スキーマヘッダーがない場合、サーバーは、 別途署名された v1 移行用アーティファクトをデフォルトで提供するため、インストール済みの古いクライアントも 引き続き更新を受信できます。
  2. これはダウンロード専用のアプリケーションフローですが、それでも HTTPS リクエストです。ホストされている インフラストラクチャは、送信元 IP などの通常の接続メタデータを受信します。サポーターキーが設定されている 場合、サービスが利用資格を判定できるように、同期時にそのキーも Bearer ヘッダーで送信されます。上記の エビデンス境界で特定されたプライベートサーバーの正確なリビジョンでは、フィードリクエストの集計に、 キーのハッシュ、集約された使用量、および不正利用の手動レビュー用に日次でローテーションされる IP の 切り詰め済み HMAC が使用されます。これらのテーブルには、キーも IP も生の形式では保存されません。 インフラストラクチャのアクセスログと暗号化された配信送信トレイは、別個の運用境界です。
  3. OmniRoute が Radar サービスに、プロンプト、レスポンス、会話、プロバイダー認証情報、モデル トラフィック、稼働時間、レイテンシ、ローカルのプロバイダー構成を送信することはありません。
  4. レスポンスは検証、妥当性確認され、ローカルにキャッシュされます (セキュリティモデルを参照)。Radar のサーバー側ネットワーク経路は正確に4つです。 カタログ用の syncRadar()、紹介用の syncRadarReferrals()、およびサポーター限定のオファーと Intel 用の syncRadarOffers() / syncRadarIntel() です。

サポーターキーは任意の Bearer トークン (radar_settings.supporter_key) であり、 フィードサービスが提供するティアを決定するために使用されます (ティアを参照)。このキーは次のように扱われます。

  • プロバイダー認証情報に使用されるものと同じ AES-256-GCM encrypt()/decrypt() ヘルパー (src/lib/db/encryption.ts) を使用して、保存時に暗号化されます。
  • POST /api/radar/settings ({ supporterKey: "omr_" + 40 hex chars }) で設定され、 レスポンスでそのまま返されることはありません。レスポンスではマスクされた形式 (omr_****abcd) が返されます。
  • キーを変更または削除すると、カタログ、紹介、オファー、Intel のキャッシュがアトミックに無効化されます。 次回の同期または読み取り時に、新しい利用資格がサーバー側で解決されます。キーを保存するだけでは、 ネットワークリクエストは発生せず、単回使用のアクティベーションキーが消費されることもありません。
  • 同期の GET で Bearer トークンとしてフィードサービスに送信されます。キーに関するそれ以外の情報が クライアントの外部へ送信されることはありません。

オプトイン前に表示されるアクセスおよび安全性のルール

Section titled “オプトイン前に表示されるアクセスおよび安全性のルール”

非アクティブ状態のダッシュボードでは、いずれかの有効化操作を行う前に、 src/app/(dashboard)/dashboard/radar/RadarAccessExplainer.tsx から以下のルールが表示されます。 正式なアクセス区分は次のとおりです。

レベル 資格条件 アクセス 再付与/有効期限のルール
コミュニティ 誰でも利用可能。キーは不要 約30日遅延した完全なカタログ 常時利用可能。発行なし
Star + follow GitHub OAuth により、リポジトリへの Star と所有者のフォローの両方を確認 ライブカタログを1回読み取り、その後はコミュニティに移行 ログインごとに1回発行。再発行なし
コントリビューター Top 10 最新の確定済み週間ランキングで1~10位 365日間のライブアクセス 申請時に付与。ランキング外になっても、付与済み期間は短縮されない
コントリビューター Top 100 同ランキングで11~100位 90日間のライブアクセス 同じ申請ベース/冪等な付与ルール
サポーター購入 6か月、1年、または生涯利用権の買い切り購入 ライブカタログ、署名済みライブオファー、および Intel 自動更新なし
寄付/手動付与 所有者が審査した寄付、または所有者による明示的な日数/生涯利用権の付与 付与期間中、同じライブ利用権 監査済みの冪等な付与

マージ済み PR、コミット、および変更行数は、ランキングの入力情報にのみ使用されます。PR 数にかかわらず、ログイン時に Top 100 圏外であれば、 コントリビューター利用権は付与されません。有限期間の購入、寄付、コントリビューター期間、および 手動付与は、現在の有効期限を起点として累積されます。生涯利用権が優先されます。ランキングの変動によって、 すでに付与された期間が遡及的に取り消されたり短縮されたりすることはありません。

ホステッドライセンスは個人用であり、ユーザー向けのルールでは、一度に有効にできるインストールは1つです。この リリースでは、ハードウェアロックを謳っていません。OSS 同期ではハードウェアをフィンガープリント化せず、 暗号化されたデバイスリースも維持しません。上記の検証済みプライベートサーバーリビジョンで実装されている制御は、 利用権の検証に加え、同一の有効なキーが24時間以内に4つ目の異なる IP から確認された場合に手動レビュー用シグナルを生成することです。 このシグナルによって、キーが自動的にブロックまたは失効されることはありません。復旧時には、 既存の有効期限を維持したまま、紛失したキーを失効させて置き換えます。購入または付与された期間が 最初から再開されることはありません。

ライブオファーは手動でキュレーションされており、変更または期限切れになる場合があります。オプトイン画面には、正確な プライバシー境界も明記されています。署名済みのカタログ/紹介メタデータがダウンロードされます。有効なキーがある場合は、さらに 署名済みオファーと Intel のロックが解除されます。Bearer キーと通常の接続メタデータはホステッドサービスに送信されます。 プロンプト、応答、会話、プロバイダー認証情報、モデル通信、稼働時間、レイテンシー、およびローカルの プロバイダー設定は送信されません。


アクティベーション画面(/dashboard/radar)には、サポーターキーを取得するための2つのフローへのリンクがあります。OSSリポジトリ自体がキーを発行したり、決済コードを実行したり、価格を明示したりすることはありません。価格はこのリポジトリ内ではなく、リンク先のページで全面的に決定・表示されます(仕様決定D14)。

  • 「コントリビューターです」 — RADAR_CONTRIBUTOR_CLAIM_URL(デフォルト: https://radar.omniroute.online/auth/github)を開きます。これは、非公開のRadarサーバーでホストされるGitHub OAuth申請フローです。直近の完了済み週間ランキングを確認し、上位10位には365日間、11~100位には90日間のアクセス権を付与します。上位100位圏外では、PR数によってアクセス権が付与されることはありません。代わりに、このフローでは別途用意された、スター付与 + フォローによる1回限りのレベルを確認します。
  • 「プロジェクトを支援する」 — RADAR_SUPPORTER_PLANS_URL(デフォルト: https://radar.omniroute.online/planos)を開きます。これは、1回払いの6か月、1年、および永久利用オプションを提供するホスト済みページです。OSSページには引き続き金額は表示されません。

どちらのURLもサーバー側で解決され(src/lib/radar/links.ts、RADAR_FEED_URLと同じ環境変数によるオーバーライドパターン)、既存のGET /api/radar/settingsレスポンス(contributorClaimUrl、supporterPlansUrl)を通じてダッシュボードへ中継されます。クライアントコンポーネントがprocess.envを直接読み取ることはありません。

変数 用途
RADAR_CONTRIBUTOR_CLAIM_URL コントリビューター申請URLを上書きします(デフォルト:https://radar.omniroute.online/auth/github)。
RADAR_SUPPORTER_PLANS_URL サポータープランURLを上書きします(デフォルト:https://radar.omniroute.online/planos)。

紛失したサポーターキーの復旧

Section titled “紛失したサポーターキーの復旧”

ホスト済みサービスの復旧エントリーポイントはhttps://radar.omniroute.online/recoverです。プランページからもリンクされています。ローカルインストール環境は購入者またはコントリビューターのメールアドレスを受け取らず、暗号化された設定から生のキーを復元することもできないため、復旧処理はすべてOSSクライアントの外部で行われます。

  1. キーに関連付けられたメールアドレスを送信します。復旧可能なライセンスが存在するかどうかにかかわらず、サービスは同じ受付完了ページを返すため、このフォームからアカウントの存在を特定することはできません。
  2. 対象となる場合、配信ワーカーが有効期間の短い1回限りのリンクを送信します。このリンクを開くと、トークンは直ちに一時的な暗号化済みHttpOnly/Secure Cookieへ移され、クリーンな/recover URLへリダイレクトされます。ページには、トークン、メールアドレス、古いキー、または交換用キーは一切含まれません。
  3. 失効を確認します。非公開サービスは以前のキーを失効させ、同じプラン/有効期限を持つ交換用キーを作成し、1つのトランザクション内でメール送信キューに追加します。交換用キーがブラウザーに返されることはありません。
  4. 交換用キーを/dashboard/radarに貼り付けます。この時点で古いキーはcommunityへ降格し、交換用キーでは検証済みのlive同期が実行される必要があります。同じ復旧リンクを再度開いた場合は、無効/期限切れを示す汎用レスポンスで失敗する必要があります。

ホスト済みの復旧ルートとメールワーカーは、コード内に存在していても、特定のデプロイ環境では利用できない場合があります。サーバーがデプロイされ、管理された受信者を使用するよう配信プロバイダーが設定され、1回限りのリンクを使ったフロー全体がテストされるまでは、このフローを本番環境対応済みとしないでください。

訪問者がキー(omr_ + 40桁の16進文字)を取得すると、アクティベーション画面(src/app/(dashboard)/dashboard/radar/page.tsx)では、主な操作経路としてキー貼り付け用の入力欄が表示されます。キーを貼り付けて送信すると、POST /api/radar/settings({ optIn: true, supporterKey })が1回呼び出されます。つまり、キーを貼り付けることでキーの設定とオプトインが同時に行われ、画面のロックが解除されます。形式(omr_ + 40桁の16進文字)は、UX向上のため、まず共有のisValidSupporterKeyFormat()ヘルパー(src/lib/radar/supporterKey.ts)を使用してクライアント側で確認されますが、いずれの場合もサーバーのZodスキーマによる検証が正式な判定となります。キーが設定されると、アクティベーション画面には空の入力欄の代わりに、GET /api/radar/settingsから取得したマスク形式(supporterKeyMasked)が表示され、新しいキーを貼り付けるための「キーを変更」コントロールが提供されます。生のキーが再表示されることはありません。上記の2つの申請/プランボタンは、最初にキーを_取得する_ための手段として引き続き使用されます。この入力欄は、すでにキーを持っている運用担当者がキーを有効化する場所です。

エンドツーエンドのアクティベーションとガイド付きセットアップ

Section titled “エンドツーエンドのアクティベーションとガイド付きセットアップ”

非公開のフィードサービスとこのOSSクライアントの間には、意図的に限定された境界があります。サービスはサポーターキーを発行および検証し、ローカルのOmniRouteインストール環境はキーを暗号化し、署名済みアーティファクトをサーバー側で同期し、プロバイダーのセットアップを案内します。支援付き検証の順序は次のとおりです。

  1. コントリビューター向け請求、plans/checkout、復旧フロー、または認可済みのプライベートサーバー運用者から、新規発行または復旧されたキーを取得します。生のキーをログ、スクリーンショット、Issue のコメント、コマンドライン引数に貼り付けないでください。
  2. ローカルの OmniRoute インストールで RADAR_ENABLED 機能フラグを有効にします。これにより UI が表示されますが、別途オプトインを保存するまではネットワーク通信を行いません。
  3. /dashboard/radar を開き、キーを貼り付けて有効化します。ブラウザーはローカルの POST /api/radar/settings を 1 回、{ optIn: true, supporterKey } とともに送信します。キーはローカルで暗号化され、レスポンスには omr_****&lt;last4&gt; のみが含まれます。
  4. 有効化画面でカタログ同期が実行されるのを待つか、今すぐ同期を選択します。ページに live、フィードのバージョン、取得時刻が表示されることを確認します。認証済みのローカル診断では、GET /api/radar/status により、キーを返すことなくオプトインとキーの有無、および 4 つのキャッシュ状態を確認できます。POST /api/radar/sync-all を使用すると、カタログ、紹介、オファー、Intel を明示的に更新できます。
  5. /dashboard/radar/setup?provider=&lt;provider&gt; を開きます。プロバイダーが管理する認証情報 URL に移動し、API キーを追加を選択して、実際のプロバイダーフォームから保存します。その後ガイドに戻り、接続をテストを実行します。ガイドでは通常の /api/providers および /api/providers/&lt;connection-id&gt;/test ルートを使用します。Radar 用の認証情報を別途作成することはありません。
  6. 互換性のあるプロバイダー接続が少なくとも 2 つ有効になった後、/dashboard/radar/combos を開きます。提案されたファミリーを確認し、既存のコンボ API を使用してコンボを作成します。オファーと Intel は、引き続きライブ専用の個別の署名付きキャッシュとして扱われ、それぞれの Radar 専用ページで確認できます。
  7. /dashboard/radar とセットアップページを再読み込みします。オプトイン、マスクされたキーの状態、検証済みキャッシュ、保存済みのプロバイダー接続、テスト操作が再読み込み後も維持されている必要があります。生のキーとプロバイダーの認証情報が表示されなくなった後にのみ、証跡を取得してください。

キーを保存しただけでは、ライブ利用資格の証明にはなりません。証明となるのは、プライベートサービスの GET /v1/license/check の結果、OSS カタログから提供される live ティア、検証済みの署名付きキャッシュ、および実際のプロバイダー接続/テストフローの組み合わせです。無効、期限切れ、または失効済みのキーの場合、カタログは安全に community へフォールバックします。ライブキーの検証成功として報告してはなりません。

プライベート管理パネルへのリンク

Section titled “プライベート管理パネルへのリンク”

RADAR_ADMIN_URL を設定すると、Costs サイドバーセクション内のユーザー向け Radar 項目の直後に、オプションで Radar Admin ↗ が追加されます。デフォルト値は意図的に設定されていません。変数が未設定または無効な場合、静的サイドバー、コマンドパレット、サイドバーのカスタマイズ画面に管理項目やプライベート URL は表示されません。

この値はサーバー側で解決され、管理認証済みの GET /api/settings レスポンスを介して、認証済みのダッシュボードセッション、またはローカルのログイン不要ブートストラップ中に信頼されたループバック所有者にのみ中継されます。CLI、内部サービス、および manage スコープの API キー認証には提供されません。ブラウザーは外部リンクを生成する前にレスポンスを再度検証します。リンクは noopener noreferrer を指定して開きます。

認証情報を含まない HTTPS のトンネル/tailnet URL を使用してください。プレーン HTTP は、http://127.0.0.1:9351 のようなループバック SSH フォワードの場合にのみ許可されます。それ以外のスキーム、埋め込まれた認証情報、不正な形式の URL、リモート HTTP 宛先は安全側に倒して拒否され、ナビゲーションは機能しないままになります。


正確なバイト列に対する Ed25519 署名

Section titled “正確なバイト列に対する Ed25519 署名”

フィードのペイロードは Ed25519 で署名されています。verifyFeedBytes() (src/lib/radar/verify.ts)は、ネットワーク経由で受信した正確なレスポンスバイト列 に対して署名を検証します。検証前にペイロードが再シリアライズされることはないため、 バイト単位の再エンコードによって署名チェックが暗黙に無効化または回避されることはありません。 検証に失敗した場合(invalid_signature)、ペイロードが解析またはキャッシュされる前に 同期が中止されます。

検証用公開鍵は src/lib/radar/pinnedKeys.ts (PINNED_FEED_PUBLIC_KEYS)に固定されています。これは配列になっているため、 ローテーションに先立って新しい鍵を先頭に追加でき、以前の鍵で署名された古いキャッシュ済みフィードも、 再同期されるまでは引き続き有効です。

フォークに適した環境変数による上書き

Section titled “フォークに適した環境変数による上書き”

2 つの環境変数を使用すると、フォークやセルフホスト環境で、デフォルトの OmniRoute サービスではなく 独自のフィードをクライアントに参照させることができます。以下の フィードをセルフホストする方法を参照してください。

変数 用途
RADAR_FEED_URL フィードのベース URL を上書きします(デフォルトは https://radar.omniroute.online)。
RADAR_FEED_PUBKEY 固定公開鍵を上書きし(base64-DER SPKI または PEM)、組み込み配列をこの単一の鍵で置き換えます。

syncRadar() は、ダウンロードしたフィードの version が現在キャッシュされているバージョンより 厳密に新しくない場合、そのフィードを拒否します(compareVersions()、ドット区切りの YYYY.MM.DD.n 比較)— { status: "stale" }。これにより、侵害された、または誤って構成された フィードエンドポイントが、クライアントを以前の別署名のペイロードへロールバックすることを防ぎます。

2 つの日付と、両方を保持する理由

Section titled “2 つの日付と、両方を保持する理由”

キャッシュされたフィードには 2 つの異なる日付が含まれます。両者の混同を防ぐことこそが、 両方を保持する理由です。

フィールド 取得元 示す内容
generatedAt 署名済みフィード本文 データの古さ
fetchedAt このインストールの時計 このインストールがダウンロードした時刻

数分前に取得されたフィードでも、数週間前の数値が含まれている場合があります。そのため、 fetchedAt だけでは、そのオーバーレイが基盤となるベースラインより新しいかどうかを 運用者が判断できません。両方とも radar_feed_cache に永続化され、 getRadarCatalog().meta から返され、GET /api/radar/status で個別に報告されます。 generated_at カラムが存在する前(マイグレーション 163)にキャッシュされた行は null として読み戻されます。取得時刻を代用するのではなく、不明なものは不明のまま維持されます。 radar_referrals_cache は、マイグレーション 142 以降、独自の generated_at を保持しています。

前述のバージョン下限では、いずれの日付でもなく version を比較します。

意図的に残されている相違点が 2 つあります。ダッシュボードには依然として Last fetched のみが 表示されるため、そこでビルド日を確認できるようにするには、新しいラベル(および 41 ロケール分の エントリ)が必要です。また、offers および intel のキャッシュは、フィードスキーマにビルド日が 含まれているにもかかわらず、ビルド日を一切保持していません。そのため、GET /api/radar/status はこれら 2 つについて、「不明」と解釈される null を報告するのではなく、 該当フィールド自体を省略します。

ダウンロードされたバイト列は、署名検証の後に解析され、 RadarFeedSchema(src/lib/radar/feedSchema.ts、Zod スキーマ)に対して検証されます。 スキーマが一致しない場合は { status: "invalid_schema" } が返され、キャッシュは 変更されません。キャッシュされたペイロードも、読み取りのたびに (getRadarCatalog() で)防御的に再検証されます。破損した、または手動編集された キャッシュ行は、提供されるのではなくベースラインへフォールバックします。

レスポンスサイズの上限(10 MB)

Section titled “レスポンスサイズの上限(10 MB)”

syncRadar() は、フィードのレスポンス本文に 10 MB の厳格な上限を適用します。 署名済みフィードは KB 規模の JSON ドキュメントであるため、これを超えるものは正当な カタログではなく、誤って構成された、または悪意のある RADAR_FEED_URL (あるいは不正なデータを返す上流)を示します。制限は次の 2 層で適用されます。

  1. Content-Length の事前チェックにより、ヘッダーですでに上限超過の値が宣言されている場合は、 本文の読み取りを完全にスキップします。
  2. 本文の読み取り中に累計サイズをチェックすることで、Content-Length が存在しない場合や、 実際のサイズを過少申告している場合でも上限を適用します。ヘッダーだけを信頼することはありません。 蓄積したチャンクを連結することで、その後の Ed25519 署名チェックに必要な正確なバイト列が 保持されます。

上限を超えると { status: "too_large" } が返され、キャッシュは変更されません。 これは、その他すべての同期失敗(invalid_signature、invalid_schema、stale)と 同じ非破壊的なパターンに従います。


フィードスキーマには tier: "community" | "live" フィールドが含まれます。この値は、リクエスト(サポーターキーの有無と有効性)に基づいてフィードサービスがサーバー側で決定します。クライアントが自身のティアを決定することはありません。

  • community — 最新データから約30日遅れて提供される無料カタログです。未認証のリクエスト、または無効なキーを使用したリクエストには、これが提供されます。
  • live — 有効なサポーターキーを伴うリクエストに提供される、最新のカタログです。

無効または期限切れのサポーターキーは community にフォールバックします。エラーになることはありません。 同期処理では、署名・スキーマ・バージョンの失敗(いずれも回復可能で、キャッシュ済み状態にとって致命的ではありません)と、成功を示す { status: "updated", version, tier } のみを区別します。クライアントが処理する必要のあるティア固有のエラーパスはありません。

提供されるティアは署名済み本文ではなくレスポンスヘッダーから取得される

Section titled “提供されるティアは署名済み本文ではなくレスポンスヘッダーから取得される”

署名済みフィードの本文にある tier フィールドは常に "live" です。フィードサービスは、バージョンごとに2つの署名済み成果物を配信します。live には現在のキャンペーンが含まれ、community ではそれらが省略されます。各成果物は、それぞれの正確なバイト列に対して署名されます。本文は引き続き利用資格の判定には使われません。リクエストに対して実際に選択されたティアは、リクエストの Authorization キーに基づいてサーバー側で決定され、x-omniroute-feed-tier レスポンスヘッダーに格納されます。

syncRadar()(src/lib/radar/sync.ts::parseServedTierHeader())は、クライアントが信頼すべきティアを解決する唯一の場所です。

  1. x-omniroute-feed-tier を RadarTierSchema(Zod)で解析します。ヘッダーが存在しない場合、または値が厳密に "community" か "live" ではない場合は、存在しないものとして扱われます(そのままキャッシュ/UI に信頼できる値として渡されることはありません。これにより、このヘッダーが導入される前の古いフィードサーバーにも対応します)。
  2. 手順1で値が得られなかった場合に限り、署名済み本文の tier フィールド(常に "live")へフォールバックします。
  3. 解決されたティアがキャッシュされ、{ status: "updated", version, tier } として返されます。ダッシュボードに表示されるのはこの値であり、本文内の未加工のフィールドではありません。

読み取り時のオーバーレイマージ規則

Section titled “読み取り時のオーバーレイマージ規則”

applyFeed()(src/lib/radar/applyFeed.ts)は、getRadarCatalog() 内での読み取り時に、キャッシュ済みフィードを静的ベースラインの上にマージします。ベースライン配列(FREE_MODEL_BUDGETS)が変更されることはありません。呼び出しのたびに新しい MergedEntry[] が計算されます。

優先順位に従った4つの規則があります。

  1. フィードがローカルオーバーライドを上書きすることはありません。 フィールド単位で処理されます。オペレーターがエントリのフィールドをカスタマイズしている場合(provider:modelId をキーとする localOverrides マップ)、そのフィールドに対するフィードの値はスキップされ、オペレーターの値が優先されます。
  2. enabled: false は、出所を記録したうえでエントリを無効にします。 エントリを無効化するフィードエントリは、マージ結果に enabled: false と disabledBy: "radar" を設定します。これにより、エントリが利用可能から無効へ変わった_理由_を UI で説明できます。
  3. フィードに存在しないユーザー追加エントリは、そのまま維持されます。 ベースラインにのみ存在するエントリ(またはローカルで追加されたエントリ)で、対応するフィードエントリがないものは、変更されずにそのまま引き継がれます。
  4. トゥームストーンが設定されたエントリが復活することはありません。 オペレーターが明示的にエントリを削除した場合(tombstones セット)、後のバージョンでフィードがその provider:modelId を再追加しても、そのエントリは復活しません。

編集可能なフィールドとトゥームストーンは、radar_local_model_state(マイグレーション 153_radar_local_model_state.sql)に永続化されます。公開 DB アダプター(src/lib/db/radar.ts)は、それらの行を applyFeed() が使用する localOverrides マップと tombstones セットに変換します。本番環境の getRadarCatalog() は、フラグ、キャッシュ、スキーマの各ゲートを通過した後に、その状態を読み込みます。オペレーターが編集できるのは displayName と enabled のみです。プロバイダー/モデルの識別情報、フィードの出所、クォータ、機能、利用規約、セットアップデータは、このインターフェースを通じて書き込むことはできません。

ダッシュボードでは、次の4つのローカル操作を利用できます。

  • 編集では、ローカルの表示名と有効状態を変更します。
  • ローカル変更をリセットでは、トゥームストーンを変更せずに、編集可能な両方のフィールドをクリアします。
  • 非表示ではトゥームストーンを作成し、以後のフィード更新でその行が再作成されないようにします。
  • 復元ではトゥームストーンを削除します。別途保存されたオーバーライドは引き続き有効です。

フィードの enabled: false は、安全性のための例外として維持されます。古いローカルの enabled: true よりも優先され、マージされたエントリを無効なままにし、disabledBy: "radar" を記録します。

カタログの公開には schemaVersion: 2 を使用します。contextWindow および tools、vision、thinking の各値は、それぞれ独立した number | null / boolean | null です。null は不明を意味し、false は、D16 で確認済みの公式プロバイダーソースが、その機能が存在しないと明示していることを意味します。OmniRoute 内部のレジストリ/モデル仕様フラグが、そのままフィード上の事実へ昇格されることはありません。クライアントは引き続き v1 スナップショットを受け入れます。以前のビルダーでは不在を示すプレースホルダーとして false を使用していたため、v1 の false は不明へ正規化されますが、v1 の true は事実として維持されます。不明なスキーマバージョンは安全側に倒して失敗し、最後に有効だったキャッシュは引き続き利用できます。コンテキストまたは機能の値が null ではないすべての v2 モデルには、認証情報なしでアクセスできる HTTPS の metadataEvidenceUrls[] が必要です。これがない場合、スキーマ検証が失敗し、キャッシュは置き換えられません。カタログテーブルでは、3つの状態すべてを ✓、✕、? として表示します。

ガイド付きコンボと MCP アクセス

Section titled “ガイド付きコンボと MCP アクセス”

確認済みの familyId 値は読み取り時のオーバーレイ後も維持され、純粋な buildRadarComboSuggestions() モジュール(src/lib/radar/comboSuggestions.ts)を駆動します。ファミリーが提案されるのは、少なくとも2つの異なるプロバイダーにアクティブな接続があり、厳選された正確なモデル ID を公開している場合のみです。無効なモデル、非アクティブなプロバイダー、欠落したモデル ID、単独のファミリー、曖昧なエイリアス/プレフィックス一致は、安全側に倒して除外されます。提案では既存の priority 戦略を使用し、定期的な月間予算が大きい順に並べます。UI は POST /api/combos のみを通じてそれらを作成します。

ガイド付き UI は /dashboard/radar/combos にあります。この UI はローカルの GET /api/radar/catalog および GET /api/combos/builder/options エンドポイントのみを読み取ります。Radar の同期を実行したり、 プロバイダーの認証情報を読み取ったり、コンボデータベースへ直接書き込んだりすることはありません。

MCP クライアントは、omniroute_radar_catalog(read:radar)を使用して同じローカルプロジェクションを読み取れます。 オプションの provider、familyId、enabledOnly フィルターは、ローカルの GET /api/radar/catalog を 1 回読み取った後に評価されます。制限された出力には、カタログのメタデータに加えて、プロバイダー/モデル、 表示名、familyId、クォータ、機能、有効状態、オリジン、disabledBy が含まれます。セットアップ URL、 手順、接続、メールアドレス、キー、紹介データが返されることはありません。このツールは 読み取り専用であり、/api/radar/sync を呼び出すことはありません。

マージされた各エントリには、UI がバッジとして表示する origin フィールドがあります。

  • "baseline" — 静的リリースカタログから変更されていません。
  • "radar" — 1 つ以上のフィールドがフィードによって更新されています。
  • "local" — オペレーターがこのエントリに少なくとも 1 つのローカルオーバーライドを設定しています(フィードの内容に関係なく、 ルール 1 に従ってローカルオーバーライドが常にフィードより優先されます)。

ローカルサーフェス — フィードプロキシにはしない

Section titled “ローカルサーフェス — フィードプロキシにはしない”

以下のローカル Radar ルート群は、src/app/api/radar/ 配下の UI を支えます。

ルート メソッド 目的
/api/radar/catalog GET ローカルキャッシュから統合済みカタログ(getRadarCatalog())を返します。
/api/radar/sync POST サーバー側で syncRadar() をトリガーし、その結果のステータスを返します。
/api/radar/settings GET { optIn, hasSupporterKey, supporterKeyMasked } を返します。生のキーは決して返しません。
/api/radar/settings POST オプトイン設定および/または(暗号化された)サポーターキーを設定します。
/api/radar/referrals GET ローカルキャッシュから { fixed, campaigns, tier } を返します。以下の紹介リンクを参照してください。
/api/radar/offers GET 検証済みのローカルライブキャッシュから有効なオファーを返します。サポーターキーは決して返しません。
/api/radar/offers/sync POST サーバー側のライブキー専用 syncRadarOffers() パイプラインをトリガーします。
/api/radar/intel GET 検証済みのローカルライブ Intel とサポーター認識を示すブール値を返します。ID やキーは決して返しません。
/api/radar/intel/sync POST サーバー側のライブキー専用 syncRadarIntel() パイプラインをトリガーします。
/api/radar/status GET シークレットを含めず、カタログ、紹介、オファー、Intel の読み取り専用ローカル設定/キャッシュステータスを返します。
/api/radar/sync-all POST サーバー側の 4 つの同期モジュールをすべて実行し、フィードごとに個別のステータスを返します。
/api/radar/local-model-state GET 編集/復元コントロール向けに、永続化されたオーバーライドとトゥームストーンを一覧表示します。
/api/radar/local-model-state PATCH 検証済みの displayName/enabled オーバーライドフィールドを設定またはクリアします。
/api/radar/local-model-state PUT { provider, modelId, tombstoned } を使用してトゥームストーンを作成または削除します。
/api/radar/local-model-state DELETE トゥームストーンを保持したまま、編集可能なオーバーライドフィールドをクリアします。

厳格なルール:これらのルートはフィードサービスを決してプロキシしません。 ブラウザが通信するのは常に ローカルの OmniRoute サーバーのみです。Radar サービスにアクセスする 4 つのモジュールは、 src/lib/radar/sync.ts(カタログ)、src/lib/radar/referralsSync.ts(紹介)、 src/lib/radar/offersSync.ts(オファー)、および src/lib/radar/intelSync.ts(Intel)です。 これらはすべてサーバー側で実行され、クライアント側では決して実行されません。これにより、 フィード URL とサポーターキーがクライアント向けネットワークトラフィックに一切露出しません。

すべての Radar エンドポイントは、RADAR_ENABLED がオフの場合に 404 を返します(上記の フラグを参照)。また、リポジトリ全体のエラーサニタイズルール (docs/security/ERROR_SANITIZATION.md)に従い、ルートのエラーレスポンスは buildErrorBody()/sanitizeErrorMessage() を経由します。

すべての Radar エンドポイントでは、isAuthenticated() (src/shared/utils/apiAuth.ts)による認証が必要です。これはダッシュボードのセッション Cookie、 または管理スコープの API キーであり、その他の /api/settings/* を保護するものと同じゲートです。 フラグがオフの場合の 404 チェックは、常に認証チェックの前に実行されます。そのため、 RADAR_ENABLED がオフのインストールはバイト単位で同一の状態を維持します (サーフェスが存在しないことを知るだけのために認証プロンプトが表示されることはありません)。 フラグがオンになると、未認証のリクエストには DB の読み取りや書き込みが行われる前に 401 が返されます。 認証状態に関係なく、GET /api/radar/settings が生のサポーターキーを返すことはありません。 返されるのはマスクされた形式と hasSupporterKey ブール値のみです。


オファーは独自の署名済みアーティファクト GET /v1/offers/latest を使用し、カタログまたはリファラルのキャッシュを共有することはありません。サーバーエンドポイントには、有効かつ稼働中のサポーター用 Bearer キーが必要です。コミュニティへのフォールバックはありません。そのため、機能フラグがオフの場合、運用者がオプトインしていない場合、またはサポーターキーが設定されていない場合、syncRadarOffers() はネットワークへアクセスする前に停止します。

GET が成功すると、クライアントはレスポンスの正確なバイト列に対する Ed25519 署名を検証し、RadarOffersFeedSchema を検証します。さらに、署名済み本文と x-omniroute-feed-tier ヘッダーの両方が live であることを必須とし、ドット区切りのバージョンが厳密に新しいことを確認した後にのみ、radar_offers_cache(マイグレーション 144_radar_offers_cache.sql)をアトミックに置き換えます。他のフィードで使用されるものと同じ、ヘッダーとストリームを合わせた 10 MB の上限が適用されます。署名、スキーマ、ティア、リプレイ、サイズ、HTTP、ネットワークに関するいずれの障害が発生しても、最後に検証されたキャッシュは保持されます。

非公開のオファー形式は、比較可能な 3 種類の特典をサポートします。ベーシスポイント単位の割合、通貨の最小単位で表したクレジット、またはトライアル日数です。パートナーオファーには、同種の公開ベースラインを含める必要があり、その特典はベースラインより厳密に大きくなければなりません。公式オファーにはパートナーベースラインはありません。URL は認証情報を含まない HTTPS でなければなりません。getRadarOffers() は防御的にキャッシュ済みペイロードを再検証し、ローカルで読み取るたびに期限切れのエントリを除外します。/dashboard/radar/offers はレンダリング前に有効期限を再度フィルタリングし、利用可能な場合はポルトガル語のテキストを使用し、それ以外の場合は英語にフォールバックします。また、パートナーオファーには明示的にラベルを付けます。

ブラウザーはローカルルートのみを呼び出します。マスク済みの設定スナップショットを読み取り、POST /api/radar/offers/sync によってサーバー側での更新を要求してから、GET /api/radar/offers を読み取ります。キーがない場合、フィードリクエストを試みる代わりに、既存のコントリビューター/サポート用リンクを表示します。外部オファーのリンクは、noopener noreferrer を指定して新しいタブで開きます。このリリースでは、radar_offers MCP ツールは公開されません。


Radar Intel、サポーターバッジ、および CLI

Section titled “Radar Intel、サポーターバッジ、および CLI”

Intel は GET /v1/intel/latest にある署名済みアーティファクトです。非公開の RadarIntelFeedSchema は、確認済みの比較に基づいて非公開キュレーターが算出した Radar 所有の ELO ランキングと、署名済みカタログスナップショットから算出した、カタログの経過時間/件数に関する事実ベースの差分のみを受け入れます。算出方法は、初期レーティング 1000、K=32 に固定されています。比較が 1 件も確認されていない場合は、空のランキングが有効です。クライアントがランキングを合成することはありません。

syncRadarIntel() は、オファーと同様に、サーバー側の Bearer、30 秒のタイムアウト、ストリーミングでの 10 MiB 上限、正確なバイト列に対する Ed25519 検証、厳格なスキーマ、本文とヘッダーの両方が live であることの要件、バージョン下限、および最後の正常なキャッシュの保持を適用します。検証済みのライブスナップショットが永続化されると、クライアントは radar:<sha256(supporter key)> を導出し、その一方向の識別子のみを保存して、専用の radar_supporter 認識イベントを生成します。その radar-supporter バッジは冪等で、付与される XP はゼロです。リーダーボードを更新したり、token_share を再利用したりすることはありません。/dashboard/radar/intel は、検証済みのローカルキャッシュメタデータのみを基にバッジをレンダリングします。

CLI は omniroute radar status と omniroute radar sync を公開します。どちらもローカルの OmniRoute API とのみ通信します。status は読み取り専用の GET /api/radar/status を実行し、sync は POST /api/radar/sync-all を 1 回送信して、フィードごとの結果を出力します。どちらのコマンドもサポーターキーを読み取ったり、受け取ったり、出力したりせず、Radar サービスに直接接続することもありません。


紹介リンク(無料クレジット)

Section titled “紹介リンク(無料クレジット)”

紹介リンクは、カタログフィードとは別の、独立した常に最新のフィード — GET /v1/referrals/latest — から配信されます。これは意図的な設計です。 コミュニティティアのカタログフィードは最大30日前のスナップショットであるため、 そこから抽出された紹介リンクも、サーバー上の実際のリンク一覧に対して同じ期間だけ 遅れていました(新しく追加された紹介リンクが無料/コミュニティユーザーに届くまで、 最大1か月かかることがありました)。紹介フィードは、独立した、はるかに短い間隔で 同期することで、この遅延を解消します。

// GET /v1/referrals/latest のレスポンスボディ(Ed25519署名付き。カタログ
// フィードと同じピン留めされた鍵を使用):
{
feed: "omniroute-radar-referrals",
schemaVersion: 1,
generatedAt: string, // ISO — 決定論的: 紹介リンク全体の max(updatedAt)。
// そのため、同一の2つのリクエストは完全に同じ
// 署名対象バイト列/署名を生成する
referrals: {
fixed: RadarReferral[], // 認証なし/コミュニティを含む、すべてのティアに存在
campaigns: RadarReferral[], // 有効かつ現在利用可能な(サポーター)Bearer
// キーの場合のみ設定される。認証なし/期限切れキーのリクエストでは []
},
}
// RadarReferral = { provider, url, kind: "fixo" | "campanha", validUntil,
// requiredAction, isDefault }

カタログフィードとは異なり、このボディには tier フィールドが一切含まれません — サーバーが Authorization キーに基づいてリクエストごとに含める内容を決定するため、 配信されたティアを判定する唯一の情報源は x-omniroute-feed-tier レスポンスヘッダーです (referralsSync.ts::syncRadarReferrals)。ヘッダーが存在しない、または認識されない場合は、 最小権限を前提として "community" にフォールバックします。 RadarReferralsFeedSchema(src/lib/radar/referralsFeedSchema.ts)はボディ全体を検証し、 feedSchema.ts からエクスポートされた、紹介ごとの同じ RadarReferralSchema を再利用するため、 両方のフィードで個々の紹介が同一の方法で検証されます。すべての RadarReferral.url は https:// でなければなりません — http:// URL はスキーマ検証に失敗します。

RadarFeedSchema(feedSchema.ts)に存在する従来のカタログ埋め込み型 referrals フィールドは、すでにキャッシュ済みのカタログフィードとの後方互換性のために維持されていますが、 getRadarReferrals() はもうこれを読み取りません — 以下のアクセサーを参照してください。

syncRadarReferrals()(src/lib/radar/referralsSync.ts)は、紹介のためにネットワークへ アクセスする唯一のモジュールであり、syncRadar() の契約を正確に踏襲します。フラグがオフ → disabled、オプトインがfalse → opt_out。${RADAR_FEED_URL}/v1/referrals/latest (カタログと同じ RADAR_FEED_URL/RADAR_FEED_PUBKEY フォーク用オーバーライド)を ダウンロードし、レスポンスの正確なバイト列に対するEd25519署名を検証 (verifyFeedBytes)し、RadarReferralsFeedSchema に対して検証したうえで、 radar_referrals_cache テーブル(マイグレーション 142_radar_referrals_cache.sql)に キャッシュします。このテーブルは、カタログの radar_feed_cache とは完全に独立しています。 10 MBのレスポンス上限と generatedAt の下限チェックにより、キャッシュ済みのものより古い 受信フィードを拒否し、以前の署名済みアーティファクトのリプレイを防止します。同一のタイムスタンプは 受け入れられます。サーバーは意図的に、コミュニティ版とライブ版の紹介に同じ決定論的な generatedAt を設定しているため、基礎となるリンクセットが変更されていなくても、 サポーターキーの変更後に署名済みペイロードと配信ティアを変更できます。例外をスローすることはなく、 常にステータスオブジェクトを返します。エラーの reason にスタックトレースが含まれることもありません。

2つのトリガーが紹介キャッシュを最新に保ちます。どちらもカタログ独自の 24時間間隔とは独立しています。

  • 読み取り時同期 — GET /api/radar/referrals 自体が、キャッシュが存在しないか、 REFERRALS_STALE_MS(1h、shouldSyncReferralsOnRead())より古い場合に、 レスポンスを返す前に syncRadarReferrals() をインラインで呼び出します。これにより、 バックグラウンドタイマーを待たず、次回のダッシュボード読み込み時には固定リンクが 「常に最新」になります。
  • スケジューラーによる副次同期 — radarSchedulerTick()(scheduler.ts)は、 カタログで使用されるものと同じ1時間ごとのティックで、紹介の鮮度を独立して評価し、 必要な場合は syncRadarReferrals() を呼び出します。これは、そのティックでカタログ自体の 同期時期に達しているかどうかに関係なく実行され、RadarTickResult の構造には一切影響しません (ベストエフォートの副作用のみで、エラーは握りつぶされます)。

src/lib/radar/index.ts は2つの読み取り専用アクセサーをエクスポートします。どちらも例外を スローしません(getRadarCatalog() と同じ防御的な契約です。フラグがオフ、キャッシュなし、 またはキャッシュ済みペイロードが破損している場合は、すべてエラーではなく空の構造に解決されます)。

  • getRadarReferrals() → { fixed: RadarReferral[], campaigns: RadarReferral[] }。 radar_referrals_cache から(getRadarReferralsCache() 経由で)読み取り、 RadarReferralsFeedSchema によって検証します — カタログキャッシュからではありません。
  • getDefaultReferralFor(provider) → そのプロバイダーについて isDefault: true である fixed 紹介、または null。fixed のみを参照します — キャンペーンがプロバイダーの 「デフォルト」リンクとして使用されることはありません。

実際の「どの紹介をプロバイダーのデフォルトにするか」というルールは、 findDefaultReferral()(src/lib/radar/referrals.ts)にあります。これは DB importを 一切含まない 小さな純粋関数であり、"use client" コンポーネントに安全にインポートできます。 getRadarReferrals/getDefaultReferralFor(index.ts 内)は @/lib/db/radar を 取り込むため、サーバー専用のままです。プロバイダーダッシュボードは、ブラウザーに better-sqlite3 がバンドルされるのを避けるため、index.ts ではなく referrals.ts を 直接インポートします(以下を参照)。

他のすべての Radar ルートとまったく同じゲート順序に従います。RADAR_ENABLED がオフ → 404(最初にチェックされ、バイト単位で同一の状態を維持)。未認証 → 401。それ以外の場合は、 古くなっていれば sync-on-read(上記参照)をトリガーし、その後 { fixed, campaigns, tier } とともに 200 を返します。tier は(直前に更新された可能性のある) キャッシュ行からそのまま取得される純粋な参考情報です(以下の UI における控えめなアップセル文言に使用されます)。 フィードサーバーへ直接プロキシすることはありません。ルート自体のソースには fetch( 呼び出しがなく、 ネットワーク通信は常に syncRadarReferrals() 内でのみ発生します。これは /api/radar/catalog と同じ、ローカルキャッシュのみを使用する原則です。

ダッシュボード UI — /dashboard/radar の「無料クレジット」タブ

Section titled “ダッシュボード UI — /dashboard/radar の「無料クレジット」タブ”

新しいルートを作成する代わりに、既存の Radar ページ (src/app/(dashboard)/dashboard/radar/page.tsx)を第 2 のタブとして再利用します。これは、 ページがすでに取得しているデータのバリエーションにすぎない機能のために、ルーティング/i18n の対象範囲を 増やさないためです。オプトインすると、タブバーには カタログ(既存のテーブル)と 無料クレジット が表示されます。

  • 固定リンクはプロバイダーごとにグループ化され、それぞれに requiredAction(存在する場合)と、 紹介 URL を開く target="_blank" rel="noopener noreferrer" ボタンが表示されます。
  • キャンペーンにも同じ情報に加えて、存在する場合は validUntil が表示されます。
  • campaigns が空で、かつ提供された tier が community の場合、UI には 短いアップセルメッセージ(「期間限定キャンペーンはサポーター向けの追加特典です」)が表示されます。これは 固定リンク一覧を隠したり制限したりすることは決してなく、固定リンクはすべての tier で常に完全に表示されます。 アップセルは控えめなメッセージにすぎず、アクセスを妨げるものではありません。

プロバイダー名の紹介リンク(プロバイダーダッシュボード)

Section titled “プロバイダー名の紹介リンク(プロバイダーダッシュボード)”

ProviderPageHeader(src/app/(dashboard)/dashboard/providers/[id]/components/)では、 providerInfo.website が存在する場合、すでにプロバイダー名からそこへリンクしていました。また、 収益化リンクの前例として、Kimi(Moonshot AI)のパートナーリンク注記 (providers.kimiPartnerLinkNote i18n キー)があります。D28 では新しいキーを導入せず、 Radar のデフォルト紹介リンクに、これとまったく同じ控えめな注記パターンを再利用します。

疎結合は意図的な設計です。

  • resolveProviderHeaderLink()(src/app/(dashboard)/dashboard/providers/providerPageUtils.ts) は純粋関数です。つまり、(staticWebsite, referralUrl) => { website, isReferralLink } であり、@/lib/radar や @/lib/db/* への依存はありません。providerPageUtils.ts 全体にも これらの import はありません( tests/unit/provider-header-referral-link.test.ts で検証されます)。
  • ProviderDetailPageClient.tsx("use client" コンポーネント)は、Radar データの取得を許可された 唯一の場所です。Radar ダッシュボードページ自体と同じローカルルートパターンである fetch("/api/radar/referrals") を使用し、DB に依存しない src/lib/radar/referrals.ts の findDefaultReferral() を使ってクライアント側でデフォルト紹介リンクを算出します。
  • RADAR_ENABLED がオフの場合、fetch は 404 を返し、referralUrl は null のままとなり、 resolveProviderHeaderLink() は静的カタログの website を変更せずに返します。つまり、 プロバイダーページはこの機能が存在する以前とバイト単位で同一です。キャッシュがまだ存在しない場合や、 その特定のプロバイダーにデフォルト紹介リンクがない場合も同じ結果になります。
  • デフォルト紹介リンクが適用される場合、ProviderPageHeader は isReferralLink を受け取り、 Kimi のパートナーリンクと同じ控えめな注記/ツールチップ (providers.kimiPartnerLinkNote キーを再利用)を表示します。新しい独自の視覚表現を 導入することはありません。

フィードをセルフホストする方法

Section titled “フィードをセルフホストする方法”

カタログを完全に制御したいフォークまたはセルフホスターは、クライアントコードを変更せずに独自のフィードサービスを実行できます。

  1. RadarFeedSchema(src/lib/radar/feedSchema.ts)を満たす JSON ボディを返す GET /v1/catalog/latest エンドポイントを提供します。トップレベルには feed: "omniroute-radar"、schemaVersion: 2、version、tier、providers、models、quirks、totals が必要です。x-omniroute-radar-schema: 2 を尊重してください。移行互換性を持つサーバーでは、このヘッダーがないリクエストに対し、個別に署名された v1 アーティファクトをデフォルトで返す必要があります。
  2. Ed25519 キーペアを使用してレスポンスの正確なバイト列に署名し、base64 形式の署名を x-omniroute-feed-signature レスポンスヘッダーで返します。
  3. RADAR_FEED_URL を新しいベース URL に、RADAR_FEED_PUBKEY を対応する公開鍵(base64-DER SPKI または PEM)に設定します。詳細については、環境変数リファレンスを参照してください。
  4. RADAR_ENABLED を有効にし、POST /api/radar/settings ({ optIn: true })を介してオプトインします。

これ以外のコード変更は必要ありません。verifyFeedBytes() はオーバーライドを自動的に取得し(src/lib/radar/pinnedKeys.ts の getFeedPublicKeys())、バージョン比較、スキーマ検証、マージルールはセルフホストされたフィードにも同様に適用されます。

紹介リンク(上記の紹介リンク(無料クレジット)を参照)は、独立した任意のアーティファクトです。/v1/catalog/latest のみを提供するフォークでも完全に動作します。syncRadarReferrals() は /v1/referrals/latest から 404 が返された場合、{ status: "error" } にフォールバックし、キャッシュは単に空のままになります。そのため、GET /api/radar/referrals はページの他の部分を失敗させることなく、引き続き { fixed: [], campaigns: [], tier: null } を返します。紹介リンクも提供するには、RadarReferralsFeedSchema(src/lib/radar/referralsFeedSchema.ts)を満たす GET /v1/referrals/latest を提供し、カタログフィードと同じ Ed25519 キーペアで署名します。

サポーター向けオファーも任意のアーティファクトです。これらを提供するには、閉じた RadarOffersFeedSchema(src/lib/radar/offersFeedSchema.ts)を使用して GET /v1/offers/latest を実装し、有効なエンタイトルメントを必須とし、x-omniroute-feed-tier: live を返し、正確なバイト列に同じ鍵で署名します。このエンドポイントを省略するフォークでも、カタログと紹介リンクの動作は変わりません。オファーの更新は既存データを破壊することなく失敗し、最後に検証されたローカルのオファーキャッシュは引き続き利用できます。

Intel も同様に任意です。セルフホスターは RadarIntelFeedSchema(src/lib/radar/intelFeedSchema.ts)を使用して GET /v1/intel/latest を提供し、有効なエンタイトルメントを必須とし、x-omniroute-feed-tier: live を返し、共有の Ed25519 鍵で正確なバイト列に署名できます。このエンドポイントを省略しても、カタログ、紹介リンク、オファーには影響しません。Intel の更新時には、最後に検証されたローカルスナップショットが保持されます。



OmniRoute ソースコード (a58000c7685f)

HagiCode

HagiCode は構造化ワークフロー、マルチエージェント実行、Hero Dungeon ビューを備えたエージェント型コーディングワークスペースです。

よりスマートで速く、楽しいエージェント型ワークフローで、使いやすいソフトウェアを形にします。

HagiCode ライトテーマのメイン画面
  • Smart構造化ワークフローは意図をアイデアから変更のリリースまで実行可能な道筋にします。
  • Efficientマルチエージェントのワークフローで調査、実装、レビューを並行して進めます。
  • FunHero Dungeon により長時間のコーディングを視覚的で協力的な体験にします。
HagiCode を見る