ホワイトラベルブランディングと販売代理店コンソール
DuDuClawは、ライセンスを受けたリセラーがdashboardをリブランディングできるようにします。プロダクト名、ロゴ、サブタイトル、会社情報が対象です。ただし、上流ベンダーのクレジット表記(嘟嘟數位科技有限公司 / DuDu Digital Technology Co., Ltd.)はAboutページに常に表示されたままです。このクレジットはすべてのレスポンスにおいて、バイナリに組み込まれた定数から構成されます。設定ファイルから読み込まれることはなく、いかなるRPC経由でも書き換えることはできません。
販売代理店向け(ホワイトラベルブランディング)
Section titled “販売代理店向け(ホワイトラベルブランディング)”前提条件:white_label 機能を含むライセンス階層で、当該インスタンス上で有効化済みであること(duduclaw license activate <blob>)。
- 管理者として 設定 → 品牌設定 (Branding) を開きます。ホワイトラベルライセンスがない場合、このタブは読み取り専用になり、アップグレードの案内が表示されます。
- プロダクト名、サブタイトル、会社名、ウェブサイト、サポートメール、説明文を入力します。
- ロゴをアップロードします。PNG、JPEG、WebPに対応、上限512 KB。SVGはスクリプトインジェクションのリスクがあるため拒否されます。画像はbase64のdata URIとして
~/.duduclaw/branding.jsonに保存され、外部ホスティングは一切使いません。 - 任意でアクセントカラー(
#rrggbb)を設定できます。dashboardはこの16進数値1つからprimary/accentのCSSスケール全体を導出します。空欄のままにすればデフォルトのアンバー色が維持されます。 - 任意でAbout HTMLブロックを書けます。Aboutページに表示される「販売代理店について」のリッチなセクションです。サーバー側で保守的なallowlist(
ammonia)によりサニタイズされ、少数の整形用タグのみ許可、<a>リンクには強制的にrel="nofollow noopener noreferrer" target="_blank"が付与され、<img>はロゴと同じdata:image/png|jpeg|webp(512 KB以下、マジックバイト検査あり)ルールに制限されます。style/class/id/on*、および<script>は除去されます。エディタはbranding.previewRPCを通じて、実際に保存される内容を正確にプレビューします。64 KBを超えると拒否されます。 - 保存すると、サイドバーのマーク、ログインページ、ブラウザタイトル、favicon、アクセントカラー、Aboutブロックが即座に更新されます。リセットでDuDuClawのデフォルトに戻せます。
About ページ(/about)は、上部にあなたのブランディングを表示し、下部に固定の「軟體開發|嘟嘟數位科技有限公司」ブロック、バージョン、ライセンス階層を表示します。
検証はfail-closedです。未知のフィールドは拒否され、テキストフィールドにはCJK-safeな長さの上限があり、ロゴは宣言されたマジックバイトと一致している必要があり、ライセンススナップショットが white_label を許可していない場合は branding.set / branding.reset が問答無用で拒否されます。
ベンダー向け(販売代理店コンソール)
Section titled “ベンダー向け(販売代理店コンソール)”管理者専用の /manage/distributors ページで、リセラーを登録し、マシンにひも付いたOEMライセンスキーを発行します。
-
発行者の署名鍵を設定します(ライセンスkeygenツールが出力するのと同じ、32バイトEd25519シード形式、base64、1行):
~/.duduclaw/config.toml [distributor]issuer_key_path = "/path/to/license-signing-v2.key"未設定の場合、発行は明示的なエラーとともに拒否され、コンソールにはセットアップ用のカードが代わりに表示されます。鍵の内容がログやレスポンスに現れることはありません。
-
販売代理店を追加し、キーを発行:リセラーが
duduclaw license fingerprintで取得したマシンフィンガープリントを貼り付け、期間を選び(デフォルト365日)、生成されたblobをコピーします。リセラーはduduclaw license activate <blob>でこれを有効化します。発行される各ライセンスは、記録される前にバイナリへ焼き込まれたv2公開鍵に対して自己検証されます。鍵ペアが一致しない場合は明確にエラーとなります。
-
失効は、ローカル台帳(
distributor.db)内でそのキーを失効済みとしてマークし、セキュリティ監査ログにも記録します。すでに有効化済みのインスタンスへの伝播は、後述するphone-home更新と署名済みCRLを通じて行われます。UIはこの時間差を正直に示し、即時失効であるかのように装いません。
発行済みキーを有効に保つ(更新と失効)
Section titled “発行済みキーを有効に保つ(更新と失効)”発行者キーが設定されている場合、オーナーのgatewayは自身が署名したキー向けに軽量なコントロールプレーンも提供し、60日間のオフラインダウングレードに引っかからないようにし、失効も伝播させます。以下の2つの公開エンドポイントは [distributor] issuer_key_path によって自己ゲートされます(未設定なら 404、つまり素のgatewayは何も公開しません):
| エンドポイント | 用途 |
|---|---|
POST /v1/license/refresh |
呼び出し元のライセンスを last_phone_home = now で再署名します。期間を延長することは絶対にありません。期限切れのキーは更新ではなく再発行によって更新されます。失効済みキーには revoked、フィンガープリント不一致や期限切れには 403 を返します。 |
GET /v1/license/crl |
署名済みの証明書失効リスト(クライアントが検証するのと同じ正規化ペイロードに対するEd25519署名)で、失効済みのすべての subscription_id を列挙します。TTLは7日です。 |
リセラー側のインスタンスはコード変更が一切不要です。環境変数を1つ設定してオーナーのgatewayを指すだけです:
# リセラー側のDuDuClawインスタンス上でexport DUDUCLAW_CONTROL_URL=https://your-gateway.example.comこれを設定すると、リセラーのgatewayは自身の階層ごとのスケジュールでphone-homeを行い、CRLをポーリングします。その結果:
- 更新によりライセンスは無期限に有効な状態を保てます(phone-homeが成功するたびに
last_phone_homeが再スタンプされます)。オーナー側のコンソールにも、各キーの最終更新時刻が「生存している」というシグナルとして表示されます。 - 失効は、phone-homeの間隔以内(OEM階層ではおおむね1週間)に、また独立してCRLのポーリング間隔(24時間)以内に、どちらか早い方でリセラー側に届きます。
発行者キーが設定されている限り、販売代理店コンソールには Endpoint active バッジと、このセットアップ用スニペットが表示されます。
正直な前提条件:リセラーが
DUDUCLAW_CONTROL_URLを設定せず(かつ到達可能なクラウドコントロールプレーンもない)場合、そのキーはphone-homeに60日間成功しないままだと、オープンソース階層へダウングレードされます。更新エンドポイントは、それを指すよう設定されたインスタンスに対してのみ、このダウングレードを取り除きます。
エンドポイントをキーに焼き込む([distributor] public_url)
Section titled “エンドポイントをキーに焼き込む([distributor] public_url)”リセラーに DUDUCLAW_CONTROL_URL の設定すら不要にしたい場合は、オーナーのgatewayの外部到達可能なURLを一度だけ宣言します:
# ~/.duduclaw/config.toml(オーナー側インスタンス)[distributor]issuer_key_path = "/path/to/license-signing-v2.key"public_url = "https://your-gateway.example.com"以降、distributor.issue はそのURLを、キー自身が保持する control_url として埋め込みます。販売代理店側のインスタンスは、次の順序でコントロールプレーンを解決します:DUDUCLAW_CONTROL_URL 環境変数 → キーの control_url → 組み込みのデフォルト値。つまり、public_url を設定した状態で発行されたキーは、クライアント側の設定ゼロでphone-homeと更新を行い、duduclaw license refresh も環境変数なしで動作します。control_url は署名対象ペイロードの一部ではありません(改変するには0600権限の license.json へのローカル書き込みアクセスが必要で、更新レスポンス自体も毎回署名検証されます。最悪の場合でも「URLに到達できない」というだけで、現状と変わりません)。更新時には元の control_url が保持されます。
ブランディングを顧客に配布する
Section titled “ブランディングを顧客に配布する”リセラーは通常、自社のブランドを顧客のインスタンス上にも表示したいと考えますが、顧客側にはホワイトラベルライセンスがありません。署名済みブランディングバンドルがこれを解決します。ブランディングはオーナーの発行者キーで署名され、~/.duduclaw/branding.bundle.json に有効なバンドルを見つけたインスタンスはそれを自動的に適用します。表示するだけならライセンスは不要です(編集には引き続きホワイトラベルライセンスが必要です)。上流ベンダーのクレジット表記は常に最上層に重ねられ、バンドルによってそれを消すことはできません。
バンドルの作成
- セルフサービス(オンライン):リセラー自身のホワイトラベルインスタンス上で 配布用バンドルを生成 をクリックします(RPC
branding.bundle.create)。gatewayは自身のsubscription_idとマシンフィンガープリント、現在のブランディングを、オーナーgatewayのPOST /v1/branding/sign(発行者キーで自己ゲートされ、IPごとに10回/分のレート制限あり)に送信します。オーナー側はサブスクリプションを再検証し(有効かつフィンガープリント一致、更新と同じゲート。失効/期限切れなら拒否)、権威あるロジックでブランディングを再サニタイズし、署名してダウンロード用バンドルを返します。 - オーナーによる共同署名(オフライン):リセラーのインスタンスがオーナーに到達できない場合、オーナーの /manage/distributors ページにはバンドル共同署名ダイアログ(RPC
distributor.bundle.sign)があります。販売代理店のブランディングJSONを貼り付けると、オーナーがローカルで発行者キーを使って署名します。
配布と適用
生成された branding.bundle.json を顧客の ~/.duduclaw/ に配置します(例えば、あなた自身のプロダクトインストーラーの一部として)。起動時、gatewayは次の順序でブランディングを解決します:
- ローカルの
branding.json(ライセンス済みエディタで設定)。存在すればこれが優先されます。 - 組み込みの発行者キーで署名を検証できる
branding.bundle.json。 - DuDuClaw組み込みのデフォルト値。
現在使用中のソースは、トップレベルの source フィールド(local / bundle / default)としてdashboardに報告されます。署名、スキーマ、フィールド検証のいずれかに失敗したバンドルは、警告1件とともに無視されます(fail-closedでデフォルトに戻ります)。バンドル本体は読み込み時にも再サニタイズされるため、手動で編集されたファイルがオーナーの署名検証をすり抜けて危険なHTMLを持ち込むことはできません。
RPC サーフェス
Section titled “RPC サーフェス”| メソッド | アクセス権限 |
|---|---|
branding.get、about.get |
ログイン済みの任意のユーザー(レスポンスに source、about_html、accent_color を含む) |
branding.set、branding.reset、branding.preview、branding.bundle.create |
管理者かつ white_label 機能が必要(fail-closed) |
distributor.status/list/add/update/remove/issue/revoke、distributor.bundle.sign |
管理者 |
HTTPコントロールプレーン(オーナーゲートウェイ、発行者キーでゲート)
Section titled “HTTPコントロールプレーン(オーナーゲートウェイ、発行者キーでゲート)”| エンドポイント | 用途 |
|---|---|
POST /v1/license/refresh |
ライセンスを再署名する(phone-home)。 |
GET /v1/license/crl |
署名済み失効リスト。 |
POST /v1/branding/sign |
資格のあるサブスクリプション向けにブランディングバンドルを署名する(10回/分/IPでレート制限)。 |