dmk-business-logic

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

DMK Business Logic & Concepts

DMK 业务逻辑与概念

This skill explains the why behind DMK API design decisions — domain model, security constraints, and trade-offs — so developers understand the reasoning, not just the calls. It is also loaded as a connector by
ledger-dmk-implementation/SKILL.md
when a term or concept needs clarification during implementation.

本技能解释DMK API设计决策背后的底层逻辑——包括领域模型、安全约束与权衡——帮助开发者理解设计缘由,而非仅知晓如何调用。当实现过程中需要澄清某个术语或概念时,
ledger-dmk-implementation/SKILL.md
也会将本技能作为连接器加载。

Clear Signing vs Blind Signing

Clear Signing 与 Blind Signing

What it means

含义

Blind signing means the user sees raw hex bytes on the Ledger screen — a long, unreadable string they cannot verify. They are asked to approve something they cannot meaningfully review.
Clear Signing means the user sees human-readable transaction details on the device screen — recipient address, amount, token name, contract name, fee. They can verify what they are actually approving before pressing the button.
Blind Signing 指用户在Ledger设备屏幕上看到的是原始十六进制字节——一串无法验证的冗长无意义字符串。用户需要批准自己无法有效审阅的内容。
Clear Signing 指用户在设备屏幕上可以看到人类可读的交易详情——收款地址、金额、代币名称、合约名称、手续费。用户可以在按下按钮前,验证自己实际要批准的内容。

Why it matters

重要性

The device screen is the only trusted display. A compromised host (malware, phishing site) can show the user anything on the browser or desktop UI. The device screen cannot be spoofed. Clear Signing makes the device screen meaningful — Blind Signing makes it useless.
设备屏幕是唯一的可信显示端。被入侵的主机(恶意软件、钓鱼网站)可以在浏览器或桌面UI上向用户展示任意内容,但设备屏幕无法被伪造。Clear Signing让设备屏幕具备实际意义,而Blind Signing则使其形同虚设。

How it works in the DMK

在DMK中的工作原理

The DMK works fully without Clear Signing — it is an enhancement, not a prerequisite. To enable it for Ethereum:
  • @ledgerhq/context-module
    must be installed regardless — it is a mandatory peer dependency of the ETH signer kit and handles fetching metadata (ABIs, token info, NFT details) the device needs to display human-readable fields.
  • originToken
    is an optional partner token passed to
    SignerEthBuilder
    . Without it, the signer works but the device shows raw hex — the experience silently degrades to blind signing with no runtime error. To obtain a token, enroll in Ledger's partner program:
    https://developers.ledger.com/docs/clear-signing/for-wallets
    .
typescript
// Clear Signing enabled (requires partner token):
new SignerEthBuilder({ dmk, sessionId, originToken: "your-partner-token" }).build();

// Blind signing — works but users see raw hex on device:
new SignerEthBuilder({ dmk, sessionId }).build();
For Bitcoin and Solana, Clear Signing is handled at the app level — no
originToken
is required, but the Ledger app on device must support the transaction type.
DMK在没有Clear Signing的情况下也能完全正常工作——它是一项增强功能,而非前置条件。要在Ethereum上启用Clear Signing:
  • 无论是否启用,都必须安装
    @ledgerhq/context-module
    ——它是ETH签名工具包的强制对等依赖,负责获取设备显示可读字段所需的元数据(ABI、代币信息、NFT详情)。
  • originToken
    是传递给
    SignerEthBuilder
    的可选合作伙伴令牌。没有该令牌时,签名功能仍可正常工作,但设备会显示原始十六进制内容——体验会静默降级为Blind Signing,且不会抛出运行时错误。如需获取令牌,请注册Ledger合作伙伴计划:
    https://developers.ledger.com/docs/clear-signing/for-wallets
typescript
// Clear Signing enabled (requires partner token):
new SignerEthBuilder({ dmk, sessionId, originToken: "your-partner-token" }).build();

// Blind signing — works but users see raw hex on device:
new SignerEthBuilder({ dmk, sessionId }).build();
对于Bitcoin和Solana,Clear Signing在应用层处理——不需要
originToken
,但设备上的Ledger应用必须支持对应交易类型。

Developer checklist

