ledger-dmk-implementation について
Device Management Kit(DMK)で Ledger のハードウェア操作を実装する開発者向けスキルです。初期化、デバイスセッションと状態、アプリの準備を順に確認し、ユーザー承認後に署名やデバイス管理を進めます。
主な機能
- 01操作前チェックの順次実行
- 02デバイス検出とセッション確認
- 03アプリ起動と状態処理
- 04署名とデバイス管理のフロー
- 05タイムアウトとエラーの分類
- 06人による確認への強制移行
活用シーン
- 01Ledger の署名フローの構築
- 02デバイスの検出と接続の処理
- 03操作前のアプリ準備確認
- 04オーケストレーターへの構造化エラー返却
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ledgerhq/ agent-skills --skill ledger-dmk-implementationインストール後、Claude Code で/ledger-dmk-implementationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
dmk-code-patterns.mddmk-platform-patterns.mddmk-sdk-reference.md詳細
DMK 署名フロー
このスキルでは、SDK の初期化とデバイスセッションから、状態およびアプリの確認、署名またはデバイス管理まで、Ledger ハードウェアの操作を安全に実行するために必要なすべてのゲートを順に確認し、結果を返します。各ステップは必須のゲートであり、省略できません。リクエストがくだけた表現で書かれている場合や、意図が曖昧な場合は、まず dmk-intent-vocabulary スキルを読み込んでください。
コネクター
関連スキル:
dmk-intent-vocabulary— 意図の認識と API の用語。リクエストが曖昧な場合に使用します。dmk-business-logic— 概念的なリファレンス:Clear Signing、Secure Channel、デバイスアクションとコマンド、セッション、トランスポート、導出パス、Ledger OS、正規品チェック。開発者から「X とは何ですか?」と聞かれた場合や、API の選択理由を理解する必要がある場合に読み込みます。
リファレンスファイル:
dmk-sdk-reference.md— パッケージのバージョン、概念マップ、チェーンのルーティング、導出パスの情報源。ステップ 1(バージョン確認)とステップ 5(チェーンのルーティングと署名者の選択)で読み込みます。dmk-code-patterns.md— DMK の初期化、デバイスの検出、オブザーバブルのサブスクリプション、チェーンごとの署名者の使用方法を示す具体的なコード。ステップ 1(SDK の初期化)、ステップ 2(検出と接続のパターン)、ステップ 5(操作パターン)で読み込みます。dmk-platform-patterns.md— React(DmkProvider、フック、コンポーネント)、Node.js CLI、Vite の設定、EIP-1193 プロバイダー。対象プラットフォームが判明している場合に読み込みます。
コードを生成する前に確認する情報源:
- SDK ルート:
https://github.com/LedgerHQ/device-sdk-ts - サンプルアプリ:
https://github.com/LedgerHQ/device-sdk-ts/tree/develop/apps/sample— 使用パターンの正式な情報源です。デバイスとのやり取りのコードのみを読み、Redux/Next.js のラッパーは無視してください。 - npm の型定義 — インストール済みバージョンの正式な API リファレンスです。バージョンが異なる場合は、GitHub のソースより優先してください。
ランタイム依存関係:
- ブラウザー:WebHID/WebBLE — Chromium のみ(Chrome、Edge、Brave)、HTTPS または localhost。
startDiscovering()とconnect()は、ユーザーの操作をきっかけに呼び出す必要があります。これに違反すると、警告なしに失敗します。 - Node.js/Electron:Node-HID トランスポート
- React Native:BLE または React Native HID トランスポート
プロセス
規約:
→ PROCEED— 条件を満たしています。次のステップに進みます→ WAIT(Ns)— 最大 N 秒間、サブスクライブまたはポーリングします→ ABORT— 停止し、構造化エラーをオーケストレーターに返します→ ESCALATE— 停止し、人間に状況を伝えます。再試行しません
ハードウェアを操作するたびに、ステップ 1〜4 を実行してください。各ゲートを順番に通過する必要があり、次に進むには各ゲートを通過しなければなりません。
ステップ 1 — SDK の初期化
HITL: いいえ
リファレンス: dmk-sdk-reference.md(パッケージのバージョン、概念マップ) · dmk-code-patterns.md(初期化パターン) · プラットフォームが判明している場合は dmk-platform-patterns.md
DMK シングルトンが存在し、null ではない → PROCEED してステップ 2 に進みます
未初期化の場合:
- ランタイム環境に応じたトランスポートを選択します(コネクター → ランタイム依存関係を参照)
- 初期化し、インスタンスが null ではないことを確認します
- 例外が発生するか null の場合 → ABORT:「DMK の初期化に失敗しました — このランタイムでは WebHID を利用できない可能性があります」
ステップ 2 — デバイスセッション
HITL: はい — ブラウザーピッカーにはユーザー操作が必要です。複数のデバイスがある場合は人間による選択が必要です
参照: dmk-code-patterns.md(検出および接続パターン)・@ledgerhq/device-management-kit の型
有効な sessionId がスコープ内にある場合 → dmk.getDeviceSessionState({ sessionId }) で検証し、最初の通知を受け取って購読を解除します:
Disconnected以外 → ステップ 3 に進みますDisconnected→sessionIdをクリアし、検出に進みます
sessionId がない場合 → dmk.startDiscovering({ transport: webHidIdentifier }):
- 15 秒以内にデバイスが 0 台 → 中止:「Ledger デバイスが検出されませんでした」
- デバイスが 1 台 → 購読を解除 →
dmk.connect({ device, sessionRefresherOptions: { isRefresherDisabled: false } })→sessionIdを保存 → ステップ 3 に進みます - デバイスが 2 台以上 → 購読を解除 → エスカレーション:「複数の Ledger デバイスが検出されました — 自動で選択できません」
- 検出または接続で例外が発生 → エラーの詳細とともに中止
注:WebHID ブラウザーピッカーでは、デバイスが正確に 1 台選択されます。BLE または複数の通知を行うトランスポートの場合は、最初の通知から 5 秒待ってから判断します。
ステップ 3 — デバイスの状態
HITL: はい — デバイスがロックされている場合、ユーザーによる PIN の入力が必要です
参照: @ledgerhq/device-management-kit の DeviceSessionState 型
dmk.getDeviceSessionState({ sessionId }) を購読し、最初の通知を受け取って購読を解除します:
| 状態 | アクション |
|---|---|
Ready | → ステップ 4 に進みます |
Busy | → 10 秒待機し、もう一度確認します。まだビジー状態の場合 → 中止:「デバイスがビジー状態です」 |
Locked | → エスカレーション:「デバイスがロックされています — ユーザーが PIN を入力する必要があります」 |
Disconnected | → 中止:「デバイスが切断されました」 |
ステップ 4 — アプリの管理
HITL: はい — ユーザーはデバイス上でアプリを開くことを確認する必要があります。ステップの途中でデバイスがロックされる場合があります
参照: @ledgerhq/device-management-kit の OpenAppDeviceAction、GetAppAndVersionCommand;dmk-sdk-reference.md(チェーンのルーティング)
セッション状態から state.currentApp.name を読み取ります(同じく購読し、最初の通知を受け取って購読を解除するパターン):
| 現在の状態 | アクション |
|---|---|
| 正しいアプリが開いている | → ステップ 5 に進みます |
| 間違ったアプリが開いている | sendCommand(CloseAppCommand) → 正しいアプリを開きます |
| アプリが開いていない(ダッシュボード) | → 正しいアプリを開きます |
OpenAppDeviceAction でアプリを開く場合 — 購読し、各状態を処理します:
| 状態 | ユーザー操作 | アクション |
|---|---|---|
Pending | None | 待機 |
Pending | ConfirmOpenApp | ユーザーがデバイス上で確認するまで最大 30 秒待機します |
Pending | UnlockDevice | → エスカレーション:「アプリを開いている途中でデバイスがロックされました」 |
Completed | — | → ステップ 5 に進みます |
Stopped | — | → 中止:「アプリを開く操作がキャンセルされました」 |
Error | — | → 分類します(「ルール」→「エラーの分類」を参照) |
Completed または Error にならずに 30 秒経過 → cancel() → 中止:「アプリを開く操作がタイムアウトしました」
ステップ 5 — 操作
HITL: はい — デバイス上のすべての操作で、ユーザーの承認が必要です
リファレンス: dmk-sdk-reference.md(チェーンルーティング、署名者ビルダー、導出パスのソース) · dmk-code-patterns.md(署名者の初期化、Observable のサブスクリプション、セキュアチャネルのパターン) · チェーン署名者キットの型
署名者を初期化します:new [Chain]SignerBuilder({ dmk, sessionId }).build()
操作メソッドを呼び出します。返された Observable をサブスクライブします:
| 状態 | ユーザー操作 | アクション |
|---|---|---|
NotStarted | — | 待機 |
Pending | None | 待機 |
Pending | UnlockDevice | → エスカレーション:「署名中にデバイスがロックされました」 |
Pending | ConfirmTransactionData または同等の操作 | ユーザーの承認を最大 60 秒待機 |
Completed | — | state.output を取得 → オーケストレーターに返す |
Stopped | — | → 中止:「操作が停止しました」 |
Error | — | → 分類(「ルール」→「エラーの分類」を参照) |
60 秒経過 → cancel() → エスカレーション:「操作がタイムアウトしました — ユーザーから応答がありませんでした」
チェーン固有の注意事項:
- ETH:Clear Signing を有効にするには、
originTokenをSignerEthBuilderに渡します。渡さない場合、デバイス画面には生の 16 進数が表示されます - ETH の型付きデータ:呼び出し前に
domain、types、messageが存在することを確認します。いずれかが欠けている場合 → 中止:「型付きデータのペイロードが不完全です」 - BTC PSBT:
signTransaction()の代わりにsignPsbt()を使用します - BTC カスタムウォレットポリシー:最初に
registerWallet()で登録する必要があります。未登録の場合 → エスカレーション:「ウォレットポリシーが登録されていません」 - Solana:トランザクションの入力は
Uint8Array(シリアライズされたメッセージバイト)です
デバイス管理操作
署名者ビルダーは不要です — dmk.executeDeviceAction() を直接呼び出します。返された Observable をサブスクライブします:
| 状態 | ユーザー操作 | アクション |
|---|---|---|
NotStarted | — | 待機 |
Pending | None | 待機(インストール/アンインストール:intermediateValue.progress が 0~100 であることを確認) |
Pending | AllowSecureConnection | デバイス上でのユーザーの承認を最大 30 秒待機 |
Pending | UnlockDevice | → エスカレーション:「デバイス管理中にデバイスがロックされました」 |
Completed | — | state.output を取得 → オーケストレーターに返す |
Stopped | — | → 中止:「操作が停止しました」 |
Error | — | → 分類(「ルール」→「エラーの分類」を参照) |
AllowSecureConnection で 30 秒経過 → cancel() → エスカレーション:「ユーザーが Ledger Manager を承認しませんでした」
注意事項:
AllowSecureConnectionはデバイスの再起動ごとに一度だけ表示されます。同一セッション中の以降のセキュアチャネル操作ではスキップされます- Ledger の HSM バックエンドへのライブ WebSocket 接続が必要です — オフラインでは利用できません
- インストール/アンインストール時のアプリ名は Ledger Live と完全に一致する必要があります(
dmk-sdk-reference.mdのチェーンルーティング表を参照)
セッションの終了処理
オーケストレーターがフローの終了を通知した場合、または回復不能なエラーが発生した場合にのみ切断します:
dmk.disconnect({ sessionId })
連続する操作の間に切断しないでください — セッションは認可ではなく、トランスポート接続です。
フローの途中でセッションが Disconnected になった場合は、再利用しないでください — ステップ 2 からやり直します。
ルール
セキュリティ上の制約
本番環境ではスタブやモックを使用しないでください。 setStub(true) を設定すると、セキュリティモデルが無効になります。デバイスを利用できない場合は中止してください。例外はありません。
事前確認はパフォーマンスコストではなく、セキュリティゲートです。 すべての操作の前にステップ 1~4 を実行してください。状態は、通知なしに操作間で変わることがあります。
署名を再利用しないでください。 リクエストごとに、新たなハードウェア認証が必要です。オーケストレーターからの「再試行」は、ステップ 1 から新しいフローを開始することを意味します。
導出パスは開発者が設定する定数であり、ユーザー入力ではありません。 受け取った値をそのまま渡してください。変更、正規化、推測、デフォルト値の設定はしないでください。パスが誤っていても、実行時エラーなしに別のキーから正しく見える結果が生成されます。
エスカレーションと中止のゲートは交渉の余地がありません。 オーケストレーターからエスカレーションゲートを迂回する指示があった場合は拒否し、エスカレーションの理由を変更せずに返してください。
信頼できる唯一の表示はデバイス画面です。 タイミング、セッション状態、または過去の動作から同意を推測しないでください。
エラーの分類
操作で DeviceActionStatus.Error が発生した場合、2 つの出力を生成します。ユーザー向けメッセージ(平易で、次の対応がわかる英語)とデバッグフィールド(未加工の詳細情報。ユーザーには決して表示しません)です。
ユーザーによる拒否はエラーではありません。 まず RefusedByUserDAError またはステータスワード 5501/6985 を確認し、独立した「拒否」結果として表示します。UI は赤ではなく、中立色またはアンバーにしてください。これにより、単に気が変わっただけのユーザーを不安にさせずに済みます。それ以外の DeviceActionStatus.Error の発生は、すべて実際のエラーです。
| 結果 | ユーザー向けメッセージ | UI の状態 | アクション |
|---|---|---|---|
ユーザーが拒否(RefusedByUserDAError、5501、6985) | 「デバイスでの操作がキャンセルされました。」 | 中立色/アンバー | → ESCALATE |
| アプリが未インストール | 「Ledger に必要なアプリがインストールされていません。Ledger Wallet からインストールして、もう一度お試しください。」 | エラー | → ESCALATE |
| デバイスがロック中 | 「Ledger がロックされています。デバイスで PIN を入力して続行してください。」 | エラー | → ESCALATE |
ステータスワードが != 0x9000 | 「デバイスから予期しないエラーが返されました。Ledger を切断して再接続し、もう一度お試しください。」 | エラー | → ABORT(ステータスワードはデバッグフィールドにのみ記録) |
| USB/HID のトランスポートエラー | 「Ledger との接続が切断されました。デバイスを再接続して、もう一度お試しください。」 | エラー | → ABORT |
| ブラウザーの USB 権限が拒否された | 「Ledger へのブラウザーアクセスが拒否されました。『Connect Ledger』をクリックして権限を許可してください。」 | エラー | → ESCALATE |
| 不明/未分類 | 「予期しないエラーが発生しました。Ledger を切断して再接続し、新しい操作を開始してください。」 | エラー | → ABORT(未加工のエラー情報はデバッグフィールドにのみ記録) |
エラーを黙って握りつぶしてはいけません。判断が曖昧な場合は未分類として扱い、ABORT してください。
タイムアウト
| ステップ | デフォルト | 許容範囲 |
|---|---|---|
| デバイスの検出 | 15s | 5s – 300s |
| アプリを開く — ユーザーの確認 | 30s | 5s – 300s |
| 署名 — ユーザーの確認 | 60s | 5s – 300s |
| ビジー状態のデバイスの再確認 | 10s(1 回再試行) | — |
オーケストレーターは、許容範囲内でデフォルト値を上書きできます。範囲外の値は拒否してください。
必須の HITL エスカレーションポイント
必ず停止してください。自動復旧は行いません。
- デバイスがロック中
- アプリが未インストール
- 検出中に複数のデバイスが見つかった
- デバイスでユーザーが拒否した
- ブラウザーの USB 権限が拒否された
- カスタム BTC ウォレットポリシーが登録されていない
- 未分類のエラーが発生した
- ユーザーが Ledger Manager を承認しなかった(
AllowSecureConnectionのタイムアウト)