export { deriveGroupKey, deriveGroupIdentity, hashGroupTag, encryptEnvelope, decryptEnvelope } from './sync-crypto.js'; export type { Persona, Identity } from './sync-crypto.js'; import type { GroupState } from './group.js'; /** A typed, serialisable description of a group state change. */ export type SyncMessage = { type: 'member-join'; pubkey: string; displayName?: string; timestamp: number; epoch: number; opId: string; protocolVersion?: number; } | { type: 'member-leave'; pubkey: string; timestamp: number; epoch: number; opId: string; protocolVersion?: number; } | { type: 'counter-advance'; counter: number; usageOffset: number; timestamp: number; protocolVersion?: number; } | { type: 'reseed'; seed: Uint8Array; counter: number; timestamp: number; epoch: number; opId: string; admins: string[]; members: string[]; protocolVersion?: number; } | { type: 'beacon'; lat: number; lon: number; accuracy: number; timestamp: number; opId: string; protocolVersion?: number; } | { type: 'duress-alert'; lat: number; lon: number; timestamp: number; opId: string; subject?: string; protocolVersion?: number; } | { type: 'duress-clear'; subject: string; timestamp: number; opId: string; protocolVersion?: number; } | { type: 'liveness-checkin'; pubkey: string; timestamp: number; opId: string; protocolVersion?: number; } /** WARNING: seed is a plaintext hex string. This message type MUST be sent inside an encrypted * envelope (NIP-44 or AES-GCM via sync-crypto). Callers MUST NOT log SyncMessage objects * containing state-snapshot data, as the seed field would be exposed in plaintext. */ | { type: 'state-snapshot'; seed: string; counter: number; usageOffset: number; members: string[]; admins: string[]; epoch: number; opId: string; timestamp: number; prevEpochSeed?: string; protocolVersion?: number; }; /** * Message types that mutate group state or are safety-critical. * These MUST be delivered to offline devices — use a stored event kind. * Fire-and-forget messages (beacons, liveness) use ephemeral kinds. */ export declare const STORED_MESSAGE_TYPES: Set; /** Maximum age (in seconds) for fire-and-forget messages before they are dropped. */ export declare const FIRE_AND_FORGET_FRESHNESS_SEC = 300; /** Maximum allowed future skew (in seconds) for fire-and-forget timestamps. */ export declare const MAX_FUTURE_SKEW_SEC = 60; /** Current protocol version. Bump on any breaking wire format change. */ export declare const PROTOCOL_VERSION = 2; /** * Encode a sync message as a JSON string for transport. * Binary fields (seed) are hex-encoded for safe JSON round-tripping. * * @param msg - The sync message to serialise. * @returns JSON string with protocolVersion injected and binary fields hex-encoded. */ export declare function encodeSyncMessage(msg: SyncMessage): string; /** * Recursively produce a JSON string with sorted keys and no whitespace. * Handles nested objects, arrays (elements stringified recursively), and * all JSON-safe primitives. Used for deterministic signing (H2). * * @param value - Any JSON-serialisable value (null, boolean, number, string, array, or plain object). * @returns Deterministic JSON string with sorted keys and no whitespace. * @throws {Error} If value contains a Uint8Array (must be hex-encoded first) or unsupported type. */ export declare function stableStringify(value: unknown): string; /** * Return the canonical string representation of a sync message for signing. * Keys are sorted recursively, no whitespace. Binary fields are hex-encoded. * The message's protocolVersion field is preserved as-is — the send side * (encodeSyncMessage) is responsible for injecting PROTOCOL_VERSION before * both encode and sign, so the canonical bytes always reflect the actual * wire value. This is the format that inner signatures are computed over (H2). * * @param msg - The sync message to canonicalise. * @returns Deterministic JSON string with sorted keys and no whitespace. */ export declare function canonicaliseSyncMessage(msg: SyncMessage): string; /** * Decode a sync message from a JSON string. * Throws on invalid or unrecognised messages. * * @param payload - JSON string to decode. * @returns Validated {@link SyncMessage} with correct types (e.g. reseed.seed as Uint8Array). * @throws {Error} If JSON is invalid, message type is unrecognised, required fields are missing, or protocolVersion does not match. */ export declare function decodeSyncMessage(payload: string): SyncMessage; /** * Apply a sync message to group state. * Returns a new GroupState with the change applied. * Beacons and duress alerts don't modify group state (fire-and-forget). * * Authority invariants (I1-I6) are enforced for privileged actions. * Privileged actions without a sender are rejected (fail-closed). * * @param group - Current group state. * @param msg - The sync message to apply. * @param nowSec - Current unix timestamp in seconds (default: `Date.now() / 1000`). * @param sender - Hex pubkey of the message sender (required for privileged actions). * @returns New group state with the change applied, or unchanged state if rejected. */ export declare function applySyncMessage(group: GroupState, msg: SyncMessage, nowSec?: number, sender?: string): GroupState; /** Result of applying a sync message, including whether it was accepted or rejected. */ export interface SyncApplyResult { /** The resulting group state (unchanged if rejected). */ state: GroupState; /** Whether the message was applied to the group state. */ applied: boolean; } /** * Apply a sync message and return a result indicating whether it was accepted. * * Unlike `applySyncMessage` which silently returns unchanged state on rejection, * this function tells the caller whether the message was actually applied — * enabling logging, alerting, and debugging of rejected messages. * * Fire-and-forget messages (beacon, duress-alert, liveness-checkin) always * return `applied: true` when they pass freshness checks, even though they * don't modify group state. * * @param group - Current group state. * @param msg - The sync message to apply. * @param nowSec - Current unix timestamp in seconds (default: `Date.now() / 1000`). * @param sender - Hex pubkey of the message sender (required for privileged actions). * @returns `{ state, applied }` where `applied` indicates whether the message was accepted. */ export declare function applySyncMessageWithResult(group: GroupState, msg: SyncMessage, nowSec?: number, sender?: string): SyncApplyResult; /** Minimal interface any sync transport must implement. */ export interface SyncTransport { /** Send a sync message to all group members. */ send(groupId: string, message: SyncMessage, recipients?: string[]): Promise; /** Subscribe to incoming messages for a group. Returns an unsubscribe function. */ subscribe(groupId: string, onMessage: (msg: SyncMessage, sender: string) => void): () => void; /** Clean up all connections. */ disconnect(): void; } /** Abstracts event signing and NIP-44 encryption for any transport that needs it. */ export interface EventSigner { /** The signer's public key (hex). */ pubkey: string; /** Sign an unsigned event. Parameter and return are `unknown` to avoid coupling to any specific event library's types. */ sign(event: unknown): Promise; /** NIP-44 encrypt plaintext for a recipient. */ encrypt(plaintext: string, recipientPubkey: string): Promise; /** NIP-44 decrypt ciphertext from a sender. */ decrypt(ciphertext: string, senderPubkey: string): Promise; } //# sourceMappingURL=sync.d.ts.map