开发者检查清单

  • originToken
    is optional — integrate and ship without it, then enroll in Ledger's partner program when ready to enable Clear Signing in production
  • Always install
    @ledgerhq/context-module
    when using the ETH signer kit, even during development
  • Always use
    checkOnDevice: true
    for
    getAddress()
    flows — this is the address verification equivalent of Clear Signing for receive flows

  • originToken
    是可选的——可以先集成并上线不带该令牌的版本,待准备好在生产环境启用Clear Signing时,再注册Ledger合作伙伴计划
  • 使用ETH签名工具包时,务必安装
    @ledgerhq/context-module
    ,即使是在开发阶段
  • getAddress()
    流程中始终使用
    checkOnDevice: true
    ——这是收款流程中相当于Clear Signing的地址验证机制

Secure Channel

Secure Channel

What it is

定义

A Secure Channel is an encrypted, authenticated connection between the Ledger device and Ledger's HSM (Hardware Security Module) backend. It allows the backend to perform privileged operations on the device that cannot be done locally.
Secure Channel是Ledger设备与Ledger HSM(硬件安全模块)后端之间的加密认证连接。它允许后端在设备上执行无法在本地完成的特权操作。

Which operations require it

哪些操作需要Secure Channel

OperationRequires secure channel
GenuineCheckDeviceAction
Yes
ListInstalledAppsDeviceAction
Yes
InstallAppDeviceAction
Yes
UninstallAppDeviceAction
Yes
All signer operations (sign, get address)No
OpenAppDeviceAction
No
操作是否需要Secure Channel
GenuineCheckDeviceAction
ListInstalledAppsDeviceAction
InstallAppDeviceAction
UninstallAppDeviceAction
所有签名器操作(签名、获取地址)
OpenAppDeviceAction

How it works

工作原理

  1. The DMK opens a WebSocket connection to Ledger's HSM at
    wss://manager.live.ledger.com
  2. The device and HSM perform a mutual authentication handshake
  3. The user is prompted with
    AllowSecureConnection
    on the device screen — they must physically approve the connection
  4. Once approved, the session is trusted for the remainder of that device reboot
  1. DMK与Ledger HSM建立WebSocket连接,地址为
    wss://manager.live.ledger.com
  2. 设备与HSM进行双向身份验证握手
  3. 设备屏幕上会向用户弹出
    AllowSecureConnection
    提示——用户必须物理确认该连接
  4. 一旦获得批准,在设备下次重启前,该会话都会保持可信状态

Practical implications

实际影响

  • Requires internet. Secure channel operations fail offline — there is no local fallback.

  • 需要联网。 Secure Channel相关操作在离线状态下会失败——没有本地回退方案。

Device Actions vs Commands

Device Actions 与 Commands

The two-level API

两级API

The DMK exposes two levels of device interaction:
Commands (
dmk.sendCommand()
) are low-level, single APDU round-trips. They send one instruction to the device and return a result synchronously. No user interaction is handled — the caller is responsible for everything else.
Device Actions (
dmk.executeDeviceAction()
, or signer kit methods) are high-level orchestrated flows. They handle the full sequence: unlock detection, app opening, user confirmation prompts, retries, and terminal state emission — all through a single observable.
DMK提供了两个层级的设备交互方式:
Commands
dmk.sendCommand()
)是底层的单次APDU往返交互。它们向设备发送一条指令,并同步返回结果。Commands不处理任何用户交互——其余所有工作都由调用方负责。
Device Actions
dmk.executeDeviceAction()
或签名器工具包方法)是高层的编排式流程。它们处理完整的操作序列:解锁检测、应用打开、用户确认提示、重试以及终态发射——所有这些都通过单个Observable完成。

When to use each

各自的适用场景

Use caseUse
Signing a transaction, getting an addressSigner kit device action (via signer builder)
Genuine check, app install/uninstall
dmk.executeDeviceAction()
with the appropriate
DeviceAction
class
Opening an app with user confirmation UI
OpenAppDeviceAction
via
dmk.executeDeviceAction()
Checking firmware version, getting current app name
sendCommand()
— simple, no user interaction needed
Custom/unsupported chain, raw APDU
sendCommand()
with
ApduBuilder
— only when no signer kit exists
适用场景使用方式
交易签名、获取地址签名器工具包的Device Action(通过签名器构建器)
正品校验、应用安装/卸载配合对应的
DeviceAction
类使用
dmk.executeDeviceAction()
带用户确认UI的应用打开操作通过
dmk.executeDeviceAction()
使用
OpenAppDeviceAction
检查固件版本、获取当前应用名称
sendCommand()
——操作简单,无需用户交互
自定义/未支持的链、原始APDU操作配合
ApduBuilder
使用
sendCommand()
——仅在没有对应签名器工具包时使用

