import { type PresetName } from './presets.js'; /** Configuration provided when creating a new group. */ export interface GroupConfig { name: string; members: string[]; /** Named threat-profile preset. Explicit fields override preset values. */ preset?: PresetName; rotationInterval?: number; wordCount?: 1 | 2 | 3; wordlist?: string; /** Counter tolerance for verification: accept tokens within ±tolerance counter values (default: 1). */ tolerance?: number; /** * Beacon broadcast interval in seconds (default: 300 = 5 minutes). * * **Privacy note:** Fixed-interval beacons are correlatable by timing. * A relay observer who knows the group's `h` tag can cluster sequential * events by publishing cadence to identify individual members or detect * when a member goes offline. Applications SHOULD add random jitter to * the publish schedule — e.g. ±20–30% of the interval — to reduce * timing correlation. The library does not add jitter because it * encrypts payloads but does not control publish scheduling. */ beaconInterval?: number; /** Geohash precision for normal beacons, 1–11 (default: 6 ≈ 1.2km). */ beaconPrecision?: number; /** Pubkey of the group creator. Only the creator is admin at bootstrap. Must be in `members`. */ creator?: string; } /** Persistent state for a canary group. All fields are serialisable. */ export interface GroupState { name: string; seed: string; members: string[]; rotationInterval: number; wordCount: 1 | 2 | 3; wordlist: string; /** Time-based counter at group creation. Advances with rotation interval. */ counter: number; /** Burn-after-use offset applied on top of the time-based counter. */ usageOffset: number; createdAt: number; /** Counter tolerance for verification: accept tokens within ±tolerance counter values. */ tolerance: number; /** Beacon broadcast interval in seconds. */ beaconInterval: number; /** Geohash precision for normal beacons (1–11). */ beaconPrecision: number; /** Pubkeys with admin privileges (reseed, add/remove others). */ admins: string[]; /** Monotonic epoch — increments on reseed. Used for replay protection. */ epoch: number; /** Consumed operation IDs within the current epoch. Cleared on epoch bump. */ consumedOps: string[]; /** Timestamp floor: reject messages with timestamps at or below this value (replay protection after consumedOps eviction). */ consumedOpsFloor?: number; } /** * Create a new group with a freshly generated seed and time-based counter. * * @param config - Group configuration including name, members, optional preset and overrides. * @returns A new {@link GroupState} with a cryptographically secure random seed. * @throws {Error} If name is empty, preset is unknown, members contain invalid pubkeys, or config values are out of range. * * @example * ```ts * const group = createGroup({ * name: 'Family', * members: [alicePubkey, bobPubkey], * preset: 'family', * }) * getCurrentWord(group) // "falcon" * ``` */ export declare function createGroup(config: GroupConfig): GroupState; /** * Return the current verification word (or space-joined phrase) that all * group members should use to authenticate with one another. * * @param state - Current group state. * @returns The current verification word or space-joined phrase. * * @example * ```ts * const group = createGroup({ name: 'Family', members: [alice, bob] }) * const word = getCurrentWord(group) // e.g. "falcon" * ``` */ export declare function getCurrentWord(state: GroupState): string; /** * Return the duress word (or phrase) for a specific member at the current * counter. Duress words are member-specific and distinct from the verification * word, allowing silent distress signalling. * * @param state - Current group state. * @param memberPubkey - 64-character hex pubkey of the member requesting their duress word. * @returns The member's duress word or space-joined phrase, distinct from the verification word. */ export declare function getCurrentDuressWord(state: GroupState, memberPubkey: string): string; /** * Advance the usage offset by one, rotating the current word (burn-after-use). * Throws a RangeError if the effective counter would exceed the current * time-based counter plus MAX_COUNTER_OFFSET, per CANARY spec §Counter Acceptance. * Returns new state — does not mutate the input. * * @param state - Current group state. * @returns New group state with usageOffset incremented by one. * @throws {RangeError} If advancing would exceed the time-based counter plus MAX_COUNTER_OFFSET. */ export declare function advanceCounter(state: GroupState): GroupState; /** * Generate a fresh seed and reset the usage offset to zero. * Call this after a security event (e.g. suspected compromise). * Returns new state — does not mutate the input. * * @param state - Current group state. * @returns New group state with a fresh cryptographically secure seed and usageOffset reset to zero. */ export declare function reseed(state: GroupState): GroupState; /** * Add a member to the group. If the pubkey is already present, returns the * existing state unchanged (idempotent). * Returns new state — does not mutate the input. * * @param state - Current group state. * @param pubkey - 64-character lowercase hex pubkey of the member to add. * @returns New group state with the member added, or unchanged state if already present. * @throws {Error} If pubkey is not a valid 64-character hex string or group is at maximum capacity. */ export declare function addMember(state: GroupState, pubkey: string): GroupState; /** * Remove a member from the group's member list. * * **Important:** This does NOT reseed. In a symmetric-key group, the removed * member still possesses the old seed and can derive valid words. Use * `removeMemberAndReseed()` instead unless you have a specific reason not to. * * Returns new state — does not mutate the input. * * @param state - Current group state. * @param pubkey - 64-character lowercase hex pubkey of the member to remove. * @returns New group state with the member removed (no-op if not found). * @throws {Error} If pubkey is not a valid 64-character hex string. */ export declare function removeMember(state: GroupState, pubkey: string): GroupState; /** * Remove a member and immediately reseed, atomically invalidating the old seed. * This is the recommended way to remove members — ensures forward secrecy by * preventing the removed member from deriving future tokens or decrypting * future beacons. * * Returns new state — does not mutate the input. * * @param state - Current group state. * @param pubkey - 64-character lowercase hex pubkey of the member to remove. * @returns New group state with the member removed and a fresh seed. * @throws {Error} If pubkey is not a valid 64-character hex string. */ export declare function removeMemberAndReseed(state: GroupState, pubkey: string): GroupState; /** * Dissolve a group, zeroing the seed and clearing all members. * Per CANARY spec §Seed Storage: seed MUST be wiped on group dissolution. * Preserves name and timestamps for audit trail. * * Note: zeroing the seed string prevents further token derivation. Full * memory erasure of prior string values is not possible in JS — platform-level * secure storage should handle that concern. Callers MUST also delete the * persisted record (IndexedDB, localStorage, backend) after calling this. * * Returns new state — does not mutate the input. * * @param state - Current group state. * @returns New group state with seed zeroed, members and admins cleared, and offsets reset. */ export declare function dissolveGroup(state: GroupState): GroupState; /** * Refresh the counter to the current time window. Call after loading persisted state. * Enforces monotonicity: the counter never regresses, preventing clock rollback attacks. * * @param state - Current group state. * @param nowSec - Current unix timestamp in seconds (default: `Date.now() / 1000`). * @returns New group state with counter updated and usageOffset reset, or unchanged if counter has not advanced. */ export declare function syncCounter(state: GroupState, nowSec?: number): GroupState; //# sourceMappingURL=group.d.ts.map