ledger-dmk-implementation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDMK Signing Flow
DMK 签名流程
This skill walks through every gate required to execute a Ledger hardware operation safely — from SDK init and device session, through state and app checks, to signing or device management — and returns the result. Every step is a gate; none are optional. If the request is phrased informally or the intent is ambiguous, load the skill first.
dmk-intent-vocabulary本技能会引导完成安全执行Ledger硬件操作所需的每一个关卡——从SDK初始化、设备会话建立,到状态与应用检查,再到签名或设备管理——并返回结果。每一步都是必经关卡,不可跳过。如果请求表述不正式或意图不明确,请先加载技能。
dmk-intent-vocabularyConnectors
相关资源
Sibling skills:
- — intent recognition and API vocabulary. Use when the request is ambiguous.
dmk-intent-vocabulary - — conceptual reference: Clear Signing, Secure Channel, Device Actions vs Commands, sessions, transports, derivation paths, Ledger OS, Genuine Check. Load when a developer asks "what is X?" or needs to understand the why behind an API decision.
dmk-business-logic
Reference files:
- — package versions, concept map, chain routing, derivation path sources. Load at Step 1 (version check) and Step 5 (chain routing and signer selection).
dmk-sdk-reference.md - — concrete code for DMK init, device discovery, observable subscription, and per-chain signer usage. Load at Step 1 (SDK init), Step 2 (discovery and connect patterns), and Step 5 (operation patterns).
dmk-code-patterns.md - — React (DmkProvider, hooks, components), Node.js CLI, Vite config, EIP-1193 provider. Load when the target platform is known.
dmk-platform-patterns.md
Source to verify before generating code:
- SDK root:
https://github.com/LedgerHQ/device-sdk-ts - Sample app: — authoritative usage patterns. Read device interaction code only — ignore Redux/Next.js wrappers.
https://github.com/LedgerHQ/device-sdk-ts/tree/develop/apps/sample - npm type definitions — authoritative API reference for the installed version. Prefer over GitHub source when versions differ.
Runtime dependencies:
- Browser: WebHID/WebBLE — Chromium only (Chrome, Edge, Brave), HTTPS or localhost. and
startDiscovering()must be called from a user gesture. Silent failure if violated.connect() - Node.js / Electron: Node-HID transport
- React Native: BLE or React Native HID transport
兄弟技能:
- — 意图识别与API词汇表。请求不明确时使用。
dmk-intent-vocabulary - — 概念参考:Clear Signing、安全通道、设备操作与命令、会话、传输协议、派生路径、Ledger OS、Genuine Check。当开发者询问「X是什么」或需要理解API决策背后的原因时加载。
dmk-business-logic
参考文件:
- — 包版本、概念图、链路由、派生路径来源。在步骤1(版本检查)和步骤5(链路由与签名者选择)时加载。
dmk-sdk-reference.md - — DMK初始化、设备发现、可观察订阅、各链签名者使用的具体代码。在步骤1(SDK初始化)、步骤2(发现与连接模式)和步骤5(操作模式)时加载。
dmk-code-patterns.md - — React(DmkProvider、hooks、组件)、Node.js CLI、Vite配置、EIP-1193 provider。当目标平台已知时加载。
dmk-platform-patterns.md
生成代码前需验证的来源:
- SDK仓库根目录:
https://github.com/LedgerHQ/device-sdk-ts - 示例应用:— 权威使用模式。仅阅读设备交互代码——忽略Redux/Next.js封装层。
https://github.com/LedgerHQ/device-sdk-ts/tree/develop/apps/sample - npm类型定义 — 已安装版本的权威API参考。版本不一致时,优先于GitHub源码使用。
运行时依赖:
- 浏览器:WebHID/WebBLE — 仅支持Chromium内核(Chrome、Edge、Brave),需HTTPS或localhost环境。和
startDiscovering()必须由用户手势触发。违反此规则会导致静默失败。connect() - Node.js / Electron:Node-HID传输协议
- React Native:BLE或React Native HID传输协议
The Process
流程
Conventions:
- — condition met, move to next step
→ PROCEED - — subscribe or poll for up to N seconds
→ WAIT(Ns) - — stop, return structured error to orchestrator
→ ABORT - — stop, surface to human, do not retry
→ ESCALATE
Run Steps 1–4 before every hardware operation. Sequential gates — each must pass before proceeding.
约定:
- — 条件满足,进入下一步
→ PROCEED - — 订阅或轮询最多N秒
→ WAIT(Ns) - — 停止,向编排器返回结构化错误
→ ABORT - — 停止,上报人工处理,不重试
→ ESCALATE
每次硬件操作前都需执行步骤1-4。这些是顺序关卡——每一步都必须通过才能继续。
Step 1 — SDK Initialization
步骤1 — SDK初始化
HITL: No
Reference: (package versions, concept map) · (init patterns) · if platform is known
dmk-sdk-reference.mddmk-code-patterns.mddmk-platform-patterns.mdDMK singleton exists and is non-null → PROCEED to Step 2
Not initialized:
- Select transport for runtime environment (see Connectors → Runtime dependencies)
- Initialize and confirm instance is non-null
- Throws or null → ABORT: "DMK initialization failed — WebHID may not be available in this runtime"
HITL: 否
参考: (包版本、概念图)· (初始化模式)· 若平台已知则参考
dmk-sdk-reference.mddmk-code-patterns.mddmk-platform-patterns.mdDMK单例存在且非空 → PROCEED 进入步骤2
未初始化:
- 为运行时环境选择传输协议(参见 相关资源 → 运行时依赖)
- 初始化并确认实例非空
- 抛出异常或返回空值 → ABORT:「DMK初始化失败——当前运行时可能不支持WebHID」
Step 2 — Device Session
步骤2 — 设备会话
HITL: Yes — browser picker requires a user gesture; multiple devices require human selection
Reference: (discovery and connect patterns) · types
dmk-code-patterns.md@ledgerhq/device-management-kitActive in scope → validate via , take first emission, unsubscribe:
sessionIddmk.getDeviceSessionState({ sessionId })- Not → PROCEED to Step 3
Disconnected - → clear
Disconnected, fall through to discoverysessionId
No → :
sessionIddmk.startDiscovering({ transport: webHidIdentifier })- 0 devices within 15s → ABORT: "No Ledger device detected"
- 1 device → unsubscribe → → store
dmk.connect({ device, sessionRefresherOptions: { isRefresherDisabled: false } })→ PROCEED to Step 3sessionId - 2+ devices → unsubscribe → ESCALATE: "Multiple Ledger devices detected — cannot select autonomously"
- Discovery or connect throws → ABORT with error detail
Note: WebHID browser picker guarantees exactly one device. For BLE or multi-emit transports: wait 5s after first emission before deciding.
HITL: 是 — 浏览器选择器需要用户手势;多设备场景需要人工选择
参考: (发现与连接模式)· 类型定义
dmk-code-patterns.md@ledgerhq/device-management-kit作用域内存在活跃的 → 通过验证,取第一个发射值后取消订阅:
sessionIddmk.getDeviceSessionState({ sessionId })- 非状态 → PROCEED 进入步骤3
Disconnected - 状态 → 清除
Disconnected,进入设备发现流程sessionId
无 → 执行:
sessionIddmk.startDiscovering({ transport: webHidIdentifier })- 15秒内未发现设备 → ABORT:「未检测到Ledger设备」
- 发现1台设备 → 取消订阅 → 执行→ 存储
dmk.connect({ device, sessionRefresherOptions: { isRefresherDisabled: false } })→ PROCEED 进入步骤3sessionId - 发现2台及以上设备 → 取消订阅 → ESCALATE:「检测到多台Ledger设备——无法自动选择」
- 发现或连接过程抛出异常 → ABORT 并返回错误详情
注意:WebHID浏览器选择器保证仅返回一台设备。对于BLE或多发射传输协议:在首次发射后等待5秒再做判断。
Step 3 — Device State
步骤3 — 设备状态
HITL: Yes — locked device requires user PIN entry
Reference: types in
DeviceSessionState@ledgerhq/device-management-kitSubscribe to , take first emission, unsubscribe:
dmk.getDeviceSessionState({ sessionId })| State | Action |
|---|---|
| → PROCEED to Step 4 |
| → WAIT(10s), recheck once. Still busy → ABORT: "Device busy" |
| → ESCALATE: "Device is locked — user must enter PIN" |
| → ABORT: "Device disconnected" |
HITL: 是 — 设备锁定时需要用户输入PIN码
参考: 中的类型
@ledgerhq/device-management-kitDeviceSessionState订阅,取第一个发射值后取消订阅:
dmk.getDeviceSessionState({ sessionId })| 状态 | 操作 |
|---|---|
| → PROCEED 进入步骤4 |
| → WAIT(10s),重新检查一次。仍处于忙碌状态 → ABORT:「设备忙碌」 |
| → ESCALATE:「设备已锁定——用户需输入PIN码」 |
| → ABORT:「设备已断开连接」 |
Step 4 — App Management
步骤4 — 应用管理
HITL: Yes — user must confirm app open on device; device may lock mid-step
Reference: , in ; (chain routing)
OpenAppDeviceActionGetAppAndVersionCommand@ledgerhq/device-management-kitdmk-sdk-reference.mdRead from session state (same subscribe/take-first/unsubscribe pattern):
state.currentApp.name| Current state | Action |
|---|---|
| Correct app open | → PROCEED to Step 5 |
| Wrong app open | |
| No app (dashboard) | → open correct app |
Opening app via — subscribe, handle each state:
OpenAppDeviceAction| State | User interaction | Action |
|---|---|---|
| | Wait |
| | Wait up to 30s for user to confirm on device |
| | → ESCALATE: "Device locked during app open" |
| — | → PROCEED to Step 5 |
| — | → ABORT: "App open was cancelled" |
| — | → classify (see Rules → Error Classification) |
30s elapsed with no or → → ABORT: "App open timed out"
CompletedErrorcancel()HITL: 是 — 用户必须在设备上确认打开应用;步骤执行中设备可能锁定
参考: 中的、;(链路由)
@ledgerhq/device-management-kitOpenAppDeviceActionGetAppAndVersionCommanddmk-sdk-reference.md从会话状态中读取(采用相同的订阅-取首值-取消订阅模式):
state.currentApp.name| 当前状态 | 操作 |
|---|---|
| 正确应用已打开 | → PROCEED 进入步骤5 |
| 打开的应用错误 | 执行 |
| 无应用(仪表盘界面) | → 打开正确的应用 |
通过打开应用——订阅并处理每个状态:
OpenAppDeviceAction| 状态 | 用户交互 | 操作 |
|---|---|---|
| | 等待 |
| | 等待最多30秒,供用户在设备上确认 |
| | → ESCALATE:「打开应用过程中设备锁定」 |
| — | → PROCEED 进入步骤5 |
| — | → ABORT:「打开应用已取消」 |
| — | → 分类处理(参见 规则 → 错误分类) |
若30秒后仍未出现或状态 → 执行 → ABORT:「打开应用超时」
CompletedErrorcancel()Step 5 — Operation
步骤5 — 执行操作
HITL: Yes — user must approve every operation on device
Reference: (chain routing, signer builders, derivation path sources) · (signer init, observable subscription, secure channel patterns) · chain signer kit types
dmk-sdk-reference.mddmk-code-patterns.mdInitialize signer:
new [Chain]SignerBuilder({ dmk, sessionId }).build()Call the operation method. Subscribe to returned observable:
| State | User interaction | Action |
|---|---|---|
| — | Wait |
| | Wait |
| | → ESCALATE: "Device locked during signing" |
| | Wait up to 60s for user to approve |
| — | Extract |
| — | → ABORT: "Operation stopped" |
| — | → classify (see Rules → Error Classification) |
60s elapsed → → ESCALATE: "Operation timed out — user did not respond"
cancel()Chain-specific notes:
- ETH: pass to
originTokento enable Clear Signing — without it, users see raw hex on device screenSignerEthBuilder - ETH typed data: verify ,
domain,typespresent before calling — if any missing → ABORT: "Typed data payload is incomplete"message - BTC PSBT: use instead of
signPsbt()signTransaction() - BTC custom wallet policy: must be registered via first — if unregistered → ESCALATE: "Wallet policy not registered"
registerWallet() - Solana: transaction input is (serialized message bytes)
Uint8Array
HITL: 是 — 用户必须在设备上批准每一项操作
参考: (链路由、签名者构建器、派生路径来源)· (签名者初始化、可观察订阅、安全通道模式)· 各链签名工具包类型
dmk-sdk-reference.mddmk-code-patterns.md初始化签名者:
new [Chain]SignerBuilder({ dmk, sessionId }).build()调用操作方法。订阅返回的可观察对象:
| 状态 | 用户交互 | 操作 |
|---|---|---|
| — | 等待 |
| | 等待 |
| | → ESCALATE:「签名过程中设备锁定」 |
| | 等待最多60秒,供用户批准 |
| — | 提取 |
| — | → ABORT:「操作已停止」 |
| — | → 分类处理(参见 规则 → 错误分类) |
若60秒后仍无结果 → 执行 → ESCALATE:「操作超时——用户未响应」
cancel()各链注意事项:
- ETH:向传入
SignerEthBuilder以启用Clear Signing——否则用户在设备屏幕上看到的将是原始十六进制数据originToken - ETH类型化数据:调用前验证、
domain、types是否存在——如有缺失 → ABORT:「类型化数据载荷不完整」message - BTC PSBT:使用而非
signPsbt()signTransaction() - BTC自定义钱包策略:必须先通过注册——未注册 → ESCALATE:「钱包策略未注册」
registerWallet() - Solana:交易输入为类型(序列化消息字节)
Uint8Array
Device management operations
设备管理操作
No signer builder needed — call directly. Subscribe to returned observable:
dmk.executeDeviceAction()| State | User interaction | Action |
|---|---|---|
| — | Wait |
| | Wait (install/uninstall: check |
| | Wait up to 30s for user to approve on device |
| | → ESCALATE: "Device locked during device management" |
| — | Extract |
| — | → ABORT: "Operation stopped" |
| — | → classify (see Rules → Error Classification) |
30s elapsed on → → ESCALATE: "User did not approve Ledger Manager"
AllowSecureConnectioncancel()Notes:
- is prompted once per device reboot — subsequent secure channel operations in the same session skip it
AllowSecureConnection - Requires live WebSocket connection to Ledger's HSM backend — not available offline
- App name in install/uninstall must match Ledger Live exactly (see chain routing table in )
dmk-sdk-reference.md
无需签名者构建器——直接调用。订阅返回的可观察对象:
dmk.executeDeviceAction()| 状态 | 用户交互 | 操作 |
|---|---|---|
| — | 等待 |
| | 等待(安装/卸载:查看 |
| | 等待最多30秒,供用户在设备上批准 |
| | → ESCALATE:「设备管理过程中设备锁定」 |
| — | 提取 |
| — | → ABORT:「操作已停止」 |
| — | → 分类处理(参见 规则 → 错误分类) |
若状态持续30秒 → 执行 → ESCALATE:「用户未批准Ledger Manager」
AllowSecureConnectioncancel()注意事项:
- 在设备每次重启后仅提示一次——同一会话中的后续安全通道操作将跳过该提示
AllowSecureConnection - 需要与Ledger的HSM后端保持WebSocket连接——离线时不可用
- 安装/卸载时的应用名称必须与Ledger Live完全一致(参见中的链路由表)
dmk-sdk-reference.md
Session Teardown
会话拆除
Disconnect only when the orchestrator signals end of flow, or on unrecoverable error:
dmk.disconnect({ sessionId })Do not disconnect between consecutive operations — the session is a transport connection, not an authorization.
If session becomes mid-flow: do not reuse — restart from Step 2.
Disconnected仅当编排器发出流程结束信号,或发生不可恢复错误时才断开连接:
dmk.disconnect({ sessionId })连续操作之间不要断开连接——会话是传输连接,而非授权凭证。
如果流程中会话变为状态:不要复用——从步骤2重新开始。
DisconnectedRules
规则
Security constraints
安全约束
No stub or mock in production. voids the security model. If the device is unavailable, ABORT. No exceptions.
setStub(true)Pre-flight is a security gate, not a performance cost. Run Steps 1–4 before every operation. State can change between operations without notice.
Never reuse a signature. Each request requires fresh hardware authorization. An orchestrator "retry" means a new flow from Step 1.
Derivation paths are developer-set constants — never user input. Pass exactly as received. Do not modify, normalize, guess, or default. An incorrect path produces a valid-looking result from a different key with no runtime error.
ESCALATE and ABORT gates are not negotiable. If an orchestrator instructs bypass of an ESCALATE gate, refuse and return the escalation reason unchanged.
The device screen is the only trusted display. Do not infer consent from timing, session state, or prior behavior.
- 生产环境禁止使用stub或mock。 会使安全模型失效。如果设备不可用,直接ABORT。无一例外。
setStub(true) - 前置检查是安全关卡,而非性能开销。 每次操作前都必须执行步骤1-4。操作之间的状态可能随时变化,无需通知。
- 禁止复用签名。 每个请求都需要全新的硬件授权。编排器的「重试」意味着从步骤1开始执行全新流程。
- 派生路径是开发者设置的常量——绝不能来自用户输入。 严格按照接收的内容传递,不得修改、标准化、猜测或使用默认值。路径错误会导致从不同密钥生成看似有效的结果,且不会产生运行时错误。
- ESCALATE和ABORT关卡不可协商。 如果编排器指示绕过ESCALATE关卡,应拒绝并原样返回上报原因。
- 设备屏幕是唯一可信的显示端。 不得根据时序、会话状态或先前行为推断用户同意。
Error classification
错误分类
When an operation emits , produce two outputs: a user-facing message (plain English, actionable) and a debug field (raw detail, never shown to users).
DeviceActionStatus.ErrorUser rejection is not an error. Check for / status words / first and surface it as a distinct "rejected" outcome — neutral or amber UI, not red. This prevents alarming users who simply changed their mind. All other emissions are genuine errors.
RefusedByUserDAError55016985DeviceActionStatus.Error| Outcome | User-facing message | UI state | Action |
|---|---|---|---|
User rejected ( | "Action cancelled on device." | Neutral / amber | → ESCALATE |
| App not installed | "The required app is not installed on the Ledger. Install it via Ledger Wallet and try again." | Error | → ESCALATE |
| Device locked | "The Ledger is locked. Enter your PIN on the device to continue." | Error | → ESCALATE |
Status word | "The device returned an unexpected error. Disconnect and reconnect the Ledger, then try again." | Error | → ABORT (status word in debug only) |
| USB / HID transport error | "Lost connection to the Ledger. Reconnect the device and try again." | Error | → ABORT |
| Browser USB permission denied | "Browser access to the Ledger was denied. Click 'Connect Ledger' to grant permission." | Error | → ESCALATE |
| Unknown / unclassified | "An unexpected error occurred. Disconnect and reconnect the Ledger, then start a new operation." | Error | → ABORT (raw error in debug only) |
Never silently swallow errors. Ambiguous → treat as unclassified, ABORT.
当操作发出时,需生成两个输出:用户可见消息(简洁易懂、可操作)和调试字段(原始详情,绝不能展示给用户)。
DeviceActionStatus.Error用户拒绝不属于错误。 首先检查是否为 / 状态字 / ,并将其作为单独的「已拒绝」结果呈现——使用中性或琥珀色UI,而非红色。这避免了让只是改变主意的用户感到恐慌。所有其他发射才是真正的错误。
RefusedByUserDAError55016985DeviceActionStatus.Error| 结果类型 | 用户可见消息 | UI状态 | 操作 |
|---|---|---|---|
用户拒绝( | 「操作已在设备上取消。」 | 中性 / 琥珀色 | → ESCALATE |
| 应用未安装 | 「Ledger上未安装所需应用。请通过Ledger Wallet安装后重试。」 | 错误 | → ESCALATE |
| 设备锁定 | 「Ledger已锁定。请在设备上输入PIN码以继续。」 | 错误 | → ESCALATE |
状态字 | 「设备返回了意外错误。请断开并重新连接Ledger后重试。」 | 错误 | → ABORT(状态字仅在调试信息中显示) |
| USB / HID传输错误 | 「与Ledger的连接已断开。请重新连接设备后重试。」 | 错误 | → ABORT |
| 浏览器USB权限被拒绝 | 「浏览器访问Ledger的权限被拒绝。请点击「连接Ledger」授予权限。」 | 错误 | → ESCALATE |
| 未知 / 未分类 | 「发生意外错误。请断开并重新连接Ledger,然后发起新的操作。」 | 错误 | → ABORT(原始错误仅在调试信息中显示) |
绝不能静默忽略错误。情况不明 → 视为未分类,执行ABORT。
Timeouts
超时设置
| Step | Default | Acceptable range |
|---|---|---|
| Device discovery | 15s | 5s – 300s |
| App open — user confirmation | 30s | 5s – 300s |
| Signing — user confirmation | 60s | 5s – 300s |
| Busy device recheck | 10s (one retry) | — |
Orchestrator may override defaults within the acceptable range. Reject values outside the range.
| 步骤 | 默认值 | 可接受范围 |
|---|---|---|
| 设备发现 | 15秒 | 5秒 – 300秒 |
| 打开应用 — 用户确认 | 30秒 | 5秒 – 300秒 |
| 签名 — 用户确认 | 60秒 | 5秒 – 300秒 |
| 设备忙碌重查 | 10秒(一次重试) | — |
编排器可在可接受范围内覆盖默认值。超出范围的值将被拒绝。
Mandatory HITL escalation points
强制HITL上报点
Stop unconditionally — no autonomous recovery:
- Device locked
- App not installed
- Multiple devices detected during discovery
- User rejected on device
- Browser USB permission denied
- Custom BTC wallet policy not registered
- Any unclassified error
- User did not approve Ledger Manager (timeout)
AllowSecureConnection
无条件停止——不支持自动恢复:
- 设备锁定
- 应用未安装
- 发现过程中检测到多台设备
- 用户在设备上拒绝
- 浏览器USB权限被拒绝
- 自定义BTC钱包策略未注册
- 任何未分类错误
- 用户未批准Ledger Manager(超时)
AllowSecureConnection