@eternl/hub-core

中文文档
0.55.0发布于 2026/11/10BUSL-1.1TypeScriptESM + CJS零依赖近 30 天下载 2619

Eternl Hub SDKs的共享基础设施.

中文使用文档
文档目录14 节
  1. 安装
  2. 所需资源
  3. 概览
  4. 进口和子路径出口
  5. 密码
  6. 从长期状态重建
  7. 会议
  8. 运输
  9. 信件
  10. 类型
  11. 错误
  12. 常数
  13. 安保模式
  14. 许可证

@eternl/ hub- core

Eternl Hub SDKs的共享基础设施. 此套件提供了两者共同使用的基础 @eternl/hub-dapp-sdk 财务报告和审定财务报表 @eternl/hub-wallet-sdk:加密的传输,密码握手,有线协议,以及将它们联系在一起的对称会话层.

这是一个低级的软件包。 大多数应用代码应该使用角色特定的SDK(dApp / 钱包),而不是用手来给这些原始人接线. 原始人在此被记载为SDK作者和高级融合.

安装

npm install @eternl/hub-core
# or
pnpm add @eternl/hub-core

所需资源

  • 运行时间与 网络加密 API (单位:千美元)globalThis.crypto.subtle: 浏览器,或 节点.js 18密码模块调用 assertCryptoCapabilities() 并抛出环境 没有它.
  • 无害环境管理或CJS的消费者均得到支持(双管齐下) import编号 :require 以类型构建).

概览

这套材料分为以下几个领域:

  • 密码 - ECDH P-256钥匙交换+AES-256-GCM AEAD,带有PIN捆绑的密钥导出和用于MITM保护的6位短认证字符串(SAS6).
  • 会议 —— 说 createPairSession,拥有有线信封的对称协议层,加密/解密,心跳,出场乒乓球,并重放验证一对.
  • 运输 —— 说 NatsTransport (NATS WebSocket) 一个小后面 Transport 用于依赖性注入的界面。 A 类 MockTransport 测试双重机可从 @eternl/hub-core/testing 子路径.
  • 信件 ——打出的信息定义,类型守护,以及 create… dApp 钱包协议工厂.
  • 出席情况 ——为JetStream-stream-stream-base 存在协议的主体构建器,头键,和cadence常数.
  • 生命周期 —— 说 LifecycleDispatcher,一个单调,它将DOM/应用生命周期事件(tab隐藏/可见,页面隐藏/显示,应用背景/前方)向存在信标进行播放.
  • 类型 / Utils / 出错 / 常数 共享类型Script类型、编码/事件帮助器、 EternlConnectError 类,和协议配置.

进口和子路径出口

生产原始从包根再出口:

import { createSessionEncryption, createPairSession, NatsTransport } from '@eternl/hub-core';

每个地区也可作为进口较窄的次路径出口:

import { createSessionEncryption } from '@eternl/hub-core/crypto';
import { EternlConnectError }      from '@eternl/hub-core/errors';
import { createApiCall }           from '@eternl/hub-core/messages';
import { createPairSession }       from '@eternl/hub-core/session';
import { NatsTransport }           from '@eternl/hub-core/transport';
import { LifecycleDispatcher }     from '@eternl/hub-core/lifecycle';
import { presencePublishSubject }  from '@eternl/hub-core/presence';
import type { AccountInfo }        from '@eternl/hub-core/types';
import { generateNonce }           from '@eternl/hub-core/utils';

有效的子路径是 ./crypto, (中文(简体) ), ./errors, (中文(简体) ), ./messages, (中文(简体) ), ./session, (中文(简体) ), ./presence, (中文(简体) ), ./lifecycle, (中文(简体) ), ./transport, (中文(简体) ), ./types,以及 ./utils。 。 。 .

测试双等 MockTransport 活在奉献下 @eternl/hub-core/testing 子路径和故意 没有 生产根出口的一部分:

import { MockTransport } from '@eternl/hub-core/testing';

密码

密码模块既暴露出低等原始人(generateKeyPair, (中文(简体) ), deriveSharedSecret, (中文(简体) ), deriveSessionKey, (中文(简体) ), encrypt, (中文(简体) ), decrypt, (中文(简体) ), deriveSAS6,.)和通过工厂创建的状态会话包装.