Why device actions exist

Device Actions存在的意义

Without device actions, every developer would have to re-implement:
  • Detecting and waiting for device unlock
  • Detecting and switching the active app
  • Handling the
    ConfirmOpenApp
    prompt
  • Mapping status word errors to user-facing messages
  • Managing timeouts and cancellation
Device actions encapsulate all of that. The observable they return emits states that map directly to UI prompts —
UserInteractionRequired.SignTransaction
means "show the user a prompt",
DeviceActionStatus.Completed
means "done, extract output".

如果没有Device Actions,每个开发者都不得不重新实现:
  • 检测并等待设备解锁
  • 检测并切换当前活跃应用
  • 处理
    ConfirmOpenApp
    提示
  • 将状态字错误映射为面向用户的消息
  • 管理超时与取消操作
Device Actions封装了所有这些逻辑。它们返回的Observable会发射直接对应UI提示的状态——
UserInteractionRequired.SignTransaction
表示「向用户显示提示」,
DeviceActionStatus.Completed
表示「操作完成,提取输出」。

Session and SessionId

Session 与 SessionId

What a session is

什么是Session

A session represents an active USB/BLE connection to a specific Ledger device. It is created by
dmk.connect()
and destroyed by
dmk.disconnect()
. The
sessionId
is an opaque string that identifies the session within the DMK instance.
Session代表与特定Ledger设备的活跃USB/BLE连接。它通过
dmk.connect()
创建,通过
dmk.disconnect()
销毁。
sessionId
是一个不透明字符串,用于在DMK实例中标识该会话。

What a session is not

Session的误区

  • It is not per-chain. The same
    sessionId
    works with ETH, BTC, SOL, and any other signer without reconnecting.
  • It is not an authorization. The device still prompts the user for every signing operation — the session is purely a transport handle.
  • It is not persistent across page loads. Refreshing the browser destroys the session.
  • 不是按链隔离的。同一个
    sessionId
    可以用于ETH、BTC、SOL等任何签名器,无需重新连接。
  • 不是授权凭证。每次签名操作时,设备仍会提示用户确认——Session纯粹是一个传输层句柄。
  • 不能跨页面加载持久化。刷新浏览器会销毁Session。

Session lifespan

Session的生命周期

A session is valid until:
  • dmk.disconnect({ sessionId })
    is called
  • The USB cable is unplugged (session state becomes
    Disconnected
    )
  • The browser tab is closed
