API 参考 — SDK 类型声明
自动从
sdk-typescript/dist/index.d.ts生成(887 行)。请勿直接编辑——修改 SDK 源码后重新构建即可。
typescript
/**
* keys/index.ts - SDK 密钥体系
*
* 架构设计 §1.3.1 HD 派生路径规范:
* - m/44'/0'/0'/0/0 → Ed25519(身份认证/签名)
* - m/44'/1'/0'/0/0 → X25519(ECDH 消息加密)
*
* 依赖:
* - @scure/bip39:助记词生成/验证
* - @noble/curves/ed25519:Ed25519 签名
* - @noble/curves/x25519:X25519 ECDH
* - @noble/hashes/sha512:PBKDF KDF
*/
interface KeyPair {
privateKey: Uint8Array;
publicKey: Uint8Array;
}
interface Identity {
mnemonic: string;
/** Ed25519 身份密钥,用于 Challenge-Response 认证 */
signingKey: KeyPair;
/** X25519 ECDH 密钥,用于消息会话密钥协商 */
ecdhKey: KeyPair;
}
/** 生成 12 词英文助记词 */
declare function newMnemonic(): string;
/** 验证助记词是否合法(12 词,BIP-39 词库)*/
declare function validateMnemonicWords(mnemonic: string): boolean;
/**
* 从助记词完整派生 Identity(包含两对密钥)
*/
declare function deriveIdentity(mnemonic: string): Identity;
/**
* 计算 60 字符安全码
* 算法:SHA-256(min(pubA, pubB) ‖ max(pubA, pubB))[0..30] → hex
* 双方使用相同的确定性拼接顺序,MITM 无法伪造一致结果
*/
declare function computeSecurityCode(myEcdhPublicKey: Uint8Array, theirEcdhPublicKey: Uint8Array): string;
declare function toBase64(bytes: Uint8Array): string;
declare function fromBase64(b64: string): Uint8Array;
declare function toHex(bytes: Uint8Array): string;
declare function fromHex(hex: string): Uint8Array;
/**
* keys/store.ts - T-012 IndexedDB 三存储持久化
* 关键数据结构:identity / sessions / offlineInbox
*/
interface StoredIdentity {
uuid: string;
aliasId: string;
nickname: string;
mnemonic: string;
signingPublicKey: string;
ecdhPublicKey: string;
}
interface SessionRecord {
conversationId: string;
theirAliasId: string;
theirEcdhPublicKey: string;
theirEd25519PublicKey?: string;
sessionKeyBase64: string;
trustState: 'unverified' | 'verified';
createdAt: number;
}
interface OfflineMessage {
conversationId: string;
seq: number;
payloadEncrypted: string;
createdAt: number;
}
declare function loadIdentity(): Promise<StoredIdentity | undefined>;
declare function clearIdentity(): Promise<void>;
declare function loadSession(conversationId: string): Promise<SessionRecord | undefined>;
declare function listSessions(): Promise<SessionRecord[]>;
declare function deleteSession(conversationId: string): Promise<void>;
declare function markSessionVerified(conversationId: string): Promise<void>;
interface StoredMessage {
id: string;
conversationId: string;
text: string;
isMe: boolean;
time: number;
status: 'sending' | 'sent' | 'delivered' | 'read' | 'failed';
msgType?: string;
mediaUrl?: string;
caption?: string;
seq?: number;
fromAliasId?: string;
replyToId?: string;
}
interface OutboxIntent {
internalId: string;
conversationId: string;
toAliasId: string;
text: string;
addedAt: number;
replyToId?: string;
}
type NetworkState = 'connected' | 'connecting' | 'disconnected';
type NetworkListener = (state: NetworkState) => void;
declare class RobustWSTransport implements WSTransport {
private ws;
isConnected: boolean;
private messageHandlers;
private openHandlers;
private closeHandlers;
private networkListeners;
private goawayListeners;
private reconnectAttempts;
private reconnectTimer;
private heartbeatTimer;
private intentionalClose;
lastUrl: string;
constructor();
onNetworkStateChange(fn: NetworkListener): () => void;
/** 监听 GOAWAY 帧(被其他设备踢下线) */
onGoaway(fn: (reason: string) => void): () => void;
private emitNetworkState;
connect(url: string): void;
private _doConnect;
private _scheduleReconnect;
private _startHeartbeat;
private _stopHeartbeat;
send(data: string): void;
onMessage(handler: (data: string) => void): void;
onOpen(handler: () => void): void;
onClose(handler: () => void): void;
disconnect(): void;
}
/**
* sdk-typescript/src/messaging/index.ts — T-100+T-101
* MessageModule:完整的消息收发封装 + 离线同步引擎 + 强制本地持久化 (Vibe Coding Refactor)
*/
interface OutgoingMessage {
conversationId: string;
toAliasId: string;
text: string;
replyToId?: string;
}
interface MessageStatus {
id: string;
status: 'sending' | 'sent' | 'delivered' | 'read' | 'failed';
}
interface WSTransport {
send(data: string): void;
onMessage(handler: (data: string) => void): void;
onOpen(handler: () => void): void;
onClose(handler: () => void): void;
isConnected: boolean;
}
declare class MessageModule {
private transport;
onMessage?: (msg: StoredMessage) => void;
onStatusChange?: (status: MessageStatus) => void;
onChannelPost?: (data: any) => void;
/** 对方正在输入通知 */
onTyping?: (data: {
fromAliasId: string;
conversationId: string;
}) => void;
/** 链上支付确认通知(pay-worker 确认后 WS 推送,由 VanityModule 订阅)*/
onPaymentConfirmed?: (data: {
type: string;
order_id: string;
ref_id: string;
}) => void;
constructor(transport: WSTransport);
send(msg: OutgoingMessage): Promise<string>;
private _trySendIntent;
sendDelivered(convId: string, seq: number, toAliasId: string): void;
sendRead(convId: string, seq: number, toAliasId: string): void;
sendTyping(toAliasId: string, convId: string): void;
/** 发送消息撤回帧(架构 §4.2 V1.1 新增) */
sendRetract(messageId: string, toAliasId: string, convId: string): void;
private handleFrame;
private handleIncomingMsg;
private handleStatusChange;
/**
* 处理 delivered/read 回执帧(基于 conv_id 批量更新)
* 回执帧格式:{type:'delivered'|'read', conv_id, seq, to}
* 不含消息 id,所以需要按 conv_id 查找自己发出的消息并更新
*/
private handleReceiptByConvId;
private onConnected;
private handleRetract;
}
declare class HttpClient {
private apiBase;
private token;
constructor(apiBase?: string);
setApiBase(apiBase: string): void;
getApiBase(): string;
setToken(token: string | null): void;
getToken(): string | null;
getHeaders(customHeaders?: Record<string, string>): Record<string, string>;
get<T = any>(path: string): Promise<T>;
post<T = any>(path: string, body: any): Promise<T>;
put<T = any>(path: string, body?: any): Promise<T>;
delete<T = any>(path: string): Promise<T>;
/**
* For direct fetch calls (like Media Presigned URL PUT / GET)
*/
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
}
declare class AuthModule {
private http;
private _uuid;
constructor(http: HttpClient);
/**
* 供 SDK 内部建立 WebSocket 时调用,防误用
*/
get internalUUID(): string;
/**
* 恢复会话:从本地 IndexedDB 加载身份 -> 防伪签名挑战 -> 写入 Token
*/
restoreSession(): Promise<{
aliasId: string;
nickname: string;
} | null>;
/**
* 注册:执行 PoW 防刷验证 -> 计算公钥 -> /register -> /auth/challenge -> /auth/verify
* V1.4.1 方案 A:靓号在注册完成后通过 vanity.bind() 接口绑定,注册时不再传入靓号订单
*/
registerAccount(mnemonic: string, nickname: string): Promise<{
aliasId: string;
}>;
/**
* 针对给定的 UUID 和私钥执行防伪鉴权,成功后将 token 注册到 http 内部并返回
*/
performAuthChallenge(userUUID: string, signingPrivateKey: Uint8Array): Promise<string>;
}
interface FriendProfile {
friendship_id: number;
alias_id: string;
nickname: string;
status: 'pending' | 'accepted' | 'rejected';
direction: 'sent' | 'received';
conversation_id: string;
x25519_public_key: string;
ed25519_public_key: string;
created_at: string;
}
declare class ContactsModule {
private http;
constructor(http: HttpClient);
/**
* 同步通讯录:获取所有好友,并为已经接受的好友自动创建本地安全会话(按需)
*/
syncFriends(): Promise<FriendProfile[]>;
/**
* 发起好友请求
*/
sendFriendRequest(toAliasId: string): Promise<void>;
/**
* 接受好友请求
*/
acceptFriendRequest(friendshipId: number): Promise<void>;
/**
* 按 Alias ID 查找用户
*/
lookupUser(aliasId: string): Promise<{
alias_id: string;
nickname: string;
x25519_public_key: string;
ed25519_public_key: string;
}>;
}
/**
* sdk-typescript/src/media/manager.ts — 多媒体上传/下载(零知识盲中转)
* 支持图片(压缩)、文件(原始)、语音(原始)三类
*/
declare class MediaModule {
private http;
constructor(http: HttpClient);
/**
* 极简外壳:自动压缩、调用端侧加密、并获得安全返回
* 成功即返回拼接好的快捷消息内容: "[img]media_key"
*/
uploadImage(conversationId: string, file: File, maxDim?: number, quality?: number): Promise<string>;
/**
* 上传通用文件(不压缩,直接加密分片上传)
* 返回 "[file]media_key|original_name|size_bytes"
*/
uploadFile(file: File, conversationId: string): Promise<string>;
/**
* 上传语音消息(不压缩,直接加密分片上传)
* @param durationMs 录音时长(毫秒)
* 返回 "[voice]media_key|duration_ms"
*/
uploadVoice(blob: Blob, conversationId: string, durationMs: number): Promise<string>;
/**
* 分片上传加密大文件 (由于原生 AES-GCM 限制,采用基于 Chunk 的流式加密)
* 返回 "[img]media_key" (此处重用业务层格式)
*/
uploadEncryptedFile(file: File, conversationId: string, maxDim?: number, quality?: number): Promise<string>;
/**
* 通用加密分片上传(共享逻辑)
* @returns media_key
*/
private encryptAndUpload;
/**
* 下载加密的媒体文件 (裸流,仅供内部解密使用)
*/
private downloadMedia;
/**
* 下载并流式解密媒体文件
*/
downloadDecryptedMedia(mediaKey: string, conversationId: string): Promise<ArrayBuffer>;
/**
* 压缩图片到指定最大尺寸(宽/高)
*/
private compressImage;
}
declare class PushModule {
private http;
constructor(http: HttpClient);
/**
* 浏览器申请推送凭证并向服务端注册
* 此方法需要依赖浏览器的 ServiceWorker API,仅在 Web 端有效
*/
enablePushNotifications(swRegistration: ServiceWorkerRegistration, vapidPublicKey?: string): Promise<void>;
private urlBase64ToUint8Array;
}
interface ChannelInfo {
id: string;
name: string;
description: string;
role?: string;
is_subscribed?: boolean;
/** 频道是否处于出售状态 */
for_sale?: boolean;
/** 出售价格(USDT),仅当 for_sale=true 时有值 */
sale_price?: number;
}
interface ChannelPost {
id: string;
type: string;
content: string;
created_at: string;
author_alias_id: string;
}
/** 频道交易订单(来自 POST /api/v1/channels/{id}/buy)*/
interface ChannelTradeOrder {
order_id: string;
/** 单位 USDT */
price_usdt: number;
/** TRON 收款地址 */
pay_to: string;
/** 订单有效期(ISO 8601) */
expired_at: string;
}
declare class ChannelsModule {
private http;
constructor(http: HttpClient);
/**
* Search for public channels
*/
search(query: string): Promise<ChannelInfo[]>;
/**
* Get channels subscribed by current user
*/
getMine(): Promise<ChannelInfo[]>;
/**
* Get channel details
*/
getDetail(channelId: string): Promise<ChannelInfo>;
/**
* Create a new channel
*/
create(name: string, description: string, isPublic?: boolean): Promise<{
channel_id: string;
}>;
/**
* Subscribe to a channel
*/
subscribe(channelId: string): Promise<void>;
/**
* Unsubscribe from a channel
*/
unsubscribe(channelId: string): Promise<void>;
/**
* Post a message to a channel
*/
postMessage(channelId: string, content: string, type?: string): Promise<{
post_id: string;
}>;
/**
* Get channel post history
*/
getPosts(channelId: string): Promise<ChannelPost[]>;
/**
* Check if current user can post in the channel
*/
canPost(channelInfo: ChannelInfo | null): boolean;
/**
* 将自有频道挂牌出售(T-096,需要 JWT,必须是频道 Owner)
*
* 使用乐观锁 CAS 设置售价,挂牌后其他用户可通过 `buyChannel` 购买。
*
* @param channelId 要出售的频道 ID
* @param priceUsdt 出售价格(USDT,整数)
*
* @example
* await client.channels.listForSale('ch_abc123', 200)
*/
listForSale(channelId: string, priceUsdt: number): Promise<void>;
/**
* 购买频道 — 创建支付订单(T-096,需要 JWT)
*
* 使用乐观锁 CAS 防止超卖。返回后向用户展示 TRON 收款地址,
* 链上确认后 pay-worker 自动完成频道所有权转移,并推送 `payment_confirmed` WS 事件。
*
* @throws 409 — 频道刚被其他人抢购,请刷新后重试
* @throws 404 — 频道不存在
* @throws 400 — 试图购买自己的频道
*
* @example
* const order = await client.channels.buyChannel('ch_abc123')
* showQRCode(order.pay_to, order.price_usdt)
*/
buyChannel(channelId: string): Promise<ChannelTradeOrder>;
/**
* 购买额外频道创建配额(每次购买增加 1 个席位,固定 5 USDT)
*
* @throws 401 — 需要登录
* @example
* const order = await client.channels.buyQuota()
* showQRCode(order.pay_to, order.price_usdt)
*/
buyQuota(): Promise<ChannelTradeOrder>;
}
/**
* sdk-typescript/src/vanity/manager.ts — T-095 VanityModule
*
* 负责靓号的搜索、购买(创建支付订单)和链上确认回调订阅。
* 支付感知通过 WS `payment_confirmed` 帧 → MessageModule.onPaymentConfirmed → 本模块路由实现。
*
* V1.4.1(方案 A):靓号商店已移至注册完成后,主流程:
* 1. purchase(aliasId) — 注册后购买(需要 JWT),创建 PENDING 订单
* 2. 监听 onPaymentConfirmed() WS 推送 → 链上确认
* 3. bind(orderId) — 支付确认后绑定靓号到账户 alias_id
*
* @deprecated reserve() / orderStatus() 为旧版注册前流程遗留接口,不再使用
*/
/** 靓号列表项(来自 GET /api/v1/vanity/search)
* V1.3 规则引擎版:价格由后端 rules.go 实时评估,不再依赖预填表 */
interface VanityItem {
alias_id: string;
price_usdt: number;
/** 靓号等级:'top' | 'premium' | 'standard' */
tier: string;
is_featured: boolean;
}
/** 购买靓号返回的支付订单(来自 POST /api/v1/vanity/purchase,需 JWT)*/
interface PurchaseOrder {
order_id: string;
alias_id: string;
/** 单位 USDT */
price_usdt: number;
/** NOWPayments 托管支付页 URL(V1.5.0 新增,接入 NOWPayments 后返回)*/
payment_url: string;
/** @deprecated 原始 TRON 地址,NOWPayments 接入后不再使用 */
pay_to?: string;
/** 订单有效期(ISO 8601) */
expired_at: string;
}
/**
* 注册前预订靓号返回的订单(来自 POST /api/v1/vanity/reserve,**无需 JWT**)
* 与 PurchaseOrder 结构相同,但不绑定用户 UUID(注册前无身份)
*/
interface ReserveOrder {
order_id: string;
alias_id: string;
price_usdt: number;
/** TRON 收款地址 */
pay_to: string;
/** 订单有效期(ISO 8601) */
expired_at: string;
}
/**
* 订单状态查询结果(来自 GET /api/v1/vanity/order/{id}/status,**无需 JWT**)
* 用于注册前用户轮询支付结果
*/
interface OrderStatus {
status: 'pending' | 'confirmed' | 'expired';
alias_id: string;
}
/** WS payment_confirmed 事件(pay-worker 链上确认后推送)*/
interface PaymentConfirmedEvent {
type: 'payment_confirmed';
order_id: string;
/** 靓号购买 → alias_id;频道交易 → channel_id */
ref_id: string;
}
declare class VanityModule {
private http;
private paymentListeners;
constructor(http: HttpClient);
/**
* 搜索靓号 / 获取精选列表(T-091 公开接口,无需 JWT)
*
* - `q` 为空 → 返回精选 (is_featured=1),按价格升序
* - `q` 非空 → 按 alias_id 前缀匹配(LIKE 'q%')
*
* @example
* const featured = await client.vanity.search()
* const results = await client.vanity.search('888')
*/
search(q?: string): Promise<VanityItem[]>;
/**
* @deprecated V1.4.1 方案 A 后不再使用。旧版注册前公开预订靓号(无需 JWT)。
* 请改用 purchase()(注册后,需 JWT)+ bind() 流程。
*/
reserve(aliasId: string): Promise<ReserveOrder>;
/**
* @deprecated V1.4.1 方案 A 后不再使用。旧版注册前轮询订单状态(无需 JWT)。
* 注册后请改用 onPaymentConfirmed() WS 推送。
*/
orderStatus(orderId: string): Promise<OrderStatus>;
/**
* 购买靓号 — 创建支付订单(T-090,**需要 JWT**)
*
* V1.4.1 方案 A:注册完成后的首次引导页调用此方法。
* 使用乐观锁 CAS 占位 15 分钟。返回后向用户展示 TRON 收款地址,
* 用户链上转账后 pay-worker 自动完成确认,并通过 WS 推送 `payment_confirmed`。
* 收到推送后,调用 bind(orderId) 将靓号正式绑定到账户。
*
* @throws 409 — 靓号已被其他人抢占,请提示用户更换
* @throws 404 — 靓号不存在
*
* @example
* const order = await client.vanity.purchase('88888888')
* // 展示支付弹窗
* client.vanity.onPaymentConfirmed(async e => {
* const { alias_id } = await client.vanity.bind(e.order_id)
* store.setAliasId(alias_id)
* })
*/
purchase(aliasId: string): Promise<PurchaseOrder>;
/**
* 绑定靓号到当前账户(**V1.4.1 新增,需要 JWT**)
*
* 在 pay-worker 确认链上支付后,调用此方法将 `alias_id` 正式写入 identity 表。
* 通常在 onPaymentConfirmed() 回调内调用。
*
* @param orderId — 已确认的 `payment_order.id`
* @returns `{ alias_id }` — 绑定成功的靓号
*
* @throws 404 — 订单不存在或不属于当前用户
* @throws 409 — 订单未确认 / 靓号绑定冲突
*
* @example
* const { alias_id } = await client.vanity.bind(orderId)
* store.setAliasId(alias_id)
*/
bind(orderId: string): Promise<{
alias_id: string;
}>;
/**
* 订阅支付完成回调(链上确认后 pay-worker → WS 推送)
*
* 返回 unsubscribe 函数,可直接在 React `useEffect` 清理函数中调用。
*
* @example
* useEffect(() => {
* return client.vanity.onPaymentConfirmed(e => {
* toast(`🎉 靓号 ${e.ref_id} 已绑定到你的账号!`)
* router.push('/profile')
* })
* }, [])
*/
onPaymentConfirmed(cb: (e: PaymentConfirmedEvent) => void): () => void;
/**
* @internal SDK 内部路由入口,由 MessageModule handleFrame 在收到
* `payment_confirmed` WS 帧时调用,App 层不应直接调用此方法。
*/
_handlePaymentConfirmed(event: PaymentConfirmedEvent): void;
}
/**
* sdk-typescript/src/calls/index.ts — T-072+T-073
* WebRTC 信令状态机 + Insertable Streams E2EE(视频帧加密)
*
* 架构 §4:通话建立流程
* Caller → call_offer(含 SDP + Ed25519 签名)
* → Relay(透明转发)
* → Callee → call_answer / call_reject
* → ICE Candidate 交换
* ← TURN 中继 ← RTP
*/
type CallState = 'idle' | 'calling' | 'ringing' | 'connecting' | 'connected' | 'hangup' | 'rejected' | 'ended';
interface CallOptions {
audio?: boolean;
video?: boolean;
}
interface SignalTransport {
send(env: unknown): void;
onMessage(handler: (env: unknown) => void): void;
}
declare class CallModule {
private transport;
private iceConfigProvider;
private pc;
private callId;
private state;
private localStream;
private remoteStream;
private pendingCandidates;
private flushIceCandidates;
private signingPrivKey;
private signingPubKey;
private myAliasId;
onStateChange?: (state: CallState) => void;
onRemoteStream?: (stream: MediaStream) => void;
onLocalStream?: (stream: MediaStream) => void;
onIncomingCall?: (fromAlias: string) => void;
onError?: (err: Error) => void;
getLocalStream(): MediaStream | null;
getRemoteStream(): MediaStream | null;
constructor(transport: SignalTransport, iceConfigProvider: () => Promise<RTCConfiguration>, opts: {
signingPrivKey: Uint8Array;
signingPubKey: Uint8Array;
myAliasId: string;
});
call(toAliasId: string, opts?: CallOptions): Promise<void>;
answer(): Promise<void>;
reject(): void;
hangup(): void;
private _callerAlias;
private _remoteAlias;
private handleSignal;
private handleOffer;
private handleAnswer;
private handleICE;
private createPeerConnection;
private sendSignal;
private setState;
private cleanup;
private _pubKeyCache;
private fetchPubKey;
private _sessionKeyCache;
private getSessionKey;
}
/**
* setupE2EETransform:为 RTCRtpSender/Receiver 安装帧级加解密 Transform
* @param kind 'sender' | 'receiver'
* @param rtpObject RTCRtpSender 或 RTCRtpReceiver
* @param keyMaterial AES-256-GCM 密钥 + BaseIV(44字节:32+12,由 HKDF 从会话密钥派生)
*/
declare function setupE2EETransform(kind: 'sender' | 'receiver', rtpObject: RTCRtpSender | RTCRtpReceiver, keyMaterial: Uint8Array): Promise<void>;
type ClientEvent = 'message' | 'status_change' | 'network_state' | 'channel_post' | 'typing' | 'goaway';
interface TypingEvent {
fromAliasId: string;
conversationId: string;
}
declare class SecureChatClient {
readonly transport: RobustWSTransport;
readonly messaging: MessageModule;
readonly auth: AuthModule;
readonly contacts: ContactsModule;
readonly media: MediaModule;
readonly push: PushModule;
readonly channels: ChannelsModule;
readonly vanity: VanityModule;
calls: CallModule | null;
http: HttpClient;
private eventListeners;
static readonly CORE_API_BASE = "https://relay.daomessage.com";
constructor();
/**
* 恢复历史会话:从本地加载身份鉴权,成功后即可调用 connect
* 返回值新增 nickname,App 不再需要从 localStorage 读取昵称
*/
restoreSession(): Promise<{
aliasId: string;
nickname: string;
} | null>;
/**
* 建立连接:自动从 AuthModule 与 HTTP 获取底层验证标识,封装并连接 WSS
*/
connect(): void;
/**
* 断开连接
*/
disconnect(): void;
/**
* 获取当前网络状态
*/
get isConnected(): boolean;
/**
* 初始化通话模块(需要提供从 DB 提取的用户身份密钥)
*/
initCalls(opts: {
signingPrivKey: Uint8Array;
signingPubKey: Uint8Array;
myAliasId: string;
alwaysRelay?: boolean;
}): void;
/**
* 事件订阅 — 返回 unsubscribe 函数,可直接在 useEffect return 中调用
* @example
* useEffect(() => {
* return client.on('message', handleMsg) // 自动解绑
* }, [])
*/
on(event: 'message', listener: (msg: StoredMessage) => void): () => void;
on(event: 'status_change', listener: (status: MessageStatus) => void): () => void;
on(event: 'network_state', listener: (state: NetworkState) => void): () => void;
on(event: 'channel_post', listener: (data: any) => void): () => void;
on(event: 'typing', listener: (data: TypingEvent) => void): () => void;
on(event: 'goaway', listener: (reason: string) => void): () => void;
/**
* 移除事件订阅(on() 已返回 unsubscribe,推荐用 on() 的返回值代替此方法)
*/
off(event: ClientEvent, listener: any): void;
/**
* 发送端到端加密消息(自动入队或发送)
* @param replyToId 可选,引用回复的原消息 ID(架构 §SendOptions.replyTo)
*/
sendMessage(conversationId: string, toAliasId: string, text: string, replyToId?: string): Promise<string>;
/**
* 发送图片消息:压缩、盲加密上传、拼接协议发回
* @param thumbnail 可选,Base64 低分辨率骨架缩略图,供接收方在高清图加载前展示
*/
sendImage(conversationId: string, toAliasId: string, file: File, thumbnail?: string): Promise<string>;
/**
* 发送文件消息:直接加密上传(不压缩)
* 消息协议: [file]media_key|filename|size
*/
sendFile(conversationId: string, toAliasId: string, file: File): Promise<string>;
/**
* 发送语音消息:录音 Blob 直接加密上传
* @param durationMs 录音时长(毫秒),前端 MediaRecorder 计时
* 消息协议: [voice]media_key|duration_ms
*/
sendVoice(conversationId: string, toAliasId: string, blob: Blob, durationMs: number): Promise<string>;
/**
* 发送正在输入状态(含节流,建议调用方在 300ms 防抖后触发)
*/
sendTyping(conversationId: string, toAliasId: string): void;
/**
* 标记收到的消息为已读
* @param toAliasId 消息发送方的 alias_id,后端据此路由已读回执
*/
markAsRead(conversationId: string, maxSeq: number, toAliasId: string): void;
/**
* 撤回消息(架构 §4.2 V1.1 新增)
* 仅可撤回自己发的消息,不限时间
* @param messageId 要撤回的消息 UUID
* @param toAliasId 对方 alias_id
* @param conversationId 会话 ID
*/
retractMessage(messageId: string, toAliasId: string, conversationId: string): Promise<void>;
/**
* 获取会话的历史消息(来自 SDK 内部持久化数据库 IndexedDB)
* @param opts.limit 最多返回条数(默认全量)
* @param opts.before 只返回时间戳小于此值的消息(用于分页加载更早消息)
*/
getHistory(conversationId: string, opts?: {
limit?: number;
before?: number;
}): Promise<StoredMessage[]>;
/**
* 获取单条消息细节
*/
getMessageData(messageId: string): Promise<StoredMessage | undefined>;
/**
* 清除某个会话历史
*/
clearHistory(conversationId: string): Promise<void>;
/**
* 清除所有会话历史
*/
clearAllHistory(): Promise<void>;
/**
* 导出会话存档(NDJSON 格式)
* @param conversationId 指定会话 ID,可传 'all' 导出全部
* @returns string 生成下载用途的 Blob Object URL
*/
exportConversation(conversationId: string): Promise<string>;
}
/**
* security/index.ts — SecurityModule(P3-004 修复)
*
* 实现文档 §2.2.1 SecurityModule 接口设计:
* - getSecurityCode(contactId) → 60位安全码(MITM 防御)
* - verifyInputCode(contactId, code) → 输入验证(主路径)
* - markAsVerified(contactId) → 手动标记已验证(辅助路径)
* - getTrustState(contactId) → 信任状态
* - resetTrustState(contactId) → 重置信任
*
* 所有状态存储于 IndexedDB(服务器完全不知情)
* 防劫持守护:每条消息触发前调用 guardMessage() 检测公钥突变
*/
interface SecurityCode {
contactId: string;
/** 60 位十六进制字符串,每 4 字符一组,如 "AB12 · F39C · ..." */
displayCode: string;
/** 原始 hex(用于 verifyInputCode 内部比对)*/
fingerprintHex: string;
}
type TrustState = {
status: 'unverified';
} | {
status: 'verified';
verifiedAt: number;
fingerprintSnapshot: string;
};
interface SecurityViolationEvent {
type: 'security_violation';
contactId: string;
previousFingerprint: string;
currentFingerprint: string;
detectedAt: number;
message: null;
}
declare class SecurityModule {
/**
* 获取与指定联系人的安全码(60 位 hex 字符串)
* 每次加好友后全自动生成,UI 打开"加密详情"页时调用
*/
getSecurityCode(contactId: string, myEcdhPublicKey: Uint8Array, theirEcdhPublicKey: Uint8Array): Promise<SecurityCode>;
/**
* 输入验证(主路径):
* 用户粘贴对方通过微信/TG 发来的 60 位安全码,SDK 自动与本地计算值比对
* 返回 true → 一致(无 MITM)→ 自动写入 verified
* 返回 false → 不一致(公钥被篡改)
*/
verifyInputCode(contactId: string, inputCode: string, myEcdhPublicKey: Uint8Array, theirEcdhPublicKey: Uint8Array): Promise<boolean>;
/**
* 手动标记为"已验证"(辅助路径):
* 用户通过截图肉眼比对后,手动点击按钮调用此方法
* 服务器完全不知情(存储于 IndexedDB)
*/
markAsVerified(contactId: string, myEcdhPublicKey: Uint8Array, theirEcdhPublicKey: Uint8Array): Promise<void>;
/**
* 获取指定联系人当前的信任状态
*/
getTrustState(contactId: string): Promise<TrustState>;
/**
* 重置验证状态:将 trust_state 还原为 'unverified'
* 场景:用户主动换设备/助记词后需重新核查
*/
resetTrustState(contactId: string): Promise<void>;
/**
* 防劫持守护(文档 §2.2 流程图三):
* 每条消息到达时自动调用,若检测到公钥突变,返回 SecurityViolationEvent
*
* @returns null → 验证通过,可正常解密
* @returns SecurityViolationEvent → 公钥突变,拒绝解密并上报 UI
*/
guardMessage(contactId: string, currentMyEcdh: Uint8Array, currentTheirEcdh: Uint8Array): Promise<SecurityViolationEvent | null>;
}
declare const securityModule: SecurityModule;
interface EstablishedSession {
conversationId: string;
securityCode: string;
trustedAt?: number;
}
interface MessageEnvelope {
type: 'msg';
id: string;
to: string;
conv_id: string;
crypto_v: number;
payload: string;
}
export { CallModule, type CallOptions, type CallState, type ChannelTradeOrder, type FriendProfile as ContactProfile, ContactsModule, type EstablishedSession, type Identity, type KeyPair, type MessageEnvelope, MessageModule, type MessageStatus, type NetworkState, type OfflineMessage, type OrderStatus, type OutboxIntent, type OutgoingMessage, type PaymentConfirmedEvent, type PurchaseOrder, type ReserveOrder, RobustWSTransport, SecureChatClient, type SecurityCode, SecurityModule, type SecurityViolationEvent, type SessionRecord, type SignalTransport, type StoredIdentity, type StoredMessage, type TrustState, type TypingEvent, type VanityItem, VanityModule, type WSTransport, clearIdentity, computeSecurityCode, deleteSession, deriveIdentity, fromBase64, fromHex, listSessions, loadIdentity, loadSession, markSessionVerified, newMnemonic, securityModule, setupE2EETransform, toBase64, toHex, validateMnemonicWords };