SessionEncryption 实值 没有 一个建筑师。 这是一个小物体暴露 { fromRaw } (别名用于: createSessionEncryptionFromRaw) (中文(简体) ). 握手驱动的实例与 createSessionEncryption(pairingId, pin) 工厂:

import { createSessionEncryption } from '@eternl/hub-core';
import { bytesToHex } from '@eternl/hub-core'; // also on '@eternl/hub-core/utils'

// Each side creates an encryption context bound to the shared pairingId + PIN.
const enc = createSessionEncryption(pairingId, pin);

// 1. Generate this side's ephemeral key pair; returns the raw public key bytes.
const myPublicKey = await enc.initialize();
const myPublicKeyHex = bytesToHex(myPublicKey); // send this to the peer
// (equivalently: enc.getPublicKeyHex())

// 2. After receiving the peer's public key (hex), complete the handshake.
//    completeHandshake takes a SINGLE argument: the peer public key as hex.
await enc.completeHandshake(peerPublicKeyHex);

// 3. Both sides display the 6-digit SAS for out-of-band confirmation.
const sas6 = enc.getSAS6(); // throws if called before completeHandshake

// 4. Encrypt. encrypt() returns { data, seq } — `seq` is a per-direction
//    monotonic counter that must travel alongside the ciphertext on the wire.
const { data, seq } = await enc.encrypt(new TextEncoder().encode('hello'));

// 5. The receiver decrypts using the same seq it read off the wire.
const plaintext = await enc.decrypt(data, seq);

// When finished, zero out the in-memory key material.
enc.dispose();

