Build from source
从源码构建
The SDK lives in packages/romzo-client. From the
repository root:
SDK 位于 packages/romzo-client。在仓库根目录执行:
The build is ESM with strict TypeScript types.
构建产物为 ESM,带严格模式 TypeScript 类型。
npm ci
npm run build -w romzo
Concepts
概念
- Room — a 10-character room code. The host signs
the room descriptor with the
hostkey pair. - 房间——10 位房间码,房主持
host密钥对 descriptor 签名。 - Tickets — admission tickets authenticate the WebSocket (60 seconds, single handshake). Recovery tickets restore a room after a client crash or relay restart.
- 票据——admission 票据用于 WebSocket 认证 (60 秒、单次握手);recovery 票据用于客户端崩溃或中继重启后恢复房间。
- Channels — declared per room in
options.channels(name plus per-channel frame cap, at most 8); there are no predefined channels. Omission means no channels. Subscribers receivejoin/leave/receiveframes. - 通道——按房间在
options.channels中声明(名字 + 逐频道帧上限,最多 8 个); 中继不预置频道,未声明时没有频道。订阅后收到join/leave/receive帧。
Host
房主
Create the room, confirm it on the relay, then open a WebSocket session and subscribe to channels.
创建房间、在中继上确认,然后建立 WebSocket 会话并订阅频道。
The public relay enforces Turnstile on room creation:
pass { turnstileToken } as the third argument of createRoom.
Relays with CREATE_AUTH=password take { password } instead.
公共中继在建房时强制 Turnstile:createRoom 第三个参数传 { turnstileToken };开了 CREATE_AUTH=password 的中继则传 { password }。
import { createRoom, RelaySession } from 'romzo';
const roomOptions = {
app: 'my-game',
channels: [
{ name: 'table', maxFrameBytes: 262144 },
{ name: 'chat', maxFrameBytes: 16384 },
],
};
const room = await createRoom('romzo.net', roomOptions);
const url = room.descriptor.origin.replace(/^http/, 'ws') + '/ws';
const session = await RelaySession.connect(
url, room.admission, room.transport
);
session.on('join', (_p, { channel, peer, key }) => {
// peer key for E2E crypto
});
session.on('receive', (value, { channel, peer }) => {
// a peer's payload
});
session.on('close', () => {
// reconnect with the recovery ticket
});
await session.subscribe('table');
import { createRoom, RelaySession } from 'romzo';
const roomOptions = {
app: 'my-game',
channels: [
{ name: 'table', maxFrameBytes: 262144 },
{ name: 'chat', maxFrameBytes: 16384 },
],
};
const room = await createRoom('romzo.net', roomOptions);
const url = room.descriptor.origin.replace(/^http/, 'ws') + '/ws';
const session = await RelaySession.connect(
url, room.admission, room.transport
);
session.on('join', (_p, { channel, peer, key }) => {
// peer 公钥 key 可用于端侧加密
});
session.on('receive', (value, { channel, peer }) => {
// 他人 send 的负载
});
session.on('close', () => {
// 断开后用 recovery 票据重连
});
await session.subscribe('table');
Guest
访客
joinRoom verifies the descriptor, config hash and
host signature before requesting admission.
joinRoom 在申请 admission 前核对 descriptor、
configHash 与宿主签名。
import { joinRoom, RelaySession } from 'romzo';
const joined = await joinRoom('romzo.net', 'ABCD2345EF');
const url = joined.signedDescriptor.descriptor.origin
.replace(/^http/, 'ws') + '/ws';
const session = await RelaySession.connect(
url, joined.admission, joined.transport
);
await session.subscribe('table');
Crash recovery
崩溃恢复
Keepalive responses update session.currentRecovery()
automatically. Persist three values — signedDescriptor,
transport, recovery — to restore the room after a
restart.
keepalive 应答自动更新 session.currentRecovery()。
持久化三个值——signedDescriptor、transport、
recovery——即可在中继重启后恢复房间。
import { restoreSession, RelaySession } from 'romzo';
const restored = await restoreSession(
saved.signedDescriptor, saved.recovery, saved.transport);
const session = await RelaySession.connect(wsUrl,
restored.admission, restored.transport);
// resubscribe and sync history in the envelope layer
Modules
模块
| module | contents | ||
|---|---|---|---|
| 模块 | 内容 | ||
| canonical | canonical(), hash(domain, v),
mac(key, domain, v), hex utilities — byte-compatible with the Go
implementation |
canonical()、hash(domain, v)、
mac(key, domain, v)、hex 工具,与 Go 实现逐字节一致 |
|
| crypto | Ed25519 key generation / import / export,
sign / verify, PBKDF2 password derivation (600000
iterations), passwordProof |
Ed25519 密钥生成 / 导入导出、sign /
verify、PBKDF2 口令派生(600000 迭代)、passwordProof |
|
| http | discover, createChallenge,
allocate, confirmRoom, requestAdmission,
fetchDescriptor, restoreRoom — injected
fetch, errors throw RelayHttpError(code, status) |
discover、createChallenge、
allocate、confirmRoom、requestAdmission、
fetchDescriptor、restoreRoom,支持注入
fetch,错误抛 RelayHttpError(code, status) |
|
| session | RelaySession.connect(url, admission, identity, opts)
— four-frame handshake, subscribe / unsubscribe /
send / keepalive, events, automatic keepalive,
currentRecovery(), injectable WebSocket factory |
RelaySession.connect(url, admission, identity, opts)——
四帧握手、subscribe / unsubscribe /
send / keepalive、事件订阅、自动保活、
currentRecovery(),支持注入 WebSocket 工厂 |
|
| facade | 门面 | createRoom() / joinRoom() /
restoreSession() wrap the full HTTP sequence |
createRoom() / joinRoom() /
restoreSession() 封装完整 HTTP 序列 |
The SDK leaves these to you
以下部分由调用方负责
- Reconnection. After a drop, reconnect with the recovery ticket, resubscribe and sync history.
- 重连。断线后凭 recovery 票据重连、重新订阅并同步历史。
- Key storage. Generated keys are extractable for export and recovery. Pick a protected storage and a clearing policy.
- 密钥存储。SDK 生成的密钥均为 extractable,存储位置与清除时机由应用决定。
- Rules. The SDK does not interpret options. Validate
them with the
validateOptionscallback. - 规则。SDK 不解读 options 内容,由应用通过
validateOptions回调校验。