/** * ChannelService — manages channel lifecycle, visibility, invites, and messaging. * * Maintains in-memory stores for channels, messages, and invite tokens. * Supports channel creation with visibility modes, join/leave, messaging, * search, invite token generation/redemption, mute/kick operations. * * Uses epoch-aware key management via IKeyEpochState for forward secrecy. * Each key rotation creates a new epoch. Messages record which epoch they * were encrypted under. On member removal, all epoch keys are re-wrapped * for remaining members only. * * When an IChatStorageProvider is injected, channels, channel messages, and * invite tokens are also persisted to the provider's collections (write-through). * Sync helper methods continue to read from the in-memory Maps so their * signatures remain unchanged. * * Requirements: 10.3, 1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 9.1 */ import { ChannelVisibility, Permission } from '../../enumerations/communication'; import { IChannel, IChannelUpdate, IChatAttachmentInput, ICommunicationMessage, IInviteToken, IPaginatedResult } from '../../interfaces/communication'; import { IBlockContentStore } from '../../interfaces/communication/blockContentStore'; import { IChatStorageProvider } from '../../interfaces/communication/chatStorageProvider'; import { ICommunicationEventEmitter } from '../../interfaces/events'; import { IKeyEpochState } from './keyEpochManager'; import { MessageOperationsService } from './messageOperationsService'; import { PermissionService } from './permissionService'; export declare class ChannelNotFoundError extends Error { constructor(channelId: string); } export declare class NotChannelMemberError extends Error { constructor(); } export declare class ChannelMessageNotFoundError extends Error { constructor(messageId: string); } export declare class ChannelPermissionError extends Error { readonly missingPermission: Permission; constructor(permission: Permission); } export declare class ChannelMemberMutedError extends Error { constructor(); } export declare class NotMessageAuthorError extends Error { constructor(); } export declare class ChannelReactionNotFoundError extends Error { constructor(reactionId: string); } export declare class MemberAlreadyInChannelError extends Error { constructor(memberId: string); } export declare class ChannelNameConflictError extends Error { constructor(name: string); } export declare class ChannelJoinDeniedError extends Error { constructor(); } export declare class InviteTokenExpiredError extends Error { constructor(); } export declare class InviteTokenNotFoundError extends Error { constructor(token: string); } /** * Callback for encrypting a symmetric key for a specific member. * Returns the encrypted key as a string (e.g. base64). */ export type ChannelKeyEncryptionHandler = (memberId: string, symmetricKey: Uint8Array) => string; /** * Extract the symmetric key from a default-encrypted key string. * Only works with the default placeholder encryption. */ export declare function extractChannelKeyFromDefault(encrypted: string): Uint8Array; export declare class ChannelService { /** channelId → IChannel */ private readonly channels; /** channelId → messages (ordered by createdAt ascending) */ private readonly messages; /** channelId → epoch-aware key state (raw keys + wrapped keys per epoch) */ private readonly keyEpochStates; /** token string → IInviteToken */ private readonly inviteTokens; /** * Per-server channel name uniqueness index. * Outer key = serverId (or '__standalone__' for channels without a server). * Inner key = normalized channel name → channelId. */ private readonly nameIndex; /** Sentinel key for channels that don't belong to any server. */ private static readonly STANDALONE_KEY; /** Whether init() has already been called (idempotency guard). */ private initialized; private readonly permissionService; private readonly encryptKey; private readonly messageOps; private readonly eventEmitter; private readonly randomBytesProvider; /** Optional persistent collection for channels (write-through). */ private readonly channelCollection; /** Optional persistent collection for channel messages (write-through). */ private readonly channelMessageCollection; /** Optional persistent collection for invite tokens (write-through). */ private readonly inviteTokenCollection; /** Optional block content store for storing message content as blocks. */ private readonly blockContentStore; constructor(permissionService: PermissionService, encryptKey?: ChannelKeyEncryptionHandler, messageOps?: MessageOperationsService, eventEmitter?: ICommunicationEventEmitter, randomBytesProvider?: (length: number) => Uint8Array, storageProvider?: IChatStorageProvider, blockContentStore?: IBlockContentStore); /** * Load persisted channels, channel messages, and invite tokens back into * in-memory Maps, rebuilding derived indexes, key epoch states, and * permission registrations. * * Idempotent: only the first call performs rehydration. No-op when no * storage provider was supplied at construction time. * * Requirements: 1.3, 1.5, 1.6, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 8.1, 8.2, 8.3, 9.1, 9.3, 9.4 */ init(): Promise; private assertChannelExists; private assertIsMember; private assertPermission; private assertNotMuted; private generateSymmetricKey; /** * Encrypt a symmetric key for multiple members, returning a Map. * Wraps key wrapping failures as KeyUnwrapError with context information. * * Requirements: 12.3 */ private encryptKeyForMembers; /** * Create initial epoch state (epoch 0) for a new channel. * Uses KeyEpochManager pattern: creates epoch 0 with wrapped keys for all members. * * Requirements: 1.3, 2.1 */ private createInitialEpochState; /** * Add a member to an existing epoch state: wrap ALL epoch keys for the new member. * This gives the new member access to full message history. * * Requirements: 2.2, 6.1, 6.3 */ private addMemberToEpochState; /** * Rotate key: increment epoch, add new key, delete removed member from ALL epochs, * re-wrap ALL epoch keys for remaining members. * * Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6 */ private rotateEpochState; /** * Rotate the channel's symmetric key using epoch-aware key management. * Generates a new key, increments epoch, re-wraps all epoch keys for remaining members, * and removes the departed member from all epochs. * * Requirements: 3.1, 3.2, 3.3, 3.4, 3.5 */ private rotateKey; /** * Assert that a key epoch exists in the epoch state for a given context. * Throws KeyEpochNotFoundError if the epoch is not found. * * Requirements: 12.3 */ private assertEpochExists; private normalizeChannelName; /** Get the nameIndex scope key for a channel's server (or standalone). */ private nameScope; /** Check if a channel name is taken within a given server scope. */ private isNameTaken; /** Register a channel name in the scoped index. */ private registerName; /** Remove a channel name from the scoped index. */ private unregisterName; /** * Create a new channel. * Generates a shared symmetric key and encrypts it for the creator. * The creator is assigned the OWNER role. * Uses KeyEpochManager pattern to create initial epoch 0. * * Requirements: 10.3, 1.3, 2.1, 9.1 */ createChannel(name: string, creatorId: string, visibility: ChannelVisibility, topic?: string, serverId?: string): Promise; /** * List channels visible to a member. * Returns public channels, plus private/secret/invisible channels * where the member is already a member. * * Requirement 10.3: visibility filtering. */ listChannels(memberId: string, cursor?: string, limit?: number): Promise>; /** * Get channel metadata. Requires membership. */ getChannel(channelId: string, requesterId: string): Promise; /** * Update channel settings (visibility, topic, name). * Requires MANAGE_CHANNEL permission. * * Requirement 10.3: immediately enforce new visibility rules. */ updateChannel(channelId: string, requesterId: string, updates: Partial): Promise; /** * Delete a channel. Requires MANAGE_CHANNEL permission. */ deleteChannel(channelId: string, requesterId: string): Promise; /** * Join a public channel. Rejects invite-only channels. * Wraps ALL epoch keys for the new member so they can read history. * * Requirements: 10.3, 2.2 */ joinChannel(channelId: string, memberId: string): Promise; /** * Remove a member from a channel (server-level operation). * Bypasses permission checks — used by ServerService when a member is removed * from a server so that key rotation is performed for each affected channel. * Silently skips if the member is not in the channel. * * Requirements: 7.1, 7.2, 7.3 */ removeMemberFromChannel(channelId: string, memberId: string): Promise; /** * Add a member to a channel (server-level operation). * Bypasses visibility checks — used by ServerService when a member joins a server * so that all epoch keys are wrapped for the new member across all server channels. * Silently skips if the member is already in the channel. * * Requirements: 6.1, 6.2, 6.3 */ addMemberToChannel(channelId: string, memberId: string): Promise; /** * Leave a channel voluntarily. Rotates the shared key. * * Requirements: 3.5 */ leaveChannel(channelId: string, memberId: string): Promise; /** * Send a message to a channel. * Encrypts content with the current epoch's CEK and records the keyEpoch. * Optionally accepts attachments which are validated against platform limits. * * Requirements: 10.3, 1.1, 1.4, 9.1, 11.1, 11.2, 11.4, 11.5 */ sendMessage(channelId: string, senderId: string, content: string, attachments?: IChatAttachmentInput[]): Promise; /** * Get messages in a channel with cursor-based pagination. * Messages are returned with their keyEpoch so clients can decrypt * using the appropriate epoch's CEK. * Validates that each message's keyEpoch exists in the epoch state; * throws KeyEpochNotFoundError if a message references a non-existent epoch. * * Requirements: 1.2, 12.3 */ getMessages(channelId: string, memberId: string, cursor?: string, limit?: number): Promise>; /** * Search messages in a channel by keyword. */ searchMessages(channelId: string, memberId: string, query: string, cursor?: string, limit?: number): Promise>; /** * Generate an invite token for a channel. * Requires CREATE_INVITES permission. * * Requirement 10.3: time-limited, single-use or multi-use invite token. */ createInvite(channelId: string, creatorId: string, maxUses?: number, expiresInMs?: number): Promise; /** * Redeem an invite token to join a channel. * Wraps ALL epoch keys for the joining member. * * Requirements: 10.3, 2.2 */ redeemInvite(token: string, memberId: string): Promise; /** * Mute a member in a channel. * Requires MUTE_MEMBERS permission. * * Requirement 10.3: prevent muted member from sending messages. */ muteMember(channelId: string, requesterId: string, targetId: string, durationMs: number): Promise; /** * Kick a member from a channel. Rotates encryption keys using epoch-aware rotation. * Requires KICK_MEMBERS permission. * * Requirements: 10.3, 3.1, 3.2, 3.3, 3.4 */ kickMember(channelId: string, requesterId: string, targetId: string): Promise; /** * Edit a message authored by the requesting member. * Delegates to MessageOperationsService. */ editMessage(channelId: string, messageId: string, memberId: string, newContent: string): Promise; /** * Delete a message. Authors can delete their own; moderators/admins can delete any. * Delegates to MessageOperationsService. */ deleteMessage(channelId: string, messageId: string, memberId: string): Promise; /** * Pin a message in a channel. * Delegates to MessageOperationsService. */ pinMessage(channelId: string, messageId: string, memberId: string): Promise; /** * Unpin a message in a channel. * Delegates to MessageOperationsService. */ unpinMessage(channelId: string, messageId: string, memberId: string): Promise; /** * Add a reaction to a message. * Delegates to MessageOperationsService. */ addReaction(channelId: string, messageId: string, memberId: string, emoji: string): Promise; /** * Remove a reaction from a message. * Delegates to MessageOperationsService. */ removeReaction(channelId: string, messageId: string, memberId: string, reactionId: string): Promise; getChannelById(channelId: string): IChannel | undefined; /** * Get the current epoch's raw symmetric key for a channel. * Returns the key for the latest epoch (backward-compatible accessor). */ getSymmetricKey(channelId: string): Uint8Array | undefined; /** * Get the full epoch state for a channel (testing / internal use). */ getKeyEpochState(channelId: string): IKeyEpochState | undefined; getAllMessages(channelId: string): ICommunicationMessage[]; getInviteToken(token: string): IInviteToken | undefined; /** * Return all channels the given member belongs to (no pagination). * Used by SearchService for cross-context keyword search. */ listChannelsForMember(memberId: string): IChannel[]; } //# sourceMappingURL=channelService.d.ts.map