关键点(经核实与 src/crypto/index.ts:

  • createSessionEncryption(pairingId, pin) —— 说 pin 也许 null 只有在从持久性状态重建实例时(见下文); 新的握手需要非无核PIN.
  • initialize() 返回时 Promise<Uint8Array> (原始公用钥匙); getPublicKeyHex() 返回与十六进制相同的密钥.
  • completeHandshake(peerPublicKeyHex) 只能用一个理由.
  • encrypt(plaintext) 决定: { data: Uint8Array; seq: bigint } 财务报告和审定财务报表 需求 握手完成; 称之为更早的掷出.
  • decrypt(data, seq) 要求 seq 是用密码传送的.
  • getSAS6() 掷出至 completeHandshake 已经运行.

从长期状态重建

在不重运行握手( 如重装后) 的情况下恢复加密上下文, 使用 SessionEncryption.fromRaw 编号 : createSessionEncryptionFromRaw数字 :

import { SessionEncryption } from '@eternl/hub-core';

const enc = await SessionEncryption.fromRaw({
  pairingId,
  sessionKeyRaw, // 32-byte AES key
  pinAadBytes,   // PIN-hash AAD bytes captured during the original handshake
});
// `enc` can encrypt/decrypt immediately, but cannot run completeHandshake again.

会议

createPairSession 是对称协议层。 它是一个 SessionEncryptionApi 加一个 Transport,拥有有线信封(一个8字节的大-endian) seq aES-GCM blob 之后的前缀,在 hub.pair.v2.<pairId>,运行心跳,自动复制到存在 pings,并验证每个输入框上的回放/protocol-version.

import {
  createSessionEncryption,
  createPairSession,
  NatsTransport,
} from '@eternl/hub-core';

const transport = new NatsTransport();
await transport.connect('wss://ws.hub.de-5.eternl.art'); // or DEFAULT_HUB_URL

const encryption = createSessionEncryption(pairId, pin);
await encryption.initialize();
// ... exchange public keys and call encryption.completeHandshake(peerPublicKeyHex) ...

const session = createPairSession({
  pairId,
  encryption,
  transport,
  enableHeartbeat: true, // default
});

// Listen for decrypted, validated inbound messages.
const off = session.on('message', ({ message, type }) => {
  // handle role-specific protocol messages (api-call/api-response, etc.)
});

// Send any protocol message object; it is stamped, encoded, encrypted, and published.
await session.send(someProtocolMessage);

// Active presence probe — resolves true if the peer pongs within the timeout.
const online = await session.requestPresenceCheck();

// Cleanup (also available via `using` / Symbol.dispose).
off();
session.dispose();

createPairSession 也发出命名事件,用于可观察性和生命周期反应: presencePing, (中文(简体) ), presencePong, (中文(简体) ), heartbeatReceived, (中文(简体) ), peerPresence, (中文(简体) ), peerDisconnect, (中文(简体) ), peerPairingRemoved,以及 error。角色层( dApp 和钱包 SDKs) 'message' 活动.

运输

import { NatsTransport } from '@eternl/hub-core';

const transport = new NatsTransport();
await transport.connect('wss://ws.hub.de-5.eternl.art'); // or DEFAULT_HUB_URL

const sub = transport.subscribe('hub.pair.v2.<pairId>', (data) => {
  // data is a Uint8Array
});

await transport.publish('hub.pair.v2.<pairId>', encodedBytes);

sub.unsubscribe();
await transport.disconnect();

connect(url, opts?) 接受每个连接的验证器。 针对生产超级集群(默认的ACLs),你必须提供一种——要么是静态的 authenticator 或,对于长期会期,a getAuthenticator 提供商在每次重联时重命名证书. MockTransport (进口自 @eternl/hub-core/testing) 执行相同的 Transport 用于测试和记录的界面 publishedMessages。 。 。 .

信件

import {
  MessageType,
  createApiCall,
  isApiResponse,
  encodeMessage,
  decodeMessage,
  generateNonce,
} from '@eternl/hub-core';

const decoded = decodeMessage(bytes);
if (isApiResponse(decoded)) {
  // handle the response
}

模块导出已键入信件界面 MessageType 数值映射, is… 类型警卫,和 create… 工厂createConnectRequest, (中文(简体) ), createApiCall, (中文(简体) ), createHeartbeat, (中文(简体) ), createPresencePing(见A/C.5/49/70,附件)。 这个 create… 工厂使用非ce生成器;在本包中,非ce由 generateNonce(sessionId)。 。 。 .

信件类型说明
CONNECT_REQUEST 编号 : CONNECT_RESPONSEDAPP 请求连接; 钱包批准/ 拒绝
API_CALL 编号 : API_RESPONSEDAPP 调用 CIP- 30/ CIP- 95 方法; 钱包返回结果
ACCESS_GRANTED 编号 : ACCESS_DENIED按要求作出的访问决定
ACCOUNT_SWITCH 编号 : ACCOUNT_CHANGED更改账户请求/通知
DISCONNECT 编号 : PAIRING_REMOVED临时断开/ 永久断开
HEARTBEAT保持存活状态
PRESENCE 编号 : PRESENCE_PING 编号 : PRESENCE_PONG存在状态和往返探测

类型

import type { AccountInfo, NetworkType } from '@eternl/hub-core';
import { Permission } from '@eternl/hub-core';

const network: NetworkType = 'mainnet';

const permissions = [
  Permission.ADDRESS_READ, // 'address_read'
  Permission.SIGN_TX,      // 'sign_tx'
];

Permission 包括CIP-30核心方法(NETWORK_ID, (中文(简体) ), ADDRESS_READ, (中文(简体) ), SIGN_TX, (中文(简体) ), SUBMIT_TX(......)和CIP-95治理(GOVERNANCE, (中文(简体) ), DREP_KEY, (中文(简体) ), STAKE_KEYS) (中文(简体) ).

错误

import { EternlConnectError, ErrorCode } from '@eternl/hub-core';

try {
  // ... operation
} catch (error) {
  if (error instanceof EternlConnectError) {
    console.log(error.code);        // ErrorCode enum value
    console.log(error.recoverable); // boolean — whether a retry may succeed
    console.log(error.cause);       // original error, if wrapping one
  }
}

EternlConnectError 是整个软件包中使用的单一错误类型。 它携带一个 ErrorCode (例如:. TIMEOUT, (中文(简体) ), DECRYPTION_FAILED, (中文(简体) ), REPLAY_DETECTED, (中文(简体) ), HUB_UNAVAILABLE页:1 recoverable 旗帜和可选 cause静态帮助者(EternlConnectError.timeout, (中文(简体) ), .decryptionFailed, (中文(简体) ), .replayDetected,.)构建常见案例.

常数

import {
  PROTOCOL_VERSION,
  DEFAULT_HUB_URL,
  SESSION_EXPIRY_MS,
  RECONNECTION,
  NONCE_WINDOW_MS,
  CHANNELS, // CHANNELS.PAIR_V2(pairId) === `hub.pair.v2.${pairId}`
} from '@eternl/hub-core';

DEFAULT_HUB_URL 是 Eternl 参考 NATS WebSocket 端点, 目前 wss://ws.hub.de-5.eternl.art。通过将不同的 URL 传递到 transport.connect(url)。 。 。 .

安保模式

包裹的中央安全财产是 耐MITM配对 在两个设备之间共享一个简短的PIN。 设计(见 src/crypto/index.ts将四种机制结合起来:

  1. ECDH P-256 + AES-256-GCM. 欧洲人大常委会. 每方生成一对电平钥匙,交换公用钥匙,并通过ECDH获得共同的秘密. 框架由AES-256-GCM(认证加密)封存.

  2. PIN 键导出 . 会话密钥是 HKDF-SHA256(sharedSecret, salt = SHA-256(utf8(pin) || pairingId), info = "eternl-hub-v2")。 。 。 . 将盐装入PIN意味着攻击者可以代替公用钥匙,但确实 没有 了解PIN不能获得相同的会话密钥,即使完全了解他们谈判的共同秘密.

  3. SAS6( Short 认证字符串). 6位数的代码来源于共享密语 + PIN 绑定的盐 在一个独特的 HKDF 下 info 标签。 双同位素显示,用户确认匹配出带. 如果一个MITM替换出一个或两个公用密钥,那么每一边都会得到不同的共享密钥,所以两个SAS6代码存在分歧,用户在任何签名流量流之前都会中止.

  4. 每帧 AAD + 重放/非保护. 每个 AEAD 框架的额外认证数据是 utf8(pairingId) || SHA-256(pin)[:8] || be64(seq)。使用不同的 PIN 或配对Id 的对等点会在每个框架上失败 AES-GCM 认证。 滑动窗口非ce验证符( E)createNonceValidator窗口 = NONCE_WINDOW_MS拒绝重复播放或陈旧的电文,以及 createPairSession 拒绝协议版本不匹配的帧 PROTOCOL_VERSION。 。 。 .

seq aAD和电线信封中都装着一个每个方向的单调计数器。 注意严格 命令执行被推迟到 SDK 层 —— 说 hub-core 验证非ce窗口和 AAD seq 匹配密码文本,但本身并不拒绝超出再播放检测的订单外发送.

许可证

商业来源许可证1.1(BUSL-1.1) (中文(简体) ). 见 自由主义。 。 。 .

许可是Tastenkunst GmbH。 额外使用赠款 : None - 你可以复制、修改和制作 非生产 使用此软件包。 生产/商业用途需要从塔斯滕昆斯特获得单独的许可证。 在"更改日期"(每个版本首次出版的四周年)上,该版本会转换为GPL-2.0-或-后期.

联系人 : https://tastenkunst.com/ (中文(简体) )

版本与发布记录

@eternl/hub-core 共发布 66 个版本,首次发布于 2026/05/08;过去 12 个月发布了 66 个版本。

版本发布日期距今
0.55.0latest2026/11/103 天前
0.54.02026/11/103 天前
0.53.02026/11/103 天前
0.52.02026/11/103 天前
0.51.02026/11/104 天前
0.50.02026/11/094 天前

关于 @eternl/hub-core 的常见问题

@eternl/hub-core 是什么?

Eternl Hub SDKs的共享基础设施.

如何安装 @eternl/hub-core?

在项目目录运行 npm install @eternl/hub-core,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@nats-io/nats-core(3.4.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@eternl/hub-core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@eternl/hub-core 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@eternl/hub-core 可以商用吗?

@eternl/hub-core 使用 BUSL-1.1 许可。声明的许可为 BUSL-1.1,请阅读许可原文确认使用条件。以上为概括,不构成法律意见,请以许可原文为准。

@eternl/hub-core 的下载量有多少?

根据 npm 官方下载统计,@eternl/hub-core 在 2026/10/14 – 2026/11/12 这 30 天内被下载 2,619 次,日均约 87 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@eternl/hub-core 还在维护吗?

最新版本 0.55.0 发布于 2026/11/10(3 天前);至今共发布 66 个版本,其中过去 12 个月发布了 66 个;首次发布于 2026/05/08。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。