Do not try to reconnect using an existing
sessionId
— once
Disconnected
, start a new flow from
startDiscovering()
.
Session在以下情况前保持有效:
  • 调用
    dmk.disconnect({ sessionId })
  • USB线被拔出(Session状态变为
    Disconnected
  • 浏览器标签页被关闭
不要尝试使用已有的
sessionId
重新连接——一旦状态变为
Disconnected
,请从
startDiscovering()
开始新的流程。

Session refresher

Session刷新器

The DMK polls the device periodically to keep session state up to date (current app, device status). This is the session refresher. In multi-tab scenarios where two instances share the same physical device, disable it to avoid conflicting polls:
typescript
dmk.connect({ device, sessionRefresherOptions: { isRefresherDisabled: true } });

DMK会定期轮询设备,以保持Session状态的更新(当前应用、设备状态),这就是Session刷新器。在多标签页场景下,如果两个实例共享同一台物理设备,请禁用刷新器以避免轮询冲突:
typescript
dmk.connect({ device, sessionRefresherOptions: { isRefresherDisabled: true } });

Transport

Transport

What it abstracts

它抽象了什么

The transport layer handles the physical communication protocol between the host and the Ledger device. The DMK's API is identical regardless of transport — only the initialization differs.
TransportProtocolEnvironment
WebHIDUSB HID, 64-byte framesBrowser (Chromium only)
WebBLEBluetooth GATTBrowser
Node-HIDUSB HID via native addonNode.js, Electron
React Native HIDUSB HIDReact Native
React Native BLEBluetoothReact Native
SpeculosTCP socket to emulatorDevelopment/CI only
传输层负责处理主机与Ledger设备之间的物理通信协议。无论使用哪种Transport,DMK的API都是完全一致的——仅初始化方式不同。
Transport协议适用环境
WebHIDUSB HID,64字节帧浏览器(仅Chromium内核)
WebBLE蓝牙GATT浏览器
Node-HID通过原生插件实现的USB HIDNode.js、Electron
React Native HIDUSB HIDReact Native
React Native BLE蓝牙React Native
Speculos连接模拟器的TCP套接字仅用于开发/CI

Browser constraints

浏览器限制

WebHID requires:
  • Chromium-based browser (Chrome, Edge, Brave) — Firefox and Safari do not support WebHID
  • HTTPS or localhost — WebHID is blocked on plain HTTP
  • User gesture
    startDiscovering()
    and
    connect()
    must be called from a button click or equivalent. Silent failure if called on page load.
WebHID需要满足以下条件:
  • 基于Chromium的浏览器(Chrome、Edge、Brave)——Firefox和Safari不支持WebHID
  • HTTPS或localhost——普通HTTP环境下WebHID会被阻止
  • 用户手势触发——
    startDiscovering()
    connect()
    必须在按钮点击或等效的用户操作中调用。如果在页面加载时调用,会静默失败。

One transport per DMK instance

每个DMK实例对应一个Transport

Each
DeviceManagementKitBuilder
takes one or more transports. In practice, use one. The DMK will attempt transports in registration order.

每个
DeviceManagementKitBuilder
可以接收一个或多个Transport。实际使用中建议只使用一个。DMK会按照注册顺序尝试使用各个Transport。

Derivation Paths

派生路径

What they are

定义

A derivation path is a sequence of indices that specifies which key to derive from the root seed stored on the device. Given the same seed phrase, the same path always produces the same key — and a different path produces a completely different key with no runtime error.
派生路径是一组索引序列,用于指定从设备存储的根种子派生哪一个密钥。使用相同的助记词时,同一路径始终生成相同的密钥——而不同路径会生成完全不同的密钥,且不会抛出运行时错误。

Why they are developer constants, not user input

为什么派生路径应该是开发者常量,而非用户输入

There is no "wrong path" from the device's perspective — every path produces a valid key. If a user enters a path that differs by even one segment, they get a different address. Funds sent to that address are inaccessible unless the correct path is known. This is a silent, unrecoverable mistake.
Paths must be hardcoded as constants in application code, matching the standard your users' wallets use.
从设备的角度来看,不存在「错误的路径」——每一条路径都能生成有效的密钥。如果用户输入的路径哪怕只有一段不同,得到的地址都会不同。发送到该地址的资金将无法找回,除非知道正确的路径。这是一种静默的、不可恢复的错误。
派生路径必须作为常量硬编码在应用代码中,与你的用户所使用的钱包标准保持一致。

Path format rules

路径格式规则

  • No
    m/
    prefix.
    DerivationPathUtils.splitPath
    parses segments separated by
    /
    . The leading
    m
    is not a valid segment —
    parseInt("m")
    returns
    NaN
    and throws "invalid number provided". Use
    "44'/60'/0'/0/0"
    not
    "m/44'/60'/0'/0/0"
    .
  • Hardened segments use the
    '
    suffix (e.g.
    44'
    ). This is shorthand for adding
    0x80000000
    to the index.
  • Account and index are separate concepts.
    account
    selects the wallet (most users use
    0
    ).
    index
    selects the address within that wallet.
  • 不要加
    m/
    前缀。
    DerivationPathUtils.splitPath
    会解析以
    /
    分隔的分段。开头的
    m
    不是有效分段——
    parseInt("m")
    会返回
    NaN
    并抛出「invalid number provided」错误。请使用
    "44'/60'/0'/0/0"
    而非
    "m/44'/60'/0'/0/0"
  • 硬化分段使用
    '
    后缀(例如
    44'
    )。这是将索引值加上
    0x80000000
    的简写。
  • 账户(account)与索引(index) 是不同的概念。
    account
    用于选择钱包(大多数用户使用
    0
    ),
    index
    用于选择该钱包下的地址。

Standard paths by chain

各链的标准路径

ChainLedger Live pathNotes
Ethereum
44'/60'/0'/0/0
BIP44, account 0, index 0
Bitcoin Native SegWit
84'/0'/0'
BIP84, passed to wallet policy
Solana
44'/501'/0'/0'
Community standard
Cosmos
44'/118'/0'/0/0
BIP44
For unlisted chains: SLIP-0044 defines coin types (
github.com/satoshilabs/slips/blob/master/slip-0044.md
).

Ledger Live路径说明
Ethereum
44'/60'/0'/0/0
BIP44,账户0,索引0
Bitcoin Native SegWit
84'/0'/0'
BIP84,传递给钱包策略
Solana
44'/501'/0'/0'
社区标准
Cosmos
44'/118'/0'/0/0
BIP44
对于未列出的链:SLIP-0044定义了币种类型(
github.com/satoshilabs/slips/blob/master/slip-0044.md
)。

BOLOS

BOLOS

BOLOS (Blockchain Open Ledger Operating System), now known as Ledger OS, is Ledger device's operating system. When no app is open and the device is at the home screen (dashboard),
state.currentApp.name
returns
"BOLOS"
.
This is how you detect that no app is open:
typescript
const isOnDashboard =
  state.currentApp.name === "BOLOS" || state.currentApp.name === "Dashboard";
You do not need to detect this explicitly when using signer kit device actions — they handle app switching automatically. It is useful for pre-operation UX feedback ("no app open, the correct app will open when you proceed").

BOLOS(Blockchain Open Ledger Operating System,区块链开放账本操作系统)现也称为Ledger OS,是Ledger设备的操作系统。当没有打开任何应用,设备处于主屏幕(仪表盘)时,
state.currentApp.name
会返回
"BOLOS"
你可以通过以下方式检测是否没有打开任何应用:
typescript
const isOnDashboard =
  state.currentApp.name === "BOLOS" || state.currentApp.name === "Dashboard";
使用签名器工具包的Device Actions时,你不需要显式检测这一点——它们会自动处理应用切换。该检测对于操作前的UX反馈很有用(例如「未打开应用,继续操作时将自动打开正确的应用」)。

Genuine Check

Genuine Check

A genuine check verifies that a physical Ledger device is authentic hardware manufactured by Ledger, not a counterfeit. It works by having the device prove possession of a private key whose corresponding certificate was signed by Ledger's root CA, verified through the Secure Channel.
It returns
output.isGenuine: boolean
.
When to use it: In high-security flows where device authenticity must be established before proceeding — for example, an enterprise key management setup or a first-time device onboarding flow. It is not required for routine signing operations.
What it does not prove: It does not prove the device hasn't been tampered with after manufacture, or that the seed phrase is correctly backed up.

Genuine Check用于验证实体Ledger设备是否为Ledger生产的正品硬件,而非仿冒品。其工作原理是:设备证明自己持有某一私钥,该私钥对应的证书由Ledger根CA签名,通过Secure Channel完成验证。
它会返回
output.isGenuine: boolean
类型的结果。
适用场景: 高安全级别的流程中,需要先确认设备真实性才能继续操作——例如企业密钥管理设置或设备首次上手指南流程。日常签名操作不需要使用Genuine Check。
它不能证明: 设备在出厂后未被篡改,也不能证明助记词已正确备份。

Ledger Wallet / Ledger Live

Ledger Wallet / Ledger Live

Ledger Live is the previous name for Ledger Wallet — they refer to the same desktop and mobile application. Documentation, skill files, and SDK references may use either name; treat them as identical.
App names used in device actions (
OpenAppDeviceAction
,
InstallAppDeviceAction
,
UninstallAppDeviceAction
) must match exactly what the Ledger app catalog uses — the same names shown in Ledger Wallet. A typo or casing difference causes a silent failure or a cryptic error with no indication of what went wrong.
Ledger Live是Ledger Wallet的曾用名——两者指的是同一款桌面和移动应用。文档、技能文件和SDK参考中可能会使用任意一个名称,请将它们视为等同。
Device Actions(
OpenAppDeviceAction
InstallAppDeviceAction
UninstallAppDeviceAction
)中使用的应用名称必须与Ledger应用目录中的名称完全一致——也就是Ledger Wallet中显示的名称。拼写错误或大小写差异会导致静默失败,或出现含义模糊的错误,无法定位问题原因。