{
  "api/client-articles/index.html": {
    "href": "api/client-articles/index.html",
    "title": "Client Articles | EchoHub Documentation",
    "summary": "Client Articles Articles related to the EchoHub TUI client built with Terminal.Gui v2. Topics Terminal.Gui v2 patterns and conventions Theme system and customization (including transparent theme) Command system reference Configuration and session persistence Audio playback and file downloads Automatic update checking Notification sounds"
  },
  "api/client/EchoHub.Client.AppOrchestrator.html": {
    "href": "api/client/EchoHub.Client.AppOrchestrator.html",
    "title": "Class AppOrchestrator | EchoHub Documentation",
    "summary": "Class AppOrchestrator Namespace EchoHub.Client Assembly EchoHub.Client.dll Central orchestrator for the EchoHub TUI client. Wires UI events to service calls and connection events to UI updates. public sealed class AppOrchestrator : IDisposable Inheritance object AppOrchestrator Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AppOrchestrator(IApplication, ClientConfig) public AppOrchestrator(IApplication app, ClientConfig config) Parameters app IApplication config ClientConfig Properties MainWindow public MainWindow MainWindow { get; } Property Value MainWindow PendingUpdate Set when the user confirms an update. The host must run this after the Terminal.Gui main loop exits (console restored), so the updater's in-place restart doesn't fight the TUI. public Func<Task>? PendingUpdate { get; } Property Value Func<Task> Methods Dispose() public void Dispose()"
  },
  "api/client/EchoHub.Client.Commands.CommandHandler.html": {
    "href": "api/client/EchoHub.Client.Commands.CommandHandler.html",
    "title": "Class CommandHandler | EchoHub Documentation",
    "summary": "Class CommandHandler Namespace EchoHub.Client.Commands Assembly EchoHub.Client.dll public class CommandHandler Inheritance object CommandHandler Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors CommandHandler() public CommandHandler() Methods HandleAsync(string) public Task<CommandResult> HandleAsync(string input) Parameters input string Returns Task<CommandResult> IsCommand(string) public bool IsCommand(string input) Parameters input string Returns bool Events OnAssignRole public event Func<string, string, Task>? OnAssignRole Event Type Func<string, string, Task> OnBanUser public event Func<string, string?, Task>? OnBanUser Event Type Func<string, string, Task> OnChangeRoomPassword public event Func<string, string, Task>? OnChangeRoomPassword Event Type Func<string, string, Task> OnClearAttachments public event Func<Task>? OnClearAttachments Event Type Func<Task> OnCreateInvite public event Func<int?, int?, Task>? OnCreateInvite Event Type Func<int?, int?, Task> OnDeleteAccount public event Func<Task>? OnDeleteAccount Event Type Func<Task> OnExportData public event Func<Task>? OnExportData Event Type Func<Task> OnHelp public event Func<Task>? OnHelp Event Type Func<Task> OnJoinChannel public event Func<string, string?, Task>? OnJoinChannel Event Type Func<string, string, Task> OnKickUser public event Func<string, string?, Task>? OnKickUser Event Type Func<string, string, Task> OnLeaveChannel public event Func<Task>? OnLeaveChannel Event Type Func<Task> OnListInvites public event Func<Task>? OnListInvites Event Type Func<Task> OnListUsers public event Func<Task>? OnListUsers Event Type Func<Task> OnMuteUser public event Func<string, int?, Task>? OnMuteUser Event Type Func<string, int?, Task> OnNukeChannel public event Func<Task>? OnNukeChannel Event Type Func<Task> OnOpenProfile public event Func<string?, Task>? OnOpenProfile Event Type Func<string, Task> OnOpenServers public event Func<Task>? OnOpenServers Event Type Func<Task> OnQuit public event Func<Task>? OnQuit Event Type Func<Task> OnRevokeInvite public event Func<string, Task>? OnRevokeInvite Event Type Func<string, Task> OnRoomInfo public event Func<Task>? OnRoomInfo Event Type Func<Task> OnSendAction public event Func<string, Task>? OnSendAction Event Type Func<string, Task> OnSendBanner public event Func<string, Task>? OnSendBanner Event Type Func<string, Task> OnSendFile public event Func<string, string?, Task>? OnSendFile Event Type Func<string, string, Task> OnSetAsciiSize public event Func<string, Task>? OnSetAsciiSize Event Type Func<string, Task> OnSetAvatar public event Func<string, Task>? OnSetAvatar Event Type Func<string, Task> OnSetColor public event Func<string, Task>? OnSetColor Event Type Func<string, Task> OnSetDownloadPath public event Func<string, Task>? OnSetDownloadPath Event Type Func<string, Task> OnSetNick public event Func<string, Task>? OnSetNick Event Type Func<string, Task> OnSetStatus Status update. A null status means \"keep the current status\"; a null message means \"keep the current message\" and an empty message means \"clear it\". The orchestrator resolves both against the session state. public event Func<UserStatus?, string?, Task>? OnSetStatus Event Type Func<UserStatus?, string, Task> OnSetTheme public event Func<string, Task>? OnSetTheme Event Type Func<string, Task> OnSetTopic public event Func<string, Task>? OnSetTopic Event Type Func<string, Task> OnTestSound public event Func<Task>? OnTestSound Event Type Func<Task> OnUnbanUser public event Func<string, Task>? OnUnbanUser Event Type Func<string, Task> OnUnmuteUser public event Func<string, Task>? OnUnmuteUser Event Type Func<string, Task>"
  },
  "api/client/EchoHub.Client.Commands.CommandResult.html": {
    "href": "api/client/EchoHub.Client.Commands.CommandResult.html",
    "title": "Class CommandResult | EchoHub Documentation",
    "summary": "Class CommandResult Namespace EchoHub.Client.Commands Assembly EchoHub.Client.dll public record CommandResult : IEquatable<CommandResult> Inheritance object CommandResult Implements IEquatable<CommandResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors CommandResult(CommandResult) protected CommandResult(CommandResult original) Parameters original CommandResult CommandResult(bool, string?, bool) public CommandResult(bool Handled, string? Message = null, bool IsError = false) Parameters Handled bool Message string IsError bool Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Handled public bool Handled { get; init; } Property Value bool IsError public bool IsError { get; init; } Property Value bool Message public string? Message { get; init; } Property Value string Methods Deconstruct(out bool, out string?, out bool) public void Deconstruct(out bool Handled, out string? Message, out bool IsError) Parameters Handled bool Message string IsError bool Equals(CommandResult?) public virtual bool Equals(CommandResult? other) Parameters other CommandResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(CommandResult?, CommandResult?) public static bool operator ==(CommandResult? left, CommandResult? right) Parameters left CommandResult right CommandResult Returns bool operator !=(CommandResult?, CommandResult?) public static bool operator !=(CommandResult? left, CommandResult? right) Parameters left CommandResult right CommandResult Returns bool"
  },
  "api/client/EchoHub.Client.Commands.html": {
    "href": "api/client/EchoHub.Client.Commands.html",
    "title": "Namespace EchoHub.Client.Commands | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.Commands Classes CommandHandler CommandResult"
  },
  "api/client/EchoHub.Client.Config.AccountPreset.html": {
    "href": "api/client/EchoHub.Client.Config.AccountPreset.html",
    "title": "Class AccountPreset | EchoHub Documentation",
    "summary": "Class AccountPreset Namespace EchoHub.Client.Config Assembly EchoHub.Client.dll public class AccountPreset Inheritance object AccountPreset Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AccountPreset() public AccountPreset() Properties Bio public string? Bio { get; set; } Property Value string DisplayName public string? DisplayName { get; set; } Property Value string NicknameColor public string? NicknameColor { get; set; } Property Value string"
  },
  "api/client/EchoHub.Client.Config.ClientConfig.html": {
    "href": "api/client/EchoHub.Client.Config.ClientConfig.html",
    "title": "Class ClientConfig | EchoHub Documentation",
    "summary": "Class ClientConfig Namespace EchoHub.Client.Config Assembly EchoHub.Client.dll public class ClientConfig Inheritance object ClientConfig Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ClientConfig() public ClientConfig() Properties ActiveTheme public string ActiveTheme { get; set; } Property Value string DefaultAsciiSize ASCII-art rendering size for images you attach: \"s\" (40×40), \"m\" (80×80), or \"l\" (120×120). Applies to copy-paste/drag-drop attachments, which have no per-file size flag. public string DefaultAsciiSize { get; set; } Property Value string DefaultPreset public AccountPreset DefaultPreset { get; set; } Property Value AccountPreset DownloadPath Folder where downloaded attachments and saved images are written. When null, the OS Downloads folder is used. Set via the native folder picker or /downloadpath. public string? DownloadPath { get; set; } Property Value string Notifications public NotificationConfig Notifications { get; set; } Property Value NotificationConfig SavedServers public List<SavedServer> SavedServers { get; set; } Property Value List<SavedServer>"
  },
  "api/client/EchoHub.Client.Config.ConfigManager.html": {
    "href": "api/client/EchoHub.Client.Config.ConfigManager.html",
    "title": "Class ConfigManager | EchoHub Documentation",
    "summary": "Class ConfigManager Namespace EchoHub.Client.Config Assembly EchoHub.Client.dll public static class ConfigManager Inheritance object ConfigManager Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Properties ConfigDirectory Directory holding the client config and local key material. public static string ConfigDirectory { get; } Property Value string Methods Load() public static ClientConfig Load() Returns ClientConfig RemoveServer(string) public static void RemoveServer(string url) Parameters url string Save(ClientConfig) public static void Save(ClientConfig config) Parameters config ClientConfig SaveServer(SavedServer) public static void SaveServer(SavedServer server) Parameters server SavedServer"
  },
  "api/client/EchoHub.Client.Config.NotificationConfig.html": {
    "href": "api/client/EchoHub.Client.Config.NotificationConfig.html",
    "title": "Class NotificationConfig | EchoHub Documentation",
    "summary": "Class NotificationConfig Namespace EchoHub.Client.Config Assembly EchoHub.Client.dll public class NotificationConfig Inheritance object NotificationConfig Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors NotificationConfig() public NotificationConfig() Properties Enabled public bool Enabled { get; set; } Property Value bool SoundFile public string? SoundFile { get; set; } Property Value string Volume public byte Volume { get; set; } Property Value byte"
  },
  "api/client/EchoHub.Client.Config.SavedServer.html": {
    "href": "api/client/EchoHub.Client.Config.SavedServer.html",
    "title": "Class SavedServer | EchoHub Documentation",
    "summary": "Class SavedServer Namespace EchoHub.Client.Config Assembly EchoHub.Client.dll public class SavedServer Inheritance object SavedServer Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors SavedServer() public SavedServer() Properties ChannelKeys Cached room content keys for end-to-end encrypted channels on this server, keyed by channel name and encrypted at rest (see RoomKeyProtector; legacy entries were plain base64). Like RefreshToken, these live only on the user's machine — the server never sees them. public Dictionary<string, string> ChannelKeys { get; set; } Property Value Dictionary<string, string> LastConnected public DateTimeOffset LastConnected { get; set; } Property Value DateTimeOffset LastReadMessages Last message the user has read per channel (message id as string), persisted so unread counts, @mention highlights, and the \"new messages\" marker survive restarts. public Dictionary<string, string> LastReadMessages { get; set; } Property Value Dictionary<string, string> LeftChannels Channels the user explicitly left with /leave. Excluded from the automatic join-all-channels pass at connect until the user joins them again. public List<string> LeftChannels { get; set; } Property Value List<string> Name public required string Name { get; set; } Property Value string RefreshToken public string? RefreshToken { get; set; } Property Value string RememberMe public bool RememberMe { get; set; } Property Value bool Url public required string Url { get; set; } Property Value string Username public string? Username { get; set; } Property Value string"
  },
  "api/client/EchoHub.Client.Config.html": {
    "href": "api/client/EchoHub.Client.Config.html",
    "title": "Namespace EchoHub.Client.Config | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.Config Classes AccountPreset ClientConfig ConfigManager NotificationConfig SavedServer"
  },
  "api/client/EchoHub.Client.Services.ApiClient.html": {
    "href": "api/client/EchoHub.Client.Services.ApiClient.html",
    "title": "Class ApiClient | EchoHub Documentation",
    "summary": "Class ApiClient Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class ApiClient : IDisposable Inheritance object ApiClient Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ApiClient(string) public ApiClient(string baseUrl) Parameters baseUrl string Properties BaseUrl public string BaseUrl { get; } Property Value string RefreshToken public string? RefreshToken { get; } Property Value string Token public string? Token { get; } Property Value string Methods AssignRoleAsync(string, ServerRole) public Task AssignRoleAsync(string username, ServerRole role) Parameters username string role ServerRole Returns Task BanUserAsync(string, string?) public Task BanUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task CreateChannelAsync(string, string?, bool, string?, string?, string?) public Task<ChannelDto?> CreateChannelAsync(string name, string? topic = null, bool isPublic = true, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task<ChannelDto> CreateInviteAsync(int?, int?) public Task<InviteDto?> CreateInviteAsync(int? maxUses = null, int? expiresInHours = null) Parameters maxUses int? expiresInHours int? Returns Task<InviteDto> DeleteChannelAsync(string) public Task DeleteChannelAsync(string channelName) Parameters channelName string Returns Task DeleteMessageAsync(Guid) public Task DeleteMessageAsync(Guid messageId) Parameters messageId Guid Returns Task DeleteMyAccountAsync(string) Deletes the caller's account. The password re-confirms intent. public Task DeleteMyAccountAsync(string password) Parameters password string Returns Task Dispose() public void Dispose() DownloadFileToTempAsync(string, string) public Task<string> DownloadFileToTempAsync(string relativeUrl, string fileName) Parameters relativeUrl string fileName string Returns Task<string> ExportMyDataAsync() Downloads the caller's full data export as raw JSON text. public Task<string> ExportMyDataAsync() Returns Task<string> GetChannelCryptoAsync(string) Fetches a channel's public crypto metadata (whether it's E2E-encrypted and its key-derivation salt). Returns null when the channel doesn't exist. public Task<ChannelCryptoDto?> GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task<ChannelCryptoDto> GetChannelMetaAsync(string) Fetches a channel's human-facing metadata (message count, unique posters, estimated size, created date, room id) for the /meta command. Returns null if it doesn't exist. public Task<ChannelMetaDto?> GetChannelMetaAsync(string channelName) Parameters channelName string Returns Task<ChannelMetaDto> GetChannelsAsync() public Task<List<ChannelDto>> GetChannelsAsync() Returns Task<List<ChannelDto>> GetEncryptionKeyAsync() public Task<string> GetEncryptionKeyAsync() Returns Task<string> GetInvitesAsync() public Task<List<InviteDto>> GetInvitesAsync() Returns Task<List<InviteDto>> GetServerInfoAsync() public Task<ServerStatusDto?> GetServerInfoAsync() Returns Task<ServerStatusDto> GetUserProfileAsync(string) public Task<UserProfileDto?> GetUserProfileAsync(string username) Parameters username string Returns Task<UserProfileDto> GetValidTokenAsync() Returns a valid access token, refreshing if expired. Used by EchoHubConnection for SignalR token provider. public Task<string?> GetValidTokenAsync() Returns Task<string> KickUserAsync(string, string?) public Task KickUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task LoginAsync(string, string) public Task<LoginResponse> LoginAsync(string username, string password) Parameters username string password string Returns Task<LoginResponse> LoginWithRefreshTokenAsync(string) public Task<LoginResponse> LoginWithRefreshTokenAsync(string refreshToken) Parameters refreshToken string Returns Task<LoginResponse> LogoutAsync() public Task LogoutAsync() Returns Task MuteUserAsync(string, int?, string?) public Task MuteUserAsync(string username, int? durationMinutes = null, string? reason = null) Parameters username string durationMinutes int? reason string Returns Task NukeChannelAsync(string) public Task NukeChannelAsync(string channelName) Parameters channelName string Returns Task RefreshTokenAsync() public Task RefreshTokenAsync() Returns Task RegisterAsync(string, string, string?, string?) public Task<LoginResponse> RegisterAsync(string username, string password, string? displayName = null, string? inviteCode = null) Parameters username string password string displayName string inviteCode string Returns Task<LoginResponse> RekeyChannelAsync(string, RekeyChannelRequest) public Task<ChannelDto?> RekeyChannelAsync(string channelName, RekeyChannelRequest request) Parameters channelName string request RekeyChannelRequest Returns Task<ChannelDto> RevokeInviteAsync(string) public Task RevokeInviteAsync(string code) Parameters code string Returns Task SendMessageWithAttachmentsAsync(string, string, IReadOnlyList<OutgoingAttachment>, string?) Sends one message with optional text and one or more file attachments. For end-to-end encrypted channels each attachment carries a declared kind and a room-encrypted preview (empty when none); the caption is likewise room-encrypted. public Task<MessageDto?> SendMessageWithAttachmentsAsync(string channelName, string content, IReadOnlyList<OutgoingAttachment> attachments, string? size = null) Parameters channelName string content string attachments IReadOnlyList<OutgoingAttachment> size string Returns Task<MessageDto> SendUrlAsync(string, string, string?) public Task<MessageDto?> SendUrlAsync(string channelName, string url, string? size = null) Parameters channelName string url string size string Returns Task<MessageDto> UnbanUserAsync(string) public Task UnbanUserAsync(string username) Parameters username string Returns Task UnmuteUserAsync(string) public Task UnmuteUserAsync(string username) Parameters username string Returns Task UpdateChannelTopicAsync(string, string?) public Task<ChannelDto?> UpdateChannelTopicAsync(string channelName, string? topic) Parameters channelName string topic string Returns Task<ChannelDto> UpdateProfileAsync(UpdateProfileRequest) public Task<UserProfileDto?> UpdateProfileAsync(UpdateProfileRequest request) Parameters request UpdateProfileRequest Returns Task<UserProfileDto> UploadAvatarAsync(Stream, string) public Task<string?> UploadAvatarAsync(Stream imageStream, string fileName) Parameters imageStream Stream fileName string Returns Task<string> Events OnTokensRefreshed public event Action? OnTokensRefreshed Event Type Action"
  },
  "api/client/EchoHub.Client.Services.AsyncRunner.html": {
    "href": "api/client/EchoHub.Client.Services.AsyncRunner.html",
    "title": "Class AsyncRunner | EchoHub Documentation",
    "summary": "Class AsyncRunner Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Eliminates repeated Task.Run/try/catch/app.Invoke(ShowError) boilerplate. Runs async work on a background thread and routes exceptions to the UI. public static class AsyncRunner Inheritance object AsyncRunner Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods Run(IApplication, Func<Task>, Action<string>, string, string?) public static void Run(IApplication app, Func<Task> work, Action<string> showError, string errorPrefix, string? logContext = null) Parameters app IApplication work Func<Task> showError Action<string> errorPrefix string logContext string"
  },
  "api/client/EchoHub.Client.Services.AudioPlaybackService.html": {
    "href": "api/client/EchoHub.Client.Services.AudioPlaybackService.html",
    "title": "Class AudioPlaybackService | EchoHub Documentation",
    "summary": "Class AudioPlaybackService Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public class AudioPlaybackService Inheritance object AudioPlaybackService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AudioPlaybackService() public AudioPlaybackService() Properties IsPaused public bool IsPaused { get; } Property Value bool IsPlaying public bool IsPlaying { get; } Property Value bool Methods PauseAsync() public Task PauseAsync() Returns Task PlayAsync(string) public Task PlayAsync(string filePath) Parameters filePath string Returns Task ResumeAsync() public Task ResumeAsync() Returns Task SetVolumeAsync(byte) public Task SetVolumeAsync(byte volume) Parameters volume byte Returns Task StopAsync() public Task StopAsync() Returns Task Events PlaybackFinished public event EventHandler? PlaybackFinished Event Type EventHandler"
  },
  "api/client/EchoHub.Client.Services.BackupInfo.html": {
    "href": "api/client/EchoHub.Client.Services.BackupInfo.html",
    "title": "Class BackupInfo | EchoHub Documentation",
    "summary": "Class BackupInfo Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public record BackupInfo : IEquatable<BackupInfo> Inheritance object BackupInfo Implements IEquatable<BackupInfo> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors BackupInfo(BackupInfo) protected BackupInfo(BackupInfo original) Parameters original BackupInfo BackupInfo(string, string, DateTimeOffset) public BackupInfo(string Version, string AppDirectory, DateTimeOffset CreatedAt) Parameters Version string AppDirectory string CreatedAt DateTimeOffset Properties AppDirectory public string AppDirectory { get; init; } Property Value string CreatedAt public DateTimeOffset CreatedAt { get; init; } Property Value DateTimeOffset EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Version public string Version { get; init; } Property Value string Methods Deconstruct(out string, out string, out DateTimeOffset) public void Deconstruct(out string Version, out string AppDirectory, out DateTimeOffset CreatedAt) Parameters Version string AppDirectory string CreatedAt DateTimeOffset Equals(BackupInfo?) public virtual bool Equals(BackupInfo? other) Parameters other BackupInfo Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(BackupInfo?, BackupInfo?) public static bool operator ==(BackupInfo? left, BackupInfo? right) Parameters left BackupInfo right BackupInfo Returns bool operator !=(BackupInfo?, BackupInfo?) public static bool operator !=(BackupInfo? left, BackupInfo? right) Parameters left BackupInfo right BackupInfo Returns bool"
  },
  "api/client/EchoHub.Client.Services.ChannelPasswordRequiredException.html": {
    "href": "api/client/EchoHub.Client.Services.ChannelPasswordRequiredException.html",
    "title": "Class ChannelPasswordRequiredException | EchoHub Documentation",
    "summary": "Class ChannelPasswordRequiredException Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Thrown when joining a channel fails because a password is required or incorrect. The UI catches this to prompt the user and retry. public sealed class ChannelPasswordRequiredException : Exception Inheritance object Exception ChannelPasswordRequiredException Inherited Members Exception.GetBaseException() Exception.ToString() Exception.GetType() Exception.TargetSite Exception.Message Exception.Data Exception.InnerException Exception.HelpLink Exception.Source Exception.HResult Exception.StackTrace object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelPasswordRequiredException(string, string) public ChannelPasswordRequiredException(string channelName, string message) Parameters channelName string message string Properties ChannelName public string ChannelName { get; } Property Value string"
  },
  "api/client/EchoHub.Client.Services.ClientEncryptionService.html": {
    "href": "api/client/EchoHub.Client.Services.ClientEncryptionService.html",
    "title": "Class ClientEncryptionService | EchoHub Documentation",
    "summary": "Class ClientEncryptionService Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Client-side encryption service. Uses the same AES-256-GCM format as the server so messages are encrypted end-to-end between client and server. public sealed class ClientEncryptionService : IMessageEncryptionService Inheritance object ClientEncryptionService Implements IMessageEncryptionService Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ClientEncryptionService() public ClientEncryptionService() Properties EncryptDatabaseEnabled Whether database content should be encrypted at rest (server setting). public bool EncryptDatabaseEnabled { get; } Property Value bool IsInitialized public bool IsInitialized { get; } Property Value bool Methods Decrypt(string) public string Decrypt(string content) Parameters content string Returns string DecryptNullable(string?) public string? DecryptNullable(string? value) Parameters value string Returns string Encrypt(string) public string Encrypt(string plaintext) Parameters plaintext string Returns string EncryptNullable(string?) public string? EncryptNullable(string? value) Parameters value string Returns string SetKey(string) Initialize with the server's encryption key (fetched after login). public void SetKey(string base64Key) Parameters base64Key string"
  },
  "api/client/EchoHub.Client.Services.ClipboardFiles.html": {
    "href": "api/client/EchoHub.Client.Services.ClipboardFiles.html",
    "title": "Class ClipboardFiles | EchoHub Documentation",
    "summary": "Class ClipboardFiles Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Reads file paths that live on the OS clipboard as a file list (e.g. after copying a file in Explorer/Finder/Nautilus), which terminals do not paste as text. Lets Ctrl+V attach a copied file directly instead of requiring the user to paste a raw path. public static class ClipboardFiles Inheritance object ClipboardFiles Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods TryGetFiles(out List<string>) public static bool TryGetFiles(out List<string> files) Parameters files List<string> Returns bool"
  },
  "api/client/EchoHub.Client.Services.ClipboardImage.html": {
    "href": "api/client/EchoHub.Client.Services.ClipboardImage.html",
    "title": "Class ClipboardImage | EchoHub Documentation",
    "summary": "Class ClipboardImage Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Reads raw image data from the OS clipboard (e.g. an image copied from a browser, or a Win+Shift+S screenshot), which terminals cannot paste as text. Always returns PNG bytes: clipboard PNG data is passed through, clipboard bitmaps (CF_DIB) are re-encoded. public static class ClipboardImage Inheritance object ClipboardImage Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods DibToPng(byte[]) Converts clipboard DIB bytes (a BITMAPINFOHEADER/V4/V5 + optional palette/masks + pixel data, i.e. a .bmp file without its 14-byte file header) to PNG. Returns null when the data is malformed or not decodable as a bitmap. public static byte[]? DibToPng(byte[] dib) Parameters dib byte[] Returns byte[] TryGetPng(out byte[]) public static bool TryGetPng(out byte[] png) Parameters png byte[] Returns bool"
  },
  "api/client/EchoHub.Client.Services.EchoHubConnection.html": {
    "href": "api/client/EchoHub.Client.Services.EchoHubConnection.html",
    "title": "Class EchoHubConnection | EchoHub Documentation",
    "summary": "Class EchoHubConnection Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class EchoHubConnection : IAsyncDisposable Inheritance object EchoHubConnection Implements IAsyncDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors EchoHubConnection(string, ApiClient, ClientEncryptionService, RoomKeyStore) public EchoHubConnection(string serverUrl, ApiClient apiClient, ClientEncryptionService encryption, RoomKeyStore roomKeys) Parameters serverUrl string apiClient ApiClient encryption ClientEncryptionService roomKeys RoomKeyStore Fields LockedMessagePlaceholder public const string LockedMessagePlaceholder = \"[encrypted — rejoin this channel with its passphrase to unlock]\" Field Value string Properties IsConnected public bool IsConnected { get; } Property Value bool Methods ConnectAsync() public Task ConnectAsync() Returns Task DisconnectAsync() public Task DisconnectAsync() Returns Task DisposeAsync() public ValueTask DisposeAsync() Returns ValueTask GetHistoryAsync(string, int, int) public Task<List<MessageDto>> GetHistoryAsync(string channelName, int count = 100, int offset = 0) Parameters channelName string count int offset int Returns Task<List<MessageDto>> GetOnlineUsersAsync(string) public Task<List<UserPresenceDto>> GetOnlineUsersAsync(string channelName) Parameters channelName string Returns Task<List<UserPresenceDto>> JoinChannelAsync(string, string?) public Task<JoinOutcome> JoinChannelAsync(string channelName, string? password = null) Parameters channelName string password string Returns Task<JoinOutcome> LeaveChannelAsync(string) public Task LeaveChannelAsync(string channelName) Parameters channelName string Returns Task SendMessageAsync(string, string, Guid?) public Task SendMessageAsync(string channelName, string content, Guid? replyToMessageId = null) Parameters channelName string content string replyToMessageId Guid? Returns Task UpdateStatusAsync(UserStatus, string?) public Task UpdateStatusAsync(UserStatus status, string? statusMessage = null) Parameters status UserStatus statusMessage string Returns Task Events OnChannelDeleted public event Action<string>? OnChannelDeleted Event Type Action<string> OnChannelNuked public event Action<string>? OnChannelNuked Event Type Action<string> OnChannelUpdated public event Action<ChannelDto>? OnChannelUpdated Event Type Action<ChannelDto> OnConnectionStateChanged public event Action<string>? OnConnectionStateChanged Event Type Action<string> OnError public event Action<string>? OnError Event Type Action<string> OnForceDisconnect public event Action<string>? OnForceDisconnect Event Type Action<string> OnMessageDeleted public event Action<string, Guid>? OnMessageDeleted Event Type Action<string, Guid> OnMessageReceived public event Action<MessageDto>? OnMessageReceived Event Type Action<MessageDto> OnReconnected public event Action? OnReconnected Event Type Action OnUserBanned public event Action<string, string?>? OnUserBanned Event Type Action<string, string> OnUserJoined public event Action<string, string, UserPresenceDto?>? OnUserJoined Event Type Action<string, string, UserPresenceDto> OnUserKicked public event Action<string, string, string?>? OnUserKicked Event Type Action<string, string, string> OnUserLeft public event Action<string, string>? OnUserLeft Event Type Action<string, string> OnUserStatusChanged public event Action<UserPresenceDto>? OnUserStatusChanged Event Type Action<UserPresenceDto>"
  },
  "api/client/EchoHub.Client.Services.FolderPickResult.html": {
    "href": "api/client/EchoHub.Client.Services.FolderPickResult.html",
    "title": "Class FolderPickResult | EchoHub Documentation",
    "summary": "Class FolderPickResult Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed record FolderPickResult : IEquatable<FolderPickResult> Inheritance object FolderPickResult Implements IEquatable<FolderPickResult> Inherited Members object.GetType() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors FolderPickResult(PickerOutcome, string?) public FolderPickResult(PickerOutcome Outcome, string? Path) Parameters Outcome PickerOutcome Path string Properties Outcome public PickerOutcome Outcome { get; init; } Property Value PickerOutcome Path public string? Path { get; init; } Property Value string Methods Deconstruct(out PickerOutcome, out string?) public void Deconstruct(out PickerOutcome Outcome, out string? Path) Parameters Outcome PickerOutcome Path string Equals(FolderPickResult?) public bool Equals(FolderPickResult? other) Parameters other FolderPickResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int ToString() public override string ToString() Returns string Operators operator ==(FolderPickResult?, FolderPickResult?) public static bool operator ==(FolderPickResult? left, FolderPickResult? right) Parameters left FolderPickResult right FolderPickResult Returns bool operator !=(FolderPickResult?, FolderPickResult?) public static bool operator !=(FolderPickResult? left, FolderPickResult? right) Parameters left FolderPickResult right FolderPickResult Returns bool"
  },
  "api/client/EchoHub.Client.Services.JoinOutcome.html": {
    "href": "api/client/EchoHub.Client.Services.JoinOutcome.html",
    "title": "Class JoinOutcome | EchoHub Documentation",
    "summary": "Class JoinOutcome Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Result of joining a channel: decrypted history plus, for end-to-end encrypted channels, the key envelope needed to unlock the room content key. public sealed record JoinOutcome : IEquatable<JoinOutcome> Inheritance object JoinOutcome Implements IEquatable<JoinOutcome> Inherited Members object.GetType() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors JoinOutcome(List<MessageDto>, string?, string?) Result of joining a channel: decrypted history plus, for end-to-end encrypted channels, the key envelope needed to unlock the room content key. public JoinOutcome(List<MessageDto> History, string? EncryptionSalt, string? WrappedRoomKey) Parameters History List<MessageDto> EncryptionSalt string WrappedRoomKey string Properties EncryptionSalt public string? EncryptionSalt { get; init; } Property Value string History public List<MessageDto> History { get; init; } Property Value List<MessageDto> WrappedRoomKey public string? WrappedRoomKey { get; init; } Property Value string Methods Deconstruct(out List<MessageDto>, out string?, out string?) public void Deconstruct(out List<MessageDto> History, out string? EncryptionSalt, out string? WrappedRoomKey) Parameters History List<MessageDto> EncryptionSalt string WrappedRoomKey string Equals(JoinOutcome?) public bool Equals(JoinOutcome? other) Parameters other JoinOutcome Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int ToString() public override string ToString() Returns string Operators operator ==(JoinOutcome?, JoinOutcome?) public static bool operator ==(JoinOutcome? left, JoinOutcome? right) Parameters left JoinOutcome right JoinOutcome Returns bool operator !=(JoinOutcome?, JoinOutcome?) public static bool operator !=(JoinOutcome? left, JoinOutcome? right) Parameters left JoinOutcome right JoinOutcome Returns bool"
  },
  "api/client/EchoHub.Client.Services.NativeFolderPicker.html": {
    "href": "api/client/EchoHub.Client.Services.NativeFolderPicker.html",
    "title": "Class NativeFolderPicker | EchoHub Documentation",
    "summary": "Class NativeFolderPicker Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Opens the OS-native folder chooser (Windows Explorer, macOS Finder, Linux GTK/KDE) by shelling out, so the TUI doesn't need a GUI toolkit reference. Returns Unavailable when no native dialog can run, so callers can fall back to a configured path. public static class NativeFolderPicker Inheritance object NativeFolderPicker Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods PickFolderAsync(string?) public static Task<FolderPickResult> PickFolderAsync(string? initialDir) Parameters initialDir string Returns Task<FolderPickResult>"
  },
  "api/client/EchoHub.Client.Services.NotificationSoundService.html": {
    "href": "api/client/EchoHub.Client.Services.NotificationSoundService.html",
    "title": "Class NotificationSoundService | EchoHub Documentation",
    "summary": "Class NotificationSoundService Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public class NotificationSoundService Inheritance object NotificationSoundService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors NotificationSoundService(NotificationConfig) public NotificationSoundService(NotificationConfig config) Parameters config NotificationConfig Methods PlayAsync() public Task PlayAsync() Returns Task PlayTestAsync() Plays the notification sound regardless of the Enabled setting (for /test-sound). public Task PlayTestAsync() Returns Task SetEnabled(bool) public void SetEnabled(bool enabled) Parameters enabled bool SetVolume(byte) public void SetVolume(byte volume) Parameters volume byte"
  },
  "api/client/EchoHub.Client.Services.OutgoingAttachment.html": {
    "href": "api/client/EchoHub.Client.Services.OutgoingAttachment.html",
    "title": "Class OutgoingAttachment | EchoHub Documentation",
    "summary": "Class OutgoingAttachment Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll One file to upload as part of a message. For end-to-end encrypted channels the stream is already ciphertext, DeclaredKind is set (image/audio/file), and EncryptedPreview holds the room-encrypted ASCII art for images. For normal channels only Stream and FileName are set. public sealed record OutgoingAttachment : IEquatable<OutgoingAttachment> Inheritance object OutgoingAttachment Implements IEquatable<OutgoingAttachment> Inherited Members object.GetType() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors OutgoingAttachment(Stream, string, string?, string?) One file to upload as part of a message. For end-to-end encrypted channels the stream is already ciphertext, DeclaredKind is set (image/audio/file), and EncryptedPreview holds the room-encrypted ASCII art for images. For normal channels only Stream and FileName are set. public OutgoingAttachment(Stream Stream, string FileName, string? DeclaredKind = null, string? EncryptedPreview = null) Parameters Stream Stream FileName string DeclaredKind string EncryptedPreview string Properties DeclaredKind public string? DeclaredKind { get; init; } Property Value string EncryptedPreview public string? EncryptedPreview { get; init; } Property Value string FileName public string FileName { get; init; } Property Value string Stream public Stream Stream { get; init; } Property Value Stream Methods Deconstruct(out Stream, out string, out string?, out string?) public void Deconstruct(out Stream Stream, out string FileName, out string? DeclaredKind, out string? EncryptedPreview) Parameters Stream Stream FileName string DeclaredKind string EncryptedPreview string Equals(OutgoingAttachment?) public bool Equals(OutgoingAttachment? other) Parameters other OutgoingAttachment Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int ToString() public override string ToString() Returns string Operators operator ==(OutgoingAttachment?, OutgoingAttachment?) public static bool operator ==(OutgoingAttachment? left, OutgoingAttachment? right) Parameters left OutgoingAttachment right OutgoingAttachment Returns bool operator !=(OutgoingAttachment?, OutgoingAttachment?) public static bool operator !=(OutgoingAttachment? left, OutgoingAttachment? right) Parameters left OutgoingAttachment right OutgoingAttachment Returns bool"
  },
  "api/client/EchoHub.Client.Services.PathSetup.html": {
    "href": "api/client/EchoHub.Client.Services.PathSetup.html",
    "title": "Class PathSetup | EchoHub Documentation",
    "summary": "Class PathSetup Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Ensures the application's directory is on the system PATH so users can run 'echohub' from any terminal session. public static class PathSetup Inheritance object PathSetup Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods EnsureOnPath() Checks if the app directory is on PATH; if not, adds it persistently. On Windows: modifies user-level PATH environment variable. On Linux/macOS: appends an export line to shell profile files. public static void EnsureOnPath()"
  },
  "api/client/EchoHub.Client.Services.PickerOutcome.html": {
    "href": "api/client/EchoHub.Client.Services.PickerOutcome.html",
    "title": "Enum PickerOutcome | EchoHub Documentation",
    "summary": "Enum PickerOutcome Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public enum PickerOutcome Fields Cancelled = 1 The native dialog ran but the user cancelled it. Chosen = 0 The user picked a folder (Path is set). Unavailable = 2 No native picker is available on this machine (headless, missing tool, etc.)."
  },
  "api/client/EchoHub.Client.Services.RoomKeyProtector.html": {
    "href": "api/client/EchoHub.Client.Services.RoomKeyProtector.html",
    "title": "Class RoomKeyProtector | EchoHub Documentation",
    "summary": "Class RoomKeyProtector Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Encrypts cached room content keys at rest so the client config never holds them as plain base64. Windows uses DPAPI (current-user scope, format prefix \"dp1:\"). On other platforms the keys are AES-GCM encrypted with a per-user master key file stored next to the config with 0600 permissions (prefix \"k1:\") — without an OS keychain that is file-permission-level protection, not zero-knowledge: anyone who can read both the config and the key file can recover the room keys. Values with no recognized prefix are legacy plain-base64 keys from older clients; they load once and are re-encrypted. The room passphrase itself is never stored in any form. public sealed class RoomKeyProtector Inheritance object RoomKeyProtector Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors RoomKeyProtector(string, bool?) public RoomKeyProtector(string keyDirectory, bool? useDpapi = null) Parameters keyDirectory string Directory holding the master key file (the client config dir). useDpapi bool? Overrides the platform default (DPAPI on Windows) — for tests. Fields DpapiPrefix public const string DpapiPrefix = \"dp1:\" Field Value string KeyFilePrefix public const string KeyFilePrefix = \"k1:\" Field Value string Methods Protect(byte[]) Encrypts a room key for storage in the config file. public string Protect(byte[] roomKey) Parameters roomKey byte[] Returns string TryUnprotect(string, out byte[], out bool) Decrypts a stored value back into a room key. wasLegacy is true when the value was an unencrypted legacy entry that should be re-persisted via Protect(byte[]). Returns false for unreadable values (wrong user/machine, missing or regenerated key file, malformed data) — the caller drops the entry and the user can recover it by re-entering the passphrase. public bool TryUnprotect(string stored, out byte[] roomKey, out bool wasLegacy) Parameters stored string roomKey byte[] wasLegacy bool Returns bool"
  },
  "api/client/EchoHub.Client.Services.RoomKeyStore.html": {
    "href": "api/client/EchoHub.Client.Services.RoomKeyStore.html",
    "title": "Class RoomKeyStore | EchoHub Documentation",
    "summary": "Class RoomKeyStore Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Holds room content keys for end-to-end encrypted channels: in-memory for the active session, persisted per-server in the client config (like saved sessions) so users don't retype the passphrase every launch. Keys never leave this machine and are encrypted at rest by RoomKeyProtector. Also tracks which channels are known to be end-to-end encrypted, so senders can refuse to emit plaintext into a room whose key isn't cached yet. public sealed class RoomKeyStore Inheritance object RoomKeyStore Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors RoomKeyStore() public RoomKeyStore() RoomKeyStore(RoomKeyProtector) public RoomKeyStore(RoomKeyProtector protector) Parameters protector RoomKeyProtector Methods Clear() public void Clear() HasKey(string) public bool HasKey(string channelName) Parameters channelName string Returns bool IsChannelEncrypted(string) public bool IsChannelEncrypted(string channelName) Parameters channelName string Returns bool LoadForServer(string) Binds the store to a server and loads that server's cached keys from config. public void LoadForServer(string serverUrl) Parameters serverUrl string MarkChannelEncrypted(string, bool) Records whether a channel is end-to-end encrypted (from channel listings, crypto metadata, or join outcomes). Senders consult this to block plaintext into rooms whose key isn't cached. public void MarkChannelEncrypted(string channelName, bool isEncrypted) Parameters channelName string isEncrypted bool RemoveKey(string) public void RemoveKey(string channelName) Parameters channelName string StoreKey(string, byte[]) Stores a key for the session and persists it to the server's config entry. public void StoreKey(string channelName, byte[] key) Parameters channelName string key byte[] TryGetKey(string, out byte[]) public bool TryGetKey(string channelName, out byte[] key) Parameters channelName string key byte[] Returns bool TryStoreFromEnvelope(string, string, byte[]) Unwraps a fresh key envelope and caches the key, overwriting any stale cached key (e.g. the channel was deleted and recreated under the same name, so the old key would encrypt messages nobody else can read). Returns false when the KEK doesn't open the envelope — the cache is left untouched. public bool TryStoreFromEnvelope(string channelName, string wrappedRoomKey, byte[] kek) Parameters channelName string wrappedRoomKey string kek byte[] Returns bool"
  },
  "api/client/EchoHub.Client.Services.RoomLockedException.html": {
    "href": "api/client/EchoHub.Client.Services.RoomLockedException.html",
    "title": "Class RoomLockedException | EchoHub Documentation",
    "summary": "Class RoomLockedException Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Thrown when sending into an end-to-end encrypted channel whose room key isn't cached: without the key the message would leave the client as plaintext, which must never happen. public sealed class RoomLockedException : Exception Inheritance object Exception RoomLockedException Inherited Members Exception.GetBaseException() Exception.ToString() Exception.GetType() Exception.TargetSite Exception.Message Exception.Data Exception.InnerException Exception.HelpLink Exception.Source Exception.HResult Exception.StackTrace object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors RoomLockedException(string) public RoomLockedException(string channelName) Parameters channelName string Properties ChannelName public string ChannelName { get; } Property Value string"
  },
  "api/client/EchoHub.Client.Services.UpdateBackupService.html": {
    "href": "api/client/EchoHub.Client.Services.UpdateBackupService.html",
    "title": "Class UpdateBackupService | EchoHub Documentation",
    "summary": "Class UpdateBackupService Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll Manages pre-update backups and rollback restoration for the auto-updater. Backup location: ~/.echohub/update-backup/ public static class UpdateBackupService Inheritance object UpdateBackupService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Properties IsPostUpdate True if a backup exists from a recent update (set at startup). public static bool IsPostUpdate { get; set; } Property Value bool Methods BackupExists() Returns true if a valid backup exists (both ZIP and metadata file present). public static bool BackupExists() Returns bool CreateBackup() Creates a ZIP backup of the current app directory before an update. Deletes any previous backup first. Uses fastest compression for speed. public static void CreateBackup() DeleteBackup() Deletes the backup directory and all contents. public static void DeleteBackup() GetBackupInfo() Reads backup metadata. Returns null if no backup exists or metadata is unreadable. public static BackupInfo? GetBackupInfo() Returns BackupInfo RestoreBackup() Restores the backup ZIP to the app directory, then restarts the process. This method does not return — it calls Environment.Exit(0). public static void RestoreBackup()"
  },
  "api/client/EchoHub.Client.Services.UpdateChecker.html": {
    "href": "api/client/EchoHub.Client.Services.UpdateChecker.html",
    "title": "Class UpdateChecker | EchoHub Documentation",
    "summary": "Class UpdateChecker Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class UpdateChecker : IDisposable Inheritance object UpdateChecker Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UpdateChecker(IApplication) public UpdateChecker(IApplication app) Parameters app IApplication Properties CurrentVersion public static string CurrentVersion { get; } Property Value string PendingUpdate Set when the user confirms an update. The host runs this after the Terminal.Gui main loop has exited and the console is restored, so the library's in-place restart doesn't deadlock against a TUI that still owns the console. public Func<Task>? PendingUpdate { get; } Property Value Func<Task> Methods CheckNowAsync() public Task CheckNowAsync() Returns Task Dispose() public void Dispose() Start() public void Start()"
  },
  "api/client/EchoHub.Client.Services.html": {
    "href": "api/client/EchoHub.Client.Services.html",
    "title": "Namespace EchoHub.Client.Services | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.Services Classes ApiClient AsyncRunner Eliminates repeated Task.Run/try/catch/app.Invoke(ShowError) boilerplate. Runs async work on a background thread and routes exceptions to the UI. AudioPlaybackService BackupInfo ChannelPasswordRequiredException Thrown when joining a channel fails because a password is required or incorrect. The UI catches this to prompt the user and retry. ClientEncryptionService Client-side encryption service. Uses the same AES-256-GCM format as the server so messages are encrypted end-to-end between client and server. ClipboardFiles Reads file paths that live on the OS clipboard as a file list (e.g. after copying a file in Explorer/Finder/Nautilus), which terminals do not paste as text. Lets Ctrl+V attach a copied file directly instead of requiring the user to paste a raw path. ClipboardImage Reads raw image data from the OS clipboard (e.g. an image copied from a browser, or a Win+Shift+S screenshot), which terminals cannot paste as text. Always returns PNG bytes: clipboard PNG data is passed through, clipboard bitmaps (CF_DIB) are re-encoded. EchoHubConnection FolderPickResult JoinOutcome Result of joining a channel: decrypted history plus, for end-to-end encrypted channels, the key envelope needed to unlock the room content key. NativeFolderPicker Opens the OS-native folder chooser (Windows Explorer, macOS Finder, Linux GTK/KDE) by shelling out, so the TUI doesn't need a GUI toolkit reference. Returns Unavailable when no native dialog can run, so callers can fall back to a configured path. NotificationSoundService OutgoingAttachment One file to upload as part of a message. For end-to-end encrypted channels the stream is already ciphertext, DeclaredKind is set (image/audio/file), and EncryptedPreview holds the room-encrypted ASCII art for images. For normal channels only Stream and FileName are set. PathSetup Ensures the application's directory is on the system PATH so users can run 'echohub' from any terminal session. RoomKeyProtector Encrypts cached room content keys at rest so the client config never holds them as plain base64. Windows uses DPAPI (current-user scope, format prefix \"dp1:\"). On other platforms the keys are AES-GCM encrypted with a per-user master key file stored next to the config with 0600 permissions (prefix \"k1:\") — without an OS keychain that is file-permission-level protection, not zero-knowledge: anyone who can read both the config and the key file can recover the room keys. Values with no recognized prefix are legacy plain-base64 keys from older clients; they load once and are re-encrypted. The room passphrase itself is never stored in any form. RoomKeyStore Holds room content keys for end-to-end encrypted channels: in-memory for the active session, persisted per-server in the client config (like saved sessions) so users don't retype the passphrase every launch. Keys never leave this machine and are encrypted at rest by RoomKeyProtector. Also tracks which channels are known to be end-to-end encrypted, so senders can refuse to emit plaintext into a room whose key isn't cached yet. RoomLockedException Thrown when sending into an end-to-end encrypted channel whose room key isn't cached: without the key the message would leave the client as plaintext, which must never happen. UpdateBackupService Manages pre-update backups and rollback restoration for the auto-updater. Backup location: ~/.echohub/update-backup/ UpdateChecker Enums PickerOutcome"
  },
  "api/client/EchoHub.Client.Themes.Theme.html": {
    "href": "api/client/EchoHub.Client.Themes.Theme.html",
    "title": "Class Theme | EchoHub Documentation",
    "summary": "Class Theme Namespace EchoHub.Client.Themes Assembly EchoHub.Client.dll public class Theme Inheritance object Theme Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors Theme() public Theme() Properties Base public ThemeColors Base { get; set; } Property Value ThemeColors Border Colors for the main-window frame borders (and their titles). Null falls back to Base. Lets themes tone borders down independently of text — e.g. the transparent themes use a dim gray for a subtler, glassy look. Supports hex values (\"#6E6E6E\") as well as named colors. public ThemeColors? Border { get; set; } Property Value ThemeColors Dialog public ThemeColors Dialog { get; set; } Property Value ThemeColors Menu public ThemeColors Menu { get; set; } Property Value ThemeColors Name public required string Name { get; set; } Property Value string Status public ThemeColors Status { get; set; } Property Value ThemeColors"
  },
  "api/client/EchoHub.Client.Themes.ThemeColors.html": {
    "href": "api/client/EchoHub.Client.Themes.ThemeColors.html",
    "title": "Class ThemeColors | EchoHub Documentation",
    "summary": "Class ThemeColors Namespace EchoHub.Client.Themes Assembly EchoHub.Client.dll public class ThemeColors Inheritance object ThemeColors Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ThemeColors() public ThemeColors() Properties Background public string Background { get; set; } Property Value string FocusBackground public string FocusBackground { get; set; } Property Value string FocusForeground public string FocusForeground { get; set; } Property Value string Foreground public string Foreground { get; set; } Property Value string"
  },
  "api/client/EchoHub.Client.Themes.ThemeManager.html": {
    "href": "api/client/EchoHub.Client.Themes.ThemeManager.html",
    "title": "Class ThemeManager | EchoHub Documentation",
    "summary": "Class ThemeManager Namespace EchoHub.Client.Themes Assembly EchoHub.Client.dll public static class ThemeManager Inheritance object ThemeManager Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods ApplyTheme(Theme) public static void ApplyTheme(Theme theme) Parameters theme Theme GetAvailableThemes() public static List<Theme> GetAvailableThemes() Returns List<Theme> GetTheme(string) public static Theme GetTheme(string name) Parameters name string Returns Theme SaveTheme(Theme) public static void SaveTheme(Theme theme) Parameters theme Theme"
  },
  "api/client/EchoHub.Client.Themes.html": {
    "href": "api/client/EchoHub.Client.Themes.html",
    "title": "Namespace EchoHub.Client.Themes | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.Themes Classes Theme ThemeColors ThemeManager"
  },
  "api/client/EchoHub.Client.UI.Chat.AttachmentAction.html": {
    "href": "api/client/EchoHub.Client.UI.Chat.AttachmentAction.html",
    "title": "Enum AttachmentAction | EchoHub Documentation",
    "summary": "Enum AttachmentAction Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll An action a click on an attachment line can trigger. public enum AttachmentAction Fields OpenImage = 0 SaveImage = 1"
  },
  "api/client/EchoHub.Client.UI.Chat.AttachmentActionSpan.html": {
    "href": "api/client/EchoHub.Client.UI.Chat.AttachmentActionSpan.html",
    "title": "Struct AttachmentActionSpan | EchoHub Documentation",
    "summary": "Struct AttachmentActionSpan Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll Inclusive column range on a chat line that triggers an attachment action when clicked. public readonly struct AttachmentActionSpan : IEquatable<AttachmentActionSpan> Implements IEquatable<AttachmentActionSpan> Inherited Members object.GetType() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors AttachmentActionSpan(int, int, AttachmentAction) Inclusive column range on a chat line that triggers an attachment action when clicked. public AttachmentActionSpan(int StartCol, int EndCol, AttachmentAction Action) Parameters StartCol int EndCol int Action AttachmentAction Properties Action public AttachmentAction Action { get; init; } Property Value AttachmentAction EndCol public int EndCol { get; init; } Property Value int StartCol public int StartCol { get; init; } Property Value int Methods Deconstruct(out int, out int, out AttachmentAction) public void Deconstruct(out int StartCol, out int EndCol, out AttachmentAction Action) Parameters StartCol int EndCol int Action AttachmentAction Equals(AttachmentActionSpan) public bool Equals(AttachmentActionSpan other) Parameters other AttachmentActionSpan Returns bool Equals(object) public override bool Equals(object obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int ToString() public override string ToString() Returns string Operators operator ==(AttachmentActionSpan, AttachmentActionSpan) public static bool operator ==(AttachmentActionSpan left, AttachmentActionSpan right) Parameters left AttachmentActionSpan right AttachmentActionSpan Returns bool operator !=(AttachmentActionSpan, AttachmentActionSpan) public static bool operator !=(AttachmentActionSpan left, AttachmentActionSpan right) Parameters left AttachmentActionSpan right AttachmentActionSpan Returns bool"
  },
  "api/client/EchoHub.Client.UI.Chat.ChatColors.html": {
    "href": "api/client/EchoHub.Client.UI.Chat.ChatColors.html",
    "title": "Class ChatColors | EchoHub Documentation",
    "summary": "Class ChatColors Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll Shared color attributes for chat rendering (timestamps, system messages). public static class ChatColors Inheritance object ChatColors Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields AudioAttr public static readonly Attribute AudioAttr Field Value Attribute ChannelRefAttr public static readonly Attribute ChannelRefAttr Field Value Attribute DateRuleAttr Horizontal date-separator rules (── Wed, Jul 16 ──…). public static readonly Attribute DateRuleAttr Field Value Attribute EmbedBorderAttr public static readonly Attribute EmbedBorderAttr Field Value Attribute EmbedDescAttr public static readonly Attribute EmbedDescAttr Field Value Attribute EmbedTitleAttr public static readonly Attribute EmbedTitleAttr Field Value Attribute EmbedUrlAttr public static readonly Attribute EmbedUrlAttr Field Value Attribute FileAttr public static readonly Attribute FileAttr Field Value Attribute MentionHighlightAttr public static readonly Attribute MentionHighlightAttr Field Value Attribute MentionTextAttr public static readonly Attribute MentionTextAttr Field Value Attribute RailAttr The dim vertical rail (│) separating the nick column from message text. public static readonly Attribute RailAttr Field Value Attribute SystemAttr public static readonly Attribute SystemAttr Field Value Attribute TimestampAttr public static readonly Attribute TimestampAttr Field Value Attribute UnreadMarkerAttr The irssi-style \"new messages\" unread marker rule. public static readonly Attribute UnreadMarkerAttr Field Value Attribute Methods SplitMentions(string, Attribute?) Split text around @mentions and #channels, giving each the appropriate accent color. Non-special text uses the provided default color. public static List<ChatSegment> SplitMentions(string text, Attribute? defaultColor = null) Parameters text string defaultColor Attribute? Returns List<ChatSegment>"
  },
  "api/client/EchoHub.Client.UI.Chat.ChatLine.html": {
    "href": "api/client/EchoHub.Client.UI.Chat.ChatLine.html",
    "title": "Class ChatLine | EchoHub Documentation",
    "summary": "Class ChatLine Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll A single line in the chat, composed of colored segments. public class ChatLine Inheritance object ChatLine Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatLine(List<ChatSegment>) public ChatLine(List<ChatSegment> segments) Parameters segments List<ChatSegment> ChatLine(string) public ChatLine(string plainText) Parameters plainText string Properties ActionSpans Clickable sub-line targets (e.g. the \"[open]\" and \"[save original]\" brackets under an image). Columns are relative to the unwrapped line, so only the first wrapped line keeps them. Null means the whole line uses the kind's default action. public List<AttachmentActionSpan>? ActionSpans { get; set; } Property Value List<AttachmentActionSpan> AttachmentFileName public string? AttachmentFileName { get; set; } Property Value string AttachmentKind public AttachmentKind? AttachmentKind { get; set; } Property Value AttachmentKind? AttachmentUrl public string? AttachmentUrl { get; set; } Property Value string ContinuationIndent Number of spaces to prepend on continuation lines when this line is word-wrapped. public int ContinuationIndent { get; set; } Property Value int ContinuationPrefixSegments Colored segments to prepend on continuation lines instead of plain spaces (e.g. the nick-column rail \" │ \"). When set, takes precedence over ContinuationIndent. public List<ChatSegment>? ContinuationPrefixSegments { get; set; } Property Value List<ChatSegment> IsMention public bool IsMention { get; set; } Property Value bool IsUnreadMarker True for the \"new messages\" unread-marker rule so it can be removed on channel switch. public bool IsUnreadMarker { get; set; } Property Value bool JumpToMessageId Set on a reply's quote line: activating the line jumps to this message if it is in the loaded history. public Guid? JumpToMessageId { get; set; } Property Value Guid? MessageId public Guid? MessageId { get; set; } Property Value Guid? RuleAttr Color for a rule line; null falls back to DateRuleAttr. public Attribute? RuleAttr { get; set; } Property Value Attribute? RuleLabel When set, this line is a horizontal separator rule (date change, unread marker). The view regenerates it to the current viewport width instead of word-wrapping. public string? RuleLabel { get; set; } Property Value string Segments public List<ChatSegment> Segments { get; } Property Value List<ChatSegment> SenderUsername public string? SenderUsername { get; set; } Property Value string TextLength public int TextLength { get; } Property Value int Methods FromColoredText(string, Attribute?) Parse a string containing printable color tags into colored segments. Format: {F:RRGGBB} (foreground), {B:RRGGBB} (background), {X} (reset). public static ChatLine FromColoredText(string text, Attribute? defaultAttr = null) Parameters text string defaultAttr Attribute? Returns ChatLine HasColorTags(string) Returns true if a line contains printable color tags. public static bool HasColorTags(string text) Parameters text string Returns bool StripColorTags(string) Remove all color tags from text, returning only the visible characters. public static string StripColorTags(string text) Parameters text string Returns string ToString() public override string ToString() Returns string Wrap(int, int) Wrap this line into multiple lines that fit within the given width. Continuation lines are indented with the specified number of spaces. public List<ChatLine> Wrap(int width, int continuationIndent = 0) Parameters width int continuationIndent int Returns List<ChatLine>"
  },
  "api/client/EchoHub.Client.UI.Chat.ChatListSource.html": {
    "href": "api/client/EchoHub.Client.UI.Chat.ChatListSource.html",
    "title": "Class ChatListSource | EchoHub Documentation",
    "summary": "Class ChatListSource Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll Custom list data source for chat messages with per-segment coloring. public class ChatListSource : IListDataSource, IDisposable Inheritance object ChatListSource Implements IListDataSource IDisposable Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatListSource() public ChatListSource() Properties Count public int Count { get; } Property Value int MaxItemLength public int MaxItemLength { get; } Property Value int SuspendCollectionChangedEvent public bool SuspendCollectionChangedEvent { get; set; } Property Value bool Methods Add(ChatLine) public void Add(ChatLine line) Parameters line ChatLine AddRange(IEnumerable<ChatLine>) public void AddRange(IEnumerable<ChatLine> lines) Parameters lines IEnumerable<ChatLine> Clear() public void Clear() Dispose() public void Dispose() GetLine(int) public ChatLine? GetLine(int index) Parameters index int Returns ChatLine InsertRange(int, IEnumerable<ChatLine>) public void InsertRange(int index, IEnumerable<ChatLine> lines) Parameters index int lines IEnumerable<ChatLine> IsMarked(int) public bool IsMarked(int item) Parameters item int Returns bool Render(ListView, bool, int, int, int, int, int) public void Render(ListView listView, bool selected, int item, int col, int row, int width, int viewportX = 0) Parameters listView ListView selected bool item int col int row int width int viewportX int SetMark(int, bool) public void SetMark(int item, bool value) Parameters item int value bool ToList() public IList ToList() Returns IList Events CollectionChanged public event NotifyCollectionChangedEventHandler? CollectionChanged Event Type NotifyCollectionChangedEventHandler"
  },
  "api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html": {
    "href": "api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html",
    "title": "Class ChatMessageManager | EchoHub Documentation",
    "summary": "Class ChatMessageManager Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll Owns chat message storage, formatting, and mutation. Fires MessagesChanged when a channel's message list is modified so the UI layer can refresh. public sealed class ChatMessageManager Inheritance object ChatMessageManager Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatMessageManager() public ChatMessageManager() Fields ContentIndentCols Columns before message text starts: \"HH:mm \" + nick column + \" │ \". public const int ContentIndentCols = 21 Field Value int NickColWidth Columns reserved for the right-aligned nick column (WeeChat-style). public const int NickColWidth = 12 Field Value int Properties CurrentChannel The currently active channel (used for unread tracking and @mention detection). public string CurrentChannel { get; set; } Property Value string CurrentUser public string CurrentUser { get; } Property Value string LastReadIds Last message the user has read per channel — persisted by the orchestrator so unread/mention state can be seeded from history on the next connect. public IReadOnlyDictionary<string, Guid> LastReadIds { get; } Property Value IReadOnlyDictionary<string, Guid> MentionChannels Channels with an unread @mention of the current user (cleared by ClearUnread(string)). public IReadOnlySet<string> MentionChannels { get; } Property Value IReadOnlySet<string> Methods AddMessage(MessageDto) Format and store a received message. Increments unread count if not the active channel. public void AddMessage(MessageDto message) Parameters message MessageDto AddStatusMessage(string, string, string) Add a status change message to a channel with colored styling. public void AddStatusMessage(string channelName, string username, string status) Parameters channelName string username string status string AddSystemMessage(string, string) Add a system/informational message to a channel with colored styling. public void AddSystemMessage(string channelName, string text) Parameters channelName string text string ClearAll() Reset all message state (used on disconnect). public void ClearAll() ClearChannelMessages(string) Clear all messages from a specific channel. public void ClearChannelMessages(string channelName) Parameters channelName string ClearUnread(string) public void ClearUnread(string channelName) Parameters channelName string GetMessages(string) public List<ChatLine>? GetMessages(string channelName) Parameters channelName string Returns List<ChatLine> GetUnreadCount(string) public int GetUnreadCount(string channelName) Parameters channelName string Returns int LoadHistory(string, List<MessageDto>, Guid?) Load historical messages into a channel, replacing any existing messages. When lastReadId is given (persisted from a previous session), messages after it seed the unread count, @mention highlight, and the \"new messages\" marker — so activity that happened while offline still lights up. public void LoadHistory(string channelName, List<MessageDto> messages, Guid? lastReadId = null) Parameters channelName string messages List<MessageDto> lastReadId Guid? PrependHistory(string, List<MessageDto>) Prepend older messages at the front of a channel's buffer, skipping any that are already present. Fires HistoryPrepended when new lines are actually inserted. public void PrependHistory(string channelName, List<MessageDto> olderMessages) Parameters channelName string olderMessages List<MessageDto> RemoveMessage(string, Guid) Remove all lines associated with a specific message ID. public void RemoveMessage(string channelName, Guid messageId) Parameters channelName string messageId Guid SetChatWidth(int) public void SetChatWidth(int width) Parameters width int SetCurrentUser(string) public void SetCurrentUser(string username) Parameters username string Events HistoryPrepended Fired after older messages are prepended to a channel's buffer. Parameter is the channel name. public event Action<string>? HistoryPrepended Event Type Action<string> MessagesChanged Fired after any mutation to a channel's messages. Parameter is the channel name. public event Action<string>? MessagesChanged Event Type Action<string>"
  },
  "api/client/EchoHub.Client.UI.Chat.ChatSegment.html": {
    "href": "api/client/EchoHub.Client.UI.Chat.ChatSegment.html",
    "title": "Class ChatSegment | EchoHub Documentation",
    "summary": "Class ChatSegment Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll A colored text segment within a chat line. public record ChatSegment : IEquatable<ChatSegment> Inheritance object ChatSegment Implements IEquatable<ChatSegment> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ChatSegment(ChatSegment) protected ChatSegment(ChatSegment original) Parameters original ChatSegment ChatSegment(string, Attribute?) A colored text segment within a chat line. public ChatSegment(string Text, Attribute? Color) Parameters Text string Color Attribute? Properties Color public Attribute? Color { get; init; } Property Value Attribute? EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Text public string Text { get; init; } Property Value string Methods Deconstruct(out string, out Attribute?) public void Deconstruct(out string Text, out Attribute? Color) Parameters Text string Color Attribute? Equals(ChatSegment?) public virtual bool Equals(ChatSegment? other) Parameters other ChatSegment Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ChatSegment?, ChatSegment?) public static bool operator ==(ChatSegment? left, ChatSegment? right) Parameters left ChatSegment right ChatSegment Returns bool operator !=(ChatSegment?, ChatSegment?) public static bool operator !=(ChatSegment? left, ChatSegment? right) Parameters left ChatSegment right ChatSegment Returns bool"
  },
  "api/client/EchoHub.Client.UI.Chat.html": {
    "href": "api/client/EchoHub.Client.UI.Chat.html",
    "title": "Namespace EchoHub.Client.UI.Chat | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.UI.Chat Classes ChatColors Shared color attributes for chat rendering (timestamps, system messages). ChatLine A single line in the chat, composed of colored segments. ChatListSource Custom list data source for chat messages with per-segment coloring. ChatMessageManager Owns chat message storage, formatting, and mutation. Fires MessagesChanged when a channel's message list is modified so the UI layer can refresh. ChatSegment A colored text segment within a chat line. Structs AttachmentActionSpan Inclusive column range on a chat line that triggers an attachment action when clicked. Enums AttachmentAction An action a click on an attachment line can trigger."
  },
  "api/client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html",
    "title": "Class AudioPlayerDialog | EchoHub Documentation",
    "summary": "Class AudioPlayerDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll public sealed class AudioPlayerDialog Inheritance object AudioPlayerDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AudioPlayerDialog() public AudioPlayerDialog() Methods Show(IApplication, AudioPlaybackService, string, string) public static void Show(IApplication app, AudioPlaybackService audioService, string filePath, string fileName) Parameters app IApplication audioService AudioPlaybackService filePath string fileName string"
  },
  "api/client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html",
    "title": "Class ChannelPasswordDialog | EchoHub Documentation",
    "summary": "Class ChannelPasswordDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll Prompts for a channel password when joining a protected channel. Returns the entered password, or null if the user cancels. public sealed class ChannelPasswordDialog Inheritance object ChannelPasswordDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelPasswordDialog() public ChannelPasswordDialog() Methods Show(IApplication, string, string?) public static string? Show(IApplication app, string channelName, string? message = null) Parameters app IApplication channelName string message string Returns string"
  },
  "api/client/EchoHub.Client.UI.Dialogs.ConnectDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.ConnectDialog.html",
    "title": "Class ConnectDialog | EchoHub Documentation",
    "summary": "Class ConnectDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll A Terminal.Gui dialog for entering server connection and authentication details. Includes a saved servers selector when saved servers are available. public sealed class ConnectDialog Inheritance object ConnectDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ConnectDialog() public ConnectDialog() Methods Show(IApplication, List<SavedServer>?) Shows the connect dialog with an optional list of saved servers. Returns the result, or null if cancelled. public static ConnectDialogResult? Show(IApplication app, List<SavedServer>? savedServers = null) Parameters app IApplication savedServers List<SavedServer> Returns ConnectDialogResult"
  },
  "api/client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html",
    "title": "Class ConnectDialogResult | EchoHub Documentation",
    "summary": "Class ConnectDialogResult Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll Result returned from the connect dialog. public record ConnectDialogResult : IEquatable<ConnectDialogResult> Inheritance object ConnectDialogResult Implements IEquatable<ConnectDialogResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ConnectDialogResult(ConnectDialogResult) protected ConnectDialogResult(ConnectDialogResult original) Parameters original ConnectDialogResult ConnectDialogResult(string, string, string, bool, bool, string?, string?, string?) Result returned from the connect dialog. public ConnectDialogResult(string ServerUrl, string Username, string Password, bool IsRegister, bool RememberMe, string? SavedRefreshToken, string? DisplayName = null, string? InviteCode = null) Parameters ServerUrl string Username string Password string IsRegister bool RememberMe bool SavedRefreshToken string DisplayName string InviteCode string Properties DisplayName public string? DisplayName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type InviteCode public string? InviteCode { get; init; } Property Value string IsRegister public bool IsRegister { get; init; } Property Value bool Password public string Password { get; init; } Property Value string RememberMe public bool RememberMe { get; init; } Property Value bool SavedRefreshToken public string? SavedRefreshToken { get; init; } Property Value string ServerUrl public string ServerUrl { get; init; } Property Value string Username public string Username { get; init; } Property Value string Methods Deconstruct(out string, out string, out string, out bool, out bool, out string?, out string?, out string?) public void Deconstruct(out string ServerUrl, out string Username, out string Password, out bool IsRegister, out bool RememberMe, out string? SavedRefreshToken, out string? DisplayName, out string? InviteCode) Parameters ServerUrl string Username string Password string IsRegister bool RememberMe bool SavedRefreshToken string DisplayName string InviteCode string Equals(ConnectDialogResult?) public virtual bool Equals(ConnectDialogResult? other) Parameters other ConnectDialogResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ConnectDialogResult?, ConnectDialogResult?) public static bool operator ==(ConnectDialogResult? left, ConnectDialogResult? right) Parameters left ConnectDialogResult right ConnectDialogResult Returns bool operator !=(ConnectDialogResult?, ConnectDialogResult?) public static bool operator !=(ConnectDialogResult? left, ConnectDialogResult? right) Parameters left ConnectDialogResult right ConnectDialogResult Returns bool"
  },
  "api/client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html",
    "title": "Class CreateChannelDialog | EchoHub Documentation",
    "summary": "Class CreateChannelDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll public sealed class CreateChannelDialog Inheritance object CreateChannelDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors CreateChannelDialog() public CreateChannelDialog() Methods Show(IApplication) public static CreateChannelResult? Show(IApplication app) Parameters app IApplication Returns CreateChannelResult"
  },
  "api/client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html",
    "title": "Class CreateChannelResult | EchoHub Documentation",
    "summary": "Class CreateChannelResult Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll public record CreateChannelResult : IEquatable<CreateChannelResult> Inheritance object CreateChannelResult Implements IEquatable<CreateChannelResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors CreateChannelResult(CreateChannelResult) protected CreateChannelResult(CreateChannelResult original) Parameters original CreateChannelResult CreateChannelResult(string, string?, bool, string?) public CreateChannelResult(string Name, string? Topic, bool IsPublic, string? Password) Parameters Name string Topic string IsPublic bool Password string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type IsPublic public bool IsPublic { get; init; } Property Value bool Name public string Name { get; init; } Property Value string Password public string? Password { get; init; } Property Value string Topic public string? Topic { get; init; } Property Value string Methods Deconstruct(out string, out string?, out bool, out string?) public void Deconstruct(out string Name, out string? Topic, out bool IsPublic, out string? Password) Parameters Name string Topic string IsPublic bool Password string Equals(CreateChannelResult?) public virtual bool Equals(CreateChannelResult? other) Parameters other CreateChannelResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(CreateChannelResult?, CreateChannelResult?) public static bool operator ==(CreateChannelResult? left, CreateChannelResult? right) Parameters left CreateChannelResult right CreateChannelResult Returns bool operator !=(CreateChannelResult?, CreateChannelResult?) public static bool operator !=(CreateChannelResult? left, CreateChannelResult? right) Parameters left CreateChannelResult right CreateChannelResult Returns bool"
  },
  "api/client/EchoHub.Client.UI.Dialogs.ProfileAction.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.ProfileAction.html",
    "title": "Enum ProfileAction | EchoHub Documentation",
    "summary": "Enum ProfileAction Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll Action selected by the user in their own profile dialog. public enum ProfileAction Fields Close = 0 EditProfile = 1 SetStatus = 2"
  },
  "api/client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html",
    "title": "Class ProfileEditDialog | EchoHub Documentation",
    "summary": "Class ProfileEditDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll A Terminal.Gui dialog for editing the user's profile (display name, bio, nickname color). public sealed class ProfileEditDialog Inheritance object ProfileEditDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ProfileEditDialog() public ProfileEditDialog() Methods Show(IApplication, string?, string?, string?, bool, byte) Shows the profile edit dialog and returns the result, or null if cancelled. public static ProfileEditResult? Show(IApplication app, string? currentDisplayName, string? currentBio, string? currentColor, bool notificationSoundEnabled = false, byte notificationVolume = 30) Parameters app IApplication currentDisplayName string currentBio string currentColor string notificationSoundEnabled bool notificationVolume byte Returns ProfileEditResult"
  },
  "api/client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html",
    "title": "Class ProfileEditResult | EchoHub Documentation",
    "summary": "Class ProfileEditResult Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll Result returned from the profile edit dialog. public record ProfileEditResult : IEquatable<ProfileEditResult> Inheritance object ProfileEditResult Implements IEquatable<ProfileEditResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ProfileEditResult(ProfileEditResult) protected ProfileEditResult(ProfileEditResult original) Parameters original ProfileEditResult ProfileEditResult(string?, string?, string?, string?, bool?, byte?) Result returned from the profile edit dialog. public ProfileEditResult(string? DisplayName, string? Bio, string? NicknameColor, string? AvatarPath, bool? NotificationSoundEnabled, byte? NotificationVolume) Parameters DisplayName string Bio string NicknameColor string AvatarPath string NotificationSoundEnabled bool? NotificationVolume byte? Properties AvatarPath public string? AvatarPath { get; init; } Property Value string Bio public string? Bio { get; init; } Property Value string DisplayName public string? DisplayName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type NicknameColor public string? NicknameColor { get; init; } Property Value string NotificationSoundEnabled public bool? NotificationSoundEnabled { get; init; } Property Value bool? NotificationVolume public byte? NotificationVolume { get; init; } Property Value byte? Methods Deconstruct(out string?, out string?, out string?, out string?, out bool?, out byte?) public void Deconstruct(out string? DisplayName, out string? Bio, out string? NicknameColor, out string? AvatarPath, out bool? NotificationSoundEnabled, out byte? NotificationVolume) Parameters DisplayName string Bio string NicknameColor string AvatarPath string NotificationSoundEnabled bool? NotificationVolume byte? Equals(ProfileEditResult?) public virtual bool Equals(ProfileEditResult? other) Parameters other ProfileEditResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ProfileEditResult?, ProfileEditResult?) public static bool operator ==(ProfileEditResult? left, ProfileEditResult? right) Parameters left ProfileEditResult right ProfileEditResult Returns bool operator !=(ProfileEditResult?, ProfileEditResult?) public static bool operator !=(ProfileEditResult? left, ProfileEditResult? right) Parameters left ProfileEditResult right ProfileEditResult Returns bool"
  },
  "api/client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html",
    "title": "Class ProfileViewDialog | EchoHub Documentation",
    "summary": "Class ProfileViewDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll Dialog for viewing a user's server profile. Shows Edit Profile / Set Status buttons when viewing own profile. public sealed class ProfileViewDialog Inheritance object ProfileViewDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ProfileViewDialog() public ProfileViewDialog() Methods Show(IApplication, UserProfileDto?) Show a read-only profile view for another user. public static void Show(IApplication app, UserProfileDto? profile) Parameters app IApplication profile UserProfileDto ShowOwn(IApplication, UserProfileDto?, UserStatus, string?) Show the profile view for the current user with action buttons. Returns the action the user selected. public static ProfileAction ShowOwn(IApplication app, UserProfileDto? profile, UserStatus currentStatus, string? currentStatusMessage) Parameters app IApplication profile UserProfileDto currentStatus UserStatus currentStatusMessage string Returns ProfileAction"
  },
  "api/client/EchoHub.Client.UI.Dialogs.SearchDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.SearchDialog.html",
    "title": "Class SearchDialog | EchoHub Documentation",
    "summary": "Class SearchDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll Command-palette style search dialog (Ctrl+K) for navigating channels and triggering app actions. public static class SearchDialog Inheritance object SearchDialog Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods Show(IApplication, IReadOnlyList<string>) public static SearchResult? Show(IApplication app, IReadOnlyList<string> channels) Parameters app IApplication channels IReadOnlyList<string> Returns SearchResult"
  },
  "api/client/EchoHub.Client.UI.Dialogs.SearchResult.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.SearchResult.html",
    "title": "Class SearchResult | EchoHub Documentation",
    "summary": "Class SearchResult Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll public record SearchResult : IEquatable<SearchResult> Inheritance object SearchResult Implements IEquatable<SearchResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors SearchResult(SearchResult) protected SearchResult(SearchResult original) Parameters original SearchResult SearchResult(SearchResultType, string, string) public SearchResult(SearchResultType Type, string Key, string Label) Parameters Type SearchResultType Key string Label string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Key public string Key { get; init; } Property Value string Label public string Label { get; init; } Property Value string Type public SearchResultType Type { get; init; } Property Value SearchResultType Methods Deconstruct(out SearchResultType, out string, out string) public void Deconstruct(out SearchResultType Type, out string Key, out string Label) Parameters Type SearchResultType Key string Label string Equals(SearchResult?) public virtual bool Equals(SearchResult? other) Parameters other SearchResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(SearchResult?, SearchResult?) public static bool operator ==(SearchResult? left, SearchResult? right) Parameters left SearchResult right SearchResult Returns bool operator !=(SearchResult?, SearchResult?) public static bool operator !=(SearchResult? left, SearchResult? right) Parameters left SearchResult right SearchResult Returns bool"
  },
  "api/client/EchoHub.Client.UI.Dialogs.SearchResultType.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.SearchResultType.html",
    "title": "Enum SearchResultType | EchoHub Documentation",
    "summary": "Enum SearchResultType Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll public enum SearchResultType Fields Action = 1 Channel = 0"
  },
  "api/client/EchoHub.Client.UI.Dialogs.StatusDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.StatusDialog.html",
    "title": "Class StatusDialog | EchoHub Documentation",
    "summary": "Class StatusDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll A Terminal.Gui dialog for setting the user's status and status message. public sealed class StatusDialog Inheritance object StatusDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors StatusDialog() public StatusDialog() Methods Show(IApplication, UserStatus, string?) Shows the status dialog and returns the result, or null if cancelled. public static StatusDialogResult? Show(IApplication app, UserStatus currentStatus, string? currentMessage) Parameters app IApplication currentStatus UserStatus currentMessage string Returns StatusDialogResult"
  },
  "api/client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html",
    "title": "Class StatusDialogResult | EchoHub Documentation",
    "summary": "Class StatusDialogResult Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll Result returned from the status dialog. public record StatusDialogResult : IEquatable<StatusDialogResult> Inheritance object StatusDialogResult Implements IEquatable<StatusDialogResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors StatusDialogResult(StatusDialogResult) protected StatusDialogResult(StatusDialogResult original) Parameters original StatusDialogResult StatusDialogResult(UserStatus, string?) Result returned from the status dialog. public StatusDialogResult(UserStatus Status, string? StatusMessage) Parameters Status UserStatus StatusMessage string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Status public UserStatus Status { get; init; } Property Value UserStatus StatusMessage public string? StatusMessage { get; init; } Property Value string Methods Deconstruct(out UserStatus, out string?) public void Deconstruct(out UserStatus Status, out string? StatusMessage) Parameters Status UserStatus StatusMessage string Equals(StatusDialogResult?) public virtual bool Equals(StatusDialogResult? other) Parameters other StatusDialogResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(StatusDialogResult?, StatusDialogResult?) public static bool operator ==(StatusDialogResult? left, StatusDialogResult? right) Parameters left StatusDialogResult right StatusDialogResult Returns bool operator !=(StatusDialogResult?, StatusDialogResult?) public static bool operator !=(StatusDialogResult? left, StatusDialogResult? right) Parameters left StatusDialogResult right StatusDialogResult Returns bool"
  },
  "api/client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html",
    "title": "Class UpdateConfirmDialog | EchoHub Documentation",
    "summary": "Class UpdateConfirmDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll public sealed class UpdateConfirmDialog Inheritance object UpdateConfirmDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UpdateConfirmDialog() public UpdateConfirmDialog() Methods Show(IApplication, string, string) public static bool Show(IApplication app, string currentVersion, string newVersion) Parameters app IApplication currentVersion string newVersion string Returns bool"
  },
  "api/client/EchoHub.Client.UI.Dialogs.html": {
    "href": "api/client/EchoHub.Client.UI.Dialogs.html",
    "title": "Namespace EchoHub.Client.UI.Dialogs | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.UI.Dialogs Classes AudioPlayerDialog ChannelPasswordDialog Prompts for a channel password when joining a protected channel. Returns the entered password, or null if the user cancels. ConnectDialog A Terminal.Gui dialog for entering server connection and authentication details. Includes a saved servers selector when saved servers are available. ConnectDialogResult Result returned from the connect dialog. CreateChannelDialog CreateChannelResult ProfileEditDialog A Terminal.Gui dialog for editing the user's profile (display name, bio, nickname color). ProfileEditResult Result returned from the profile edit dialog. ProfileViewDialog Dialog for viewing a user's server profile. Shows Edit Profile / Set Status buttons when viewing own profile. SearchDialog Command-palette style search dialog (Ctrl+K) for navigating channels and triggering app actions. SearchResult StatusDialog A Terminal.Gui dialog for setting the user's status and status message. StatusDialogResult Result returned from the status dialog. UpdateConfirmDialog Enums ProfileAction Action selected by the user in their own profile dialog. SearchResultType"
  },
  "api/client/EchoHub.Client.UI.Helpers.DroppedFileParser.html": {
    "href": "api/client/EchoHub.Client.UI.Helpers.DroppedFileParser.html",
    "title": "Class DroppedFileParser | EchoHub Documentation",
    "summary": "Class DroppedFileParser Namespace EchoHub.Client.UI.Helpers Assembly EchoHub.Client.dll Recognizes a dragged-and-dropped file (or files) that a terminal delivers into the input as an absolute path. Terminals differ: some paste the whole path at once, others send it character by character; either way this checks whether the current input text resolves to existing file(s). public static class DroppedFileParser Inheritance object DroppedFileParser Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods LooksLikePath(string) Cheap pre-check so callers only stat the filesystem when the input plausibly holds a path: a quoted path, a Windows drive path (X:</code>/X:/), a UNC path (\\), or a POSIX absolute path (/). Normal chat text never starts this way. public static bool LooksLikePath(string text) Parameters text string Returns bool TryGetFiles(string, out List<string>, Func<string, bool>?) Returns true when text resolves to one or more existing files. Handles a single path (quoted or not, possibly containing spaces) and multiple space-separated (optionally quoted) paths. fileExists is injectable for testing; production passes Exists(string). public static bool TryGetFiles(string text, out List<string> files, Func<string, bool>? fileExists = null) Parameters text string files List<string> fileExists Func<string, bool> Returns bool"
  },
  "api/client/EchoHub.Client.UI.Helpers.EmojiHelper.html": {
    "href": "api/client/EchoHub.Client.UI.Helpers.EmojiHelper.html",
    "title": "Class EmojiHelper | EchoHub Documentation",
    "summary": "Class EmojiHelper Namespace EchoHub.Client.UI.Helpers Assembly EchoHub.Client.dll Converts emoji grapheme clusters to text shortcodes for safe TUI rendering. Terminal width calculations for emoji are unreliable across different terminals, so we replace them with fixed-width ASCII shortcodes for display only. public static class EmojiHelper Inheritance object EmojiHelper Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods ReplaceEmoji(string) Replace emoji graphemes in the text with :shortcode: equivalents. Non-emoji text passes through unchanged. public static string ReplaceEmoji(string text) Parameters text string Returns string"
  },
  "api/client/EchoHub.Client.UI.Helpers.HexColorHelper.html": {
    "href": "api/client/EchoHub.Client.UI.Helpers.HexColorHelper.html",
    "title": "Class HexColorHelper | EchoHub Documentation",
    "summary": "Class HexColorHelper Namespace EchoHub.Client.UI.Helpers Assembly EchoHub.Client.dll Helper to parse hex colors to Terminal.Gui Attributes. public static class HexColorHelper Inheritance object HexColorHelper Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods ParseHexColor(string?) public static Attribute? ParseHexColor(string? hex) Parameters hex string Returns Attribute? ParseHexToColor(string?, Color) Parse a hex color string (with or without #) to a Terminal.Gui Color. Returns fallback if parsing fails. public static Color ParseHexToColor(string? hex, Color fallback = default) Parameters hex string fallback Color Returns Color"
  },
  "api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html": {
    "href": "api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html",
    "title": "Class NickColorHelper | EchoHub Documentation",
    "summary": "Class NickColorHelper Namespace EchoHub.Client.UI.Helpers Assembly EchoHub.Client.dll Deterministic per-nick colors for users who haven't picked a nickname color. The same nick always maps to the same palette entry (classic IRC client behavior), so a busy channel stays scannable without any configuration. public static class NickColorHelper Inheritance object NickColorHelper Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods GetAttribute(string) The color attribute for a nick. Used as a fallback when the user has no explicit nickname color set. public static Attribute GetAttribute(string nick) Parameters nick string Returns Attribute GetPaletteIndex(string, int) Stable palette index for a nick: case-insensitive FNV-1a over the nick, reduced modulo paletteSize. Pure function (no Terminal.Gui types) so it is unit-testable without a display driver. public static int GetPaletteIndex(string nick, int paletteSize) Parameters nick string paletteSize int Returns int"
  },
  "api/client/EchoHub.Client.UI.Helpers.html": {
    "href": "api/client/EchoHub.Client.UI.Helpers.html",
    "title": "Namespace EchoHub.Client.UI.Helpers | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.UI.Helpers Classes DroppedFileParser Recognizes a dragged-and-dropped file (or files) that a terminal delivers into the input as an absolute path. Terminals differ: some paste the whole path at once, others send it character by character; either way this checks whether the current input text resolves to existing file(s). EmojiHelper Converts emoji grapheme clusters to text shortcodes for safe TUI rendering. Terminal width calculations for emoji are unreliable across different terminals, so we replace them with fixed-width ASCII shortcodes for display only. HexColorHelper Helper to parse hex colors to Terminal.Gui Attributes. NickColorHelper Deterministic per-nick colors for users who haven't picked a nickname color. The same nick always maps to the same palette entry (classic IRC client behavior), so a busy channel stays scannable without any configuration."
  },
  "api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html": {
    "href": "api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html",
    "title": "Class ChannelListSource | EchoHub Documentation",
    "summary": "Class ChannelListSource Namespace EchoHub.Client.UI.ListSources Assembly EchoHub.Client.dll Custom list data source for colored channel list rendering. Active channel gets a > indicator, unread channels are bright with a count badge. public class ChannelListSource : IListDataSource, IDisposable Inheritance object ChannelListSource Implements IListDataSource IDisposable Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelListSource() public ChannelListSource() Properties Count public int Count { get; } Property Value int MaxItemLength public int MaxItemLength { get; } Property Value int SuspendCollectionChangedEvent public bool SuspendCollectionChangedEvent { get; set; } Property Value bool Methods Dispose() public void Dispose() IsMarked(int) public bool IsMarked(int item) Parameters item int Returns bool Render(ListView, bool, int, int, int, int, int) public void Render(ListView listView, bool selected, int item, int col, int row, int width, int viewportX = 0) Parameters listView ListView selected bool item int col int row int width int viewportX int SetMark(int, bool) public void SetMark(int item, bool value) Parameters item int value bool ToList() public IList ToList() Returns IList Update(List<string>, Dictionary<string, int>, string, IReadOnlySet<string>?, IReadOnlySet<string>?, IReadOnlySet<string>?, IReadOnlySet<string>?) public void Update(List<string> channels, Dictionary<string, int> unread, string activeChannel, IReadOnlySet<string>? protectedChannels = null, IReadOnlySet<string>? mentionChannels = null, IReadOnlySet<string>? privateChannels = null, IReadOnlySet<string>? systemChannels = null) Parameters channels List<string> unread Dictionary<string, int> activeChannel string protectedChannels IReadOnlySet<string> mentionChannels IReadOnlySet<string> privateChannels IReadOnlySet<string> systemChannels IReadOnlySet<string> Events CollectionChanged public event NotifyCollectionChangedEventHandler? CollectionChanged Event Type NotifyCollectionChangedEventHandler"
  },
  "api/client/EchoHub.Client.UI.ListSources.SearchListSource.html": {
    "href": "api/client/EchoHub.Client.UI.ListSources.SearchListSource.html",
    "title": "Class SearchListSource | EchoHub Documentation",
    "summary": "Class SearchListSource Namespace EchoHub.Client.UI.ListSources Assembly EchoHub.Client.dll List data source for the search dialog with filtering and colored rendering. public class SearchListSource : IListDataSource, IDisposable Inheritance object SearchListSource Implements IListDataSource IDisposable Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors SearchListSource(List<SearchResult>) List data source for the search dialog with filtering and colored rendering. public SearchListSource(List<SearchResult> items) Parameters items List<SearchResult> Properties Count public int Count { get; } Property Value int MaxItemLength public int MaxItemLength { get; } Property Value int SuspendCollectionChangedEvent public bool SuspendCollectionChangedEvent { get; set; } Property Value bool Methods Dispose() public void Dispose() Filter(string) public void Filter(string query) Parameters query string GetItem(int) public SearchResult? GetItem(int index) Parameters index int Returns SearchResult IsMarked(int) public bool IsMarked(int item) Parameters item int Returns bool Render(ListView, bool, int, int, int, int, int) public void Render(ListView listView, bool selected, int item, int col, int row, int width, int viewportX = 0) Parameters listView ListView selected bool item int col int row int width int viewportX int SetMark(int, bool) public void SetMark(int item, bool value) Parameters item int value bool ToList() public IList ToList() Returns IList Events CollectionChanged public event NotifyCollectionChangedEventHandler? CollectionChanged Event Type NotifyCollectionChangedEventHandler"
  },
  "api/client/EchoHub.Client.UI.ListSources.UserListSource.html": {
    "href": "api/client/EchoHub.Client.UI.ListSources.UserListSource.html",
    "title": "Class UserListSource | EchoHub Documentation",
    "summary": "Class UserListSource Namespace EchoHub.Client.UI.ListSources Assembly EchoHub.Client.dll Custom list data source for the online users panel with per-user nickname colors. public class UserListSource : IListDataSource, IDisposable Inheritance object UserListSource Implements IListDataSource IDisposable Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UserListSource() public UserListSource() Properties Count public int Count { get; } Property Value int MaxItemLength public int MaxItemLength { get; } Property Value int SuspendCollectionChangedEvent public bool SuspendCollectionChangedEvent { get; set; } Property Value bool Methods Dispose() public void Dispose() GetUsername(int) public string? GetUsername(int index) Parameters index int Returns string IsMarked(int) public bool IsMarked(int item) Parameters item int Returns bool Render(ListView, bool, int, int, int, int, int) public void Render(ListView listView, bool selected, int item, int col, int row, int width, int viewportX = 0) Parameters listView ListView selected bool item int col int row int width int viewportX int SetMark(int, bool) public void SetMark(int item, bool value) Parameters item int value bool ToList() public IList ToList() Returns IList Update(List<(string Text, Attribute? NameColor, string Username)>) public void Update(List<(string Text, Attribute? NameColor, string Username)> users) Parameters users List<(string Text, Attribute? NameColor, string Username)> Events CollectionChanged public event NotifyCollectionChangedEventHandler? CollectionChanged Event Type NotifyCollectionChangedEventHandler"
  },
  "api/client/EchoHub.Client.UI.ListSources.html": {
    "href": "api/client/EchoHub.Client.UI.ListSources.html",
    "title": "Namespace EchoHub.Client.UI.ListSources | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.UI.ListSources Classes ChannelListSource Custom list data source for colored channel list rendering. Active channel gets a > indicator, unread channels are bright with a count badge. SearchListSource List data source for the search dialog with filtering and colored rendering. UserListSource Custom list data source for the online users panel with per-user nickname colors."
  },
  "api/client/EchoHub.Client.UI.MainWindow.html": {
    "href": "api/client/EchoHub.Client.UI.MainWindow.html",
    "title": "Class MainWindow | EchoHub Documentation",
    "summary": "Class MainWindow Namespace EchoHub.Client.UI Assembly EchoHub.Client.dll Main Terminal.Gui window for the EchoHub chat client. public sealed class MainWindow : Runnable, IDisposable, IRunnable Inheritance object View Runnable MainWindow Implements IDisposable IRunnable Inherited Members Runnable.SetApp(IApplication) Runnable.SetIsRunning(bool) Runnable.RequestStop() Runnable.RaiseIsRunningChanging(bool, bool) Runnable.RaiseIsRunningChangedEvent(bool) Runnable.SetIsModal(bool) Runnable.RaiseIsModalChangedEvent(bool) Runnable.Result Runnable.IsRunning Runnable.IsModal Runnable.StopRequested Runnable.IsRunningChanging Runnable.IsRunningChanged Runnable.IsModalChanged View.NewMouseEvent(Mouse) View.RaiseMouseEvent(Mouse) View.GetAdornmentsThickness() View.GetSupportedCommands() View.InvokeCommands(Command[], ICommandBinding) View.InvokeCommand(Command, ICommandBinding) View.InvokeCommand(Command, ICommandContext) View.InvokeCommand(Command) View.SetContentSize(Size?) View.GetContentSize() View.GetWidthRequiredForSubViews() View.GetHeightRequiredForSubViews() View.ContentToScreen(in Point) View.ScreenToContent(in Point) View.ViewportToScreen(in Rectangle) View.ViewportToScreen(in Point) View.ViewportToScreen() View.ScreenToViewport(in Point) View.GetViewportOffsetFromFrame() View.ScrollVertical(int) View.ScrollHorizontal(int) View.Dispose() View.ToString() View.ToDebugString() View.BeginInit() View.EndInit() View.OnEnabledChanged() View.SetCursorNeedsUpdate() View.GetCurrentAttribute() View.GetAttributeForRole(VisualRole) View.SetAttribute(Attribute) View.SetAttributeForRole(VisualRole) View.GetClip() View.SetClip(Region) View.SetClipToScreen() View.ExcludeFromClip(Rectangle) View.ExcludeFromClip(Region) View.AddViewportToClip() View.Draw(DrawContext) View.DrawAdornments() View.ClearViewport(DrawContext) View.DrawText(DrawContext) View.DrawSubViews(DrawContext) View.RenderLineCanvas(DrawContext) View.Move(int, int) View.AddRune(Rune) View.AddRune(char) View.AddRune(int, int, Rune) View.AddStr(string) View.AddStr(int, int, string) View.DrawHotString(string, Attribute, Attribute) View.DrawHotString(string, bool) View.FillRect(Rectangle, Color?) View.FillRect(Rectangle, Rune) View.GetScheme() View.SetScheme(Scheme) View.GetSubViews(bool, bool, bool) View.Add(View) View.Add(params View[]) View.Remove(View) View.RemoveAll() View.RemoveAll<TView>() View.GetTopSuperView(View, View) View.IsInHierarchy(View, View, bool) View.MoveSubViewTowardsEnd(View) View.MoveSubViewToEnd(View) View.MoveSubViewTowardsStart(View) View.MoveSubViewToStart(View) View.AddKeyBindingsForHotKey(Key, Key, object) View.AssignHotKeysToSubViews() View.NewKeyDownEvent(Key) View.Contains(in Point) View.FrameToScreen() View.ScreenToFrame(in Point) View.Layout(Size) View.Layout() View.SetRelativeLayout(Size) View.SetNeedsLayout() View.GetContainerSize() View.GetViewsUnderLocation(in Point, ViewportSettingsFlags) View.AdvanceFocus(NavigationDirection, TabBehavior?) View.FocusDeepest(NavigationDirection, TabBehavior?) View.OnCanFocusChanged() View.ClearFocus() View.SetFocus() View.SetNeedsDraw() View.SetNeedsDraw(Rectangle) View.OnTextChanged() View.MouseBindings View.MouseHoldRepeat View.MousePositionTracking View.MouseState View.MouseHighlightStates View.Margin View.ShadowStyle View.Border View.BorderStyle View.Padding View.Arrangement View.DefaultAcceptView View.CommandsToBubbleUp View.ContentSizeTracksViewport View.ViewportSettings View.Viewport View.Data View.Id View.App View.IsInitialized View.Enabled View.Visible View.Title View.Cursor View.Diagnostics View.LineCanvas View.SuperViewRendersLineCanvas View.SchemeName View.HasScheme View.SubViews View.SuperView View.HotKey View.HotKeySpecifier View.AssignHotKeys View.UsedHotKeys View.KeyBindings View.HotKeyBindings View.Frame View.X View.Y View.Height View.Width View.NeedsLayout View.ValidatePosDim View.CanFocus View.Focused View.IsCurrentTop View.MostFocused View.HasFocus View.TabStop View.NeedsDraw View.HorizontalScrollBar View.VerticalScrollBar View.PreserveTrailingSpaces View.Text View.TextAlignment View.TextDirection View.TextFormatter View.VerticalTextAlignment View.MouseEnter View.MouseLeave View.MouseHoldRepeatChanging View.MouseHoldRepeatChanged View.MouseEvent View.MouseStateChanged View.BorderStyleChanged View.CommandNotBound View.Accepting View.Accepted View.Activating View.Activated View.HandlingHotKey View.HotKeyCommand View.ContentSizeChanging View.ContentSizeChanged View.ViewportChanged View.Disposing View.Initialized View.EnabledChanged View.VisibleChanging View.VisibleChanged View.TitleChanging View.TitleChanged View.GettingAttributeForRole View.ClearingViewport View.ClearedViewport View.DrawingText View.DrewText View.DrawingContent View.DrawingSubViews View.DrawComplete View.SchemeNameChanging View.SchemeNameChanged View.GettingScheme View.SchemeChanging View.SchemeChanged View.SuperViewChanging View.SuperViewChanged View.SubViewAdded View.SubViewRemoved View.Removed View.HotKeyChanged View.KeyDown View.KeyDownNotHandled View.FrameChanged View.HeightChanging View.HeightChanged View.WidthChanging View.WidthChanged View.SubViewLayout View.SubViewsLaidOut View.AdvancingFocus View.CanFocusChanged View.FocusedChanged View.HasFocusChanging View.HasFocusChanged View.TextChanged object.GetType() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors MainWindow(IApplication, ChatMessageManager) public MainWindow(IApplication app, ChatMessageManager messageManager) Parameters app IApplication messageManager ChatMessageManager Properties CurrentChannel Get the current channel name. public string CurrentChannel { get; } Property Value string HasPendingReplyIndicator public bool HasPendingReplyIndicator { get; } Property Value bool Methods ApplyColorSchemes() Applies the currently registered color schemes to all views. Call after theme changes to refresh colors. public void ApplyColorSchemes() ClearAll() Clear all messages and channels (used on disconnect). public void ClearAll() EnsureChannelInList(string, bool?, bool?, bool?) Ensure a channel exists in the left panel list (used for private channels joined via /join). public void EnsureChannelInList(string channelName, bool? isPublic = null, bool? isProtected = null, bool? isSystem = null) Parameters channelName string isPublic bool? isProtected bool? isSystem bool? FocusInput() Focus the input field for typing. public void FocusInput() GetChannelNames() Get all channel names that have message buffers (for broadcasting status changes). public IReadOnlyList<string> GetChannelNames() Returns IReadOnlyList<string> RefreshMenuBar() Rebuilds and replaces the menu bar (e.g., after theme list changes). public void RefreshMenuBar() RemoveChannel(string) Remove a channel from the left panel list. public void RemoveChannel(string channelName) Parameters channelName string SetChannelTopic(string, string?) Update the topic for a specific channel. public void SetChannelTopic(string channelName, string? topic) Parameters channelName string topic string SetChannels(List<ChannelDto>) Set the list of available channels, storing topics, and refresh the channel list view. public void SetChannels(List<ChannelDto> channels) Parameters channels List<ChannelDto> SetCurrentUser(string) Set the current user name (delegates to message manager for @mention detection). public void SetCurrentUser(string username) Parameters username string SetReplyingTo(string?) Shows/clears the \"replying to\" strip on the input frame's title. Pass null to clear. public void SetReplyingTo(string? label) Parameters label string SetStagedAttachments(IReadOnlyList<string>, string) Updates the attachment staging indicator shown on the input frame's title, including the current ASCII-art size for images. Passing an empty list restores the default hint. public void SetStagedAttachments(IReadOnlyList<string> fileNames, string asciiSizeLabel) Parameters fileNames IReadOnlyList<string> asciiSizeLabel string ShowError(string) Show an error message to the user. public void ShowError(string message) Parameters message string SwitchToChannel(string) Switch the chat view to the given channel, resetting its unread count and updating the topic bar. public void SwitchToChannel(string channelName) Parameters channelName string ToggleUsersPanel() Toggle the online users panel visibility (F2). public void ToggleUsersPanel() UpdateOnlineUsers(List<UserPresenceDto>) Update the online users list display. public void UpdateOnlineUsers(List<UserPresenceDto> users) Parameters users List<UserPresenceDto> UpdateStatusBar(string) Update the connection status displayed in the status bar. public void UpdateStatusBar(string status) Parameters status string Events OnAudioPlayRequested Fired when the user activates (Enter/click) an audio message. Parameters: attachmentUrl, fileName. public event Action<string, string>? OnAudioPlayRequested Event Type Action<string, string> OnChannelJoinRequested Fired when the user activates a #channel reference in a message. Parameter is the channel name. public event Action<string>? OnChannelJoinRequested Event Type Action<string> OnChannelSelected Fired when the user selects a channel. Parameter is the channel name. public event Action<string>? OnChannelSelected Event Type Action<string> OnCheckForUpdatesRequested Fired when the user requests to check for updates. public event Action? OnCheckForUpdatesRequested Event Type Action OnConnectRequested Fired when the user requests to connect via the menu. public event Action? OnConnectRequested Event Type Action OnCreateChannelRequested Fired when the user requests to create a new channel. public event Action? OnCreateChannelRequested Event Type Action OnDeleteChannelRequested Fired when the user requests to delete the current channel. public event Action? OnDeleteChannelRequested Event Type Action OnDeleteMessageRequested Fired when the user presses Delete on the selected message. Parameter is the message id. public event Action<Guid>? OnDeleteMessageRequested Event Type Action<Guid> OnDisconnectRequested Fired when the user requests to disconnect via the menu. public event Action? OnDisconnectRequested Event Type Action OnFileDownloadRequested Fired when the user activates (Enter/click) a file message. Parameters: attachmentUrl, fileName. public event Action<string, string>? OnFileDownloadRequested Event Type Action<string, string> OnFilesStaged Fired when local files arrive via paste or drag-and-drop to be staged as attachments. Parameters: channel name, absolute paths of existing files. public event Action<string, IReadOnlyList<string>>? OnFilesStaged Event Type Action<string, IReadOnlyList<string>> OnImageOpenRequested Fired when the user activates an image's \"[open]\" action to view it without saving. Parameters: attachmentUrl, fileName. public event Action<string, string>? OnImageOpenRequested Event Type Action<string, string> OnImagePasted Fired when raw image data is pasted from the clipboard (e.g. copied from a browser or a screenshot tool). Parameters: channel name, PNG-encoded image bytes. public event Action<string, byte[]>? OnImagePasted Event Type Action<string, byte[]> OnImageSaveRequested Fired when the user activates an image's \"[save original]\" line. Parameters: attachmentUrl, fileName. public event Action<string, string>? OnImageSaveRequested Event Type Action<string, string> OnLoadMoreRequested Fired when the user scrolls to the top of the message list and older messages should be loaded. public event Action? OnLoadMoreRequested Event Type Action OnLogoutRequested Fired when the user requests to logout (disconnect + revoke session). public event Action? OnLogoutRequested Event Type Action OnMessageSubmitted Fired when the user presses Enter in the input field. Parameters: channel name, message content. public event Action<string, string>? OnMessageSubmitted Event Type Action<string, string> OnProfileRequested Fired when the user requests to open their profile panel. public event Action? OnProfileRequested Event Type Action OnReplyCancelRequested Fired when the user cancels a pending reply (Esc in the input field). public event Action? OnReplyCancelRequested Event Type Action OnReplyRequested Fired when the user picks \"Reply\" on a message. Parameters: message id, sender username, a short plain-text snippet for the reply strip. public event Action<Guid, string, string>? OnReplyRequested Event Type Action<Guid, string, string> OnRollbackRequested Fired when the user requests to rollback to the previous version. public event Action? OnRollbackRequested Event Type Action OnSavedServersRequested Fired when the user requests to view saved servers. public event Action? OnSavedServersRequested Event Type Action OnSearchRequested Fired when the user requests to open the search dialog (via menu or Ctrl+K). public event Action? OnSearchRequested Event Type Action OnStatusRequested Fired when the user requests to set their status. public event Action? OnStatusRequested Event Type Action OnThemeSelected Fired when the user selects a theme from the menu. Parameter is the theme name. public event Action<string>? OnThemeSelected Event Type Action<string> OnUserProfileRequested Fired when the user activates a username (in userlist or message). Parameter is the username. public event Action<string>? OnUserProfileRequested Event Type Action<string>"
  },
  "api/client/EchoHub.Client.UI.html": {
    "href": "api/client/EchoHub.Client.UI.html",
    "title": "Namespace EchoHub.Client.UI | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client.UI Classes MainWindow Main Terminal.Gui window for the EchoHub chat client."
  },
  "api/client/EchoHub.Client.html": {
    "href": "api/client/EchoHub.Client.html",
    "title": "Namespace EchoHub.Client | EchoHub Documentation",
    "summary": "Namespace EchoHub.Client Classes AppOrchestrator Central orchestrator for the EchoHub TUI client. Wires UI events to service calls and connection events to UI updates."
  },
  "api/core-articles/index.html": {
    "href": "api/core-articles/index.html",
    "title": "Core Articles | EchoHub Documentation",
    "summary": "Core Articles Articles related to the EchoHub.Core shared library. Topics Data models and DTOs Contract interfaces (IChatService, IChannelService, IChatBroadcaster, IEchoHubClient) Validation constants and shared rules"
  },
  "api/core/EchoHub.Core.Constants.HubConstants.html": {
    "href": "api/core/EchoHub.Core.Constants.HubConstants.html",
    "title": "Class HubConstants | EchoHub Documentation",
    "summary": "Class HubConstants Namespace EchoHub.Core.Constants Assembly EchoHub.Core.dll public static class HubConstants Inheritance object HubConstants Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields AsciiArtHeight public const int AsciiArtHeight = 40 Field Value int AsciiArtHeightHalfBlock public const int AsciiArtHeightHalfBlock = 80 Field Value int AsciiArtWidth public const int AsciiArtWidth = 80 Field Value int ChatHubPath public const string ChatHubPath = \"/hubs/chat\" Field Value string DefaultChannel public const string DefaultChannel = \"general\" Field Value string DefaultHistoryCount public const int DefaultHistoryCount = 100 Field Value int EmbedFetchTimeoutSeconds public const int EmbedFetchTimeoutSeconds = 5 Field Value int EmbedMaxDescriptionLength public const int EmbedMaxDescriptionLength = 500 Field Value int EmbedMaxHtmlBytes public const int EmbedMaxHtmlBytes = 65536 Field Value int EmbedMaxUrlsPerMessage public const int EmbedMaxUrlsPerMessage = 3 Field Value int IrcConnectionIdPrefix Connection-id prefix for IRC gateway connections. The presence tracker uses it to tell IRC-only users apart from native (SignalR) clients. public const string IrcConnectionIdPrefix = \"irc-\" Field Value string MaxAttachmentsPerMessage public const int MaxAttachmentsPerMessage = 10 Field Value int MaxAudioFileSizeBytes public const int MaxAudioFileSizeBytes = 10485760 Field Value int MaxAvatarSizeBytes public const int MaxAvatarSizeBytes = 2097152 Field Value int MaxConsecutiveNewlines public const int MaxConsecutiveNewlines = 1 Field Value int MaxFileSizeBytes public const int MaxFileSizeBytes = 104857600 Field Value int MaxImageSizeBytes public const int MaxImageSizeBytes = 10485760 Field Value int MaxMessageLength public const int MaxMessageLength = 2000 Field Value int MaxMessageNewlines public const int MaxMessageNewlines = 30 Field Value int"
  },
  "api/core/EchoHub.Core.Constants.MessageConventions.html": {
    "href": "api/core/EchoHub.Core.Constants.MessageConventions.html",
    "title": "Class MessageConventions | EchoHub Documentation",
    "summary": "Class MessageConventions Namespace EchoHub.Core.Constants Assembly EchoHub.Core.dll Cross-protocol message conventions. Action messages (/me) use the IRC CTCP ACTION wire format 0x01 + \"ACTION \" + text + 0x01 as the stored content, so IRC clients interoperate natively (irssi's /me arrives in exactly this shape) and the TUI renders \"* nick text\". In end-to-end encrypted rooms the marker encrypts along with the text. public static class MessageConventions Inheritance object MessageConventions Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields ActionPrefix public const string ActionPrefix = \"\\u0001ACTION \" Field Value string ActionSuffix public const string ActionSuffix = \"\\u0001\" Field Value string Methods FormatAction(string) public static string FormatAction(string text) Parameters text string Returns string TryParseAction(string, out string?) public static bool TryParseAction(string content, out string? actionText) Parameters content string actionText string Returns bool"
  },
  "api/core/EchoHub.Core.Constants.ValidationConstants.html": {
    "href": "api/core/EchoHub.Core.Constants.ValidationConstants.html",
    "title": "Class ValidationConstants | EchoHub Documentation",
    "summary": "Class ValidationConstants Namespace EchoHub.Core.Constants Assembly EchoHub.Core.dll public static class ValidationConstants Inheritance object ValidationConstants Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields ChannelNamePattern public const string ChannelNamePattern = \"^[a-zA-Z0-9_-]{2,100}$\" Field Value string HexColorPattern public const string HexColorPattern = \"^#[0-9a-fA-F]{6}$\" Field Value string MaxBioLength public const int MaxBioLength = 500 Field Value int MaxChannelTopicLength public const int MaxChannelTopicLength = 500 Field Value int MaxDisplayNameLength public const int MaxDisplayNameLength = 100 Field Value int MaxHistoryCount public const int MaxHistoryCount = 200 Field Value int MaxPasswordLength public const int MaxPasswordLength = 128 Field Value int MaxStatusMessageLength public const int MaxStatusMessageLength = 100 Field Value int MinChannelPasswordLength public const int MinChannelPasswordLength = 3 Field Value int UsernamePattern public const string UsernamePattern = \"^[a-zA-Z0-9_-]{3,50}$\" Field Value string Methods ChannelNameRegex() [GeneratedRegex(\"^[a-zA-Z0-9_-]{2,100}$\")] public static Regex ChannelNameRegex() Returns Regex Remarks Pattern: ^[a-zA-Z0-9_-]{2,100}$ Explanation: ○ Match if at the beginning of the string. ○ Match a character in the set [\\-0-9A-Z_a-z] atomically at least 2 and at most 100 times. ○ Match if at the end of the string or if before an ending newline. HexColorRegex() [GeneratedRegex(\"^#[0-9a-fA-F]{6}$\")] public static Regex HexColorRegex() Returns Regex Remarks Pattern: ^#[0-9a-fA-F]{6}$ Explanation: ○ Match if at the beginning of the string. ○ Match '#'. ○ Match a hexadecimal digit exactly 6 times. ○ Match if at the end of the string or if before an ending newline. UsernameRegex() [GeneratedRegex(\"^[a-zA-Z0-9_-]{3,50}$\")] public static Regex UsernameRegex() Returns Regex Remarks Pattern: ^[a-zA-Z0-9_-]{3,50}$ Explanation: ○ Match if at the beginning of the string. ○ Match a character in the set [\\-0-9A-Z_a-z] atomically at least 3 and at most 50 times. ○ Match if at the end of the string or if before an ending newline."
  },
  "api/core/EchoHub.Core.Constants.html": {
    "href": "api/core/EchoHub.Core.Constants.html",
    "title": "Namespace EchoHub.Core.Constants | EchoHub Documentation",
    "summary": "Namespace EchoHub.Core.Constants Classes HubConstants MessageConventions Cross-protocol message conventions. Action messages (/me) use the IRC CTCP ACTION wire format 0x01 + \"ACTION \" + text + 0x01 as the stored content, so IRC clients interoperate natively (irssi's /me arrives in exactly this shape) and the TUI renders \"* nick text\". In end-to-end encrypted rooms the marker encrypts along with the text. ValidationConstants"
  },
  "api/core/EchoHub.Core.Contracts.ChannelListItem.html": {
    "href": "api/core/EchoHub.Core.Contracts.ChannelListItem.html",
    "title": "Class ChannelListItem | EchoHub Documentation",
    "summary": "Class ChannelListItem Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public record ChannelListItem : IEquatable<ChannelListItem> Inheritance object ChannelListItem Implements IEquatable<ChannelListItem> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ChannelListItem(ChannelListItem) protected ChannelListItem(ChannelListItem original) Parameters original ChannelListItem ChannelListItem(string, string?, int, bool, bool) public ChannelListItem(string Name, string? Topic, int OnlineCount, bool IsPublic = true, bool IsProtected = false) Parameters Name string Topic string OnlineCount int IsPublic bool IsProtected bool Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type IsProtected public bool IsProtected { get; init; } Property Value bool IsPublic public bool IsPublic { get; init; } Property Value bool Name public string Name { get; init; } Property Value string OnlineCount public int OnlineCount { get; init; } Property Value int Topic public string? Topic { get; init; } Property Value string Methods Deconstruct(out string, out string?, out int, out bool, out bool) public void Deconstruct(out string Name, out string? Topic, out int OnlineCount, out bool IsPublic, out bool IsProtected) Parameters Name string Topic string OnlineCount int IsPublic bool IsProtected bool Equals(ChannelListItem?) public virtual bool Equals(ChannelListItem? other) Parameters other ChannelListItem Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ChannelListItem?, ChannelListItem?) public static bool operator ==(ChannelListItem? left, ChannelListItem? right) Parameters left ChannelListItem right ChannelListItem Returns bool operator !=(ChannelListItem?, ChannelListItem?) public static bool operator !=(ChannelListItem? left, ChannelListItem? right) Parameters left ChannelListItem right ChannelListItem Returns bool"
  },
  "api/core/EchoHub.Core.Contracts.IChannelService.html": {
    "href": "api/core/EchoHub.Core.Contracts.IChannelService.html",
    "title": "Interface IChannelService | EchoHub Documentation",
    "summary": "Interface IChannelService Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IChannelService Methods CreateChannelAsync(Guid, string, string?, bool, string?, string?, string?) Task<ChannelOperationResult> CreateChannelAsync(Guid creatorUserId, string name, string? topic, bool isPublic, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters creatorUserId Guid name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task<ChannelOperationResult> DeleteChannelAsync(Guid, string) Task<ChannelOperationResult> DeleteChannelAsync(Guid callerUserId, string channelName) Parameters callerUserId Guid channelName string Returns Task<ChannelOperationResult> EnsureChannelMembershipAsync(Guid, string, string?) Task<(bool Success, string? Error, bool PasswordRequired)> EnsureChannelMembershipAsync(Guid userId, string channelName, string? password = null) Parameters userId Guid channelName string password string Returns Task<(bool Success, string Error, bool PasswordRequired)> EnsureSystemChannelAsync(string, string?) Task<ChannelDto> EnsureSystemChannelAsync(string channelName, string? topic = null) Parameters channelName string topic string Returns Task<ChannelDto> GetChannelByNameAsync(string) Task<ChannelDto?> GetChannelByNameAsync(string channelName) Parameters channelName string Returns Task<ChannelDto> GetChannelCryptoAsync(string) Task<ChannelCryptoDto?> GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task<ChannelCryptoDto> GetChannelKeyEnvelopeAsync(string) Task<(string? EncryptionSalt, string? WrappedRoomKey)> GetChannelKeyEnvelopeAsync(string channelName) Parameters channelName string Returns Task<(string EncryptionSalt, string WrappedRoomKey)> GetChannelListAsync() Task<List<ChannelListItem>> GetChannelListAsync() Returns Task<List<ChannelListItem>> GetChannelMetaAsync(string) Task<ChannelMetaDto?> GetChannelMetaAsync(string channelName) Parameters channelName string Returns Task<ChannelMetaDto> GetChannelTopicAsync(string) Task<(string? Topic, bool Exists)> GetChannelTopicAsync(string channelName) Parameters channelName string Returns Task<(string Topic, bool Exists)> GetChannelsAsync(Guid, int, int) Task<PaginatedResponse<ChannelDto>> GetChannelsAsync(Guid userId, int offset, int limit) Parameters userId Guid offset int limit int Returns Task<PaginatedResponse<ChannelDto>> RekeyChannelAsync(Guid, string, string, string, string, string) Task<ChannelOperationResult> RekeyChannelAsync(Guid callerUserId, string channelName, string oldPassword, string newPassword, string newEncryptionSalt, string newWrappedRoomKey) Parameters callerUserId Guid channelName string oldPassword string newPassword string newEncryptionSalt string newWrappedRoomKey string Returns Task<ChannelOperationResult> SetChannelPasswordAsync(Guid, string, string?) Task<ChannelOperationResult> SetChannelPasswordAsync(Guid callerUserId, string channelName, string? password) Parameters callerUserId Guid channelName string password string Returns Task<ChannelOperationResult> UpdateTopicAsync(Guid, string, string?) Task<ChannelOperationResult> UpdateTopicAsync(Guid callerUserId, string channelName, string? topic) Parameters callerUserId Guid channelName string topic string Returns Task<ChannelOperationResult>"
  },
  "api/core/EchoHub.Core.Contracts.IChatBroadcaster.html": {
    "href": "api/core/EchoHub.Core.Contracts.IChatBroadcaster.html",
    "title": "Interface IChatBroadcaster | EchoHub Documentation",
    "summary": "Interface IChatBroadcaster Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IChatBroadcaster Methods ForceDisconnectUserAsync(List<string>, string) Task ForceDisconnectUserAsync(List<string> connectionIds, string reason) Parameters connectionIds List<string> reason string Returns Task SendChannelDeletedAsync(string) Task SendChannelDeletedAsync(string channelName) Parameters channelName string Returns Task SendChannelNukedAsync(string) Task SendChannelNukedAsync(string channelName) Parameters channelName string Returns Task SendChannelUpdatedAsync(ChannelDto, string?) Task SendChannelUpdatedAsync(ChannelDto channel, string? channelName = null) Parameters channel ChannelDto channelName string Returns Task SendErrorAsync(string, string) Task SendErrorAsync(string connectionId, string message) Parameters connectionId string message string Returns Task SendMessageDeletedAsync(string, Guid) Task SendMessageDeletedAsync(string channelName, Guid messageId) Parameters channelName string messageId Guid Returns Task SendMessageToChannelAsync(string, MessageDto, string?) Broadcast a chat message to a channel. excludeConnectionId is the connection the message originated from (IRC convention: never echo a message back to the connection that sent it — its client already displayed it locally). Other connections of the same user (e.g. an IRC session alongside a TUI session) still receive the message. Task SendMessageToChannelAsync(string channelName, MessageDto message, string? excludeConnectionId = null) Parameters channelName string message MessageDto excludeConnectionId string Returns Task SendUserBannedAsync(string, string?) Task SendUserBannedAsync(string username, string? reason) Parameters username string reason string Returns Task SendUserJoinedAsync(string, string, UserPresenceDto?, string?) Task SendUserJoinedAsync(string channelName, string username, UserPresenceDto? presence, string? excludeConnectionId = null) Parameters channelName string username string presence UserPresenceDto excludeConnectionId string Returns Task SendUserKickedAsync(string, string, string?) Task SendUserKickedAsync(string channelName, string username, string? reason) Parameters channelName string username string reason string Returns Task SendUserLeftAsync(string, string) Task SendUserLeftAsync(string channelName, string username) Parameters channelName string username string Returns Task SendUserStatusChangedAsync(List<string>, UserPresenceDto) Task SendUserStatusChangedAsync(List<string> channelNames, UserPresenceDto presence) Parameters channelNames List<string> presence UserPresenceDto Returns Task"
  },
  "api/core/EchoHub.Core.Contracts.IChatService.html": {
    "href": "api/core/EchoHub.Core.Contracts.IChatService.html",
    "title": "Interface IChatService | EchoHub Documentation",
    "summary": "Interface IChatService Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IChatService Methods BroadcastChannelDeletedAsync(string) Task BroadcastChannelDeletedAsync(string channelName) Parameters channelName string Returns Task BroadcastChannelUpdatedAsync(ChannelDto, string?) Task BroadcastChannelUpdatedAsync(ChannelDto channel, string? channelName = null) Parameters channel ChannelDto channelName string Returns Task BroadcastMessageAsync(string, MessageDto) Task BroadcastMessageAsync(string channelName, MessageDto message) Parameters channelName string message MessageDto Returns Task GetChannelHistoryAsync(string, int, int) Task<List<MessageDto>> GetChannelHistoryAsync(string channelName, int count, int offset = 0) Parameters channelName string count int offset int Returns Task<List<MessageDto>> GetChannelsForUserAsync(string) Task<List<string>> GetChannelsForUserAsync(string username) Parameters username string Returns Task<List<string>> GetOnlineUsersAsync(string) Task<List<UserPresenceDto>> GetOnlineUsersAsync(string channelName) Parameters channelName string Returns Task<List<UserPresenceDto>> JoinChannelAsync(string, Guid, string, string, string?) Task<(List<MessageDto> History, string? Error, bool PasswordRequired)> JoinChannelAsync(string connectionId, Guid userId, string username, string channelName, string? password = null) Parameters connectionId string userId Guid username string channelName string password string Returns Task<(List<MessageDto> History, string Error, bool PasswordRequired)> LeaveChannelAsync(string, string, string) Task LeaveChannelAsync(string connectionId, string username, string channelName) Parameters connectionId string username string channelName string Returns Task SendMessageAsync(Guid, string, string, string, string?, Guid?) Task<string?> SendMessageAsync(Guid userId, string username, string channelName, string content, string? originConnectionId = null, Guid? replyToMessageId = null) Parameters userId Guid username string channelName string content string originConnectionId string replyToMessageId Guid? Returns Task<string> UpdateStatusAsync(Guid, string, UserStatus, string?) Task<string?> UpdateStatusAsync(Guid userId, string username, UserStatus status, string? statusMessage) Parameters userId Guid username string status UserStatus statusMessage string Returns Task<string> UserConnectedAsync(string, Guid, string) Task UserConnectedAsync(string connectionId, Guid userId, string username) Parameters connectionId string userId Guid username string Returns Task UserDisconnectedAsync(string) Task<string?> UserDisconnectedAsync(string connectionId) Parameters connectionId string Returns Task<string>"
  },
  "api/core/EchoHub.Core.Contracts.IEchoHubClient.html": {
    "href": "api/core/EchoHub.Core.Contracts.IEchoHubClient.html",
    "title": "Interface IEchoHubClient | EchoHub Documentation",
    "summary": "Interface IEchoHubClient Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll Methods the server can invoke on connected clients. public interface IEchoHubClient Methods ChannelDeleted(string) Task ChannelDeleted(string channelName) Parameters channelName string Returns Task ChannelNuked(string) Task ChannelNuked(string channelName) Parameters channelName string Returns Task ChannelUpdated(ChannelDto) Task ChannelUpdated(ChannelDto channel) Parameters channel ChannelDto Returns Task Error(string) Task Error(string message) Parameters message string Returns Task ForceDisconnect(string) Task ForceDisconnect(string reason) Parameters reason string Returns Task MessageDeleted(string, Guid) Task MessageDeleted(string channelName, Guid messageId) Parameters channelName string messageId Guid Returns Task ReceiveMessage(MessageDto) Task ReceiveMessage(MessageDto message) Parameters message MessageDto Returns Task UserBanned(string, string?) Task UserBanned(string username, string? reason) Parameters username string reason string Returns Task UserJoined(string, string, UserPresenceDto?) Task UserJoined(string channelName, string username, UserPresenceDto? presence) Parameters channelName string username string presence UserPresenceDto Returns Task UserKicked(string, string, string?) Task UserKicked(string channelName, string username, string? reason) Parameters channelName string username string reason string Returns Task UserLeft(string, string) Task UserLeft(string channelName, string username) Parameters channelName string username string Returns Task UserStatusChanged(UserPresenceDto) Task UserStatusChanged(UserPresenceDto presence) Parameters presence UserPresenceDto Returns Task"
  },
  "api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html": {
    "href": "api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html",
    "title": "Interface IMessageEncryptionService | EchoHub Documentation",
    "summary": "Interface IMessageEncryptionService Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IMessageEncryptionService Fields CiphertextPrefix Prefix marking transport/at-rest encrypted content. Decrypt(string) is a pass-through for values without it. public const string CiphertextPrefix = \"$ENC$v1$\" Field Value string Properties EncryptDatabaseEnabled Whether database content should be encrypted at rest (server setting). bool EncryptDatabaseEnabled { get; } Property Value bool Methods Decrypt(string) string Decrypt(string content) Parameters content string Returns string DecryptNullable(string?) string? DecryptNullable(string? value) Parameters value string Returns string Encrypt(string) string Encrypt(string plaintext) Parameters plaintext string Returns string EncryptNullable(string?) string? EncryptNullable(string? value) Parameters value string Returns string"
  },
  "api/core/EchoHub.Core.Contracts.IUserService.html": {
    "href": "api/core/EchoHub.Core.Contracts.IUserService.html",
    "title": "Interface IUserService | EchoHub Documentation",
    "summary": "Interface IUserService Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IUserService Methods AuthenticateUserAsync(string, string) Task<UserOperationResult> AuthenticateUserAsync(string username, string password) Parameters username string password string Returns Task<UserOperationResult> GetUserByIdAsync(Guid) Task<UserProfileDto?> GetUserByIdAsync(Guid userId) Parameters userId Guid Returns Task<UserProfileDto> GetUserProfileAsync(string) Task<UserProfileDto?> GetUserProfileAsync(string username) Parameters username string Returns Task<UserProfileDto> RegisterUserAsync(string, string, string?, string?) Task<UserOperationResult> RegisterUserAsync(string username, string password, string? displayName = null, string? inviteCode = null) Parameters username string password string displayName string inviteCode string Returns Task<UserOperationResult> SetAvatarAsync(Guid, string) Task<UserOperationResult> SetAvatarAsync(Guid userId, string asciiArt) Parameters userId Guid asciiArt string Returns Task<UserOperationResult> UpdateProfileAsync(Guid, string?, string?, string?) Task<UserOperationResult> UpdateProfileAsync(Guid userId, string? displayName, string? bio, string? nicknameColor) Parameters userId Guid displayName string bio string nicknameColor string Returns Task<UserOperationResult>"
  },
  "api/core/EchoHub.Core.Contracts.html": {
    "href": "api/core/EchoHub.Core.Contracts.html",
    "title": "Namespace EchoHub.Core.Contracts | EchoHub Documentation",
    "summary": "Namespace EchoHub.Core.Contracts Classes ChannelListItem Interfaces IChannelService IChatBroadcaster IChatService IEchoHubClient Methods the server can invoke on connected clients. IMessageEncryptionService IUserService"
  },
  "api/core/EchoHub.Core.DTOs.ApiResponse-1.html": {
    "href": "api/core/EchoHub.Core.DTOs.ApiResponse-1.html",
    "title": "Class ApiResponse<T> | EchoHub Documentation",
    "summary": "Class ApiResponse<T> Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record ApiResponse<T> : IEquatable<ApiResponse<T>> Type Parameters T Inheritance object ApiResponse<T> Implements IEquatable<ApiResponse<T>> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ApiResponse(ApiResponse<T>) protected ApiResponse(ApiResponse<T> original) Parameters original ApiResponse<T> ApiResponse(bool, string?, List<string>?, T?) public ApiResponse(bool Success, string? Message = null, List<string>? Errors = null, T? Data = default) Parameters Success bool Message string Errors List<string> Data T Properties Data public T? Data { get; init; } Property Value T EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Errors public List<string>? Errors { get; init; } Property Value List<string> Message public string? Message { get; init; } Property Value string Success public bool Success { get; init; } Property Value bool Methods Deconstruct(out bool, out string?, out List<string>?, out T?) public void Deconstruct(out bool Success, out string? Message, out List<string>? Errors, out T? Data) Parameters Success bool Message string Errors List<string> Data T Equals(ApiResponse<T>?) public virtual bool Equals(ApiResponse<T>? other) Parameters other ApiResponse<T> Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ApiResponse<T>?, ApiResponse<T>?) public static bool operator ==(ApiResponse<T>? left, ApiResponse<T>? right) Parameters left ApiResponse<T> right ApiResponse<T> Returns bool operator !=(ApiResponse<T>?, ApiResponse<T>?) public static bool operator !=(ApiResponse<T>? left, ApiResponse<T>? right) Parameters left ApiResponse<T> right ApiResponse<T> Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ApiResponse.html": {
    "href": "api/core/EchoHub.Core.DTOs.ApiResponse.html",
    "title": "Class ApiResponse | EchoHub Documentation",
    "summary": "Class ApiResponse Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record ApiResponse : IEquatable<ApiResponse> Inheritance object ApiResponse Implements IEquatable<ApiResponse> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ApiResponse(ApiResponse) protected ApiResponse(ApiResponse original) Parameters original ApiResponse ApiResponse(bool, string?, List<string>?) public ApiResponse(bool Success, string? Message = null, List<string>? Errors = null) Parameters Success bool Message string Errors List<string> Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Errors public List<string>? Errors { get; init; } Property Value List<string> Message public string? Message { get; init; } Property Value string Success public bool Success { get; init; } Property Value bool Methods Deconstruct(out bool, out string?, out List<string>?) public void Deconstruct(out bool Success, out string? Message, out List<string>? Errors) Parameters Success bool Message string Errors List<string> Equals(ApiResponse?) public virtual bool Equals(ApiResponse? other) Parameters other ApiResponse Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ApiResponse?, ApiResponse?) public static bool operator ==(ApiResponse? left, ApiResponse? right) Parameters left ApiResponse right ApiResponse Returns bool operator !=(ApiResponse?, ApiResponse?) public static bool operator !=(ApiResponse? left, ApiResponse? right) Parameters left ApiResponse right ApiResponse Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.AssignRoleRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.AssignRoleRequest.html",
    "title": "Class AssignRoleRequest | EchoHub Documentation",
    "summary": "Class AssignRoleRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record AssignRoleRequest : IEquatable<AssignRoleRequest> Inheritance object AssignRoleRequest Implements IEquatable<AssignRoleRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors AssignRoleRequest(AssignRoleRequest) protected AssignRoleRequest(AssignRoleRequest original) Parameters original AssignRoleRequest AssignRoleRequest(string, ServerRole) public AssignRoleRequest(string Username, ServerRole Role) Parameters Username string Role ServerRole Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Role public ServerRole Role { get; init; } Property Value ServerRole Username public string Username { get; init; } Property Value string Methods Deconstruct(out string, out ServerRole) public void Deconstruct(out string Username, out ServerRole Role) Parameters Username string Role ServerRole Equals(AssignRoleRequest?) public virtual bool Equals(AssignRoleRequest? other) Parameters other AssignRoleRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(AssignRoleRequest?, AssignRoleRequest?) public static bool operator ==(AssignRoleRequest? left, AssignRoleRequest? right) Parameters left AssignRoleRequest right AssignRoleRequest Returns bool operator !=(AssignRoleRequest?, AssignRoleRequest?) public static bool operator !=(AssignRoleRequest? left, AssignRoleRequest? right) Parameters left AssignRoleRequest right AssignRoleRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.AttachmentDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.AttachmentDto.html",
    "title": "Class AttachmentDto | EchoHub Documentation",
    "summary": "Class AttachmentDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll A file attached to a message. AsciiPreview holds the color-tag art for images (null otherwise). For end-to-end encrypted channels the content behind Url and the preview are ciphertext the server cannot read. public record AttachmentDto : IEquatable<AttachmentDto> Inheritance object AttachmentDto Implements IEquatable<AttachmentDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors AttachmentDto(AttachmentDto) protected AttachmentDto(AttachmentDto original) Parameters original AttachmentDto AttachmentDto(AttachmentKind, string, string, long, string?) A file attached to a message. AsciiPreview holds the color-tag art for images (null otherwise). For end-to-end encrypted channels the content behind Url and the preview are ciphertext the server cannot read. public AttachmentDto(AttachmentKind Kind, string Url, string FileName, long FileSize, string? AsciiPreview = null) Parameters Kind AttachmentKind Url string FileName string FileSize long AsciiPreview string Properties AsciiPreview public string? AsciiPreview { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type FileName public string FileName { get; init; } Property Value string FileSize public long FileSize { get; init; } Property Value long Kind public AttachmentKind Kind { get; init; } Property Value AttachmentKind Url public string Url { get; init; } Property Value string Methods Deconstruct(out AttachmentKind, out string, out string, out long, out string?) public void Deconstruct(out AttachmentKind Kind, out string Url, out string FileName, out long FileSize, out string? AsciiPreview) Parameters Kind AttachmentKind Url string FileName string FileSize long AsciiPreview string Equals(AttachmentDto?) public virtual bool Equals(AttachmentDto? other) Parameters other AttachmentDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(AttachmentDto?, AttachmentDto?) public static bool operator ==(AttachmentDto? left, AttachmentDto? right) Parameters left AttachmentDto right AttachmentDto Returns bool operator !=(AttachmentDto?, AttachmentDto?) public static bool operator !=(AttachmentDto? left, AttachmentDto? right) Parameters left AttachmentDto right AttachmentDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.AvatarUploadResponse.html": {
    "href": "api/core/EchoHub.Core.DTOs.AvatarUploadResponse.html",
    "title": "Class AvatarUploadResponse | EchoHub Documentation",
    "summary": "Class AvatarUploadResponse Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record AvatarUploadResponse : IEquatable<AvatarUploadResponse> Inheritance object AvatarUploadResponse Implements IEquatable<AvatarUploadResponse> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors AvatarUploadResponse(AvatarUploadResponse) protected AvatarUploadResponse(AvatarUploadResponse original) Parameters original AvatarUploadResponse AvatarUploadResponse(string) public AvatarUploadResponse(string AvatarAscii) Parameters AvatarAscii string Properties AvatarAscii public string AvatarAscii { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Methods Deconstruct(out string) public void Deconstruct(out string AvatarAscii) Parameters AvatarAscii string Equals(AvatarUploadResponse?) public virtual bool Equals(AvatarUploadResponse? other) Parameters other AvatarUploadResponse Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(AvatarUploadResponse?, AvatarUploadResponse?) public static bool operator ==(AvatarUploadResponse? left, AvatarUploadResponse? right) Parameters left AvatarUploadResponse right AvatarUploadResponse Returns bool operator !=(AvatarUploadResponse?, AvatarUploadResponse?) public static bool operator !=(AvatarUploadResponse? left, AvatarUploadResponse? right) Parameters left AvatarUploadResponse right AvatarUploadResponse Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.BanRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.BanRequest.html",
    "title": "Class BanRequest | EchoHub Documentation",
    "summary": "Class BanRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record BanRequest : IEquatable<BanRequest> Inheritance object BanRequest Implements IEquatable<BanRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors BanRequest(BanRequest) protected BanRequest(BanRequest original) Parameters original BanRequest BanRequest(string?) public BanRequest(string? Reason = null) Parameters Reason string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Reason public string? Reason { get; init; } Property Value string Methods Deconstruct(out string?) public void Deconstruct(out string? Reason) Parameters Reason string Equals(BanRequest?) public virtual bool Equals(BanRequest? other) Parameters other BanRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(BanRequest?, BanRequest?) public static bool operator ==(BanRequest? left, BanRequest? right) Parameters left BanRequest right BanRequest Returns bool operator !=(BanRequest?, BanRequest?) public static bool operator !=(BanRequest? left, BanRequest? right) Parameters left BanRequest right BanRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ChannelCryptoDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.ChannelCryptoDto.html",
    "title": "Class ChannelCryptoDto | EchoHub Documentation",
    "summary": "Class ChannelCryptoDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll Public crypto metadata for a channel — enough for a client to derive its join credential from a passphrase. Never includes the wrapped room key. public record ChannelCryptoDto : IEquatable<ChannelCryptoDto> Inheritance object ChannelCryptoDto Implements IEquatable<ChannelCryptoDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ChannelCryptoDto(ChannelCryptoDto) protected ChannelCryptoDto(ChannelCryptoDto original) Parameters original ChannelCryptoDto ChannelCryptoDto(bool, string?) Public crypto metadata for a channel — enough for a client to derive its join credential from a passphrase. Never includes the wrapped room key. public ChannelCryptoDto(bool IsEncrypted, string? EncryptionSalt) Parameters IsEncrypted bool EncryptionSalt string Properties EncryptionSalt public string? EncryptionSalt { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type IsEncrypted public bool IsEncrypted { get; init; } Property Value bool Methods Deconstruct(out bool, out string?) public void Deconstruct(out bool IsEncrypted, out string? EncryptionSalt) Parameters IsEncrypted bool EncryptionSalt string Equals(ChannelCryptoDto?) public virtual bool Equals(ChannelCryptoDto? other) Parameters other ChannelCryptoDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ChannelCryptoDto?, ChannelCryptoDto?) public static bool operator ==(ChannelCryptoDto? left, ChannelCryptoDto? right) Parameters left ChannelCryptoDto right ChannelCryptoDto Returns bool operator !=(ChannelCryptoDto?, ChannelCryptoDto?) public static bool operator !=(ChannelCryptoDto? left, ChannelCryptoDto? right) Parameters left ChannelCryptoDto right ChannelCryptoDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ChannelDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.ChannelDto.html",
    "title": "Class ChannelDto | EchoHub Documentation",
    "summary": "Class ChannelDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record ChannelDto : IEquatable<ChannelDto> Inheritance object ChannelDto Implements IEquatable<ChannelDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ChannelDto(ChannelDto) protected ChannelDto(ChannelDto original) Parameters original ChannelDto ChannelDto(Guid, string, string?, bool, int, DateTimeOffset, bool, bool, bool) public ChannelDto(Guid Id, string Name, string? Topic, bool IsPublic, int MessageCount, DateTimeOffset CreatedAt, bool IsProtected = false, bool IsEncrypted = false, bool IsSystem = false) Parameters Id Guid Name string Topic string IsPublic bool MessageCount int CreatedAt DateTimeOffset IsProtected bool IsEncrypted bool IsSystem bool Properties CreatedAt public DateTimeOffset CreatedAt { get; init; } Property Value DateTimeOffset EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Id public Guid Id { get; init; } Property Value Guid IsEncrypted public bool IsEncrypted { get; init; } Property Value bool IsProtected public bool IsProtected { get; init; } Property Value bool IsPublic public bool IsPublic { get; init; } Property Value bool IsSystem public bool IsSystem { get; init; } Property Value bool MessageCount public int MessageCount { get; init; } Property Value int Name public string Name { get; init; } Property Value string Topic public string? Topic { get; init; } Property Value string Methods Deconstruct(out Guid, out string, out string?, out bool, out int, out DateTimeOffset, out bool, out bool, out bool) public void Deconstruct(out Guid Id, out string Name, out string? Topic, out bool IsPublic, out int MessageCount, out DateTimeOffset CreatedAt, out bool IsProtected, out bool IsEncrypted, out bool IsSystem) Parameters Id Guid Name string Topic string IsPublic bool MessageCount int CreatedAt DateTimeOffset IsProtected bool IsEncrypted bool IsSystem bool Equals(ChannelDto?) public virtual bool Equals(ChannelDto? other) Parameters other ChannelDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ChannelDto?, ChannelDto?) public static bool operator ==(ChannelDto? left, ChannelDto? right) Parameters left ChannelDto right ChannelDto Returns bool operator !=(ChannelDto?, ChannelDto?) public static bool operator !=(ChannelDto? left, ChannelDto? right) Parameters left ChannelDto right ChannelDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ChannelError.html": {
    "href": "api/core/EchoHub.Core.DTOs.ChannelError.html",
    "title": "Enum ChannelError | EchoHub Documentation",
    "summary": "Enum ChannelError Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public enum ChannelError Fields AlreadyExists = 1 Forbidden = 3 NotFound = 2 Protected = 4 ValidationFailed = 0"
  },
  "api/core/EchoHub.Core.DTOs.ChannelMetaDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.ChannelMetaDto.html",
    "title": "Class ChannelMetaDto | EchoHub Documentation",
    "summary": "Class ChannelMetaDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll Human-facing summary of a channel (the /meta command). For encrypted channels the server still knows these figures — count, timestamps, and stored blob sizes — even though it cannot read the content itself. EstimatedSizeBytes is the sum of stored attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total. public record ChannelMetaDto : IEquatable<ChannelMetaDto> Inheritance object ChannelMetaDto Implements IEquatable<ChannelMetaDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ChannelMetaDto(ChannelMetaDto) protected ChannelMetaDto(ChannelMetaDto original) Parameters original ChannelMetaDto ChannelMetaDto(Guid, string, string?, bool, bool, int, int, long, DateTimeOffset) Human-facing summary of a channel (the /meta command). For encrypted channels the server still knows these figures — count, timestamps, and stored blob sizes — even though it cannot read the content itself. EstimatedSizeBytes is the sum of stored attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total. public ChannelMetaDto(Guid Id, string Name, string? Topic, bool IsEncrypted, bool IsProtected, int MessageCount, int UniqueUserCount, long EstimatedSizeBytes, DateTimeOffset CreatedAt) Parameters Id Guid Name string Topic string IsEncrypted bool IsProtected bool MessageCount int UniqueUserCount int EstimatedSizeBytes long CreatedAt DateTimeOffset Properties CreatedAt public DateTimeOffset CreatedAt { get; init; } Property Value DateTimeOffset EqualityContract protected virtual Type EqualityContract { get; } Property Value Type EstimatedSizeBytes public long EstimatedSizeBytes { get; init; } Property Value long Id public Guid Id { get; init; } Property Value Guid IsEncrypted public bool IsEncrypted { get; init; } Property Value bool IsProtected public bool IsProtected { get; init; } Property Value bool MessageCount public int MessageCount { get; init; } Property Value int Name public string Name { get; init; } Property Value string Topic public string? Topic { get; init; } Property Value string UniqueUserCount public int UniqueUserCount { get; init; } Property Value int Methods Deconstruct(out Guid, out string, out string?, out bool, out bool, out int, out int, out long, out DateTimeOffset) public void Deconstruct(out Guid Id, out string Name, out string? Topic, out bool IsEncrypted, out bool IsProtected, out int MessageCount, out int UniqueUserCount, out long EstimatedSizeBytes, out DateTimeOffset CreatedAt) Parameters Id Guid Name string Topic string IsEncrypted bool IsProtected bool MessageCount int UniqueUserCount int EstimatedSizeBytes long CreatedAt DateTimeOffset Equals(ChannelMetaDto?) public virtual bool Equals(ChannelMetaDto? other) Parameters other ChannelMetaDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ChannelMetaDto?, ChannelMetaDto?) public static bool operator ==(ChannelMetaDto? left, ChannelMetaDto? right) Parameters left ChannelMetaDto right ChannelMetaDto Returns bool operator !=(ChannelMetaDto?, ChannelMetaDto?) public static bool operator !=(ChannelMetaDto? left, ChannelMetaDto? right) Parameters left ChannelMetaDto right ChannelMetaDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ChannelOperationResult.html": {
    "href": "api/core/EchoHub.Core.DTOs.ChannelOperationResult.html",
    "title": "Class ChannelOperationResult | EchoHub Documentation",
    "summary": "Class ChannelOperationResult Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record ChannelOperationResult : IEquatable<ChannelOperationResult> Inheritance object ChannelOperationResult Implements IEquatable<ChannelOperationResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ChannelOperationResult(ChannelDto?, ChannelError?, string?) public ChannelOperationResult(ChannelDto? Channel, ChannelError? Error, string? ErrorMessage) Parameters Channel ChannelDto Error ChannelError? ErrorMessage string ChannelOperationResult(ChannelOperationResult) protected ChannelOperationResult(ChannelOperationResult original) Parameters original ChannelOperationResult Properties Channel public ChannelDto? Channel { get; init; } Property Value ChannelDto EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Error public ChannelError? Error { get; init; } Property Value ChannelError? ErrorMessage public string? ErrorMessage { get; init; } Property Value string IsSuccess public bool IsSuccess { get; } Property Value bool Methods Deconstruct(out ChannelDto?, out ChannelError?, out string?) public void Deconstruct(out ChannelDto? Channel, out ChannelError? Error, out string? ErrorMessage) Parameters Channel ChannelDto Error ChannelError? ErrorMessage string Equals(ChannelOperationResult?) public virtual bool Equals(ChannelOperationResult? other) Parameters other ChannelOperationResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool Fail(ChannelError, string) public static ChannelOperationResult Fail(ChannelError error, string message) Parameters error ChannelError message string Returns ChannelOperationResult GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool Success(ChannelDto) public static ChannelOperationResult Success(ChannelDto channel) Parameters channel ChannelDto Returns ChannelOperationResult ToString() public override string ToString() Returns string Operators operator ==(ChannelOperationResult?, ChannelOperationResult?) public static bool operator ==(ChannelOperationResult? left, ChannelOperationResult? right) Parameters left ChannelOperationResult right ChannelOperationResult Returns bool operator !=(ChannelOperationResult?, ChannelOperationResult?) public static bool operator !=(ChannelOperationResult? left, ChannelOperationResult? right) Parameters left ChannelOperationResult right ChannelOperationResult Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.CreateChannelRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.CreateChannelRequest.html",
    "title": "Class CreateChannelRequest | EchoHub Documentation",
    "summary": "Class CreateChannelRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record CreateChannelRequest : IEquatable<CreateChannelRequest> Inheritance object CreateChannelRequest Implements IEquatable<CreateChannelRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors CreateChannelRequest(CreateChannelRequest) protected CreateChannelRequest(CreateChannelRequest original) Parameters original CreateChannelRequest CreateChannelRequest(string, string?, bool, string?, string?, string?) public CreateChannelRequest(string Name, string? Topic = null, bool IsPublic = true, string? Password = null, string? EncryptionSalt = null, string? WrappedRoomKey = null) Parameters Name string Topic string IsPublic bool Password string EncryptionSalt string WrappedRoomKey string Properties EncryptionSalt public string? EncryptionSalt { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type IsPublic public bool IsPublic { get; init; } Property Value bool Name public string Name { get; init; } Property Value string Password public string? Password { get; init; } Property Value string Topic public string? Topic { get; init; } Property Value string WrappedRoomKey public string? WrappedRoomKey { get; init; } Property Value string Methods Deconstruct(out string, out string?, out bool, out string?, out string?, out string?) public void Deconstruct(out string Name, out string? Topic, out bool IsPublic, out string? Password, out string? EncryptionSalt, out string? WrappedRoomKey) Parameters Name string Topic string IsPublic bool Password string EncryptionSalt string WrappedRoomKey string Equals(CreateChannelRequest?) public virtual bool Equals(CreateChannelRequest? other) Parameters other CreateChannelRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(CreateChannelRequest?, CreateChannelRequest?) public static bool operator ==(CreateChannelRequest? left, CreateChannelRequest? right) Parameters left CreateChannelRequest right CreateChannelRequest Returns bool operator !=(CreateChannelRequest?, CreateChannelRequest?) public static bool operator !=(CreateChannelRequest? left, CreateChannelRequest? right) Parameters left CreateChannelRequest right CreateChannelRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.CreateInviteRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.CreateInviteRequest.html",
    "title": "Class CreateInviteRequest | EchoHub Documentation",
    "summary": "Class CreateInviteRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record CreateInviteRequest : IEquatable<CreateInviteRequest> Inheritance object CreateInviteRequest Implements IEquatable<CreateInviteRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors CreateInviteRequest(CreateInviteRequest) protected CreateInviteRequest(CreateInviteRequest original) Parameters original CreateInviteRequest CreateInviteRequest(int?, int?) public CreateInviteRequest(int? MaxUses = null, int? ExpiresInHours = null) Parameters MaxUses int? ExpiresInHours int? Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type ExpiresInHours public int? ExpiresInHours { get; init; } Property Value int? MaxUses public int? MaxUses { get; init; } Property Value int? Methods Deconstruct(out int?, out int?) public void Deconstruct(out int? MaxUses, out int? ExpiresInHours) Parameters MaxUses int? ExpiresInHours int? Equals(CreateInviteRequest?) public virtual bool Equals(CreateInviteRequest? other) Parameters other CreateInviteRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(CreateInviteRequest?, CreateInviteRequest?) public static bool operator ==(CreateInviteRequest? left, CreateInviteRequest? right) Parameters left CreateInviteRequest right CreateInviteRequest Returns bool operator !=(CreateInviteRequest?, CreateInviteRequest?) public static bool operator !=(CreateInviteRequest? left, CreateInviteRequest? right) Parameters left CreateInviteRequest right CreateInviteRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.DeleteAccountRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.DeleteAccountRequest.html",
    "title": "Class DeleteAccountRequest | EchoHub Documentation",
    "summary": "Class DeleteAccountRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll Password re-confirmation for destructive self-service account actions. public record DeleteAccountRequest : IEquatable<DeleteAccountRequest> Inheritance object DeleteAccountRequest Implements IEquatable<DeleteAccountRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors DeleteAccountRequest(DeleteAccountRequest) protected DeleteAccountRequest(DeleteAccountRequest original) Parameters original DeleteAccountRequest DeleteAccountRequest(string) Password re-confirmation for destructive self-service account actions. public DeleteAccountRequest(string Password) Parameters Password string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Password public string Password { get; init; } Property Value string Methods Deconstruct(out string) public void Deconstruct(out string Password) Parameters Password string Equals(DeleteAccountRequest?) public virtual bool Equals(DeleteAccountRequest? other) Parameters other DeleteAccountRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(DeleteAccountRequest?, DeleteAccountRequest?) public static bool operator ==(DeleteAccountRequest? left, DeleteAccountRequest? right) Parameters left DeleteAccountRequest right DeleteAccountRequest Returns bool operator !=(DeleteAccountRequest?, DeleteAccountRequest?) public static bool operator !=(DeleteAccountRequest? left, DeleteAccountRequest? right) Parameters left DeleteAccountRequest right DeleteAccountRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.EmbedDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.EmbedDto.html",
    "title": "Class EmbedDto | EchoHub Documentation",
    "summary": "Class EmbedDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record EmbedDto : IEquatable<EmbedDto> Inheritance object EmbedDto Implements IEquatable<EmbedDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors EmbedDto(EmbedDto) protected EmbedDto(EmbedDto original) Parameters original EmbedDto EmbedDto(string?, string?, string?, string?, string, string?) public EmbedDto(string? SiteName, string? Title, string? Description, string? ImageAscii, string Url, string? ThemeColor = null) Parameters SiteName string Title string Description string ImageAscii string Url string ThemeColor string Properties Description public string? Description { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type ImageAscii public string? ImageAscii { get; init; } Property Value string SiteName public string? SiteName { get; init; } Property Value string ThemeColor public string? ThemeColor { get; init; } Property Value string Title public string? Title { get; init; } Property Value string Url public string Url { get; init; } Property Value string Methods Deconstruct(out string?, out string?, out string?, out string?, out string, out string?) public void Deconstruct(out string? SiteName, out string? Title, out string? Description, out string? ImageAscii, out string Url, out string? ThemeColor) Parameters SiteName string Title string Description string ImageAscii string Url string ThemeColor string Equals(EmbedDto?) public virtual bool Equals(EmbedDto? other) Parameters other EmbedDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(EmbedDto?, EmbedDto?) public static bool operator ==(EmbedDto? left, EmbedDto? right) Parameters left EmbedDto right EmbedDto Returns bool operator !=(EmbedDto?, EmbedDto?) public static bool operator !=(EmbedDto? left, EmbedDto? right) Parameters left EmbedDto right EmbedDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.EncryptionKeyResponse.html": {
    "href": "api/core/EchoHub.Core.DTOs.EncryptionKeyResponse.html",
    "title": "Class EncryptionKeyResponse | EchoHub Documentation",
    "summary": "Class EncryptionKeyResponse Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record EncryptionKeyResponse : IEquatable<EncryptionKeyResponse> Inheritance object EncryptionKeyResponse Implements IEquatable<EncryptionKeyResponse> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors EncryptionKeyResponse(EncryptionKeyResponse) protected EncryptionKeyResponse(EncryptionKeyResponse original) Parameters original EncryptionKeyResponse EncryptionKeyResponse(string) public EncryptionKeyResponse(string Key) Parameters Key string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Key public string Key { get; init; } Property Value string Methods Deconstruct(out string) public void Deconstruct(out string Key) Parameters Key string Equals(EncryptionKeyResponse?) public virtual bool Equals(EncryptionKeyResponse? other) Parameters other EncryptionKeyResponse Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(EncryptionKeyResponse?, EncryptionKeyResponse?) public static bool operator ==(EncryptionKeyResponse? left, EncryptionKeyResponse? right) Parameters left EncryptionKeyResponse right EncryptionKeyResponse Returns bool operator !=(EncryptionKeyResponse?, EncryptionKeyResponse?) public static bool operator !=(EncryptionKeyResponse? left, EncryptionKeyResponse? right) Parameters left EncryptionKeyResponse right EncryptionKeyResponse Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ErrorResponse.html": {
    "href": "api/core/EchoHub.Core.DTOs.ErrorResponse.html",
    "title": "Class ErrorResponse | EchoHub Documentation",
    "summary": "Class ErrorResponse Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record ErrorResponse : IEquatable<ErrorResponse> Inheritance object ErrorResponse Implements IEquatable<ErrorResponse> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ErrorResponse(ErrorResponse) protected ErrorResponse(ErrorResponse original) Parameters original ErrorResponse ErrorResponse(string, string?) public ErrorResponse(string Error, string? Detail = null) Parameters Error string Detail string Properties Detail public string? Detail { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Error public string Error { get; init; } Property Value string Methods Deconstruct(out string, out string?) public void Deconstruct(out string Error, out string? Detail) Parameters Error string Detail string Equals(ErrorResponse?) public virtual bool Equals(ErrorResponse? other) Parameters other ErrorResponse Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ErrorResponse?, ErrorResponse?) public static bool operator ==(ErrorResponse? left, ErrorResponse? right) Parameters left ErrorResponse right ErrorResponse Returns bool operator !=(ErrorResponse?, ErrorResponse?) public static bool operator !=(ErrorResponse? left, ErrorResponse? right) Parameters left ErrorResponse right ErrorResponse Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ExportedAttachmentDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.ExportedAttachmentDto.html",
    "title": "Class ExportedAttachmentDto | EchoHub Documentation",
    "summary": "Class ExportedAttachmentDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record ExportedAttachmentDto : IEquatable<ExportedAttachmentDto> Inheritance object ExportedAttachmentDto Implements IEquatable<ExportedAttachmentDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ExportedAttachmentDto(ExportedAttachmentDto) protected ExportedAttachmentDto(ExportedAttachmentDto original) Parameters original ExportedAttachmentDto ExportedAttachmentDto(string, string, long, string, string, DateTimeOffset) public ExportedAttachmentDto(string FileName, string Url, long FileSize, string Kind, string ChannelName, DateTimeOffset SentAt) Parameters FileName string Url string FileSize long Kind string ChannelName string SentAt DateTimeOffset Properties ChannelName public string ChannelName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type FileName public string FileName { get; init; } Property Value string FileSize public long FileSize { get; init; } Property Value long Kind public string Kind { get; init; } Property Value string SentAt public DateTimeOffset SentAt { get; init; } Property Value DateTimeOffset Url public string Url { get; init; } Property Value string Methods Deconstruct(out string, out string, out long, out string, out string, out DateTimeOffset) public void Deconstruct(out string FileName, out string Url, out long FileSize, out string Kind, out string ChannelName, out DateTimeOffset SentAt) Parameters FileName string Url string FileSize long Kind string ChannelName string SentAt DateTimeOffset Equals(ExportedAttachmentDto?) public virtual bool Equals(ExportedAttachmentDto? other) Parameters other ExportedAttachmentDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ExportedAttachmentDto?, ExportedAttachmentDto?) public static bool operator ==(ExportedAttachmentDto? left, ExportedAttachmentDto? right) Parameters left ExportedAttachmentDto right ExportedAttachmentDto Returns bool operator !=(ExportedAttachmentDto?, ExportedAttachmentDto?) public static bool operator !=(ExportedAttachmentDto? left, ExportedAttachmentDto? right) Parameters left ExportedAttachmentDto right ExportedAttachmentDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ExportedMessageDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.ExportedMessageDto.html",
    "title": "Class ExportedMessageDto | EchoHub Documentation",
    "summary": "Class ExportedMessageDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record ExportedMessageDto : IEquatable<ExportedMessageDto> Inheritance object ExportedMessageDto Implements IEquatable<ExportedMessageDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ExportedMessageDto(ExportedMessageDto) protected ExportedMessageDto(ExportedMessageDto original) Parameters original ExportedMessageDto ExportedMessageDto(Guid, string, DateTimeOffset, string, Guid?) public ExportedMessageDto(Guid Id, string ChannelName, DateTimeOffset SentAt, string Content, Guid? ReplyToMessageId) Parameters Id Guid ChannelName string SentAt DateTimeOffset Content string ReplyToMessageId Guid? Properties ChannelName public string ChannelName { get; init; } Property Value string Content public string Content { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Id public Guid Id { get; init; } Property Value Guid ReplyToMessageId public Guid? ReplyToMessageId { get; init; } Property Value Guid? SentAt public DateTimeOffset SentAt { get; init; } Property Value DateTimeOffset Methods Deconstruct(out Guid, out string, out DateTimeOffset, out string, out Guid?) public void Deconstruct(out Guid Id, out string ChannelName, out DateTimeOffset SentAt, out string Content, out Guid? ReplyToMessageId) Parameters Id Guid ChannelName string SentAt DateTimeOffset Content string ReplyToMessageId Guid? Equals(ExportedMessageDto?) public virtual bool Equals(ExportedMessageDto? other) Parameters other ExportedMessageDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ExportedMessageDto?, ExportedMessageDto?) public static bool operator ==(ExportedMessageDto? left, ExportedMessageDto? right) Parameters left ExportedMessageDto right ExportedMessageDto Returns bool operator !=(ExportedMessageDto?, ExportedMessageDto?) public static bool operator !=(ExportedMessageDto? left, ExportedMessageDto? right) Parameters left ExportedMessageDto right ExportedMessageDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.InviteDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.InviteDto.html",
    "title": "Class InviteDto | EchoHub Documentation",
    "summary": "Class InviteDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record InviteDto : IEquatable<InviteDto> Inheritance object InviteDto Implements IEquatable<InviteDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors InviteDto(InviteDto) protected InviteDto(InviteDto original) Parameters original InviteDto InviteDto(string, string, DateTimeOffset, DateTimeOffset?, int, int) public InviteDto(string Code, string CreatedByUsername, DateTimeOffset CreatedAt, DateTimeOffset? ExpiresAt, int MaxUses, int UseCount) Parameters Code string CreatedByUsername string CreatedAt DateTimeOffset ExpiresAt DateTimeOffset? MaxUses int UseCount int Properties Code public string Code { get; init; } Property Value string CreatedAt public DateTimeOffset CreatedAt { get; init; } Property Value DateTimeOffset CreatedByUsername public string CreatedByUsername { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type ExpiresAt public DateTimeOffset? ExpiresAt { get; init; } Property Value DateTimeOffset? MaxUses public int MaxUses { get; init; } Property Value int UseCount public int UseCount { get; init; } Property Value int Methods Deconstruct(out string, out string, out DateTimeOffset, out DateTimeOffset?, out int, out int) public void Deconstruct(out string Code, out string CreatedByUsername, out DateTimeOffset CreatedAt, out DateTimeOffset? ExpiresAt, out int MaxUses, out int UseCount) Parameters Code string CreatedByUsername string CreatedAt DateTimeOffset ExpiresAt DateTimeOffset? MaxUses int UseCount int Equals(InviteDto?) public virtual bool Equals(InviteDto? other) Parameters other InviteDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(InviteDto?, InviteDto?) public static bool operator ==(InviteDto? left, InviteDto? right) Parameters left InviteDto right InviteDto Returns bool operator !=(InviteDto?, InviteDto?) public static bool operator !=(InviteDto? left, InviteDto? right) Parameters left InviteDto right InviteDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.JoinChannelResult.html": {
    "href": "api/core/EchoHub.Core.DTOs.JoinChannelResult.html",
    "title": "Class JoinChannelResult | EchoHub Documentation",
    "summary": "Class JoinChannelResult Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record JoinChannelResult : IEquatable<JoinChannelResult> Inheritance object JoinChannelResult Implements IEquatable<JoinChannelResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors JoinChannelResult(JoinChannelResult) protected JoinChannelResult(JoinChannelResult original) Parameters original JoinChannelResult JoinChannelResult(bool, List<MessageDto>, string?, bool, string?, string?) public JoinChannelResult(bool Success, List<MessageDto> History, string? Error = null, bool PasswordRequired = false, string? EncryptionSalt = null, string? WrappedRoomKey = null) Parameters Success bool History List<MessageDto> Error string PasswordRequired bool EncryptionSalt string WrappedRoomKey string Properties EncryptionSalt public string? EncryptionSalt { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Error public string? Error { get; init; } Property Value string History public List<MessageDto> History { get; init; } Property Value List<MessageDto> PasswordRequired public bool PasswordRequired { get; init; } Property Value bool Success public bool Success { get; init; } Property Value bool WrappedRoomKey public string? WrappedRoomKey { get; init; } Property Value string Methods Deconstruct(out bool, out List<MessageDto>, out string?, out bool, out string?, out string?) public void Deconstruct(out bool Success, out List<MessageDto> History, out string? Error, out bool PasswordRequired, out string? EncryptionSalt, out string? WrappedRoomKey) Parameters Success bool History List<MessageDto> Error string PasswordRequired bool EncryptionSalt string WrappedRoomKey string Equals(JoinChannelResult?) public virtual bool Equals(JoinChannelResult? other) Parameters other JoinChannelResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(JoinChannelResult?, JoinChannelResult?) public static bool operator ==(JoinChannelResult? left, JoinChannelResult? right) Parameters left JoinChannelResult right JoinChannelResult Returns bool operator !=(JoinChannelResult?, JoinChannelResult?) public static bool operator !=(JoinChannelResult? left, JoinChannelResult? right) Parameters left JoinChannelResult right JoinChannelResult Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.KickRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.KickRequest.html",
    "title": "Class KickRequest | EchoHub Documentation",
    "summary": "Class KickRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record KickRequest : IEquatable<KickRequest> Inheritance object KickRequest Implements IEquatable<KickRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors KickRequest(KickRequest) protected KickRequest(KickRequest original) Parameters original KickRequest KickRequest(string?) public KickRequest(string? Reason = null) Parameters Reason string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Reason public string? Reason { get; init; } Property Value string Methods Deconstruct(out string?) public void Deconstruct(out string? Reason) Parameters Reason string Equals(KickRequest?) public virtual bool Equals(KickRequest? other) Parameters other KickRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(KickRequest?, KickRequest?) public static bool operator ==(KickRequest? left, KickRequest? right) Parameters left KickRequest right KickRequest Returns bool operator !=(KickRequest?, KickRequest?) public static bool operator !=(KickRequest? left, KickRequest? right) Parameters left KickRequest right KickRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.LoginRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.LoginRequest.html",
    "title": "Class LoginRequest | EchoHub Documentation",
    "summary": "Class LoginRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record LoginRequest : IEquatable<LoginRequest> Inheritance object LoginRequest Implements IEquatable<LoginRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors LoginRequest(LoginRequest) protected LoginRequest(LoginRequest original) Parameters original LoginRequest LoginRequest(string, string) public LoginRequest(string Username, string Password) Parameters Username string Password string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Password public string Password { get; init; } Property Value string Username public string Username { get; init; } Property Value string Methods Deconstruct(out string, out string) public void Deconstruct(out string Username, out string Password) Parameters Username string Password string Equals(LoginRequest?) public virtual bool Equals(LoginRequest? other) Parameters other LoginRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(LoginRequest?, LoginRequest?) public static bool operator ==(LoginRequest? left, LoginRequest? right) Parameters left LoginRequest right LoginRequest Returns bool operator !=(LoginRequest?, LoginRequest?) public static bool operator !=(LoginRequest? left, LoginRequest? right) Parameters left LoginRequest right LoginRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.LoginResponse.html": {
    "href": "api/core/EchoHub.Core.DTOs.LoginResponse.html",
    "title": "Class LoginResponse | EchoHub Documentation",
    "summary": "Class LoginResponse Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record LoginResponse : IEquatable<LoginResponse> Inheritance object LoginResponse Implements IEquatable<LoginResponse> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors LoginResponse(LoginResponse) protected LoginResponse(LoginResponse original) Parameters original LoginResponse LoginResponse(string, string, DateTimeOffset, string, string?, string?) public LoginResponse(string Token, string RefreshToken, DateTimeOffset ExpiresAt, string Username, string? DisplayName, string? NicknameColor) Parameters Token string RefreshToken string ExpiresAt DateTimeOffset Username string DisplayName string NicknameColor string Properties DisplayName public string? DisplayName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type ExpiresAt public DateTimeOffset ExpiresAt { get; init; } Property Value DateTimeOffset NicknameColor public string? NicknameColor { get; init; } Property Value string RefreshToken public string RefreshToken { get; init; } Property Value string Token public string Token { get; init; } Property Value string Username public string Username { get; init; } Property Value string Methods Deconstruct(out string, out string, out DateTimeOffset, out string, out string?, out string?) public void Deconstruct(out string Token, out string RefreshToken, out DateTimeOffset ExpiresAt, out string Username, out string? DisplayName, out string? NicknameColor) Parameters Token string RefreshToken string ExpiresAt DateTimeOffset Username string DisplayName string NicknameColor string Equals(LoginResponse?) public virtual bool Equals(LoginResponse? other) Parameters other LoginResponse Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(LoginResponse?, LoginResponse?) public static bool operator ==(LoginResponse? left, LoginResponse? right) Parameters left LoginResponse right LoginResponse Returns bool operator !=(LoginResponse?, LoginResponse?) public static bool operator !=(LoginResponse? left, LoginResponse? right) Parameters left LoginResponse right LoginResponse Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.MessageDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.MessageDto.html",
    "title": "Class MessageDto | EchoHub Documentation",
    "summary": "Class MessageDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record MessageDto : IEquatable<MessageDto> Inheritance object MessageDto Implements IEquatable<MessageDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors MessageDto(MessageDto) protected MessageDto(MessageDto original) Parameters original MessageDto MessageDto(Guid, string, string, string?, string, DateTimeOffset, List<AttachmentDto>?, List<EmbedDto>?, string?, ReplyRefDto?) public MessageDto(Guid Id, string Content, string SenderUsername, string? SenderNicknameColor, string ChannelName, DateTimeOffset SentAt, List<AttachmentDto>? Attachments = null, List<EmbedDto>? Embeds = null, string? SenderDisplayName = null, ReplyRefDto? ReplyTo = null) Parameters Id Guid Content string SenderUsername string SenderNicknameColor string ChannelName string SentAt DateTimeOffset Attachments List<AttachmentDto> Embeds List<EmbedDto> SenderDisplayName string ReplyTo ReplyRefDto Properties Attachments public List<AttachmentDto>? Attachments { get; init; } Property Value List<AttachmentDto> ChannelName public string ChannelName { get; init; } Property Value string Content public string Content { get; init; } Property Value string Embeds public List<EmbedDto>? Embeds { get; init; } Property Value List<EmbedDto> EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Id public Guid Id { get; init; } Property Value Guid ReplyTo public ReplyRefDto? ReplyTo { get; init; } Property Value ReplyRefDto SenderDisplayName public string? SenderDisplayName { get; init; } Property Value string SenderNicknameColor public string? SenderNicknameColor { get; init; } Property Value string SenderUsername public string SenderUsername { get; init; } Property Value string SentAt public DateTimeOffset SentAt { get; init; } Property Value DateTimeOffset Methods Deconstruct(out Guid, out string, out string, out string?, out string, out DateTimeOffset, out List<AttachmentDto>?, out List<EmbedDto>?, out string?, out ReplyRefDto?) public void Deconstruct(out Guid Id, out string Content, out string SenderUsername, out string? SenderNicknameColor, out string ChannelName, out DateTimeOffset SentAt, out List<AttachmentDto>? Attachments, out List<EmbedDto>? Embeds, out string? SenderDisplayName, out ReplyRefDto? ReplyTo) Parameters Id Guid Content string SenderUsername string SenderNicknameColor string ChannelName string SentAt DateTimeOffset Attachments List<AttachmentDto> Embeds List<EmbedDto> SenderDisplayName string ReplyTo ReplyRefDto Equals(MessageDto?) public virtual bool Equals(MessageDto? other) Parameters other MessageDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(MessageDto?, MessageDto?) public static bool operator ==(MessageDto? left, MessageDto? right) Parameters left MessageDto right MessageDto Returns bool operator !=(MessageDto?, MessageDto?) public static bool operator !=(MessageDto? left, MessageDto? right) Parameters left MessageDto right MessageDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.MuteRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.MuteRequest.html",
    "title": "Class MuteRequest | EchoHub Documentation",
    "summary": "Class MuteRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record MuteRequest : IEquatable<MuteRequest> Inheritance object MuteRequest Implements IEquatable<MuteRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors MuteRequest(MuteRequest) protected MuteRequest(MuteRequest original) Parameters original MuteRequest MuteRequest(string?, int?) public MuteRequest(string? Reason = null, int? DurationMinutes = null) Parameters Reason string DurationMinutes int? Properties DurationMinutes public int? DurationMinutes { get; init; } Property Value int? EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Reason public string? Reason { get; init; } Property Value string Methods Deconstruct(out string?, out int?) public void Deconstruct(out string? Reason, out int? DurationMinutes) Parameters Reason string DurationMinutes int? Equals(MuteRequest?) public virtual bool Equals(MuteRequest? other) Parameters other MuteRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(MuteRequest?, MuteRequest?) public static bool operator ==(MuteRequest? left, MuteRequest? right) Parameters left MuteRequest right MuteRequest Returns bool operator !=(MuteRequest?, MuteRequest?) public static bool operator !=(MuteRequest? left, MuteRequest? right) Parameters left MuteRequest right MuteRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.PaginatedResponse-1.html": {
    "href": "api/core/EchoHub.Core.DTOs.PaginatedResponse-1.html",
    "title": "Class PaginatedResponse<T> | EchoHub Documentation",
    "summary": "Class PaginatedResponse<T> Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record PaginatedResponse<T> : IEquatable<PaginatedResponse<T>> Type Parameters T Inheritance object PaginatedResponse<T> Implements IEquatable<PaginatedResponse<T>> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors PaginatedResponse(PaginatedResponse<T>) protected PaginatedResponse(PaginatedResponse<T> original) Parameters original PaginatedResponse<T> PaginatedResponse(List<T>, int, int, int) public PaginatedResponse(List<T> Items, int Total, int Offset, int Limit) Parameters Items List<T> Total int Offset int Limit int Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Items public List<T> Items { get; init; } Property Value List<T> Limit public int Limit { get; init; } Property Value int Offset public int Offset { get; init; } Property Value int Total public int Total { get; init; } Property Value int Methods Deconstruct(out List<T>, out int, out int, out int) public void Deconstruct(out List<T> Items, out int Total, out int Offset, out int Limit) Parameters Items List<T> Total int Offset int Limit int Equals(PaginatedResponse<T>?) public virtual bool Equals(PaginatedResponse<T>? other) Parameters other PaginatedResponse<T> Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(PaginatedResponse<T>?, PaginatedResponse<T>?) public static bool operator ==(PaginatedResponse<T>? left, PaginatedResponse<T>? right) Parameters left PaginatedResponse<T> right PaginatedResponse<T> Returns bool operator !=(PaginatedResponse<T>?, PaginatedResponse<T>?) public static bool operator !=(PaginatedResponse<T>? left, PaginatedResponse<T>? right) Parameters left PaginatedResponse<T> right PaginatedResponse<T> Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.RefreshRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.RefreshRequest.html",
    "title": "Class RefreshRequest | EchoHub Documentation",
    "summary": "Class RefreshRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record RefreshRequest : IEquatable<RefreshRequest> Inheritance object RefreshRequest Implements IEquatable<RefreshRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors RefreshRequest(RefreshRequest) protected RefreshRequest(RefreshRequest original) Parameters original RefreshRequest RefreshRequest(string) public RefreshRequest(string RefreshToken) Parameters RefreshToken string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type RefreshToken public string RefreshToken { get; init; } Property Value string Methods Deconstruct(out string) public void Deconstruct(out string RefreshToken) Parameters RefreshToken string Equals(RefreshRequest?) public virtual bool Equals(RefreshRequest? other) Parameters other RefreshRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(RefreshRequest?, RefreshRequest?) public static bool operator ==(RefreshRequest? left, RefreshRequest? right) Parameters left RefreshRequest right RefreshRequest Returns bool operator !=(RefreshRequest?, RefreshRequest?) public static bool operator !=(RefreshRequest? left, RefreshRequest? right) Parameters left RefreshRequest right RefreshRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.RegisterRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.RegisterRequest.html",
    "title": "Class RegisterRequest | EchoHub Documentation",
    "summary": "Class RegisterRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record RegisterRequest : IEquatable<RegisterRequest> Inheritance object RegisterRequest Implements IEquatable<RegisterRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors RegisterRequest(RegisterRequest) protected RegisterRequest(RegisterRequest original) Parameters original RegisterRequest RegisterRequest(string, string, string?, string?) public RegisterRequest(string Username, string Password, string? DisplayName = null, string? InviteCode = null) Parameters Username string Password string DisplayName string InviteCode string Properties DisplayName public string? DisplayName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type InviteCode public string? InviteCode { get; init; } Property Value string Password public string Password { get; init; } Property Value string Username public string Username { get; init; } Property Value string Methods Deconstruct(out string, out string, out string?, out string?) public void Deconstruct(out string Username, out string Password, out string? DisplayName, out string? InviteCode) Parameters Username string Password string DisplayName string InviteCode string Equals(RegisterRequest?) public virtual bool Equals(RegisterRequest? other) Parameters other RegisterRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(RegisterRequest?, RegisterRequest?) public static bool operator ==(RegisterRequest? left, RegisterRequest? right) Parameters left RegisterRequest right RegisterRequest Returns bool operator !=(RegisterRequest?, RegisterRequest?) public static bool operator !=(RegisterRequest? left, RegisterRequest? right) Parameters left RegisterRequest right RegisterRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.RekeyChannelRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.RekeyChannelRequest.html",
    "title": "Class RekeyChannelRequest | EchoHub Documentation",
    "summary": "Class RekeyChannelRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll Passphrase change for an encrypted channel: the client proves knowledge of the old passphrase (old auth key), then supplies the re-wrapped room key under the new one. public record RekeyChannelRequest : IEquatable<RekeyChannelRequest> Inheritance object RekeyChannelRequest Implements IEquatable<RekeyChannelRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors RekeyChannelRequest(RekeyChannelRequest) protected RekeyChannelRequest(RekeyChannelRequest original) Parameters original RekeyChannelRequest RekeyChannelRequest(string, string, string, string) Passphrase change for an encrypted channel: the client proves knowledge of the old passphrase (old auth key), then supplies the re-wrapped room key under the new one. public RekeyChannelRequest(string OldPassword, string NewPassword, string NewEncryptionSalt, string NewWrappedRoomKey) Parameters OldPassword string NewPassword string NewEncryptionSalt string NewWrappedRoomKey string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type NewEncryptionSalt public string NewEncryptionSalt { get; init; } Property Value string NewPassword public string NewPassword { get; init; } Property Value string NewWrappedRoomKey public string NewWrappedRoomKey { get; init; } Property Value string OldPassword public string OldPassword { get; init; } Property Value string Methods Deconstruct(out string, out string, out string, out string) public void Deconstruct(out string OldPassword, out string NewPassword, out string NewEncryptionSalt, out string NewWrappedRoomKey) Parameters OldPassword string NewPassword string NewEncryptionSalt string NewWrappedRoomKey string Equals(RekeyChannelRequest?) public virtual bool Equals(RekeyChannelRequest? other) Parameters other RekeyChannelRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(RekeyChannelRequest?, RekeyChannelRequest?) public static bool operator ==(RekeyChannelRequest? left, RekeyChannelRequest? right) Parameters left RekeyChannelRequest right RekeyChannelRequest Returns bool operator !=(RekeyChannelRequest?, RekeyChannelRequest?) public static bool operator !=(RekeyChannelRequest? left, RekeyChannelRequest? right) Parameters left RekeyChannelRequest right RekeyChannelRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ReplyRefDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.ReplyRefDto.html",
    "title": "Class ReplyRefDto | EchoHub Documentation",
    "summary": "Class ReplyRefDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll Reference to the message a reply targets. Content is treated exactly like message content on the wire: transport-encrypted, and for end-to-end encrypted rooms it is room ciphertext the client must decrypt (the server truncates only plaintext snippets). Null on a MessageDto when the original message no longer exists. public record ReplyRefDto : IEquatable<ReplyRefDto> Inheritance object ReplyRefDto Implements IEquatable<ReplyRefDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ReplyRefDto(ReplyRefDto) protected ReplyRefDto(ReplyRefDto original) Parameters original ReplyRefDto ReplyRefDto(Guid, string, string) Reference to the message a reply targets. Content is treated exactly like message content on the wire: transport-encrypted, and for end-to-end encrypted rooms it is room ciphertext the client must decrypt (the server truncates only plaintext snippets). Null on a MessageDto when the original message no longer exists. public ReplyRefDto(Guid MessageId, string SenderUsername, string Content) Parameters MessageId Guid SenderUsername string Content string Properties Content public string Content { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type MessageId public Guid MessageId { get; init; } Property Value Guid SenderUsername public string SenderUsername { get; init; } Property Value string Methods Deconstruct(out Guid, out string, out string) public void Deconstruct(out Guid MessageId, out string SenderUsername, out string Content) Parameters MessageId Guid SenderUsername string Content string Equals(ReplyRefDto?) public virtual bool Equals(ReplyRefDto? other) Parameters other ReplyRefDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ReplyRefDto?, ReplyRefDto?) public static bool operator ==(ReplyRefDto? left, ReplyRefDto? right) Parameters left ReplyRefDto right ReplyRefDto Returns bool operator !=(ReplyRefDto?, ReplyRefDto?) public static bool operator !=(ReplyRefDto? left, ReplyRefDto? right) Parameters left ReplyRefDto right ReplyRefDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.SendMessageRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.SendMessageRequest.html",
    "title": "Class SendMessageRequest | EchoHub Documentation",
    "summary": "Class SendMessageRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record SendMessageRequest : IEquatable<SendMessageRequest> Inheritance object SendMessageRequest Implements IEquatable<SendMessageRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors SendMessageRequest(SendMessageRequest) protected SendMessageRequest(SendMessageRequest original) Parameters original SendMessageRequest SendMessageRequest(string, string) public SendMessageRequest(string ChannelName, string Content) Parameters ChannelName string Content string Properties ChannelName public string ChannelName { get; init; } Property Value string Content public string Content { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Methods Deconstruct(out string, out string) public void Deconstruct(out string ChannelName, out string Content) Parameters ChannelName string Content string Equals(SendMessageRequest?) public virtual bool Equals(SendMessageRequest? other) Parameters other SendMessageRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(SendMessageRequest?, SendMessageRequest?) public static bool operator ==(SendMessageRequest? left, SendMessageRequest? right) Parameters left SendMessageRequest right SendMessageRequest Returns bool operator !=(SendMessageRequest?, SendMessageRequest?) public static bool operator !=(SendMessageRequest? left, SendMessageRequest? right) Parameters left SendMessageRequest right SendMessageRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.SendUrlRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.SendUrlRequest.html",
    "title": "Class SendUrlRequest | EchoHub Documentation",
    "summary": "Class SendUrlRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record SendUrlRequest : IEquatable<SendUrlRequest> Inheritance object SendUrlRequest Implements IEquatable<SendUrlRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors SendUrlRequest(SendUrlRequest) protected SendUrlRequest(SendUrlRequest original) Parameters original SendUrlRequest SendUrlRequest(string) public SendUrlRequest(string Url) Parameters Url string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Url public string Url { get; init; } Property Value string Methods Deconstruct(out string) public void Deconstruct(out string Url) Parameters Url string Equals(SendUrlRequest?) public virtual bool Equals(SendUrlRequest? other) Parameters other SendUrlRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(SendUrlRequest?, SendUrlRequest?) public static bool operator ==(SendUrlRequest? left, SendUrlRequest? right) Parameters left SendUrlRequest right SendUrlRequest Returns bool operator !=(SendUrlRequest?, SendUrlRequest?) public static bool operator !=(SendUrlRequest? left, SendUrlRequest? right) Parameters left SendUrlRequest right SendUrlRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.ServerStatusDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.ServerStatusDto.html",
    "title": "Class ServerStatusDto | EchoHub Documentation",
    "summary": "Class ServerStatusDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record ServerStatusDto : IEquatable<ServerStatusDto> Inheritance object ServerStatusDto Implements IEquatable<ServerStatusDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ServerStatusDto(ServerStatusDto) protected ServerStatusDto(ServerStatusDto original) Parameters original ServerStatusDto ServerStatusDto(string, string?, int, int, string) public ServerStatusDto(string Name, string? Description, int OnlineUsers, int TotalChannels, string RegistrationMode = \"open\") Parameters Name string Description string OnlineUsers int TotalChannels int RegistrationMode string Properties Description public string? Description { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Name public string Name { get; init; } Property Value string OnlineUsers public int OnlineUsers { get; init; } Property Value int RegistrationMode public string RegistrationMode { get; init; } Property Value string TotalChannels public int TotalChannels { get; init; } Property Value int Methods Deconstruct(out string, out string?, out int, out int, out string) public void Deconstruct(out string Name, out string? Description, out int OnlineUsers, out int TotalChannels, out string RegistrationMode) Parameters Name string Description string OnlineUsers int TotalChannels int RegistrationMode string Equals(ServerStatusDto?) public virtual bool Equals(ServerStatusDto? other) Parameters other ServerStatusDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ServerStatusDto?, ServerStatusDto?) public static bool operator ==(ServerStatusDto? left, ServerStatusDto? right) Parameters left ServerStatusDto right ServerStatusDto Returns bool operator !=(ServerStatusDto?, ServerStatusDto?) public static bool operator !=(ServerStatusDto? left, ServerStatusDto? right) Parameters left ServerStatusDto right ServerStatusDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.UpdateProfileRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.UpdateProfileRequest.html",
    "title": "Class UpdateProfileRequest | EchoHub Documentation",
    "summary": "Class UpdateProfileRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record UpdateProfileRequest : IEquatable<UpdateProfileRequest> Inheritance object UpdateProfileRequest Implements IEquatable<UpdateProfileRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors UpdateProfileRequest(UpdateProfileRequest) protected UpdateProfileRequest(UpdateProfileRequest original) Parameters original UpdateProfileRequest UpdateProfileRequest(string?, string?, string?) public UpdateProfileRequest(string? DisplayName = null, string? Bio = null, string? NicknameColor = null) Parameters DisplayName string Bio string NicknameColor string Properties Bio public string? Bio { get; init; } Property Value string DisplayName public string? DisplayName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type NicknameColor public string? NicknameColor { get; init; } Property Value string Methods Deconstruct(out string?, out string?, out string?) public void Deconstruct(out string? DisplayName, out string? Bio, out string? NicknameColor) Parameters DisplayName string Bio string NicknameColor string Equals(UpdateProfileRequest?) public virtual bool Equals(UpdateProfileRequest? other) Parameters other UpdateProfileRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(UpdateProfileRequest?, UpdateProfileRequest?) public static bool operator ==(UpdateProfileRequest? left, UpdateProfileRequest? right) Parameters left UpdateProfileRequest right UpdateProfileRequest Returns bool operator !=(UpdateProfileRequest?, UpdateProfileRequest?) public static bool operator !=(UpdateProfileRequest? left, UpdateProfileRequest? right) Parameters left UpdateProfileRequest right UpdateProfileRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.UpdateStatusRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.UpdateStatusRequest.html",
    "title": "Class UpdateStatusRequest | EchoHub Documentation",
    "summary": "Class UpdateStatusRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record UpdateStatusRequest : IEquatable<UpdateStatusRequest> Inheritance object UpdateStatusRequest Implements IEquatable<UpdateStatusRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors UpdateStatusRequest(UpdateStatusRequest) protected UpdateStatusRequest(UpdateStatusRequest original) Parameters original UpdateStatusRequest UpdateStatusRequest(UserStatus, string?) public UpdateStatusRequest(UserStatus Status, string? StatusMessage = null) Parameters Status UserStatus StatusMessage string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Status public UserStatus Status { get; init; } Property Value UserStatus StatusMessage public string? StatusMessage { get; init; } Property Value string Methods Deconstruct(out UserStatus, out string?) public void Deconstruct(out UserStatus Status, out string? StatusMessage) Parameters Status UserStatus StatusMessage string Equals(UpdateStatusRequest?) public virtual bool Equals(UpdateStatusRequest? other) Parameters other UpdateStatusRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(UpdateStatusRequest?, UpdateStatusRequest?) public static bool operator ==(UpdateStatusRequest? left, UpdateStatusRequest? right) Parameters left UpdateStatusRequest right UpdateStatusRequest Returns bool operator !=(UpdateStatusRequest?, UpdateStatusRequest?) public static bool operator !=(UpdateStatusRequest? left, UpdateStatusRequest? right) Parameters left UpdateStatusRequest right UpdateStatusRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.UpdateTopicRequest.html": {
    "href": "api/core/EchoHub.Core.DTOs.UpdateTopicRequest.html",
    "title": "Class UpdateTopicRequest | EchoHub Documentation",
    "summary": "Class UpdateTopicRequest Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record UpdateTopicRequest : IEquatable<UpdateTopicRequest> Inheritance object UpdateTopicRequest Implements IEquatable<UpdateTopicRequest> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors UpdateTopicRequest(UpdateTopicRequest) protected UpdateTopicRequest(UpdateTopicRequest original) Parameters original UpdateTopicRequest UpdateTopicRequest(string?) public UpdateTopicRequest(string? Topic) Parameters Topic string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Topic public string? Topic { get; init; } Property Value string Methods Deconstruct(out string?) public void Deconstruct(out string? Topic) Parameters Topic string Equals(UpdateTopicRequest?) public virtual bool Equals(UpdateTopicRequest? other) Parameters other UpdateTopicRequest Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(UpdateTopicRequest?, UpdateTopicRequest?) public static bool operator ==(UpdateTopicRequest? left, UpdateTopicRequest? right) Parameters left UpdateTopicRequest right UpdateTopicRequest Returns bool operator !=(UpdateTopicRequest?, UpdateTopicRequest?) public static bool operator !=(UpdateTopicRequest? left, UpdateTopicRequest? right) Parameters left UpdateTopicRequest right UpdateTopicRequest Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.UserDataExportDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.UserDataExportDto.html",
    "title": "Class UserDataExportDto | EchoHub Documentation",
    "summary": "Class UserDataExportDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll Everything the server holds about a user, as stored. For end-to-end encrypted rooms the message content in here is room ciphertext — the server cannot include plaintext it never had. public record UserDataExportDto : IEquatable<UserDataExportDto> Inheritance object UserDataExportDto Implements IEquatable<UserDataExportDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors UserDataExportDto(UserDataExportDto) protected UserDataExportDto(UserDataExportDto original) Parameters original UserDataExportDto UserDataExportDto(DateTimeOffset, string, UserProfileDto, List<ExportedMessageDto>, List<ExportedAttachmentDto>) Everything the server holds about a user, as stored. For end-to-end encrypted rooms the message content in here is room ciphertext — the server cannot include plaintext it never had. public UserDataExportDto(DateTimeOffset ExportedAt, string ServerName, UserProfileDto Profile, List<ExportedMessageDto> Messages, List<ExportedAttachmentDto> Attachments) Parameters ExportedAt DateTimeOffset ServerName string Profile UserProfileDto Messages List<ExportedMessageDto> Attachments List<ExportedAttachmentDto> Properties Attachments public List<ExportedAttachmentDto> Attachments { get; init; } Property Value List<ExportedAttachmentDto> EqualityContract protected virtual Type EqualityContract { get; } Property Value Type ExportedAt public DateTimeOffset ExportedAt { get; init; } Property Value DateTimeOffset Messages public List<ExportedMessageDto> Messages { get; init; } Property Value List<ExportedMessageDto> Profile public UserProfileDto Profile { get; init; } Property Value UserProfileDto ServerName public string ServerName { get; init; } Property Value string Methods Deconstruct(out DateTimeOffset, out string, out UserProfileDto, out List<ExportedMessageDto>, out List<ExportedAttachmentDto>) public void Deconstruct(out DateTimeOffset ExportedAt, out string ServerName, out UserProfileDto Profile, out List<ExportedMessageDto> Messages, out List<ExportedAttachmentDto> Attachments) Parameters ExportedAt DateTimeOffset ServerName string Profile UserProfileDto Messages List<ExportedMessageDto> Attachments List<ExportedAttachmentDto> Equals(UserDataExportDto?) public virtual bool Equals(UserDataExportDto? other) Parameters other UserDataExportDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(UserDataExportDto?, UserDataExportDto?) public static bool operator ==(UserDataExportDto? left, UserDataExportDto? right) Parameters left UserDataExportDto right UserDataExportDto Returns bool operator !=(UserDataExportDto?, UserDataExportDto?) public static bool operator !=(UserDataExportDto? left, UserDataExportDto? right) Parameters left UserDataExportDto right UserDataExportDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.UserDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.UserDto.html",
    "title": "Class UserDto | EchoHub Documentation",
    "summary": "Class UserDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record UserDto : IEquatable<UserDto> Inheritance object UserDto Implements IEquatable<UserDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors UserDto(UserDto) protected UserDto(UserDto original) Parameters original UserDto UserDto(Guid, string, string?, string?, UserStatus, DateTimeOffset) public UserDto(Guid Id, string Username, string? DisplayName, string? NicknameColor, UserStatus Status, DateTimeOffset LastSeenAt) Parameters Id Guid Username string DisplayName string NicknameColor string Status UserStatus LastSeenAt DateTimeOffset Properties DisplayName public string? DisplayName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Id public Guid Id { get; init; } Property Value Guid LastSeenAt public DateTimeOffset LastSeenAt { get; init; } Property Value DateTimeOffset NicknameColor public string? NicknameColor { get; init; } Property Value string Status public UserStatus Status { get; init; } Property Value UserStatus Username public string Username { get; init; } Property Value string Methods Deconstruct(out Guid, out string, out string?, out string?, out UserStatus, out DateTimeOffset) public void Deconstruct(out Guid Id, out string Username, out string? DisplayName, out string? NicknameColor, out UserStatus Status, out DateTimeOffset LastSeenAt) Parameters Id Guid Username string DisplayName string NicknameColor string Status UserStatus LastSeenAt DateTimeOffset Equals(UserDto?) public virtual bool Equals(UserDto? other) Parameters other UserDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(UserDto?, UserDto?) public static bool operator ==(UserDto? left, UserDto? right) Parameters left UserDto right UserDto Returns bool operator !=(UserDto?, UserDto?) public static bool operator !=(UserDto? left, UserDto? right) Parameters left UserDto right UserDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.UserError.html": {
    "href": "api/core/EchoHub.Core.DTOs.UserError.html",
    "title": "Enum UserError | EchoHub Documentation",
    "summary": "Enum UserError Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public enum UserError Fields AlreadyExists = 1 Banned = 4 InvalidCredentials = 3 NotFound = 2 ValidationFailed = 0"
  },
  "api/core/EchoHub.Core.DTOs.UserOperationResult.html": {
    "href": "api/core/EchoHub.Core.DTOs.UserOperationResult.html",
    "title": "Class UserOperationResult | EchoHub Documentation",
    "summary": "Class UserOperationResult Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record UserOperationResult : IEquatable<UserOperationResult> Inheritance object UserOperationResult Implements IEquatable<UserOperationResult> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors UserOperationResult(UserOperationResult) protected UserOperationResult(UserOperationResult original) Parameters original UserOperationResult UserOperationResult(UserProfileDto?, UserError?, string?) public UserOperationResult(UserProfileDto? User, UserError? Error, string? ErrorMessage) Parameters User UserProfileDto Error UserError? ErrorMessage string Properties EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Error public UserError? Error { get; init; } Property Value UserError? ErrorMessage public string? ErrorMessage { get; init; } Property Value string IsSuccess public bool IsSuccess { get; } Property Value bool User public UserProfileDto? User { get; init; } Property Value UserProfileDto Methods Deconstruct(out UserProfileDto?, out UserError?, out string?) public void Deconstruct(out UserProfileDto? User, out UserError? Error, out string? ErrorMessage) Parameters User UserProfileDto Error UserError? ErrorMessage string Equals(UserOperationResult?) public virtual bool Equals(UserOperationResult? other) Parameters other UserOperationResult Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool Fail(UserError, string) public static UserOperationResult Fail(UserError error, string message) Parameters error UserError message string Returns UserOperationResult GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool Success(UserProfileDto) public static UserOperationResult Success(UserProfileDto user) Parameters user UserProfileDto Returns UserOperationResult ToString() public override string ToString() Returns string Operators operator ==(UserOperationResult?, UserOperationResult?) public static bool operator ==(UserOperationResult? left, UserOperationResult? right) Parameters left UserOperationResult right UserOperationResult Returns bool operator !=(UserOperationResult?, UserOperationResult?) public static bool operator !=(UserOperationResult? left, UserOperationResult? right) Parameters left UserOperationResult right UserOperationResult Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.UserPresenceDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.UserPresenceDto.html",
    "title": "Class UserPresenceDto | EchoHub Documentation",
    "summary": "Class UserPresenceDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record UserPresenceDto : IEquatable<UserPresenceDto> Inheritance object UserPresenceDto Implements IEquatable<UserPresenceDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors UserPresenceDto(UserPresenceDto) protected UserPresenceDto(UserPresenceDto original) Parameters original UserPresenceDto UserPresenceDto(string, string?, string?, UserStatus, string?, ServerRole, bool) public UserPresenceDto(string Username, string? DisplayName, string? NicknameColor, UserStatus Status, string? StatusMessage, ServerRole Role, bool IsIrc = false) Parameters Username string DisplayName string NicknameColor string Status UserStatus StatusMessage string Role ServerRole IsIrc bool Properties DisplayName public string? DisplayName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type IsIrc public bool IsIrc { get; init; } Property Value bool NicknameColor public string? NicknameColor { get; init; } Property Value string Role public ServerRole Role { get; init; } Property Value ServerRole Status public UserStatus Status { get; init; } Property Value UserStatus StatusMessage public string? StatusMessage { get; init; } Property Value string Username public string Username { get; init; } Property Value string Methods Deconstruct(out string, out string?, out string?, out UserStatus, out string?, out ServerRole, out bool) public void Deconstruct(out string Username, out string? DisplayName, out string? NicknameColor, out UserStatus Status, out string? StatusMessage, out ServerRole Role, out bool IsIrc) Parameters Username string DisplayName string NicknameColor string Status UserStatus StatusMessage string Role ServerRole IsIrc bool Equals(UserPresenceDto?) public virtual bool Equals(UserPresenceDto? other) Parameters other UserPresenceDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(UserPresenceDto?, UserPresenceDto?) public static bool operator ==(UserPresenceDto? left, UserPresenceDto? right) Parameters left UserPresenceDto right UserPresenceDto Returns bool operator !=(UserPresenceDto?, UserPresenceDto?) public static bool operator !=(UserPresenceDto? left, UserPresenceDto? right) Parameters left UserPresenceDto right UserPresenceDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.UserProfileDto.html": {
    "href": "api/core/EchoHub.Core.DTOs.UserProfileDto.html",
    "title": "Class UserProfileDto | EchoHub Documentation",
    "summary": "Class UserProfileDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public record UserProfileDto : IEquatable<UserProfileDto> Inheritance object UserProfileDto Implements IEquatable<UserProfileDto> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors UserProfileDto(UserProfileDto) protected UserProfileDto(UserProfileDto original) Parameters original UserProfileDto UserProfileDto(Guid, string, string?, string?, string?, string?, UserStatus, string?, ServerRole, DateTimeOffset, DateTimeOffset) public UserProfileDto(Guid Id, string Username, string? DisplayName, string? Bio, string? NicknameColor, string? AvatarAscii, UserStatus Status, string? StatusMessage, ServerRole Role, DateTimeOffset CreatedAt, DateTimeOffset LastSeenAt) Parameters Id Guid Username string DisplayName string Bio string NicknameColor string AvatarAscii string Status UserStatus StatusMessage string Role ServerRole CreatedAt DateTimeOffset LastSeenAt DateTimeOffset Properties AvatarAscii public string? AvatarAscii { get; init; } Property Value string Bio public string? Bio { get; init; } Property Value string CreatedAt public DateTimeOffset CreatedAt { get; init; } Property Value DateTimeOffset DisplayName public string? DisplayName { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Id public Guid Id { get; init; } Property Value Guid LastSeenAt public DateTimeOffset LastSeenAt { get; init; } Property Value DateTimeOffset NicknameColor public string? NicknameColor { get; init; } Property Value string Role public ServerRole Role { get; init; } Property Value ServerRole Status public UserStatus Status { get; init; } Property Value UserStatus StatusMessage public string? StatusMessage { get; init; } Property Value string Username public string Username { get; init; } Property Value string Methods Deconstruct(out Guid, out string, out string?, out string?, out string?, out string?, out UserStatus, out string?, out ServerRole, out DateTimeOffset, out DateTimeOffset) public void Deconstruct(out Guid Id, out string Username, out string? DisplayName, out string? Bio, out string? NicknameColor, out string? AvatarAscii, out UserStatus Status, out string? StatusMessage, out ServerRole Role, out DateTimeOffset CreatedAt, out DateTimeOffset LastSeenAt) Parameters Id Guid Username string DisplayName string Bio string NicknameColor string AvatarAscii string Status UserStatus StatusMessage string Role ServerRole CreatedAt DateTimeOffset LastSeenAt DateTimeOffset Equals(UserProfileDto?) public virtual bool Equals(UserProfileDto? other) Parameters other UserProfileDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(UserProfileDto?, UserProfileDto?) public static bool operator ==(UserProfileDto? left, UserProfileDto? right) Parameters left UserProfileDto right UserProfileDto Returns bool operator !=(UserProfileDto?, UserProfileDto?) public static bool operator !=(UserProfileDto? left, UserProfileDto? right) Parameters left UserProfileDto right UserProfileDto Returns bool"
  },
  "api/core/EchoHub.Core.DTOs.html": {
    "href": "api/core/EchoHub.Core.DTOs.html",
    "title": "Namespace EchoHub.Core.DTOs | EchoHub Documentation",
    "summary": "Namespace EchoHub.Core.DTOs Classes ApiResponse ApiResponse<T> AssignRoleRequest AttachmentDto A file attached to a message. AsciiPreview holds the color-tag art for images (null otherwise). For end-to-end encrypted channels the content behind Url and the preview are ciphertext the server cannot read. AvatarUploadResponse BanRequest ChannelCryptoDto Public crypto metadata for a channel — enough for a client to derive its join credential from a passphrase. Never includes the wrapped room key. ChannelDto ChannelMetaDto Human-facing summary of a channel (the /meta command). For encrypted channels the server still knows these figures — count, timestamps, and stored blob sizes — even though it cannot read the content itself. EstimatedSizeBytes is the sum of stored attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total. ChannelOperationResult CreateChannelRequest CreateInviteRequest DeleteAccountRequest Password re-confirmation for destructive self-service account actions. EmbedDto EncryptionKeyResponse ErrorResponse ExportedAttachmentDto ExportedMessageDto InviteDto JoinChannelResult KickRequest LoginRequest LoginResponse MessageDto MuteRequest PaginatedResponse<T> RefreshRequest RegisterRequest RekeyChannelRequest Passphrase change for an encrypted channel: the client proves knowledge of the old passphrase (old auth key), then supplies the re-wrapped room key under the new one. ReplyRefDto Reference to the message a reply targets. Content is treated exactly like message content on the wire: transport-encrypted, and for end-to-end encrypted rooms it is room ciphertext the client must decrypt (the server truncates only plaintext snippets). Null on a MessageDto when the original message no longer exists. SendMessageRequest SendUrlRequest ServerStatusDto UpdateProfileRequest UpdateStatusRequest UpdateTopicRequest UserDataExportDto Everything the server holds about a user, as stored. For end-to-end encrypted rooms the message content in here is room ciphertext — the server cannot include plaintext it never had. UserDto UserOperationResult UserPresenceDto UserProfileDto Enums ChannelError UserError"
  },
  "api/core/EchoHub.Core.Models.Attachment.html": {
    "href": "api/core/EchoHub.Core.Models.Attachment.html",
    "title": "Class Attachment | EchoHub Documentation",
    "summary": "Class Attachment Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll A file attached to a message (image, audio, or any file). A message may carry zero or more attachments alongside its text content (Discord-style). public class Attachment Inheritance object Attachment Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors Attachment() public Attachment() Properties AsciiPreview Rendered ASCII-art preview for images (color-tag format). Null for audio/files. Stored encrypted-at-rest when database encryption is enabled, and room-encrypted for end-to-end encrypted channels. public string? AsciiPreview { get; set; } Property Value string FileName public required string FileName { get; set; } Property Value string FileSize Stored blob size in bytes (ciphertext size for encrypted channels). public long FileSize { get; set; } Property Value long Id public Guid Id { get; set; } Property Value Guid Kind public AttachmentKind Kind { get; set; } Property Value AttachmentKind Message public Message? Message { get; set; } Property Value Message MessageId public Guid MessageId { get; set; } Property Value Guid Url Relative download URL, e.g. /api/files/{fileId}. public required string Url { get; set; } Property Value string"
  },
  "api/core/EchoHub.Core.Models.AttachmentKind.html": {
    "href": "api/core/EchoHub.Core.Models.AttachmentKind.html",
    "title": "Enum AttachmentKind | EchoHub Documentation",
    "summary": "Enum AttachmentKind Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll The kind of a message attachment. Determines how the client renders it (ASCII preview for images, a play affordance for audio, a download line for files). public enum AttachmentKind Fields Audio = 1 File = 2 Image = 0"
  },
  "api/core/EchoHub.Core.Models.Channel.html": {
    "href": "api/core/EchoHub.Core.Models.Channel.html",
    "title": "Class Channel | EchoHub Documentation",
    "summary": "Class Channel Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll public class Channel Inheritance object Channel Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors Channel() public Channel() Properties CreatedAt public DateTimeOffset CreatedAt { get; set; } Property Value DateTimeOffset CreatedByUserId public Guid CreatedByUserId { get; set; } Property Value Guid EncryptionSalt public string? EncryptionSalt { get; set; } Property Value string Id public Guid Id { get; set; } Property Value Guid IsPublic public bool IsPublic { get; set; } Property Value bool IsSystem public bool IsSystem { get; set; } Property Value bool Messages public List<Message> Messages { get; set; } Property Value List<Message> Name public required string Name { get; set; } Property Value string PasswordHash public string? PasswordHash { get; set; } Property Value string Topic public string? Topic { get; set; } Property Value string WrappedRoomKey public string? WrappedRoomKey { get; set; } Property Value string"
  },
  "api/core/EchoHub.Core.Models.ChannelMembership.html": {
    "href": "api/core/EchoHub.Core.Models.ChannelMembership.html",
    "title": "Class ChannelMembership | EchoHub Documentation",
    "summary": "Class ChannelMembership Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll public class ChannelMembership Inheritance object ChannelMembership Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelMembership() public ChannelMembership() Properties ChannelId public Guid ChannelId { get; set; } Property Value Guid JoinedAt public DateTimeOffset JoinedAt { get; set; } Property Value DateTimeOffset UserId public Guid UserId { get; set; } Property Value Guid"
  },
  "api/core/EchoHub.Core.Models.InviteCode.html": {
    "href": "api/core/EchoHub.Core.Models.InviteCode.html",
    "title": "Class InviteCode | EchoHub Documentation",
    "summary": "Class InviteCode Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll A registration invite code. When the server runs with Server:Registration = \"invite\", new accounts (REST and IRC alike) require a valid, unexpired, not-fully-used code. public class InviteCode Inheritance object InviteCode Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors InviteCode() public InviteCode() Properties Code public required string Code { get; set; } Property Value string CreatedAt public DateTimeOffset CreatedAt { get; set; } Property Value DateTimeOffset CreatedByUserId public Guid CreatedByUserId { get; set; } Property Value Guid CreatedByUsername public string CreatedByUsername { get; set; } Property Value string ExpiresAt Null means the code never expires. public DateTimeOffset? ExpiresAt { get; set; } Property Value DateTimeOffset? Id public Guid Id { get; set; } Property Value Guid MaxUses public int MaxUses { get; set; } Property Value int UseCount public int UseCount { get; set; } Property Value int"
  },
  "api/core/EchoHub.Core.Models.Message.html": {
    "href": "api/core/EchoHub.Core.Models.Message.html",
    "title": "Class Message | EchoHub Documentation",
    "summary": "Class Message Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll public class Message Inheritance object Message Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors Message() public Message() Properties AttachmentFileName public string? AttachmentFileName { get; set; } Property Value string AttachmentFileSize public long? AttachmentFileSize { get; set; } Property Value long? AttachmentUrl public string? AttachmentUrl { get; set; } Property Value string Attachments Files attached to this message. Empty for a plain text message. public List<Attachment> Attachments { get; set; } Property Value List<Attachment> Channel public Channel? Channel { get; set; } Property Value Channel ChannelId public Guid ChannelId { get; set; } Property Value Guid Content The message text/caption. May be empty when the message only carries attachments. public required string Content { get; set; } Property Value string EmbedJson public string? EmbedJson { get; set; } Property Value string Id public Guid Id { get; set; } Property Value Guid ReplyToMessageId Message this one replies to, if any. The target may since have been deleted. public Guid? ReplyToMessageId { get; set; } Property Value Guid? SenderUserId public Guid SenderUserId { get; set; } Property Value Guid SenderUsername public required string SenderUsername { get; set; } Property Value string SentAt public DateTimeOffset SentAt { get; set; } Property Value DateTimeOffset Type public MessageType Type { get; set; } Property Value MessageType"
  },
  "api/core/EchoHub.Core.Models.MessageType.html": {
    "href": "api/core/EchoHub.Core.Models.MessageType.html",
    "title": "Enum MessageType | EchoHub Documentation",
    "summary": "Enum MessageType Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll public enum MessageType Fields Audio = 3 File = 2 Image = 1 Text = 0"
  },
  "api/core/EchoHub.Core.Models.RefreshToken.html": {
    "href": "api/core/EchoHub.Core.Models.RefreshToken.html",
    "title": "Class RefreshToken | EchoHub Documentation",
    "summary": "Class RefreshToken Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll public class RefreshToken Inheritance object RefreshToken Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors RefreshToken() public RefreshToken() Properties CreatedAt public DateTimeOffset CreatedAt { get; set; } Property Value DateTimeOffset ExpiresAt public DateTimeOffset ExpiresAt { get; set; } Property Value DateTimeOffset Id public Guid Id { get; set; } Property Value Guid IsActive public bool IsActive { get; } Property Value bool IsExpired public bool IsExpired { get; } Property Value bool IsRevoked public bool IsRevoked { get; } Property Value bool RevokedAt public DateTimeOffset? RevokedAt { get; set; } Property Value DateTimeOffset? TokenHash public required string TokenHash { get; set; } Property Value string User public User? User { get; set; } Property Value User UserId public Guid UserId { get; set; } Property Value Guid"
  },
  "api/core/EchoHub.Core.Models.ServerRole.html": {
    "href": "api/core/EchoHub.Core.Models.ServerRole.html",
    "title": "Enum ServerRole | EchoHub Documentation",
    "summary": "Enum ServerRole Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll public enum ServerRole Fields Admin = 2 Member = 0 Mod = 1 Owner = 3"
  },
  "api/core/EchoHub.Core.Models.ServerStatsReport.html": {
    "href": "api/core/EchoHub.Core.Models.ServerStatsReport.html",
    "title": "Class ServerStatsReport | EchoHub Documentation",
    "summary": "Class ServerStatsReport Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll A snapshot of server activity over one reporting window, produced periodically by the stats-report background job. Each report is logged as pretty-printed JSON and persisted for historical trend analysis. The window is \"since the previous report\" (or since startup for the first report of a run). public class ServerStatsReport Inheritance object ServerStatsReport Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerStatsReport() public ServerStatsReport() Properties ActiveMembers Distinct users who sent at least one message during the window. public int ActiveMembers { get; set; } Property Value int Bans Users banned during the window. public int Bans { get; set; } Property Value int BytesUploaded Total bytes across all attachments uploaded during the window. public long BytesUploaded { get; set; } Property Value long Connections Session connects during the window (per-connection, across SignalR + IRC). public int Connections { get; set; } Property Value int Disconnections Session disconnects during the window (\"members left\" sessions). public int Disconnections { get; set; } Property Value int FilesUploaded Attachments (files/images/audio) uploaded during the window. public int FilesUploaded { get; set; } Property Value int GeneratedAt When this report was generated (equals PeriodEnd). public DateTimeOffset GeneratedAt { get; set; } Property Value DateTimeOffset Id public Guid Id { get; set; } Property Value Guid Kicks Users kicked during the window. public int Kicks { get; set; } Property Value int MessagesSent Messages sent during the window. public int MessagesSent { get; set; } Property Value int NewMembers Accounts registered during the window (\"new members joined\"). public int NewMembers { get; set; } Property Value int OnlineNow Distinct users online at window end. public int OnlineNow { get; set; } Property Value int PeakOnline Peak distinct users online observed during the window. public int PeakOnline { get; set; } Property Value int PeriodEnd End of the reporting window. public DateTimeOffset PeriodEnd { get; set; } Property Value DateTimeOffset PeriodStart Start of the reporting window. public DateTimeOffset PeriodStart { get; set; } Property Value DateTimeOffset TotalMembers Total registered (unique) members at window end. public int TotalMembers { get; set; } Property Value int WindowHours Length of the reporting window in hours. public double WindowHours { get; set; } Property Value double"
  },
  "api/core/EchoHub.Core.Models.User.html": {
    "href": "api/core/EchoHub.Core.Models.User.html",
    "title": "Class User | EchoHub Documentation",
    "summary": "Class User Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll public class User Inheritance object User Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors User() public User() Properties AvatarAscii public string? AvatarAscii { get; set; } Property Value string Bio public string? Bio { get; set; } Property Value string CreatedAt public DateTimeOffset CreatedAt { get; set; } Property Value DateTimeOffset DisplayName public string? DisplayName { get; set; } Property Value string Id public Guid Id { get; set; } Property Value Guid IsBanned public bool IsBanned { get; set; } Property Value bool IsMuted public bool IsMuted { get; set; } Property Value bool LastSeenAt public DateTimeOffset LastSeenAt { get; set; } Property Value DateTimeOffset MutedUntil public DateTimeOffset? MutedUntil { get; set; } Property Value DateTimeOffset? NicknameColor public string? NicknameColor { get; set; } Property Value string PasswordHash public required string PasswordHash { get; set; } Property Value string Role public ServerRole Role { get; set; } Property Value ServerRole Status public UserStatus Status { get; set; } Property Value UserStatus StatusMessage public string? StatusMessage { get; set; } Property Value string Username public required string Username { get; set; } Property Value string"
  },
  "api/core/EchoHub.Core.Models.UserStatus.html": {
    "href": "api/core/EchoHub.Core.Models.UserStatus.html",
    "title": "Enum UserStatus | EchoHub Documentation",
    "summary": "Enum UserStatus Namespace EchoHub.Core.Models Assembly EchoHub.Core.dll public enum UserStatus Fields Away = 1 DoNotDisturb = 2 Invisible = 3 Online = 0"
  },
  "api/core/EchoHub.Core.Models.html": {
    "href": "api/core/EchoHub.Core.Models.html",
    "title": "Namespace EchoHub.Core.Models | EchoHub Documentation",
    "summary": "Namespace EchoHub.Core.Models Classes Attachment A file attached to a message (image, audio, or any file). A message may carry zero or more attachments alongside its text content (Discord-style). Channel ChannelMembership InviteCode A registration invite code. When the server runs with Server:Registration = \"invite\", new accounts (REST and IRC alike) require a valid, unexpired, not-fully-used code. Message RefreshToken ServerStatsReport A snapshot of server activity over one reporting window, produced periodically by the stats-report background job. Each report is logged as pretty-printed JSON and persisted for historical trend analysis. The window is \"since the previous report\" (or since startup for the first report of a run). User Enums AttachmentKind The kind of a message attachment. Determines how the client renders it (ASCII preview for images, a play affordance for audio, a download line for files). MessageType ServerRole UserStatus"
  },
  "api/core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html": {
    "href": "api/core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html",
    "title": "Class RoomCrypto.DerivedKeys | EchoHub Documentation",
    "summary": "Class RoomCrypto.DerivedKeys Namespace EchoHub.Core.Security Assembly EchoHub.Core.dll public sealed record RoomCrypto.DerivedKeys : IEquatable<RoomCrypto.DerivedKeys> Inheritance object RoomCrypto.DerivedKeys Implements IEquatable<RoomCrypto.DerivedKeys> Inherited Members object.GetType() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors DerivedKeys(string, byte[]) public DerivedKeys(string AuthKeyHex, byte[] KeyEncryptionKey) Parameters AuthKeyHex string KeyEncryptionKey byte[] Properties AuthKeyHex public string AuthKeyHex { get; init; } Property Value string KeyEncryptionKey public byte[] KeyEncryptionKey { get; init; } Property Value byte[] Methods Deconstruct(out string, out byte[]) public void Deconstruct(out string AuthKeyHex, out byte[] KeyEncryptionKey) Parameters AuthKeyHex string KeyEncryptionKey byte[] Equals(DerivedKeys?) public bool Equals(RoomCrypto.DerivedKeys? other) Parameters other RoomCrypto.DerivedKeys Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int ToString() public override string ToString() Returns string Operators operator ==(DerivedKeys?, DerivedKeys?) public static bool operator ==(RoomCrypto.DerivedKeys? left, RoomCrypto.DerivedKeys? right) Parameters left RoomCrypto.DerivedKeys right RoomCrypto.DerivedKeys Returns bool operator !=(DerivedKeys?, DerivedKeys?) public static bool operator !=(RoomCrypto.DerivedKeys? left, RoomCrypto.DerivedKeys? right) Parameters left RoomCrypto.DerivedKeys right RoomCrypto.DerivedKeys Returns bool"
  },
  "api/core/EchoHub.Core.Security.RoomCrypto.html": {
    "href": "api/core/EchoHub.Core.Security.RoomCrypto.html",
    "title": "Class RoomCrypto | EchoHub Documentation",
    "summary": "Class RoomCrypto Namespace EchoHub.Core.Security Assembly EchoHub.Core.dll Client-side envelope encryption for private (end-to-end encrypted) channels. Design: at creation the client generates a random 256-bit room content key (RCK) that encrypts all room content. The RCK is stored on the server wrapped (AES-GCM encrypted) by a key derived from the passphrase, next to a BCrypt hash of a separately derived auth key used as the join gate. The passphrase, the key-encryption key, and the RCK never leave the client, so the server can gate joins and count/measure content without being able to read it. Changing the passphrase only re-wraps the RCK — history is never re-encrypted. Derivation: PBKDF2-SHA256(passphrase, salt, 210000 iterations) → 64 bytes; first 32 bytes are the auth key (sent to the server as lowercase hex), last 32 bytes are the key-encryption key (never sent). public static class RoomCrypto Inheritance object RoomCrypto Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields CiphertextPrefix public const string CiphertextPrefix = \"$RC1$\" Field Value string Methods DecryptBytes(byte[], byte[]) Decrypts a blob produced by EncryptBytes(byte[], byte[]). Throws System.Security.Cryptography.CryptographicException on key mismatch. public static byte[] DecryptBytes(byte[] blob, byte[] key) Parameters blob byte[] key byte[] Returns byte[] DeriveKeys(string, byte[]) Derives the auth key (join gate credential) and key-encryption key from a passphrase. public static RoomCrypto.DerivedKeys DeriveKeys(string passphrase, byte[] salt) Parameters passphrase string salt byte[] Returns RoomCrypto.DerivedKeys EncryptBytes(byte[], byte[]) Encrypts a binary blob (file contents) with the room key: nonce||tag||ciphertext. public static byte[] EncryptBytes(byte[] plaintext, byte[] key) Parameters plaintext byte[] key byte[] Returns byte[] EncryptText(string, byte[]) Encrypts UTF-8 text with the room key. Output: $RC1$base64(nonce||tag||ciphertext). public static string EncryptText(string plaintext, byte[] key) Parameters plaintext string key byte[] Returns string GenerateRoomKey() public static byte[] GenerateRoomKey() Returns byte[] GenerateSalt() public static byte[] GenerateSalt() Returns byte[] IsRoomCiphertext(string?) public static bool IsRoomCiphertext(string? content) Parameters content string Returns bool TryDecryptText(string, byte[], out string) Decrypts text produced by EncryptText(string, byte[]). Returns false when the input is not room ciphertext or the key does not match. public static bool TryDecryptText(string content, byte[] key, out string plaintext) Parameters content string key byte[] plaintext string Returns bool TryUnwrapRoomKey(string, byte[], out byte[]) Unwraps the stored room content key. Returns false when the KEK (passphrase) is wrong. public static bool TryUnwrapRoomKey(string wrappedRoomKey, byte[] kek, out byte[] roomKey) Parameters wrappedRoomKey string kek byte[] roomKey byte[] Returns bool WrapRoomKey(byte[], byte[]) Wraps the room content key under the key-encryption key for server storage. public static string WrapRoomKey(byte[] roomKey, byte[] kek) Parameters roomKey byte[] kek byte[] Returns string"
  },
  "api/core/EchoHub.Core.Security.html": {
    "href": "api/core/EchoHub.Core.Security.html",
    "title": "Namespace EchoHub.Core.Security | EchoHub Documentation",
    "summary": "Namespace EchoHub.Core.Security Classes RoomCrypto Client-side envelope encryption for private (end-to-end encrypted) channels. Design: at creation the client generates a random 256-bit room content key (RCK) that encrypts all room content. The RCK is stored on the server wrapped (AES-GCM encrypted) by a key derived from the passphrase, next to a BCrypt hash of a separately derived auth key used as the join gate. The passphrase, the key-encryption key, and the RCK never leave the client, so the server can gate joins and count/measure content without being able to read it. Changing the passphrase only re-wraps the RCK — history is never re-encrypted. Derivation: PBKDF2-SHA256(passphrase, salt, 210000 iterations) → 64 bytes; first 32 bytes are the auth key (sent to the server as lowercase hex), last 32 bytes are the key-encryption key (never sent). RoomCrypto.DerivedKeys"
  },
  "api/core/EchoHub.Core.Services.AsciiBannerService.html": {
    "href": "api/core/EchoHub.Core.Services.AsciiBannerService.html",
    "title": "Class AsciiBannerService | EchoHub Documentation",
    "summary": "Class AsciiBannerService Namespace EchoHub.Core.Services Assembly EchoHub.Core.dll Renders short text as a 5-row block-character banner (the /banner command). Entirely local — a hand-rolled figlet-style font, no dependencies, no network. Output is plain message content, so it travels (and encrypts) like any other text. public static class AsciiBannerService Inheritance object AsciiBannerService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields MaxInputLength public const int MaxInputLength = 20 Field Value int Methods Render(string) Renders text as banner lines joined with '\\n'. Returns null when the input is empty or contains no renderable characters. Characters outside the font (letters, digits, and basic punctuation) are skipped. public static string? Render(string text) Parameters text string Returns string"
  },
  "api/core/EchoHub.Core.Services.FileValidationHelper.html": {
    "href": "api/core/EchoHub.Core.Services.FileValidationHelper.html",
    "title": "Class FileValidationHelper | EchoHub Documentation",
    "summary": "Class FileValidationHelper Namespace EchoHub.Core.Services Assembly EchoHub.Core.dll public static class FileValidationHelper Inheritance object FileValidationHelper Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods IsAudioFile(string) Checks whether the file name has a recognized audio extension. public static bool IsAudioFile(string fileName) Parameters fileName string Returns bool IsValidImage(Stream) Validates that a stream contains a recognized image format by checking magic bytes. The stream position is reset to the beginning after validation. public static bool IsValidImage(Stream stream) Parameters stream Stream Returns bool"
  },
  "api/core/EchoHub.Core.Services.ImageToAsciiService.html": {
    "href": "api/core/EchoHub.Core.Services.ImageToAsciiService.html",
    "title": "Class ImageToAsciiService | EchoHub Documentation",
    "summary": "Class ImageToAsciiService Namespace EchoHub.Core.Services Assembly EchoHub.Core.dll public class ImageToAsciiService Inheritance object ImageToAsciiService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ImageToAsciiService() public ImageToAsciiService() Methods ConvertToAscii(Stream, int, int) Converts an image to ASCII art using half-block characters (▀▄█) with printable color tags for 2x vertical resolution. Each character cell represents two vertical pixels. Format: {F:RRGGBB} foreground, {B:RRGGBB} background, {X} reset. Uses only printable ASCII — no terminal escape bytes. public string ConvertToAscii(Stream imageStream, int width = 80, int height = 80) Parameters imageStream Stream width int height int Returns string GetDimensions(string?) Returns (width, height) dimensions for the given size code. s = small (40x40), m = medium/default (80x80), l = large (120x120). public static (int Width, int Height) GetDimensions(string? size) Parameters size string Returns (int Width, int Height)"
  },
  "api/core/EchoHub.Core.Services.html": {
    "href": "api/core/EchoHub.Core.Services.html",
    "title": "Namespace EchoHub.Core.Services | EchoHub Documentation",
    "summary": "Namespace EchoHub.Core.Services Classes AsciiBannerService Renders short text as a 5-row block-character banner (the /banner command). Entirely local — a hand-rolled figlet-style font, no dependencies, no network. Output is plain message content, so it travels (and encrypts) like any other text. FileValidationHelper ImageToAsciiService"
  },
  "api/index.html": {
    "href": "api/index.html",
    "title": "API Reference | EchoHub Documentation",
    "summary": "API Reference Browse the generated API documentation for each EchoHub project. Projects Client Terminal.Gui v2 TUI application -- UI components, services, themes, and configuration. Core Shared library -- DTOs, models, constants, and contracts (IChatService, IChannelService, IChatBroadcaster, IEchoHubClient). Server ASP.NET Core server -- controllers, hubs, ChatService, ChannelService, SignalRBroadcaster, authentication, file cleanup, and data access. Server.Irc IRC protocol gateway -- TCP listener, command handler, IrcBroadcaster, and message formatter."
  },
  "api/server-articles/index.html": {
    "href": "api/server-articles/index.html",
    "title": "Server Articles | EchoHub Documentation",
    "summary": "Server Articles Articles related to the EchoHub server built with ASP.NET Core. Topics Authentication and JWT tokens SignalR hub and real-time messaging IRC gateway and protocol bridging ChatService and broadcaster pattern ChannelService and channel CRUD File upload, validation, and cleanup Rate limiting configuration Database schema, migrations, and DataMigrationService"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcBroadcaster.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcBroadcaster.html",
    "title": "Class IrcBroadcaster | EchoHub Documentation",
    "summary": "Class IrcBroadcaster Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll public class IrcBroadcaster : IChatBroadcaster Inheritance object IrcBroadcaster Implements IChatBroadcaster Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors IrcBroadcaster(IrcGatewayService, IMessageEncryptionService) public IrcBroadcaster(IrcGatewayService gateway, IMessageEncryptionService encryption) Parameters gateway IrcGatewayService encryption IMessageEncryptionService Methods ForceDisconnectUserAsync(List<string>, string) public Task ForceDisconnectUserAsync(List<string> connectionIds, string reason) Parameters connectionIds List<string> reason string Returns Task SendChannelDeletedAsync(string) public Task SendChannelDeletedAsync(string channelName) Parameters channelName string Returns Task SendChannelNukedAsync(string) public Task SendChannelNukedAsync(string channelName) Parameters channelName string Returns Task SendChannelUpdatedAsync(ChannelDto, string?) public Task SendChannelUpdatedAsync(ChannelDto channel, string? channelName = null) Parameters channel ChannelDto channelName string Returns Task SendErrorAsync(string, string) public Task SendErrorAsync(string connectionId, string message) Parameters connectionId string message string Returns Task SendMessageDeletedAsync(string, Guid) public Task SendMessageDeletedAsync(string channelName, Guid messageId) Parameters channelName string messageId Guid Returns Task SendMessageToChannelAsync(string, MessageDto, string?) Broadcast a chat message to a channel. excludeConnectionId is the connection the message originated from (IRC convention: never echo a message back to the connection that sent it — its client already displayed it locally). Other connections of the same user (e.g. an IRC session alongside a TUI session) still receive the message. public Task SendMessageToChannelAsync(string channelName, MessageDto message, string? excludeConnectionId = null) Parameters channelName string message MessageDto excludeConnectionId string Returns Task SendUserBannedAsync(string, string?) public Task SendUserBannedAsync(string username, string? reason) Parameters username string reason string Returns Task SendUserJoinedAsync(string, string, UserPresenceDto?, string?) public Task SendUserJoinedAsync(string channelName, string username, UserPresenceDto? presence, string? excludeConnectionId = null) Parameters channelName string username string presence UserPresenceDto excludeConnectionId string Returns Task SendUserKickedAsync(string, string, string?) public Task SendUserKickedAsync(string channelName, string username, string? reason) Parameters channelName string username string reason string Returns Task SendUserLeftAsync(string, string) public Task SendUserLeftAsync(string channelName, string username) Parameters channelName string username string Returns Task SendUserStatusChangedAsync(List<string>, UserPresenceDto) public Task SendUserStatusChangedAsync(List<string> channelNames, UserPresenceDto presence) Parameters channelNames List<string> presence UserPresenceDto Returns Task"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcClientConnection.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcClientConnection.html",
    "title": "Class IrcClientConnection | EchoHub Documentation",
    "summary": "Class IrcClientConnection Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll Manages a single IRC client TCP connection. public sealed class IrcClientConnection : IAsyncDisposable Inheritance object IrcClientConnection Implements IAsyncDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors IrcClientConnection(TcpClient, Stream) public IrcClientConnection(TcpClient tcpClient, Stream stream) Parameters tcpClient TcpClient stream Stream Properties AwayMessage public string? AwayMessage { get; set; } Property Value string CapNegotiating public bool CapNegotiating { get; set; } Property Value bool ConnectionId public string ConnectionId { get; } Property Value string Hostmask public string Hostmask { get; } Property Value string IsAuthenticated public bool IsAuthenticated { get; set; } Property Value bool IsRegistered public bool IsRegistered { get; set; } Property Value bool IsSasl public bool IsSasl { get; set; } Property Value bool Nickname public string? Nickname { get; set; } Property Value string Password public string? Password { get; set; } Property Value string RealName public string? RealName { get; set; } Property Value string UserId public Guid? UserId { get; set; } Property Value Guid? Username public string? Username { get; set; } Property Value string Methods DisposeAsync() public ValueTask DisposeAsync() Returns ValueTask GetJoinedChannels() public List<string> GetJoinedChannels() Returns List<string> IsInChannel(string) public bool IsInChannel(string channel) Parameters channel string Returns bool JoinChannel(string) public void JoinChannel(string channel) Parameters channel string LeaveChannel(string) public void LeaveChannel(string channel) Parameters channel string ReadLineAsync(CancellationToken) public Task<string?> ReadLineAsync(CancellationToken ct) Parameters ct CancellationToken Returns Task<string> SendAsync(string) public Task SendAsync(string line) Parameters line string Returns Task SendNumericAsync(string, string, string) public Task SendNumericAsync(string serverName, string numeric, string text) Parameters serverName string numeric string text string Returns Task SendNumericAsync(string, string, string, string) public Task SendNumericAsync(string serverName, string numeric, string target, string text) Parameters serverName string numeric string target string text string Returns Task"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcCommandHandler.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcCommandHandler.html",
    "title": "Class IrcCommandHandler | EchoHub Documentation",
    "summary": "Class IrcCommandHandler Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll public sealed class IrcCommandHandler Inheritance object IrcCommandHandler Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors IrcCommandHandler(IrcClientConnection, IrcOptions, IChatService, IUserService, IChannelService, IMessageEncryptionService, ILogger) public IrcCommandHandler(IrcClientConnection conn, IrcOptions options, IChatService chatService, IUserService userService, IChannelService channelService, IMessageEncryptionService encryption, ILogger logger) Parameters conn IrcClientConnection options IrcOptions chatService IChatService userService IUserService channelService IChannelService encryption IMessageEncryptionService logger ILogger Methods RunAsync(CancellationToken) public Task RunAsync(CancellationToken ct) Parameters ct CancellationToken Returns Task"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcGatewayService.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcGatewayService.html",
    "title": "Class IrcGatewayService | EchoHub Documentation",
    "summary": "Class IrcGatewayService Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll public sealed class IrcGatewayService : BackgroundService, IDisposable Inheritance object BackgroundService IrcGatewayService Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors IrcGatewayService(IOptions<IrcOptions>, IServiceProvider, ILogger<IrcGatewayService>) public IrcGatewayService(IOptions<IrcOptions> options, IServiceProvider services, ILogger<IrcGatewayService> logger) Parameters options IOptions<IrcOptions> services IServiceProvider logger ILogger<IrcGatewayService> Properties Connections public IReadOnlyDictionary<string, IrcClientConnection> Connections { get; } Property Value IReadOnlyDictionary<string, IrcClientConnection> Options public IrcOptions Options { get; } Property Value IrcOptions Methods ExecuteAsync(CancellationToken) protected override Task ExecuteAsync(CancellationToken stoppingToken) Parameters stoppingToken CancellationToken Returns Task GetAllConnections() public IEnumerable<IrcClientConnection> GetAllConnections() Returns IEnumerable<IrcClientConnection> GetConnectionsInChannel(string) public IEnumerable<IrcClientConnection> GetConnectionsInChannel(string channelName) Parameters channelName string Returns IEnumerable<IrcClientConnection> StopAsync(CancellationToken) public override Task StopAsync(CancellationToken cancellationToken) Parameters cancellationToken CancellationToken Returns Task"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcMessage.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcMessage.html",
    "title": "Class IrcMessage | EchoHub Documentation",
    "summary": "Class IrcMessage Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll Parsed representation of an IRC protocol line. Format: [:prefix] COMMAND [params...] [:trailing] public sealed class IrcMessage Inheritance object IrcMessage Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors IrcMessage() public IrcMessage() Properties Command public string Command { get; init; } Property Value string Parameters public List<string> Parameters { get; init; } Property Value List<string> Prefix public string? Prefix { get; init; } Property Value string Trailing public string? Trailing { get; } Property Value string Methods Parse(string) Parse a raw IRC line: [:prefix SPACE] command [SPACE params] CRLF public static IrcMessage Parse(string line) Parameters line string Returns IrcMessage"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html",
    "title": "Class IrcMessageFormatter | EchoHub Documentation",
    "summary": "Class IrcMessageFormatter Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll public static class IrcMessageFormatter Inheritance object IrcMessageFormatter Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods FormatMessage(MessageDto, string?) Format a MessageDto as one or more IRC PRIVMSG lines. Attachments are rendered as single link lines \\u2014 the widely-supported IRC convention (clients auto-preview or open plain http(s) URLs) \\u2014 never as terminal color art. publicBaseUrl makes the links absolute so any IRC client can open them. public static List<string> FormatMessage(MessageDto message, string? publicBaseUrl = null) Parameters message MessageDto publicBaseUrl string Returns List<string> SplitMessage(string, int) Split a message into chunks of approximately maxBytes (UTF-8), at word boundaries. public static List<string> SplitMessage(string content, int maxBytes) Parameters content string maxBytes int Returns List<string> ToAbsoluteUrl(string, string?) Joins a relative attachment path onto the configured public base URL. Already-absolute URLs and unset base URLs pass through unchanged. public static string ToAbsoluteUrl(string url, string? publicBaseUrl) Parameters url string publicBaseUrl string Returns string"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcNumericReply.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcNumericReply.html",
    "title": "Class IrcNumericReply | EchoHub Documentation",
    "summary": "Class IrcNumericReply Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll public static class IrcNumericReply Inheritance object IrcNumericReply Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields ERR_ALREADYREGISTERED public const string ERR_ALREADYREGISTERED = \"462\" Field Value string ERR_BADCHANNELKEY public const string ERR_BADCHANNELKEY = \"475\" Field Value string ERR_CANNOTSENDTOCHAN public const string ERR_CANNOTSENDTOCHAN = \"404\" Field Value string ERR_CHANOPRIVSNEEDED public const string ERR_CHANOPRIVSNEEDED = \"482\" Field Value string ERR_ERRONEUSNICKNAME public const string ERR_ERRONEUSNICKNAME = \"432\" Field Value string ERR_KEYSET public const string ERR_KEYSET = \"467\" Field Value string ERR_NEEDMOREPARAMS public const string ERR_NEEDMOREPARAMS = \"461\" Field Value string ERR_NICKNAMEINUSE public const string ERR_NICKNAMEINUSE = \"433\" Field Value string ERR_NOMOTD public const string ERR_NOMOTD = \"422\" Field Value string ERR_NONICKNAMEGIVEN public const string ERR_NONICKNAMEGIVEN = \"431\" Field Value string ERR_NOSUCHCHANNEL public const string ERR_NOSUCHCHANNEL = \"403\" Field Value string ERR_NOSUCHNICK public const string ERR_NOSUCHNICK = \"401\" Field Value string ERR_NOTONCHANNEL public const string ERR_NOTONCHANNEL = \"442\" Field Value string ERR_NOTREGISTERED public const string ERR_NOTREGISTERED = \"451\" Field Value string ERR_PASSWDMISMATCH public const string ERR_PASSWDMISMATCH = \"464\" Field Value string ERR_SASLFAIL public const string ERR_SASLFAIL = \"904\" Field Value string ERR_UNKNOWNCOMMAND public const string ERR_UNKNOWNCOMMAND = \"421\" Field Value string ERR_UNKNOWNMODE public const string ERR_UNKNOWNMODE = \"472\" Field Value string RPL_AWAY public const string RPL_AWAY = \"301\" Field Value string RPL_CHANNELMODEIS public const string RPL_CHANNELMODEIS = \"324\" Field Value string RPL_CREATED public const string RPL_CREATED = \"003\" Field Value string RPL_ENDOFBANLIST public const string RPL_ENDOFBANLIST = \"368\" Field Value string RPL_ENDOFMOTD public const string RPL_ENDOFMOTD = \"376\" Field Value string RPL_ENDOFNAMES public const string RPL_ENDOFNAMES = \"366\" Field Value string RPL_ENDOFWHO public const string RPL_ENDOFWHO = \"315\" Field Value string RPL_ENDOFWHOIS public const string RPL_ENDOFWHOIS = \"318\" Field Value string RPL_ISUPPORT public const string RPL_ISUPPORT = \"005\" Field Value string RPL_LIST public const string RPL_LIST = \"322\" Field Value string RPL_LISTEND public const string RPL_LISTEND = \"323\" Field Value string RPL_LOGGEDIN public const string RPL_LOGGEDIN = \"900\" Field Value string RPL_MOTD public const string RPL_MOTD = \"372\" Field Value string RPL_MOTDSTART public const string RPL_MOTDSTART = \"375\" Field Value string RPL_MYINFO public const string RPL_MYINFO = \"004\" Field Value string RPL_NAMREPLY public const string RPL_NAMREPLY = \"353\" Field Value string RPL_NOTOPIC public const string RPL_NOTOPIC = \"331\" Field Value string RPL_NOWAWAY public const string RPL_NOWAWAY = \"306\" Field Value string RPL_SASLSUCCESS public const string RPL_SASLSUCCESS = \"903\" Field Value string RPL_TOPIC public const string RPL_TOPIC = \"332\" Field Value string RPL_UMODEIS public const string RPL_UMODEIS = \"221\" Field Value string RPL_UNAWAY public const string RPL_UNAWAY = \"305\" Field Value string RPL_WELCOME public const string RPL_WELCOME = \"001\" Field Value string RPL_WHOISCHANNELS public const string RPL_WHOISCHANNELS = \"319\" Field Value string RPL_WHOISIDLE public const string RPL_WHOISIDLE = \"317\" Field Value string RPL_WHOISSERVER public const string RPL_WHOISSERVER = \"312\" Field Value string RPL_WHOISUSER public const string RPL_WHOISUSER = \"311\" Field Value string RPL_WHOREPLY public const string RPL_WHOREPLY = \"352\" Field Value string RPL_YOURHOST public const string RPL_YOURHOST = \"002\" Field Value string"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcOptions.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcOptions.html",
    "title": "Class IrcOptions | EchoHub Documentation",
    "summary": "Class IrcOptions Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll public sealed class IrcOptions Inheritance object IrcOptions Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors IrcOptions() public IrcOptions() Fields SectionName public const string SectionName = \"Irc\" Field Value string Properties Enabled public bool Enabled { get; set; } Property Value bool Motd public string? Motd { get; set; } Property Value string Port public int Port { get; set; } Property Value int PublicBaseUrl Public HTTP(S) base of this EchoHub server (e.g. \"https://chat.example.com\"), used to turn relative attachment URLs into absolute links IRC clients can open. When unset, attachment lines fall back to the relative path. public string? PublicBaseUrl { get; set; } Property Value string ServerName public string ServerName { get; set; } Property Value string TlsCertPassword public string? TlsCertPassword { get; set; } Property Value string TlsCertPath public string? TlsCertPath { get; set; } Property Value string TlsEnabled public bool TlsEnabled { get; set; } Property Value bool TlsPort public int TlsPort { get; set; } Property Value int"
  },
  "api/server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html",
    "title": "Class IrcServiceExtensions | EchoHub Documentation",
    "summary": "Class IrcServiceExtensions Namespace EchoHub.Server.Irc Assembly EchoHub.Server.Irc.dll public static class IrcServiceExtensions Inheritance object IrcServiceExtensions Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods AddIrcGateway(WebApplicationBuilder) public static WebApplicationBuilder AddIrcGateway(this WebApplicationBuilder builder) Parameters builder WebApplicationBuilder Returns WebApplicationBuilder"
  },
  "api/server-irc/EchoHub.Server.Irc.html": {
    "href": "api/server-irc/EchoHub.Server.Irc.html",
    "title": "Namespace EchoHub.Server.Irc | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Irc Classes IrcBroadcaster IrcClientConnection Manages a single IRC client TCP connection. IrcCommandHandler IrcGatewayService IrcMessage Parsed representation of an IRC protocol line. Format: [:prefix] COMMAND [params...] [:trailing] IrcMessageFormatter IrcNumericReply IrcOptions IrcServiceExtensions"
  },
  "api/server/EchoHub.Server.Auth.JwtTokenService.html": {
    "href": "api/server/EchoHub.Server.Auth.JwtTokenService.html",
    "title": "Class JwtTokenService | EchoHub Documentation",
    "summary": "Class JwtTokenService Namespace EchoHub.Server.Auth Assembly EchoHub.Server.dll public class JwtTokenService Inheritance object JwtTokenService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors JwtTokenService(IConfiguration) public JwtTokenService(IConfiguration configuration) Parameters configuration IConfiguration Fields RefreshTokenLifetime public static readonly TimeSpan RefreshTokenLifetime Field Value TimeSpan Methods GenerateAccessToken(UserProfileDto) public (string Token, DateTimeOffset ExpiresAt) GenerateAccessToken(UserProfileDto profile) Parameters profile UserProfileDto Returns (string Token, DateTimeOffset ExpiresAt) GenerateAccessToken(User) public (string Token, DateTimeOffset ExpiresAt) GenerateAccessToken(User user) Parameters user User Returns (string Token, DateTimeOffset ExpiresAt) GenerateRefreshToken() public static string GenerateRefreshToken() Returns string HashToken(string) public static string HashToken(string token) Parameters token string Returns string"
  },
  "api/server/EchoHub.Server.Auth.html": {
    "href": "api/server/EchoHub.Server.Auth.html",
    "title": "Namespace EchoHub.Server.Auth | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Auth Classes JwtTokenService"
  },
  "api/server/EchoHub.Server.Config.ServerLogsOptions.html": {
    "href": "api/server/EchoHub.Server.Config.ServerLogsOptions.html",
    "title": "Class ServerLogsOptions | EchoHub Documentation",
    "summary": "Class ServerLogsOptions Namespace EchoHub.Server.Config Assembly EchoHub.Server.dll Live server-log room settings, bound from the \"ServerLogs\" config section (env override: ServerLogs__Enabled etc.). When enabled, a read-only system channel is auto-created and log events are streamed to it live — log lines are never stored as messages in the database; the rolling Serilog log files remain the only persistence. public sealed class ServerLogsOptions Inheritance object ServerLogsOptions Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerLogsOptions() public ServerLogsOptions() Properties BacklogLines How many recent log entries are replayed from the log file when someone opens the room. public int BacklogLines { get; set; } Property Value int Enabled Master switch for the live log room. public bool Enabled { get; set; } Property Value bool LogDirectory Directory holding the rolling log files. Must match the Serilog file sink's path. public string LogDirectory { get; set; } Property Value string LogFilePattern Filename glob for the rolling log files inside LogDirectory. public string LogFilePattern { get; set; } Property Value string MinLevel Minimum level of log events streamed to the room (Verbose/Debug/Information/Warning/ Error/Fatal). Only affects the room — file and console sinks keep their own levels. public LogEventLevel MinLevel { get; set; } Property Value LogEventLevel MinRole Minimum server role allowed to see and join the log room (Member/Mod/Admin/Owner). public ServerRole MinRole { get; set; } Property Value ServerRole NormalizedRoomName Channel names are stored lowercased; compare against this form. public string NormalizedRoomName { get; } Property Value string RoomName Name of the auto-created system channel. Must satisfy the normal channel-name rules; the name is reserved — users cannot create a channel with it. public string RoomName { get; set; } Property Value string"
  },
  "api/server/EchoHub.Server.Config.SpamOptions.html": {
    "href": "api/server/EchoHub.Server.Config.SpamOptions.html",
    "title": "Class SpamOptions | EchoHub Documentation",
    "summary": "Class SpamOptions Namespace EchoHub.Server.Config Assembly EchoHub.Server.dll Anti-spam thresholds, bound from the \"Spam\" config section. Defaults are lenient enough that a fast typist never trips them; Mods and above are always exempt. public sealed class SpamOptions Inheritance object SpamOptions Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors SpamOptions() public SpamOptions() Properties AutoMuteMinutes Auto-mute duration once a user accumulates ViolationThreshold rejected sends within ViolationWindowMinutes. 0 disables auto-mute (rejections still apply). The mute uses the normal timed-mute machinery, so moderators see it and MuteExpirationService lifts it. public int AutoMuteMinutes { get; set; } Property Value int ChannelCreateWindowMinutes public int ChannelCreateWindowMinutes { get; set; } Property Value int Enabled Master switch for all spam protection. public bool Enabled { get; set; } Property Value bool JoinWindowSeconds public int JoinWindowSeconds { get; set; } Property Value int MaxChannelCreatesPerWindow Max channel creations per ChannelCreateWindowMinutes window. public int MaxChannelCreatesPerWindow { get; set; } Property Value int MaxDuplicateMessages How many identical messages in a row are tolerated; the next one is rejected. (End-to-end encrypted rooms are naturally exempt — identical plaintext produces different ciphertext per message, so repeats never look identical to the server.) public int MaxDuplicateMessages { get; set; } Property Value int MaxJoinsPerWindow Max first-time channel joins per JoinWindowSeconds window. Joins of channels the user already belongs to (reconnect/auto-join) never count, but a brand-new user's first connect joins every public channel at once — keep this above your public channel count. public int MaxJoinsPerWindow { get; set; } Property Value int MaxMessagesPerWindow Max messages a user may send per WindowSeconds window. public int MaxMessagesPerWindow { get; set; } Property Value int ViolationThreshold public int ViolationThreshold { get; set; } Property Value int ViolationWindowMinutes public int ViolationWindowMinutes { get; set; } Property Value int WindowSeconds public int WindowSeconds { get; set; } Property Value int"
  },
  "api/server/EchoHub.Server.Config.StatsOptions.html": {
    "href": "api/server/EchoHub.Server.Config.StatsOptions.html",
    "title": "Class StatsOptions | EchoHub Documentation",
    "summary": "Class StatsOptions Namespace EchoHub.Server.Config Assembly EchoHub.Server.dll Periodic server-stats report settings, bound from the \"Stats\" config section (env override: Stats__Enabled etc.). When enabled, a background job periodically snapshots server activity, logs it as pretty-printed JSON, and persists it to the database. public sealed class StatsOptions Inheritance object StatsOptions Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors StatsOptions() public StatsOptions() Properties Enabled Master switch for the periodic stats report job. public bool Enabled { get; set; } Property Value bool IntervalHours How often a report is generated, in hours. Default: every 6 hours. public double IntervalHours { get; set; } Property Value double RetentionDays How long persisted reports are kept before being pruned, in days. Set to 0 to keep reports indefinitely. Default: 90 days. public int RetentionDays { get; set; } Property Value int"
  },
  "api/server/EchoHub.Server.Config.UploadLimits.html": {
    "href": "api/server/EchoHub.Server.Config.UploadLimits.html",
    "title": "Class UploadLimits | EchoHub Documentation",
    "summary": "Class UploadLimits Namespace EchoHub.Server.Config Assembly EchoHub.Server.dll Admin-configurable upload limits, bound from the Uploads configuration section. Sizes are expressed in megabytes in configuration; the *Bytes accessors convert them for enforcement. Every default mirrors HubConstants so an absent or partial Uploads section preserves the historical built-in limits. public sealed class UploadLimits Inheritance object UploadLimits Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UploadLimits() public UploadLimits() Properties MaxAttachmentsPerMessage public int MaxAttachmentsPerMessage { get; init; } Property Value int MaxAudioSizeBytes public long MaxAudioSizeBytes { get; } Property Value long MaxAudioSizeMB public int MaxAudioSizeMB { get; init; } Property Value int MaxAvatarSizeBytes public long MaxAvatarSizeBytes { get; } Property Value long MaxAvatarSizeMB public int MaxAvatarSizeMB { get; init; } Property Value int MaxFileSizeBytes public long MaxFileSizeBytes { get; } Property Value long MaxFileSizeMB public int MaxFileSizeMB { get; init; } Property Value int MaxImageSizeBytes public long MaxImageSizeBytes { get; } Property Value long MaxImageSizeMB public int MaxImageSizeMB { get; init; } Property Value int MaxRequestBodyBytes Absolute ceiling for one message request body (largest file × the attachment cap). Used to size the request-body and multipart limits so a configured increase actually takes effect. public long MaxRequestBodyBytes { get; } Property Value long Methods MaxForKind(AttachmentKind) Maximum accepted size for a single attachment of the given kind. public long MaxForKind(AttachmentKind kind) Parameters kind AttachmentKind Returns long"
  },
  "api/server/EchoHub.Server.Config.html": {
    "href": "api/server/EchoHub.Server.Config.html",
    "title": "Namespace EchoHub.Server.Config | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Config Classes ServerLogsOptions Live server-log room settings, bound from the \"ServerLogs\" config section (env override: ServerLogs__Enabled etc.). When enabled, a read-only system channel is auto-created and log events are streamed to it live — log lines are never stored as messages in the database; the rolling Serilog log files remain the only persistence. SpamOptions Anti-spam thresholds, bound from the \"Spam\" config section. Defaults are lenient enough that a fast typist never trips them; Mods and above are always exempt. StatsOptions Periodic server-stats report settings, bound from the \"Stats\" config section (env override: Stats__Enabled etc.). When enabled, a background job periodically snapshots server activity, logs it as pretty-printed JSON, and persists it to the database. UploadLimits Admin-configurable upload limits, bound from the Uploads configuration section. Sizes are expressed in megabytes in configuration; the *Bytes accessors convert them for enforcement. Every default mirrors HubConstants so an absent or partial Uploads section preserves the historical built-in limits."
  },
  "api/server/EchoHub.Server.Controllers.AuthController.html": {
    "href": "api/server/EchoHub.Server.Controllers.AuthController.html",
    "title": "Class AuthController | EchoHub Documentation",
    "summary": "Class AuthController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/auth\")] [EnableRateLimiting(\"auth\")] public class AuthController : ControllerBase Inheritance object ControllerBase AuthController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AuthController(EchoHubDbContext, JwtTokenService, IUserService) public AuthController(EchoHubDbContext db, JwtTokenService jwt, IUserService userService) Parameters db EchoHubDbContext jwt JwtTokenService userService IUserService Methods Login(LoginRequest) [HttpPost(\"login\")] public Task<IActionResult> Login(LoginRequest request) Parameters request LoginRequest Returns Task<IActionResult> Logout(RefreshRequest) [HttpPost(\"logout\")] public Task<IActionResult> Logout(RefreshRequest request) Parameters request RefreshRequest Returns Task<IActionResult> Refresh(RefreshRequest) [HttpPost(\"refresh\")] public Task<IActionResult> Refresh(RefreshRequest request) Parameters request RefreshRequest Returns Task<IActionResult> Register(RegisterRequest) [HttpPost(\"register\")] public Task<IActionResult> Register(RegisterRequest request) Parameters request RegisterRequest Returns Task<IActionResult>"
  },
  "api/server/EchoHub.Server.Controllers.ChannelsController.html": {
    "href": "api/server/EchoHub.Server.Controllers.ChannelsController.html",
    "title": "Class ChannelsController | EchoHub Documentation",
    "summary": "Class ChannelsController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/channels\")] [Authorize] [EnableRateLimiting(\"general\")] public class ChannelsController : ControllerBase Inheritance object ControllerBase ChannelsController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService, UploadLimits, ILogger<ChannelsController>) public ChannelsController(IChannelService channelService, EchoHubDbContext db, FileStorageService fileStorage, ImageToAsciiService asciiService, IHttpClientFactory httpClientFactory, IChatService chatService, IMessageEncryptionService encryption, UploadLimits uploadLimits, ILogger<ChannelsController> logger) Parameters channelService IChannelService db EchoHubDbContext fileStorage FileStorageService asciiService ImageToAsciiService httpClientFactory IHttpClientFactory chatService IChatService encryption IMessageEncryptionService uploadLimits UploadLimits logger ILogger<ChannelsController> Methods CreateChannel(CreateChannelRequest) [HttpPost] public Task<IActionResult> CreateChannel(CreateChannelRequest request) Parameters request CreateChannelRequest Returns Task<IActionResult> DeleteChannel(string) [HttpDelete(\"{channel}\")] public Task<IActionResult> DeleteChannel(string channel) Parameters channel string Returns Task<IActionResult> GetChannelCrypto(string) Public crypto metadata for a channel: whether it is end-to-end encrypted and the PBKDF2 salt clients need to derive their join credential. Never returns the wrapped room key — that is only handed out after a successful join. [HttpGet(\"{channel}/crypto\")] public Task<IActionResult> GetChannelCrypto(string channel) Parameters channel string Returns Task<IActionResult> GetChannelMeta(string) Human-facing summary of a channel (message count, unique posters, estimated size, created date, room id). Available for encrypted channels too — these are metadata the server tracks even though it cannot read the messages themselves. [HttpGet(\"{channel}/meta\")] public Task<IActionResult> GetChannelMeta(string channel) Parameters channel string Returns Task<IActionResult> GetChannels(int, int) [HttpGet] public Task<IActionResult> GetChannels(int offset = 0, int limit = 50) Parameters offset int limit int Returns Task<IActionResult> RekeyChannel(string, RekeyChannelRequest) Changes an encrypted channel's passphrase by re-wrapping its room key. The caller proves knowledge of the old passphrase via the old auth key; history is never re-encrypted (the room content key does not change). [HttpPost(\"{channel}/rekey\")] public Task<IActionResult> RekeyChannel(string channel, RekeyChannelRequest request) Parameters channel string request RekeyChannelRequest Returns Task<IActionResult> SendMessageWithAttachments(string, string?) Sends one message carrying optional text (content form field) plus zero or more file attachments (Discord-style). For non-encrypted channels the server sniffs each file's kind and renders ASCII previews for images. For end-to-end encrypted channels the client uploads ciphertext blobs and declares each file's kind (kind) and pre-rendered, room-encrypted preview (preview), aligned by file order — the server never inspects them. [HttpPost(\"{channel}/messages\")] [EnableRateLimiting(\"upload\")] public Task<IActionResult> SendMessageWithAttachments(string channel, string? size = null) Parameters channel string size string Returns Task<IActionResult> SendUrl(string, SendUrlRequest, string?) [HttpPost(\"{channel}/send-url\")] [EnableRateLimiting(\"upload\")] public Task<IActionResult> SendUrl(string channel, SendUrlRequest request, string? size = null) Parameters channel string request SendUrlRequest size string Returns Task<IActionResult> UpdateTopic(string, UpdateTopicRequest) [HttpPut(\"{channel}/topic\")] public Task<IActionResult> UpdateTopic(string channel, UpdateTopicRequest request) Parameters channel string request UpdateTopicRequest Returns Task<IActionResult>"
  },
  "api/server/EchoHub.Server.Controllers.FilesController.html": {
    "href": "api/server/EchoHub.Server.Controllers.FilesController.html",
    "title": "Class FilesController | EchoHub Documentation",
    "summary": "Class FilesController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/files\")] [Authorize] [EnableRateLimiting(\"general\")] public class FilesController : ControllerBase Inheritance object ControllerBase FilesController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors FilesController(FileStorageService) public FilesController(FileStorageService fileStorage) Parameters fileStorage FileStorageService Methods GetFile(string) Serves an uploaded file. Anonymous by design: the unguessable GUID in the URL is the access token (Discord-CDN-style capability URL), so attachment links can be opened directly in a browser and shared to IRC clients. E2E-encrypted room blobs are ciphertext at rest, so anonymous access reveals nothing for those channels. [HttpGet(\"{fileId}\")] [AllowAnonymous] public IActionResult GetFile(string fileId) Parameters fileId string Returns IActionResult"
  },
  "api/server/EchoHub.Server.Controllers.InvitesController.html": {
    "href": "api/server/EchoHub.Server.Controllers.InvitesController.html",
    "title": "Class InvitesController | EchoHub Documentation",
    "summary": "Class InvitesController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll Invite-code management for invite-gated registration (Admin+ only). Codes live in this server's own database — there is no central service. [ApiController] [Route(\"api/invites\")] [Authorize] [EnableRateLimiting(\"general\")] public class InvitesController : ControllerBase Inheritance object ControllerBase InvitesController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors InvitesController(EchoHubDbContext, ILogger<InvitesController>) public InvitesController(EchoHubDbContext db, ILogger<InvitesController> logger) Parameters db EchoHubDbContext logger ILogger<InvitesController> Methods Create(CreateInviteRequest) [HttpPost] public Task<IActionResult> Create(CreateInviteRequest request) Parameters request CreateInviteRequest Returns Task<IActionResult> List() [HttpGet] public Task<IActionResult> List() Returns Task<IActionResult> Revoke(string) [HttpDelete(\"{code}\")] public Task<IActionResult> Revoke(string code) Parameters code string Returns Task<IActionResult>"
  },
  "api/server/EchoHub.Server.Controllers.ModerationController.html": {
    "href": "api/server/EchoHub.Server.Controllers.ModerationController.html",
    "title": "Class ModerationController | EchoHub Documentation",
    "summary": "Class ModerationController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/moderation\")] [Authorize] [EnableRateLimiting(\"general\")] public class ModerationController : ControllerBase Inheritance object ControllerBase ModerationController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ModerationController(EchoHubDbContext, IChatService, PresenceTracker, FileStorageService, IEnumerable<IChatBroadcaster>, ServerStatsCollector, ILogger<ModerationController>) public ModerationController(EchoHubDbContext db, IChatService chatService, PresenceTracker presenceTracker, FileStorageService fileStorage, IEnumerable<IChatBroadcaster> broadcasters, ServerStatsCollector statsCollector, ILogger<ModerationController> logger) Parameters db EchoHubDbContext chatService IChatService presenceTracker PresenceTracker fileStorage FileStorageService broadcasters IEnumerable<IChatBroadcaster> statsCollector ServerStatsCollector logger ILogger<ModerationController> Methods AssignRole(AssignRoleRequest) [HttpPost(\"role\")] public Task<IActionResult> AssignRole(AssignRoleRequest request) Parameters request AssignRoleRequest Returns Task<IActionResult> BanUser(string, BanRequest?) [HttpPost(\"ban/{username}\")] public Task<IActionResult> BanUser(string username, BanRequest? request = null) Parameters username string request BanRequest Returns Task<IActionResult> DeleteMessage(Guid) [HttpDelete(\"messages/{messageId:guid}\")] public Task<IActionResult> DeleteMessage(Guid messageId) Parameters messageId Guid Returns Task<IActionResult> KickUser(string, KickRequest?) [HttpPost(\"kick/{username}\")] public Task<IActionResult> KickUser(string username, KickRequest? request = null) Parameters username string request KickRequest Returns Task<IActionResult> MuteUser(string, MuteRequest?) [HttpPost(\"mute/{username}\")] public Task<IActionResult> MuteUser(string username, MuteRequest? request = null) Parameters username string request MuteRequest Returns Task<IActionResult> NukeChannel(string) [HttpDelete(\"channels/{channel}/nuke\")] public Task<IActionResult> NukeChannel(string channel) Parameters channel string Returns Task<IActionResult> UnbanUser(string) [HttpPost(\"unban/{username}\")] public Task<IActionResult> UnbanUser(string username) Parameters username string Returns Task<IActionResult> UnmuteUser(string) [HttpPost(\"unmute/{username}\")] public Task<IActionResult> UnmuteUser(string username) Parameters username string Returns Task<IActionResult>"
  },
  "api/server/EchoHub.Server.Controllers.ServerController.html": {
    "href": "api/server/EchoHub.Server.Controllers.ServerController.html",
    "title": "Class ServerController | EchoHub Documentation",
    "summary": "Class ServerController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/server\")] public class ServerController : ControllerBase Inheritance object ControllerBase ServerController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerController(EchoHubDbContext, IConfiguration, DirectoryClaimStore) public ServerController(EchoHubDbContext db, IConfiguration config, DirectoryClaimStore claimStore) Parameters db EchoHubDbContext config IConfiguration claimStore DirectoryClaimStore Methods GetDirectoryStatus() Operator-facing view of the EchoHubSpace directory registration: ServerId for admin support tickets, current registration state, and the last error/conflict if any. Never exposes the claim token itself. [HttpGet(\"directory\")] [Authorize] public Task<IActionResult> GetDirectoryStatus() Returns Task<IActionResult> GetEncryptionKey() [HttpGet(\"encryption-key\")] [Authorize] [EnableRateLimiting(\"auth\")] public IActionResult GetEncryptionKey() Returns IActionResult GetInfo() [HttpGet(\"info\")] public Task<IActionResult> GetInfo() Returns Task<IActionResult>"
  },
  "api/server/EchoHub.Server.Controllers.UsersController.html": {
    "href": "api/server/EchoHub.Server.Controllers.UsersController.html",
    "title": "Class UsersController | EchoHub Documentation",
    "summary": "Class UsersController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/users\")] [Authorize] [EnableRateLimiting(\"general\")] public class UsersController : ControllerBase Inheritance object ControllerBase UsersController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UsersController(IUserService, ImageToAsciiService, UploadLimits, EchoHubDbContext, IMessageEncryptionService, FileStorageService, PresenceTracker, IEnumerable<IChatBroadcaster>, IConfiguration, ILogger<UsersController>) public UsersController(IUserService userService, ImageToAsciiService asciiService, UploadLimits uploadLimits, EchoHubDbContext db, IMessageEncryptionService encryption, FileStorageService fileStorage, PresenceTracker presenceTracker, IEnumerable<IChatBroadcaster> broadcasters, IConfiguration config, ILogger<UsersController> logger) Parameters userService IUserService asciiService ImageToAsciiService uploadLimits UploadLimits db EchoHubDbContext encryption IMessageEncryptionService fileStorage FileStorageService presenceTracker PresenceTracker broadcasters IEnumerable<IChatBroadcaster> config IConfiguration logger ILogger<UsersController> Fields DeletedUserName Username tombstoned messages are re-attributed to after account deletion. Reserved — UserService refuses to register it. public const string DeletedUserName = \"deleted-user\" Field Value string Methods DeleteMyAccount(DeleteAccountRequest) Self-service account deletion (password re-confirmed). Removes the account, its refresh tokens and memberships (FK cascade), and every attachment blob the user uploaded. Their messages are kept but tombstoned to DeletedUserName — deleting them outright would silently gut other people's conversations. [HttpDelete(\"me\")] public Task<IActionResult> DeleteMyAccount(DeleteAccountRequest request) Parameters request DeleteAccountRequest Returns Task<IActionResult> ExportMyData() Everything the server stores about the caller, as stored: profile, their messages (end-to-end encrypted room content stays ciphertext — the server never had plaintext), and metadata of their uploaded attachments. \"You own the data\" made demonstrable. [HttpGet(\"me/export\")] public Task<IActionResult> ExportMyData() Returns Task<IActionResult> GetProfile(string) [HttpGet(\"{username}/profile\")] public Task<IActionResult> GetProfile(string username) Parameters username string Returns Task<IActionResult> UpdateProfile(UpdateProfileRequest) [HttpPut(\"profile\")] public Task<IActionResult> UpdateProfile(UpdateProfileRequest request) Parameters request UpdateProfileRequest Returns Task<IActionResult> UploadAvatar() [HttpPost(\"avatar\")] [EnableRateLimiting(\"upload\")] public Task<IActionResult> UploadAvatar() Returns Task<IActionResult>"
  },
  "api/server/EchoHub.Server.Controllers.html": {
    "href": "api/server/EchoHub.Server.Controllers.html",
    "title": "Namespace EchoHub.Server.Controllers | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Controllers Classes AuthController ChannelsController FilesController InvitesController Invite-code management for invite-gated registration (Admin+ only). Codes live in this server's own database — there is no central service. ModerationController ServerController UsersController"
  },
  "api/server/EchoHub.Server.Data.EchoHubDbContext.html": {
    "href": "api/server/EchoHub.Server.Data.EchoHubDbContext.html",
    "title": "Class EchoHubDbContext | EchoHub Documentation",
    "summary": "Class EchoHubDbContext Namespace EchoHub.Server.Data Assembly EchoHub.Server.dll public class EchoHubDbContext : DbContext, IDisposable, IAsyncDisposable Inheritance object DbContext EchoHubDbContext Implements IDisposable IAsyncDisposable Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors EchoHubDbContext(DbContextOptions<EchoHubDbContext>) public EchoHubDbContext(DbContextOptions<EchoHubDbContext> options) Parameters options DbContextOptions<EchoHubDbContext> Properties Attachments public DbSet<Attachment> Attachments { get; } Property Value DbSet<Attachment> ChannelMemberships public DbSet<ChannelMembership> ChannelMemberships { get; } Property Value DbSet<ChannelMembership> Channels public DbSet<Channel> Channels { get; } Property Value DbSet<Channel> InviteCodes public DbSet<InviteCode> InviteCodes { get; } Property Value DbSet<InviteCode> Messages public DbSet<Message> Messages { get; } Property Value DbSet<Message> RefreshTokens public DbSet<RefreshToken> RefreshTokens { get; } Property Value DbSet<RefreshToken> ServerStatsReports public DbSet<ServerStatsReport> ServerStatsReports { get; } Property Value DbSet<ServerStatsReport> Users public DbSet<User> Users { get; } Property Value DbSet<User> Methods OnConfiguring(DbContextOptionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) Parameters optionsBuilder DbContextOptionsBuilder OnModelCreating(ModelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html",
    "title": "Class AddAttachmentFileSize | EchoHub Documentation",
    "summary": "Class AddAttachmentFileSize Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260221193444_AddAttachmentFileSize\")] public class AddAttachmentFileSize : Migration Inheritance object Migration AddAttachmentFileSize Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddAttachmentFileSize() public AddAttachmentFileSize() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html",
    "title": "Class AddChannelEncryptionEnvelope | EchoHub Documentation",
    "summary": "Class AddChannelEncryptionEnvelope Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260716012917_AddChannelEncryptionEnvelope\")] public class AddChannelEncryptionEnvelope : Migration Inheritance object Migration AddChannelEncryptionEnvelope Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddChannelEncryptionEnvelope() public AddChannelEncryptionEnvelope() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html",
    "title": "Class AddChannelIsPublic | EchoHub Documentation",
    "summary": "Class AddChannelIsPublic Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260219172834_AddChannelIsPublic\")] public class AddChannelIsPublic : Migration Inheritance object Migration AddChannelIsPublic Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddChannelIsPublic() public AddChannelIsPublic() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddChannelIsSystem.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddChannelIsSystem.html",
    "title": "Class AddChannelIsSystem | EchoHub Documentation",
    "summary": "Class AddChannelIsSystem Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260717182450_AddChannelIsSystem\")] public class AddChannelIsSystem : Migration Inheritance object Migration AddChannelIsSystem Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddChannelIsSystem() public AddChannelIsSystem() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddChannelMembership.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddChannelMembership.html",
    "title": "Class AddChannelMembership | EchoHub Documentation",
    "summary": "Class AddChannelMembership Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260219181720_AddChannelMembership\")] public class AddChannelMembership : Migration Inheritance object Migration AddChannelMembership Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddChannelMembership() public AddChannelMembership() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html",
    "title": "Class AddChannelPasswordHash | EchoHub Documentation",
    "summary": "Class AddChannelPasswordHash Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260715232856_AddChannelPasswordHash\")] public class AddChannelPasswordHash : Migration Inheritance object Migration AddChannelPasswordHash Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddChannelPasswordHash() public AddChannelPasswordHash() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html",
    "title": "Class AddEncryptionSupport | EchoHub Documentation",
    "summary": "Class AddEncryptionSupport Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260220133627_AddEncryptionSupport\")] public class AddEncryptionSupport : Migration Inheritance object Migration AddEncryptionSupport Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddEncryptionSupport() public AddEncryptionSupport() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddInvitesAndReplies.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddInvitesAndReplies.html",
    "title": "Class AddInvitesAndReplies | EchoHub Documentation",
    "summary": "Class AddInvitesAndReplies Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260717165218_AddInvitesAndReplies\")] public class AddInvitesAndReplies : Migration Inheritance object Migration AddInvitesAndReplies Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddInvitesAndReplies() public AddInvitesAndReplies() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html",
    "title": "Class AddMessageAttachments | EchoHub Documentation",
    "summary": "Class AddMessageAttachments Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260716020211_AddMessageAttachments\")] public class AddMessageAttachments : Migration Inheritance object Migration AddMessageAttachments Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddMessageAttachments() public AddMessageAttachments() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html",
    "title": "Class AddMessageEmbed | EchoHub Documentation",
    "summary": "Class AddMessageEmbed Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260219201704_AddMessageEmbed\")] public class AddMessageEmbed : Migration Inheritance object Migration AddMessageEmbed Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddMessageEmbed() public AddMessageEmbed() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddModerationRoles.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddModerationRoles.html",
    "title": "Class AddModerationRoles | EchoHub Documentation",
    "summary": "Class AddModerationRoles Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260219162414_AddModerationRoles\")] public class AddModerationRoles : Migration Inheritance object Migration AddModerationRoles Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddModerationRoles() public AddModerationRoles() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.AddServerStatsReports.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.AddServerStatsReports.html",
    "title": "Class AddServerStatsReports | EchoHub Documentation",
    "summary": "Class AddServerStatsReports Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260717200332_AddServerStatsReports\")] public class AddServerStatsReports : Migration Inheritance object Migration AddServerStatsReports Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AddServerStatsReports() public AddServerStatsReports() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.InitialCreate.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.InitialCreate.html",
    "title": "Class InitialCreate | EchoHub Documentation",
    "summary": "Class InitialCreate Namespace EchoHub.Server.Data.Migrations Assembly EchoHub.Server.dll [DbContext(typeof(EchoHubDbContext))] [Migration(\"20260219023113_InitialCreate\")] public class InitialCreate : Migration Inheritance object Migration InitialCreate Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors InitialCreate() public InitialCreate() Methods BuildTargetModel(ModelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder) Parameters modelBuilder ModelBuilder Down(MigrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder Up(MigrationBuilder) protected override void Up(MigrationBuilder migrationBuilder) Parameters migrationBuilder MigrationBuilder"
  },
  "api/server/EchoHub.Server.Data.Migrations.html": {
    "href": "api/server/EchoHub.Server.Data.Migrations.html",
    "title": "Namespace EchoHub.Server.Data.Migrations | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Data.Migrations Classes AddAttachmentFileSize AddChannelEncryptionEnvelope AddChannelIsPublic AddChannelIsSystem AddChannelMembership AddChannelPasswordHash AddEncryptionSupport AddInvitesAndReplies AddMessageAttachments AddMessageEmbed AddModerationRoles AddServerStatsReports InitialCreate"
  },
  "api/server/EchoHub.Server.Data.html": {
    "href": "api/server/EchoHub.Server.Data.html",
    "title": "Namespace EchoHub.Server.Data | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Data Classes EchoHubDbContext"
  },
  "api/server/EchoHub.Server.Hubs.ChatHub.html": {
    "href": "api/server/EchoHub.Server.Hubs.ChatHub.html",
    "title": "Class ChatHub | EchoHub Documentation",
    "summary": "Class ChatHub Namespace EchoHub.Server.Hubs Assembly EchoHub.Server.dll [Authorize] public class ChatHub : Hub<IEchoHubClient>, IDisposable Inheritance object Hub Hub<IEchoHubClient> ChatHub Implements IDisposable Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatHub(IChatService, IChannelService, ILogger<ChatHub>) public ChatHub(IChatService chatService, IChannelService channelService, ILogger<ChatHub> logger) Parameters chatService IChatService channelService IChannelService logger ILogger<ChatHub> Methods GetChannelHistory(string, int, int) public Task<List<MessageDto>> GetChannelHistory(string channelName, int count = 100, int offset = 0) Parameters channelName string count int offset int Returns Task<List<MessageDto>> GetOnlineUsers(string) public Task<List<UserPresenceDto>> GetOnlineUsers(string channelName) Parameters channelName string Returns Task<List<UserPresenceDto>> JoinChannel(string, string?) public Task<JoinChannelResult> JoinChannel(string channelName, string? password = null) Parameters channelName string password string Returns Task<JoinChannelResult> LeaveChannel(string) public Task LeaveChannel(string channelName) Parameters channelName string Returns Task OnConnectedAsync() public override Task OnConnectedAsync() Returns Task OnDisconnectedAsync(Exception?) public override Task OnDisconnectedAsync(Exception? exception) Parameters exception Exception Returns Task SendMessage(string, string, Guid?) public Task SendMessage(string channelName, string content, Guid? replyToMessageId = null) Parameters channelName string content string replyToMessageId Guid? Returns Task UpdateStatus(UserStatus, string?) public Task UpdateStatus(UserStatus status, string? statusMessage) Parameters status UserStatus statusMessage string Returns Task"
  },
  "api/server/EchoHub.Server.Hubs.html": {
    "href": "api/server/EchoHub.Server.Hubs.html",
    "title": "Namespace EchoHub.Server.Hubs | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Hubs Classes ChatHub"
  },
  "api/server/EchoHub.Server.Services.ChannelService.html": {
    "href": "api/server/EchoHub.Server.Services.ChannelService.html",
    "title": "Class ChannelService | EchoHub Documentation",
    "summary": "Class ChannelService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class ChannelService : IChannelService Inheritance object ChannelService Implements IChannelService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelService(IServiceScopeFactory, PresenceTracker, SpamGuard, ServerLogsService, ILogger<ChannelService>) public ChannelService(IServiceScopeFactory scopeFactory, PresenceTracker presenceTracker, SpamGuard spamGuard, ServerLogsService serverLogs, ILogger<ChannelService> logger) Parameters scopeFactory IServiceScopeFactory presenceTracker PresenceTracker spamGuard SpamGuard serverLogs ServerLogsService logger ILogger<ChannelService> Methods CreateChannelAsync(Guid, string, string?, bool, string?, string?, string?) public Task<ChannelOperationResult> CreateChannelAsync(Guid creatorUserId, string name, string? topic, bool isPublic, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters creatorUserId Guid name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task<ChannelOperationResult> DeleteChannelAsync(Guid, string) public Task<ChannelOperationResult> DeleteChannelAsync(Guid callerUserId, string channelName) Parameters callerUserId Guid channelName string Returns Task<ChannelOperationResult> EnsureChannelMembershipAsync(Guid, string, string?) public Task<(bool Success, string? Error, bool PasswordRequired)> EnsureChannelMembershipAsync(Guid userId, string channelName, string? password = null) Parameters userId Guid channelName string password string Returns Task<(bool Success, string Error, bool PasswordRequired)> EnsureSystemChannelAsync(string, string?) public Task<ChannelDto> EnsureSystemChannelAsync(string channelName, string? topic = null) Parameters channelName string topic string Returns Task<ChannelDto> GetChannelByNameAsync(string) public Task<ChannelDto?> GetChannelByNameAsync(string channelName) Parameters channelName string Returns Task<ChannelDto> GetChannelCryptoAsync(string) public Task<ChannelCryptoDto?> GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task<ChannelCryptoDto> GetChannelKeyEnvelopeAsync(string) public Task<(string? EncryptionSalt, string? WrappedRoomKey)> GetChannelKeyEnvelopeAsync(string channelName) Parameters channelName string Returns Task<(string EncryptionSalt, string WrappedRoomKey)> GetChannelListAsync() public Task<List<ChannelListItem>> GetChannelListAsync() Returns Task<List<ChannelListItem>> GetChannelMetaAsync(string) public Task<ChannelMetaDto?> GetChannelMetaAsync(string channelName) Parameters channelName string Returns Task<ChannelMetaDto> GetChannelTopicAsync(string) public Task<(string? Topic, bool Exists)> GetChannelTopicAsync(string channelName) Parameters channelName string Returns Task<(string Topic, bool Exists)> GetChannelsAsync(Guid, int, int) public Task<PaginatedResponse<ChannelDto>> GetChannelsAsync(Guid userId, int offset, int limit) Parameters userId Guid offset int limit int Returns Task<PaginatedResponse<ChannelDto>> RekeyChannelAsync(Guid, string, string, string, string, string) Changes an encrypted channel's passphrase by swapping the join-gate hash and the wrapped room key. The room content key itself never changes, so history stays readable — the client re-wraps it under the new passphrase-derived key. Creator only: admins cannot rekey a room whose passphrase they don't know. public Task<ChannelOperationResult> RekeyChannelAsync(Guid callerUserId, string channelName, string oldPassword, string newPassword, string newEncryptionSalt, string newWrappedRoomKey) Parameters callerUserId Guid channelName string oldPassword string newPassword string newEncryptionSalt string newWrappedRoomKey string Returns Task<ChannelOperationResult> SetChannelPasswordAsync(Guid, string, string?) Sets, changes, or clears (null) a channel's join password. Creator or admin only. Not available on end-to-end encrypted channels — those change passphrase via RekeyChannelAsync(Guid, string, string, string, string, string) so the room key envelope stays consistent. public Task<ChannelOperationResult> SetChannelPasswordAsync(Guid callerUserId, string channelName, string? password) Parameters callerUserId Guid channelName string password string Returns Task<ChannelOperationResult> UpdateTopicAsync(Guid, string, string?) public Task<ChannelOperationResult> UpdateTopicAsync(Guid callerUserId, string channelName, string? topic) Parameters callerUserId Guid channelName string topic string Returns Task<ChannelOperationResult>"
  },
  "api/server/EchoHub.Server.Services.ChatService.html": {
    "href": "api/server/EchoHub.Server.Services.ChatService.html",
    "title": "Class ChatService | EchoHub Documentation",
    "summary": "Class ChatService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class ChatService : IChatService Inheritance object ChatService Implements IChatService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatService(IServiceScopeFactory, PresenceTracker, IEnumerable<IChatBroadcaster>, LinkEmbedService, IMessageEncryptionService, IChannelService, FileStorageService, SpamGuard, ServerLogsService, ServerStatsCollector, ILogger<ChatService>) public ChatService(IServiceScopeFactory scopeFactory, PresenceTracker presenceTracker, IEnumerable<IChatBroadcaster> broadcasters, LinkEmbedService embedService, IMessageEncryptionService encryption, IChannelService channelService, FileStorageService fileStorage, SpamGuard spamGuard, ServerLogsService serverLogs, ServerStatsCollector statsCollector, ILogger<ChatService> logger) Parameters scopeFactory IServiceScopeFactory presenceTracker PresenceTracker broadcasters IEnumerable<IChatBroadcaster> embedService LinkEmbedService encryption IMessageEncryptionService channelService IChannelService fileStorage FileStorageService spamGuard SpamGuard serverLogs ServerLogsService statsCollector ServerStatsCollector logger ILogger<ChatService> Methods BroadcastChannelDeletedAsync(string) public Task BroadcastChannelDeletedAsync(string channelName) Parameters channelName string Returns Task BroadcastChannelUpdatedAsync(ChannelDto, string?) public Task BroadcastChannelUpdatedAsync(ChannelDto channel, string? channelName = null) Parameters channel ChannelDto channelName string Returns Task BroadcastMessageAsync(string, MessageDto) public Task BroadcastMessageAsync(string channelName, MessageDto message) Parameters channelName string message MessageDto Returns Task GetChannelHistoryAsync(string, int, int) public Task<List<MessageDto>> GetChannelHistoryAsync(string channelName, int count, int offset = 0) Parameters channelName string count int offset int Returns Task<List<MessageDto>> GetChannelsForUserAsync(string) public Task<List<string>> GetChannelsForUserAsync(string username) Parameters username string Returns Task<List<string>> GetOnlineUsersAsync(string) public Task<List<UserPresenceDto>> GetOnlineUsersAsync(string channelName) Parameters channelName string Returns Task<List<UserPresenceDto>> JoinChannelAsync(string, Guid, string, string, string?) public Task<(List<MessageDto> History, string? Error, bool PasswordRequired)> JoinChannelAsync(string connectionId, Guid userId, string username, string channelName, string? password = null) Parameters connectionId string userId Guid username string channelName string password string Returns Task<(List<MessageDto> History, string Error, bool PasswordRequired)> LeaveChannelAsync(string, string, string) public Task LeaveChannelAsync(string connectionId, string username, string channelName) Parameters connectionId string username string channelName string Returns Task SendMessageAsync(Guid, string, string, string, string?, Guid?) public Task<string?> SendMessageAsync(Guid userId, string username, string channelName, string content, string? originConnectionId = null, Guid? replyToMessageId = null) Parameters userId Guid username string channelName string content string originConnectionId string replyToMessageId Guid? Returns Task<string> UpdateStatusAsync(Guid, string, UserStatus, string?) public Task<string?> UpdateStatusAsync(Guid userId, string username, UserStatus status, string? statusMessage) Parameters userId Guid username string status UserStatus statusMessage string Returns Task<string> UserConnectedAsync(string, Guid, string) public Task UserConnectedAsync(string connectionId, Guid userId, string username) Parameters connectionId string userId Guid username string Returns Task UserDisconnectedAsync(string) public Task<string?> UserDisconnectedAsync(string connectionId) Parameters connectionId string Returns Task<string>"
  },
  "api/server/EchoHub.Server.Services.DirectoryClaimStore.html": {
    "href": "api/server/EchoHub.Server.Services.DirectoryClaimStore.html",
    "title": "Class DirectoryClaimStore | EchoHub Documentation",
    "summary": "Class DirectoryClaimStore Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll Persists and exposes the EchoHubSpace directory claim — the opaque token issued on first registration and the row's stable ServerId. Also surfaces ephemeral registration status (success/failure code, conflicting hosts) for operator-facing endpoints. Persistence uses atomic write (tmp + rename). Treat the file contents as a secret. public sealed class DirectoryClaimStore Inheritance object DirectoryClaimStore Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors DirectoryClaimStore(IConfiguration, ILogger<DirectoryClaimStore>) public DirectoryClaimStore(IConfiguration configuration, ILogger<DirectoryClaimStore> logger) Parameters configuration IConfiguration logger ILogger<DirectoryClaimStore> Properties ClaimToken public string? ClaimToken { get; } Property Value string FilePath public string FilePath { get; } Property Value string ServerId public Guid? ServerId { get; } Property Value Guid? Status public RegistrationStatus Status { get; } Property Value RegistrationStatus Methods SaveClaimAsync(string, Guid, CancellationToken) Persist a freshly-issued claim token alongside the server's stable ServerId. Called exactly once per row's lifetime — on first claim. Atomic on-disk swap. public Task SaveClaimAsync(string claimToken, Guid serverId, CancellationToken ct = default) Parameters claimToken string serverId Guid ct CancellationToken Returns Task SetFailure(string, string[]?) public void SetFailure(string errorCode, string[]? conflictingHosts) Parameters errorCode string conflictingHosts string[] SetSuccess(Guid) public void SetSuccess(Guid serverId) Parameters serverId Guid UpdateServerIdAsync(Guid, CancellationToken) Update only the ServerId — used when re-registering with an existing token (Success path, hub returns ServerId again but no fresh token). No-op if the value is unchanged. public Task UpdateServerIdAsync(Guid serverId, CancellationToken ct = default) Parameters serverId Guid ct CancellationToken Returns Task"
  },
  "api/server/EchoHub.Server.Services.FileCleanupService.html": {
    "href": "api/server/EchoHub.Server.Services.FileCleanupService.html",
    "title": "Class FileCleanupService | EchoHub Documentation",
    "summary": "Class FileCleanupService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public sealed class FileCleanupService : BackgroundService, IDisposable Inheritance object BackgroundService FileCleanupService Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors FileCleanupService(IConfiguration, ILogger<FileCleanupService>) public FileCleanupService(IConfiguration configuration, ILogger<FileCleanupService> logger) Parameters configuration IConfiguration logger ILogger<FileCleanupService> Methods ExecuteAsync(CancellationToken) protected override Task ExecuteAsync(CancellationToken stoppingToken) Parameters stoppingToken CancellationToken Returns Task"
  },
  "api/server/EchoHub.Server.Services.FileStorageService.html": {
    "href": "api/server/EchoHub.Server.Services.FileStorageService.html",
    "title": "Class FileStorageService | EchoHub Documentation",
    "summary": "Class FileStorageService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class FileStorageService Inheritance object FileStorageService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors FileStorageService(IConfiguration) public FileStorageService(IConfiguration configuration) Parameters configuration IConfiguration Methods DeleteFile(string) public void DeleteFile(string fileId) Parameters fileId string GetFilePath(string) public string? GetFilePath(string fileId) Parameters fileId string Returns string GetStoredFileIds() Returns the set of stored file ids (filenames without extension) currently on disk. One directory scan, so callers can bulk-check many attachments without a glob per file. public HashSet<string> GetStoredFileIds() Returns HashSet<string> SaveFileAsync(Stream, string) public Task<(string fileId, string filePath)> SaveFileAsync(Stream stream, string fileName) Parameters stream Stream fileName string Returns Task<(string EncryptionSalt, string WrappedRoomKey)>"
  },
  "api/server/EchoHub.Server.Services.LinkEmbedService.html": {
    "href": "api/server/EchoHub.Server.Services.LinkEmbedService.html",
    "title": "Class LinkEmbedService | EchoHub Documentation",
    "summary": "Class LinkEmbedService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class LinkEmbedService Inheritance object LinkEmbedService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors LinkEmbedService(IHttpClientFactory, ILogger<LinkEmbedService>) public LinkEmbedService(IHttpClientFactory httpClientFactory, ILogger<LinkEmbedService> logger) Parameters httpClientFactory IHttpClientFactory logger ILogger<LinkEmbedService> Methods TryGetEmbedsAsync(string) Detect all URLs in message content and attempt to fetch OG embed data for each. Returns null if no URLs found or all fetches fail. Never throws — all errors are caught internally. public Task<List<EmbedDto>?> TryGetEmbedsAsync(string content) Parameters content string Returns Task<List<EmbedDto>>"
  },
  "api/server/EchoHub.Server.Services.MessageEncryptionService.html": {
    "href": "api/server/EchoHub.Server.Services.MessageEncryptionService.html",
    "title": "Class MessageEncryptionService | EchoHub Documentation",
    "summary": "Class MessageEncryptionService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class MessageEncryptionService : IMessageEncryptionService Inheritance object MessageEncryptionService Implements IMessageEncryptionService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors MessageEncryptionService(IConfiguration, ILogger<MessageEncryptionService>) public MessageEncryptionService(IConfiguration configuration, ILogger<MessageEncryptionService> logger) Parameters configuration IConfiguration logger ILogger<MessageEncryptionService> Properties EncryptDatabaseEnabled Whether database content should be encrypted at rest (server setting). public bool EncryptDatabaseEnabled { get; } Property Value bool Methods Decrypt(string) public string Decrypt(string content) Parameters content string Returns string DecryptNullable(string?) public string? DecryptNullable(string? value) Parameters value string Returns string Encrypt(string) public string Encrypt(string plaintext) Parameters plaintext string Returns string EncryptNullable(string?) public string? EncryptNullable(string? value) Parameters value string Returns string"
  },
  "api/server/EchoHub.Server.Services.MuteExpirationService.html": {
    "href": "api/server/EchoHub.Server.Services.MuteExpirationService.html",
    "title": "Class MuteExpirationService | EchoHub Documentation",
    "summary": "Class MuteExpirationService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll Background service that periodically unmutes users whose timed mute has expired. public sealed class MuteExpirationService : BackgroundService, IDisposable Inheritance object BackgroundService MuteExpirationService Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors MuteExpirationService(IServiceScopeFactory, ILogger<MuteExpirationService>) public MuteExpirationService(IServiceScopeFactory scopeFactory, ILogger<MuteExpirationService> logger) Parameters scopeFactory IServiceScopeFactory logger ILogger<MuteExpirationService> Methods ExecuteAsync(CancellationToken) protected override Task ExecuteAsync(CancellationToken stoppingToken) Parameters stoppingToken CancellationToken Returns Task"
  },
  "api/server/EchoHub.Server.Services.PresenceTracker.html": {
    "href": "api/server/EchoHub.Server.Services.PresenceTracker.html",
    "title": "Class PresenceTracker | EchoHub Documentation",
    "summary": "Class PresenceTracker Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class PresenceTracker Inheritance object PresenceTracker Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors PresenceTracker() public PresenceTracker() Methods ForceRemoveUser(string) Forcibly remove a user from all tracking. Returns their connection IDs and channels so the caller can broadcast departures and force-disconnect connections. public (List<string> ConnectionIds, List<string> Channels) ForceRemoveUser(string username) Parameters username string Returns (List<string> ConnectionIds, List<string> Channels) GetChannelsForUser(string) public List<string> GetChannelsForUser(string username) Parameters username string Returns List<string> GetConnectionsInChannels(List<string>) Get all unique connection IDs for users who share any of the given channels. public List<string> GetConnectionsInChannels(List<string> channels) Parameters channels List<string> Returns List<string> GetOnlineUserCount() public int GetOnlineUserCount() Returns int GetOnlineUsersInChannel(string) public List<string> GetOnlineUsersInChannel(string channelName) Parameters channelName string Returns List<string> GetUsernameForConnection(string) public string? GetUsernameForConnection(string connectionId) Parameters connectionId string Returns string IsIrcOnly(string) True when the user is online exclusively through the IRC gateway. A user who also has a native client connected has full features, so they don't count as IRC-only. public bool IsIrcOnly(string username) Parameters username string Returns bool IsOnline(string) public bool IsOnline(string username) Parameters username string Returns bool JoinChannel(string, string) Returns true if this is a new join, false if the user was already in the channel. public bool JoinChannel(string username, string channelName) Parameters username string channelName string Returns bool LeaveChannel(string, string) public void LeaveChannel(string username, string channelName) Parameters username string channelName string UserConnected(string, Guid, string) public void UserConnected(string connectionId, Guid userId, string username) Parameters connectionId string userId Guid username string UserDisconnected(string) public string? UserDisconnected(string connectionId) Parameters connectionId string Returns string Events UserCountChanged Raised when the distinct online user count changes (multi-connection users only fire once). public event Action<int>? UserCountChanged Event Type Action<int>"
  },
  "api/server/EchoHub.Server.Services.RegistrationStatus.html": {
    "href": "api/server/EchoHub.Server.Services.RegistrationStatus.html",
    "title": "Class RegistrationStatus | EchoHub Documentation",
    "summary": "Class RegistrationStatus Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public sealed record RegistrationStatus : IEquatable<RegistrationStatus> Inheritance object RegistrationStatus Implements IEquatable<RegistrationStatus> Inherited Members object.GetType() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors RegistrationStatus(bool, Guid?, DateTimeOffset?, string?, string[]?) public RegistrationStatus(bool IsRegistered, Guid? ServerId, DateTimeOffset? LastRegisteredAt, string? LastError, string[]? ConflictingHosts) Parameters IsRegistered bool ServerId Guid? LastRegisteredAt DateTimeOffset? LastError string ConflictingHosts string[] Properties ConflictingHosts public string[]? ConflictingHosts { get; init; } Property Value string[] IsRegistered public bool IsRegistered { get; init; } Property Value bool LastError public string? LastError { get; init; } Property Value string LastRegisteredAt public DateTimeOffset? LastRegisteredAt { get; init; } Property Value DateTimeOffset? ServerId public Guid? ServerId { get; init; } Property Value Guid? Methods Deconstruct(out bool, out Guid?, out DateTimeOffset?, out string?, out string[]?) public void Deconstruct(out bool IsRegistered, out Guid? ServerId, out DateTimeOffset? LastRegisteredAt, out string? LastError, out string[]? ConflictingHosts) Parameters IsRegistered bool ServerId Guid? LastRegisteredAt DateTimeOffset? LastError string ConflictingHosts string[] Equals(RegistrationStatus?) public bool Equals(RegistrationStatus? other) Parameters other RegistrationStatus Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int ToString() public override string ToString() Returns string Operators operator ==(RegistrationStatus?, RegistrationStatus?) public static bool operator ==(RegistrationStatus? left, RegistrationStatus? right) Parameters left RegistrationStatus right RegistrationStatus Returns bool operator !=(RegistrationStatus?, RegistrationStatus?) public static bool operator !=(RegistrationStatus? left, RegistrationStatus? right) Parameters left RegistrationStatus right RegistrationStatus Returns bool"
  },
  "api/server/EchoHub.Server.Services.ServerDirectoryService.html": {
    "href": "api/server/EchoHub.Server.Services.ServerDirectoryService.html",
    "title": "Class ServerDirectoryService | EchoHub Documentation",
    "summary": "Class ServerDirectoryService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public sealed class ServerDirectoryService : BackgroundService, IDisposable Inheritance object BackgroundService ServerDirectoryService Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerDirectoryService(IConfiguration, PresenceTracker, DirectoryClaimStore, ILogger<ServerDirectoryService>) public ServerDirectoryService(IConfiguration configuration, PresenceTracker presenceTracker, DirectoryClaimStore claimStore, ILogger<ServerDirectoryService> logger) Parameters configuration IConfiguration presenceTracker PresenceTracker claimStore DirectoryClaimStore logger ILogger<ServerDirectoryService> Methods ExecuteAsync(CancellationToken) protected override Task ExecuteAsync(CancellationToken stoppingToken) Parameters stoppingToken CancellationToken Returns Task StopAsync(CancellationToken) public override Task StopAsync(CancellationToken cancellationToken) Parameters cancellationToken CancellationToken Returns Task"
  },
  "api/server/EchoHub.Server.Services.ServerLogs.LogBacklogEntry.html": {
    "href": "api/server/EchoHub.Server.Services.ServerLogs.LogBacklogEntry.html",
    "title": "Class LogBacklogEntry | EchoHub Documentation",
    "summary": "Class LogBacklogEntry Namespace EchoHub.Server.Services.ServerLogs Assembly EchoHub.Server.dll A backlog entry read from the log file: one timestamped log line plus any continuation lines (exception stack traces) that followed it. public record LogBacklogEntry : IEquatable<LogBacklogEntry> Inheritance object LogBacklogEntry Implements IEquatable<LogBacklogEntry> Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors LogBacklogEntry(LogBacklogEntry) protected LogBacklogEntry(LogBacklogEntry original) Parameters original LogBacklogEntry LogBacklogEntry(DateTimeOffset, string) A backlog entry read from the log file: one timestamped log line plus any continuation lines (exception stack traces) that followed it. public LogBacklogEntry(DateTimeOffset Timestamp, string Content) Parameters Timestamp DateTimeOffset Content string Properties Content public string Content { get; init; } Property Value string EqualityContract protected virtual Type EqualityContract { get; } Property Value Type Timestamp public DateTimeOffset Timestamp { get; init; } Property Value DateTimeOffset Methods Deconstruct(out DateTimeOffset, out string) public void Deconstruct(out DateTimeOffset Timestamp, out string Content) Parameters Timestamp DateTimeOffset Content string Equals(LogBacklogEntry?) public virtual bool Equals(LogBacklogEntry? other) Parameters other LogBacklogEntry Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(LogBacklogEntry?, LogBacklogEntry?) public static bool operator ==(LogBacklogEntry? left, LogBacklogEntry? right) Parameters left LogBacklogEntry right LogBacklogEntry Returns bool operator !=(LogBacklogEntry?, LogBacklogEntry?) public static bool operator !=(LogBacklogEntry? left, LogBacklogEntry? right) Parameters left LogBacklogEntry right LogBacklogEntry Returns bool"
  },
  "api/server/EchoHub.Server.Services.ServerLogs.ServerLogsService.html": {
    "href": "api/server/EchoHub.Server.Services.ServerLogs.ServerLogsService.html",
    "title": "Class ServerLogsService | EchoHub Documentation",
    "summary": "Class ServerLogsService Namespace EchoHub.Server.Services.ServerLogs Assembly EchoHub.Server.dll Shared logic for the live log room: room identity, the role gate, and reading the backlog tail from the current rolling log file. The file stays the only persistence — log lines are never stored as messages. public sealed class ServerLogsService Inheritance object ServerLogsService Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerLogsService(ServerLogsOptions) public ServerLogsService(ServerLogsOptions options) Parameters options ServerLogsOptions Fields RoomTopic public const string RoomTopic = \"Live server logs — read-only\" Field Value string SenderName Username shown as the sender of streamed log messages. public const string SenderName = \"server\" Field Value string Properties Options public ServerLogsOptions Options { get; } Property Value ServerLogsOptions Methods CanView(ServerRole) Whether a user with this role may see and join the log room. public bool CanView(ServerRole role) Parameters role ServerRole Returns bool GroupIntoEntries(IReadOnlyList<string>, bool, int) Groups raw file lines into entries by their timestamp prefix. Public for tests. public static IReadOnlyList<LogBacklogEntry> GroupIntoEntries(IReadOnlyList<string> lines, bool skipLeadingContinuations, int maxEntries) Parameters lines IReadOnlyList<string> skipLeadingContinuations bool maxEntries int Returns IReadOnlyList<LogBacklogEntry> IsLogsChannel(string) Whether the given channel is the (enabled) live log room. public bool IsLogsChannel(string channelName) Parameters channelName string Returns bool ReadBacklog() Reads the last BacklogLines entries from the newest log file. A line starting with a timestamp begins a new entry; continuation lines (stack traces) stay attached to the entry above them. Best-effort: any I/O problem yields an empty backlog rather than failing the join. public IReadOnlyList<LogBacklogEntry> ReadBacklog() Returns IReadOnlyList<LogBacklogEntry>"
  },
  "api/server/EchoHub.Server.Services.ServerLogs.ServerLogsSink.html": {
    "href": "api/server/EchoHub.Server.Services.ServerLogs.ServerLogsSink.html",
    "title": "Class ServerLogsSink | EchoHub Documentation",
    "summary": "Class ServerLogsSink Namespace EchoHub.Server.Services.ServerLogs Assembly EchoHub.Server.dll Serilog sink feeding the live log room. Events are queued in a bounded drop-oldest buffer and consumed by ServerLogsStreamService; nothing is written to the database. Events emitted by the streaming pipeline itself (the stream service and SignalR transport internals) are dropped here so a broadcast that logs — e.g. a transport warning on a dead connection — can never enter a log → broadcast → log feedback loop. public sealed class ServerLogsSink : ILogEventSink Inheritance object ServerLogsSink Implements ILogEventSink Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerLogsSink(ServerLogsOptions) public ServerLogsSink(ServerLogsOptions options) Parameters options ServerLogsOptions Properties Reader public ChannelReader<LogEvent> Reader { get; } Property Value ChannelReader<LogEvent> Methods Emit(LogEvent) public void Emit(LogEvent logEvent) Parameters logEvent LogEvent"
  },
  "api/server/EchoHub.Server.Services.ServerLogs.ServerLogsStreamService.html": {
    "href": "api/server/EchoHub.Server.Services.ServerLogs.ServerLogsStreamService.html",
    "title": "Class ServerLogsStreamService | EchoHub Documentation",
    "summary": "Class ServerLogsStreamService Namespace EchoHub.Server.Services.ServerLogs Assembly EchoHub.Server.dll Streams queued log events to the live log room as ephemeral messages: SignalR only (the IRC gateway never sees them) and no database rows. Ensures the room exists before streaming, recreating it on the fly if it was deleted. This class must never log from its streaming path — its namespace is excluded by ServerLogsSink as a second line of defense, but the primary rule is simply not to log per event, otherwise every streamed line would spawn another. public sealed class ServerLogsStreamService : BackgroundService, IDisposable Inheritance object BackgroundService ServerLogsStreamService Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerLogsStreamService(ServerLogsSink, ServerLogsOptions, IChannelService, IMessageEncryptionService, IServiceProvider) public ServerLogsStreamService(ServerLogsSink sink, ServerLogsOptions options, IChannelService channelService, IMessageEncryptionService encryption, IServiceProvider serviceProvider) Parameters sink ServerLogsSink options ServerLogsOptions channelService IChannelService encryption IMessageEncryptionService serviceProvider IServiceProvider Methods ExecuteAsync(CancellationToken) protected override Task ExecuteAsync(CancellationToken stoppingToken) Parameters stoppingToken CancellationToken Returns Task Format(LogEvent) Formats an event like the file sink's template, minus the timestamp (clients render their own). Public for tests. public static string Format(LogEvent logEvent) Parameters logEvent LogEvent Returns string"
  },
  "api/server/EchoHub.Server.Services.ServerLogs.html": {
    "href": "api/server/EchoHub.Server.Services.ServerLogs.html",
    "title": "Namespace EchoHub.Server.Services.ServerLogs | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Services.ServerLogs Classes LogBacklogEntry A backlog entry read from the log file: one timestamped log line plus any continuation lines (exception stack traces) that followed it. ServerLogsService Shared logic for the live log room: room identity, the role gate, and reading the backlog tail from the current rolling log file. The file stays the only persistence — log lines are never stored as messages. ServerLogsSink Serilog sink feeding the live log room. Events are queued in a bounded drop-oldest buffer and consumed by ServerLogsStreamService; nothing is written to the database. Events emitted by the streaming pipeline itself (the stream service and SignalR transport internals) are dropped here so a broadcast that logs — e.g. a transport warning on a dead connection — can never enter a log → broadcast → log feedback loop. ServerLogsStreamService Streams queued log events to the live log room as ephemeral messages: SignalR only (the IRC gateway never sees them) and no database rows. Ensures the room exists before streaming, recreating it on the fly if it was deleted. This class must never log from its streaming path — its namespace is excluded by ServerLogsSink as a second line of defense, but the primary rule is simply not to log per event, otherwise every streamed line would spawn another."
  },
  "api/server/EchoHub.Server.Services.SignalRBroadcaster.html": {
    "href": "api/server/EchoHub.Server.Services.SignalRBroadcaster.html",
    "title": "Class SignalRBroadcaster | EchoHub Documentation",
    "summary": "Class SignalRBroadcaster Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class SignalRBroadcaster : IChatBroadcaster Inheritance object SignalRBroadcaster Implements IChatBroadcaster Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors SignalRBroadcaster(IServiceProvider, PresenceTracker) public SignalRBroadcaster(IServiceProvider serviceProvider, PresenceTracker presenceTracker) Parameters serviceProvider IServiceProvider presenceTracker PresenceTracker Methods ForceDisconnectUserAsync(List<string>, string) public Task ForceDisconnectUserAsync(List<string> connectionIds, string reason) Parameters connectionIds List<string> reason string Returns Task SendChannelDeletedAsync(string) public Task SendChannelDeletedAsync(string channelName) Parameters channelName string Returns Task SendChannelNukedAsync(string) public Task SendChannelNukedAsync(string channelName) Parameters channelName string Returns Task SendChannelUpdatedAsync(ChannelDto, string?) public Task SendChannelUpdatedAsync(ChannelDto channel, string? channelName = null) Parameters channel ChannelDto channelName string Returns Task SendErrorAsync(string, string) public Task SendErrorAsync(string connectionId, string message) Parameters connectionId string message string Returns Task SendMessageDeletedAsync(string, Guid) public Task SendMessageDeletedAsync(string channelName, Guid messageId) Parameters channelName string messageId Guid Returns Task SendMessageToChannelAsync(string, MessageDto, string?) Broadcast a chat message to a channel. excludeConnectionId is the connection the message originated from (IRC convention: never echo a message back to the connection that sent it — its client already displayed it locally). Other connections of the same user (e.g. an IRC session alongside a TUI session) still receive the message. public Task SendMessageToChannelAsync(string channelName, MessageDto message, string? excludeConnectionId = null) Parameters channelName string message MessageDto excludeConnectionId string Returns Task SendUserBannedAsync(string, string?) public Task SendUserBannedAsync(string username, string? reason) Parameters username string reason string Returns Task SendUserJoinedAsync(string, string, UserPresenceDto?, string?) public Task SendUserJoinedAsync(string channelName, string username, UserPresenceDto? presence, string? excludeConnectionId = null) Parameters channelName string username string presence UserPresenceDto excludeConnectionId string Returns Task SendUserKickedAsync(string, string, string?) public Task SendUserKickedAsync(string channelName, string username, string? reason) Parameters channelName string username string reason string Returns Task SendUserLeftAsync(string, string) public Task SendUserLeftAsync(string channelName, string username) Parameters channelName string username string Returns Task SendUserStatusChangedAsync(List<string>, UserPresenceDto) public Task SendUserStatusChangedAsync(List<string> channelNames, UserPresenceDto presence) Parameters channelNames List<string> presence UserPresenceDto Returns Task"
  },
  "api/server/EchoHub.Server.Services.SpamGuard.html": {
    "href": "api/server/EchoHub.Server.Services.SpamGuard.html",
    "title": "Class SpamGuard | EchoHub Documentation",
    "summary": "Class SpamGuard Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll In-memory, per-user spam protection consulted from ChatService (messages, joins) and ChannelService (channel creation), so every ingress protocol — SignalR and IRC alike — shares the same limits. State is per-process and never persisted; auto-mutes land in the normal mute store. Operates only on content the server already stores (for end-to-end encrypted rooms that is ciphertext) — nothing is decrypted. public sealed class SpamGuard Inheritance object SpamGuard Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors SpamGuard(SpamOptions) public SpamGuard(SpamOptions options) Parameters options SpamOptions Properties Enabled public bool Enabled { get; } Property Value bool Methods CheckChannelCreate(Guid, ServerRole, DateTimeOffset?) Checks a channel creation. Rejections count as violations but never auto-mute. public SpamVerdict CheckChannelCreate(Guid userId, ServerRole role, DateTimeOffset? nowOverride = null) Parameters userId Guid role ServerRole nowOverride DateTimeOffset? Returns SpamVerdict CheckJoin(Guid, ServerRole, DateTimeOffset?) Checks a channel join. Rejections count as violations but never auto-mute. public SpamVerdict CheckJoin(Guid userId, ServerRole role, DateTimeOffset? nowOverride = null) Parameters userId Guid role ServerRole nowOverride DateTimeOffset? Returns SpamVerdict CheckMessage(Guid, ServerRole, string, DateTimeOffset?) Checks a message send. Rejections count as violations; enough violations inside the violation window escalate to AutoMute (evaluated only on rejected messages, so a clean message never mutes anyone). public SpamVerdict CheckMessage(Guid userId, ServerRole role, string content, DateTimeOffset? nowOverride = null) Parameters userId Guid role ServerRole content string nowOverride DateTimeOffset? Returns SpamVerdict"
  },
  "api/server/EchoHub.Server.Services.SpamVerdict.html": {
    "href": "api/server/EchoHub.Server.Services.SpamVerdict.html",
    "title": "Struct SpamVerdict | EchoHub Documentation",
    "summary": "Struct SpamVerdict Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public readonly struct SpamVerdict : IEquatable<SpamVerdict> Implements IEquatable<SpamVerdict> Inherited Members object.GetType() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors SpamVerdict(SpamVerdictKind, string?, TimeSpan) public SpamVerdict(SpamVerdictKind Kind, string? Reason = null, TimeSpan MuteDuration = default) Parameters Kind SpamVerdictKind Reason string MuteDuration TimeSpan Fields Allowed public static readonly SpamVerdict Allowed Field Value SpamVerdict Properties Kind public SpamVerdictKind Kind { get; init; } Property Value SpamVerdictKind MuteDuration public TimeSpan MuteDuration { get; init; } Property Value TimeSpan Reason public string? Reason { get; init; } Property Value string Methods Deconstruct(out SpamVerdictKind, out string?, out TimeSpan) public void Deconstruct(out SpamVerdictKind Kind, out string? Reason, out TimeSpan MuteDuration) Parameters Kind SpamVerdictKind Reason string MuteDuration TimeSpan Equals(SpamVerdict) public bool Equals(SpamVerdict other) Parameters other SpamVerdict Returns bool Equals(object) public override bool Equals(object obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int ToString() public override string ToString() Returns string Operators operator ==(SpamVerdict, SpamVerdict) public static bool operator ==(SpamVerdict left, SpamVerdict right) Parameters left SpamVerdict right SpamVerdict Returns bool operator !=(SpamVerdict, SpamVerdict) public static bool operator !=(SpamVerdict left, SpamVerdict right) Parameters left SpamVerdict right SpamVerdict Returns bool"
  },
  "api/server/EchoHub.Server.Services.SpamVerdictKind.html": {
    "href": "api/server/EchoHub.Server.Services.SpamVerdictKind.html",
    "title": "Enum SpamVerdictKind | EchoHub Documentation",
    "summary": "Enum SpamVerdictKind Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public enum SpamVerdictKind Fields Allowed = 0 AutoMute = 2 The user crossed the violation threshold — the caller should apply a timed mute. Rejected = 1"
  },
  "api/server/EchoHub.Server.Services.Stats.ServerStatsCollector.html": {
    "href": "api/server/EchoHub.Server.Services.Stats.ServerStatsCollector.html",
    "title": "Class ServerStatsCollector | EchoHub Documentation",
    "summary": "Class ServerStatsCollector Namespace EchoHub.Server.Services.Stats Assembly EchoHub.Server.dll Thread-safe, in-memory accumulator for server-activity counters that have no natural database timestamp to query after the fact — session connects/disconnects, moderation actions, and peak concurrency. The periodic stats-report job snapshots and resets these once per reporting window. Registered as a singleton; every increment is lock-free so it is safe to call from hot paths (connect/disconnect). public sealed class ServerStatsCollector Inheritance object ServerStatsCollector Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerStatsCollector() public ServerStatsCollector() Methods RecordBan() Record a ban action. public void RecordBan() RecordConnection(int) Record a session coming online, updating the running peak. public void RecordConnection(int onlineNow) Parameters onlineNow int RecordDisconnection(int) Record a session going offline, updating the running peak. public void RecordDisconnection(int onlineNow) Parameters onlineNow int RecordKick() Record a kick action. public void RecordKick() RecordOnline(int) Update the running maximum of concurrent online users (lock-free). public void RecordOnline(int onlineNow) Parameters onlineNow int SnapshotAndReset(int) Atomically read all counters and reset them for the next reporting window. The peak is reset to onlineNow so the next window's peak starts from the current concurrency rather than zero. public StatsCounters SnapshotAndReset(int onlineNow) Parameters onlineNow int Returns StatsCounters"
  },
  "api/server/EchoHub.Server.Services.Stats.ServerStatsReportService.html": {
    "href": "api/server/EchoHub.Server.Services.Stats.ServerStatsReportService.html",
    "title": "Class ServerStatsReportService | EchoHub Documentation",
    "summary": "Class ServerStatsReportService Namespace EchoHub.Server.Services.Stats Assembly EchoHub.Server.dll Background job that periodically snapshots server activity over a window, logs it as pretty-printed JSON (which also surfaces in the live server-logs room), and persists it to the database for historical trends. Interval and retention are configurable via the \"Stats\" section; the default cadence is every 6 hours. public sealed class ServerStatsReportService : BackgroundService, IDisposable Inheritance object BackgroundService ServerStatsReportService Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ServerStatsReportService(IServiceScopeFactory, PresenceTracker, ServerStatsCollector, StatsOptions, ILogger<ServerStatsReportService>) public ServerStatsReportService(IServiceScopeFactory scopeFactory, PresenceTracker presence, ServerStatsCollector collector, StatsOptions options, ILogger<ServerStatsReportService> logger) Parameters scopeFactory IServiceScopeFactory presence PresenceTracker collector ServerStatsCollector options StatsOptions logger ILogger<ServerStatsReportService> Methods ExecuteAsync(CancellationToken) protected override Task ExecuteAsync(CancellationToken stoppingToken) Parameters stoppingToken CancellationToken Returns Task"
  },
  "api/server/EchoHub.Server.Services.Stats.StatsCounters.html": {
    "href": "api/server/EchoHub.Server.Services.Stats.StatsCounters.html",
    "title": "Struct StatsCounters | EchoHub Documentation",
    "summary": "Struct StatsCounters Namespace EchoHub.Server.Services.Stats Assembly EchoHub.Server.dll Immutable snapshot of the counters held by ServerStatsCollector. public readonly struct StatsCounters : IEquatable<StatsCounters> Implements IEquatable<StatsCounters> Inherited Members object.GetType() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors StatsCounters(long, long, long, long, int) Immutable snapshot of the counters held by ServerStatsCollector. public StatsCounters(long Connections, long Disconnections, long Kicks, long Bans, int PeakOnline) Parameters Connections long Disconnections long Kicks long Bans long PeakOnline int Properties Bans public long Bans { get; init; } Property Value long Connections public long Connections { get; init; } Property Value long Disconnections public long Disconnections { get; init; } Property Value long Kicks public long Kicks { get; init; } Property Value long PeakOnline public int PeakOnline { get; init; } Property Value int Methods Deconstruct(out long, out long, out long, out long, out int) public void Deconstruct(out long Connections, out long Disconnections, out long Kicks, out long Bans, out int PeakOnline) Parameters Connections long Disconnections long Kicks long Bans long PeakOnline int Equals(StatsCounters) public bool Equals(StatsCounters other) Parameters other StatsCounters Returns bool Equals(object) public override bool Equals(object obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int ToString() public override string ToString() Returns string Operators operator ==(StatsCounters, StatsCounters) public static bool operator ==(StatsCounters left, StatsCounters right) Parameters left StatsCounters right StatsCounters Returns bool operator !=(StatsCounters, StatsCounters) public static bool operator !=(StatsCounters left, StatsCounters right) Parameters left StatsCounters right StatsCounters Returns bool"
  },
  "api/server/EchoHub.Server.Services.Stats.html": {
    "href": "api/server/EchoHub.Server.Services.Stats.html",
    "title": "Namespace EchoHub.Server.Services.Stats | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Services.Stats Classes ServerStatsCollector Thread-safe, in-memory accumulator for server-activity counters that have no natural database timestamp to query after the fact — session connects/disconnects, moderation actions, and peak concurrency. The periodic stats-report job snapshots and resets these once per reporting window. Registered as a singleton; every increment is lock-free so it is safe to call from hot paths (connect/disconnect). ServerStatsReportService Background job that periodically snapshots server activity over a window, logs it as pretty-printed JSON (which also surfaces in the live server-logs room), and persists it to the database for historical trends. Interval and retention are configurable via the \"Stats\" section; the default cadence is every 6 hours. Structs StatsCounters Immutable snapshot of the counters held by ServerStatsCollector."
  },
  "api/server/EchoHub.Server.Services.UserService.html": {
    "href": "api/server/EchoHub.Server.Services.UserService.html",
    "title": "Class UserService | EchoHub Documentation",
    "summary": "Class UserService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class UserService : IUserService Inheritance object UserService Implements IUserService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UserService(IServiceScopeFactory, IConfiguration) public UserService(IServiceScopeFactory scopeFactory, IConfiguration configuration) Parameters scopeFactory IServiceScopeFactory configuration IConfiguration Properties RegistrationMode Registration mode from config: \"open\" (default), \"invite\", or \"closed\". public string RegistrationMode { get; } Property Value string Methods AuthenticateUserAsync(string, string) public Task<UserOperationResult> AuthenticateUserAsync(string username, string password) Parameters username string password string Returns Task<UserOperationResult> GetUserByIdAsync(Guid) public Task<UserProfileDto?> GetUserByIdAsync(Guid userId) Parameters userId Guid Returns Task<UserProfileDto> GetUserProfileAsync(string) public Task<UserProfileDto?> GetUserProfileAsync(string username) Parameters username string Returns Task<UserProfileDto> RegisterUserAsync(string, string, string?, string?) public Task<UserOperationResult> RegisterUserAsync(string username, string password, string? displayName = null, string? inviteCode = null) Parameters username string password string displayName string inviteCode string Returns Task<UserOperationResult> SetAvatarAsync(Guid, string) public Task<UserOperationResult> SetAvatarAsync(Guid userId, string asciiArt) Parameters userId Guid asciiArt string Returns Task<UserOperationResult> UpdateProfileAsync(Guid, string?, string?, string?) public Task<UserOperationResult> UpdateProfileAsync(Guid userId, string? displayName, string? bio, string? nicknameColor) Parameters userId Guid displayName string bio string nicknameColor string Returns Task<UserOperationResult>"
  },
  "api/server/EchoHub.Server.Services.html": {
    "href": "api/server/EchoHub.Server.Services.html",
    "title": "Namespace EchoHub.Server.Services | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Services Classes ChannelService ChatService DirectoryClaimStore Persists and exposes the EchoHubSpace directory claim — the opaque token issued on first registration and the row's stable ServerId. Also surfaces ephemeral registration status (success/failure code, conflicting hosts) for operator-facing endpoints. Persistence uses atomic write (tmp + rename). Treat the file contents as a secret. FileCleanupService FileStorageService LinkEmbedService MessageEncryptionService MuteExpirationService Background service that periodically unmutes users whose timed mute has expired. PresenceTracker RegistrationStatus ServerDirectoryService SignalRBroadcaster SpamGuard In-memory, per-user spam protection consulted from ChatService (messages, joins) and ChannelService (channel creation), so every ingress protocol — SignalR and IRC alike — shares the same limits. State is per-process and never persisted; auto-mutes land in the normal mute store. Operates only on content the server already stores (for end-to-end encrypted rooms that is ciphertext) — nothing is decrypted. UserService Structs SpamVerdict Enums SpamVerdictKind"
  },
  "api/server/EchoHub.Server.Setup.DataMigrationService.html": {
    "href": "api/server/EchoHub.Server.Setup.DataMigrationService.html",
    "title": "Class DataMigrationService | EchoHub Documentation",
    "summary": "Class DataMigrationService Namespace EchoHub.Server.Setup Assembly EchoHub.Server.dll public static class DataMigrationService Inheritance object DataMigrationService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods AnsiToColorTags(string) Convert ANSI escape codes to printable color tags. \\x1b[38;2;R;G;Bm → {F:RRGGBB}, \\x1b[48;2;R;G;Bm → {B:RRGGBB}, \\x1b[0m → {X} public static string AnsiToColorTags(string text) Parameters text string Returns string RunAsync(IServiceProvider) public static Task RunAsync(IServiceProvider services) Parameters services IServiceProvider Returns Task"
  },
  "api/server/EchoHub.Server.Setup.DatabaseSetup.html": {
    "href": "api/server/EchoHub.Server.Setup.DatabaseSetup.html",
    "title": "Class DatabaseSetup | EchoHub Documentation",
    "summary": "Class DatabaseSetup Namespace EchoHub.Server.Setup Assembly EchoHub.Server.dll public static class DatabaseSetup Inheritance object DatabaseSetup Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods InitializeAsync(IServiceProvider) public static Task InitializeAsync(IServiceProvider services) Parameters services IServiceProvider Returns Task"
  },
  "api/server/EchoHub.Server.Setup.FirstRunSetup.html": {
    "href": "api/server/EchoHub.Server.Setup.FirstRunSetup.html",
    "title": "Class FirstRunSetup | EchoHub Documentation",
    "summary": "Class FirstRunSetup Namespace EchoHub.Server.Setup Assembly EchoHub.Server.dll public static class FirstRunSetup Inheritance object FirstRunSetup Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods EnsureAppSettings() public static void EnsureAppSettings()"
  },
  "api/server/EchoHub.Server.Setup.html": {
    "href": "api/server/EchoHub.Server.Setup.html",
    "title": "Namespace EchoHub.Server.Setup | EchoHub Documentation",
    "summary": "Namespace EchoHub.Server.Setup Classes DataMigrationService DatabaseSetup FirstRunSetup"
  },
  "articles/architecture.html": {
    "href": "articles/architecture.html",
    "title": "Architecture | EchoHub Documentation",
    "summary": "Architecture Overview EchoHub follows a decentralized model where each server is fully independent. There is no central authority or account federation. Users create one account per server. The server exposes two protocol interfaces to the same chat backend: IRC Client ──► TCP :6667 ──► IrcGateway ──┐ ├──► ChatService ──► DB + PresenceTracker TUI Client ──► WebSocket ──► ChatHub ─────┘ Both protocols call into a shared IChatService for business logic. Events fan out to all registered IChatBroadcaster implementations (SignalR and IRC). Components EchoHub.Core Shared library containing: Models: User, Channel, Message, RefreshToken DTOs: Record types for API requests/responses Contracts: IChatService (protocol-agnostic chat operations), IChannelService (channel CRUD and membership), IChatBroadcaster (event fan-out interface), IEchoHubClient (SignalR client interface) Constants: ValidationConstants (shared regex patterns), HubConstants EchoHub.Server ASP.NET Core web application: Controllers: REST API endpoints for auth, channels, users, files, server info Hubs: SignalR ChatHub -- thin adapter delegating to IChatService Auth: JWT token service (15-min access tokens, 30-day refresh tokens) Data: EF Core with SQLite Services: ChatService (core business logic), ChannelService (channel CRUD and membership), SignalRBroadcaster, presence tracking, file storage, image-to-ASCII conversion, FileCleanupService (periodic removal of expired uploads), DataMigrationService (startup schema/data evolution) EchoHub.Server.Irc IRC protocol gateway (separate project for clean separation of concerns): IrcGatewayService: BackgroundService with TCP listener on configured port(s), optional TLS IrcCommandHandler: Per-client IRC command dispatch -- handles CAP/SASL, NICK/USER/PASS, JOIN/PART/PRIVMSG/QUIT, NAMES/TOPIC/WHO/WHOIS/AWAY/LIST/MODE/MOTD IrcBroadcaster: IChatBroadcaster implementation that formats chat events as IRC protocol lines, with echo suppression (IRC convention) IrcMessageFormatter: Converts MessageDto to IRC PRIVMSG lines -- splits long text at word boundaries (~400 byte chunks), sends images as ASCII art line-by-line IRC users authenticate with existing EchoHub accounts via PASS/NICK/USER or SASL PLAIN (BCrypt verification against the database). EchoHub.Client Terminal.Gui v2 TUI application: UI: Main window, dialogs, chat renderer with ANSI color support Services: API client with automatic token refresh, SignalR connection wrapper, audio playback (NetCoreAudio), automatic update checker (AlwaysUpToDate) Themes: 14 built-in color themes (including transparent dark/light themes with true terminal transparency) Config: Client configuration management with session persistence (\"Remember Me\" refresh tokens) Communication REST API for authentication, profile management, channel CRUD, file uploads SignalR WebSocket for real-time messaging and presence updates (TUI client) IRC TCP for real-time messaging via standard IRC protocol (IRC clients) JWT tokens passed via query string for SignalR authentication IRC authentication via PASS/SASL PLAIN against BCrypt password hashes Broadcaster Pattern The IChatBroadcaster interface allows multiple protocols to receive chat events: SignalRBroadcaster: Wraps IHubContext<ChatHub>, filters out IRC connections IrcBroadcaster: Iterates live IRC connections in a channel, formats events as IRC protocol lines Both are registered in DI and called by ChatService when events occur. This means a message sent from an IRC client appears in the TUI client, and vice versa."
  },
  "articles/configuration.html": {
    "href": "articles/configuration.html",
    "title": "Configuration | EchoHub Documentation",
    "summary": "Configuration EchoHub Server generates an appsettings.json with sensible defaults on first run (including a random JWT secret), so you can launch and start chatting immediately. Tweak things later when you feel like it. Note Under the hood, EchoHub Server is built on ASP.NET Core, so it inherits the standard .NET configuration system. If you're familiar with that, everything works exactly as you'd expect. If not — no worries, this page covers everything you need. How It Works EchoHub Server loads settings from multiple sources. Each source overrides the previous one, so you can layer defaults with environment-specific values: 1. appsettings.json (base defaults) 2. appsettings.{Environment}.json (e.g. appsettings.Production.json) 3. Environment variables (great for Docker / CI) 4. Command-line arguments (highest priority) The last one wins. If appsettings.json says \"Irc:Port\": 6667 but you pass --Irc:Port=7000 on the command line, port 7000 is what you get. In practice this means you can leave appsettings.json alone and override just the settings you care about using environment variables or CLI flags — no need to edit JSON files if that's not your thing. Environment Variable Mapping Environment variables use double underscores (__) in place of the JSON nesting. The rule is simple — replace every : (or each level of JSON nesting) with __: appsettings.json path Environment variable Server:Name Server__Name Irc:Enabled Irc__Enabled Jwt:Secret Jwt__Secret Serilog:MinimumLevel:Default Serilog__MinimumLevel__Default ConnectionStrings:DefaultConnection ConnectionStrings__DefaultConnection Arrays use numeric indices: Server:Admins:0 becomes Server__Admins__0, Server:Admins:1 becomes Server__Admins__1, and so on. This is why the Docker .env file uses Server__Name=My Server instead of JSON — Docker passes these as environment variables, and the server picks them up automatically. Examples All three of these achieve the same thing — use whichever fits your setup. appsettings.json (direct editing): { \"Server\": { \"Name\": \"My EchoHub Server\", \"PublicServer\": true } } Environment variables (Docker, systemd, shell export): export Server__Name=\"My EchoHub Server\" export Server__PublicServer=true Command-line arguments (quick overrides, highest priority): ./EchoHub.Server --Server:Name=\"My EchoHub Server\" --Irc:Enabled=true Configuration Reference The full appsettings.json is auto-generated on first run from the example config. Here's every option: General Key Default Description Urls http://0.0.0.0:5000 Listen address and port AllowedHosts * Allowed host headers (leave * unless you need host filtering) Database Key Default Description ConnectionStrings:DefaultConnection (empty) SQLite connection string. Empty = echohub.db in the app directory Authentication Key Default Description Jwt:Secret (auto-generated) Signing key (min 32 chars). Auto-generated on first run Jwt:Issuer EchoHub.Server JWT issuer claim Jwt:Audience EchoHub.Client JWT audience claim Access tokens expire after 15 minutes, refresh tokens after 30 days with rotation on each use. Server Identity Key Default Description Server:Name My EchoHub Server Display name shown to clients Server:Description A self-hosted EchoHub chat server Server description Server:PublicServer false Register on the public directory Server:PublicHosts [] Hostnames advertised to the directory (array, e.g. [\"chat.example.com\"]). Required when PublicServer is true Server:Tags [] Topic tags surfaced in the directory browser (array, e.g. [\"community\", \"gaming\"]) Server:Admins [] Array of admin usernames (e.g. [\"alice\", \"bob\"]) Server:Registration open Registration mode: open, invite (codes via /invite, Admin+), or closed Server:DirectoryClaimPath (empty) Overrides where the directory claim token file is stored. Empty = directory-claim.json next to the SQLite database. Treat it as a secret and back it up with the database Uploads Per-attachment size limits by kind (in megabytes) and the per-message attachment cap. An absent or partial Uploads section keeps the built-in defaults. See Messages & Attachments for how kinds are detected. Key Default Description Uploads:MaxImageSizeMB 10 Max size for one image attachment Uploads:MaxAudioSizeMB 10 Max size for one audio attachment Uploads:MaxFileSizeMB 100 Max size for any other attachment Uploads:MaxAvatarSizeMB 2 Max avatar upload size Uploads:MaxAttachmentsPerMessage 10 Attachments allowed on a single message The server sizes its request-body limits from these values, so raising a limit here is all that's needed — no separate Kestrel tuning. Spam Protection Per-user flood, duplicate, join, and channel-create limits with auto-mute escalation. Mods and above are always exempt, and the defaults are lenient enough that a fast typist never trips them. An absent or partial Spam section keeps these defaults. Key Default Description Spam:Enabled true Master switch for all spam protection Spam:MaxMessagesPerWindow 8 Max messages per WindowSeconds before a send is rejected Spam:WindowSeconds 5 Sliding window (seconds) for the message-rate check Spam:MaxDuplicateMessages 3 Identical messages in a row tolerated before rejection (E2E rooms are exempt — ciphertext differs each time) Spam:AutoMuteMinutes 5 Auto-mute duration once a user hits the violation threshold. 0 disables auto-mute (rejections still apply) Spam:ViolationThreshold 5 Rejected sends within ViolationWindowMinutes that trigger an auto-mute Spam:ViolationWindowMinutes 5 Window (minutes) over which violations accumulate Spam:MaxJoinsPerWindow 25 Max first-time channel joins per JoinWindowSeconds — keep this above your public channel count Spam:JoinWindowSeconds 30 Sliding window (seconds) for the join-rate check Spam:MaxChannelCreatesPerWindow 3 Max channel creations per ChannelCreateWindowMinutes Spam:ChannelCreateWindowMinutes 10 Window (minutes) for the channel-create check Encryption Key Default Description Encryption:Key (auto-generated) AES key for message encryption in transit Encryption:EncryptDatabase false Also encrypt message content at rest in SQLite Storage Key Default Description Storage:Path (empty) Directory for uploaded file blobs (attachments, avatars). Empty = uploads/ in the app directory; the Docker image sets it to /app/data/uploads on the persistent volume Storage:CleanupIntervalHours 1 How often the cleanup job runs (hours) Storage:RetentionDays 30 Days to keep uploaded files before cleanup IRC Gateway Key Default Description Irc:Enabled false Enable the IRC protocol gateway Irc:Port 6667 IRC plain-text listen port Irc:TlsEnabled false Enable TLS termination for IRC Irc:TlsPort 6697 IRC TLS listen port Irc:TlsCertPath (empty) Path to a PKCS#12 (.pfx) certificate Irc:TlsCertPassword (empty) Password for the certificate file Irc:ServerName echohub IRC server name in protocol messages Irc:Motd Welcome to EchoHub IRC Gateway! Message of the day Server Logs Room When enabled, EchoHub auto-creates a read-only system channel and streams log events to it live, so operators can watch the server from inside the app. Log lines are never stored as messages — the rolling Serilog files remain the only persistence, and the room replays recent lines from those files when someone opens it. Key Default Description ServerLogs:Enabled true Master switch for the live log room ServerLogs:RoomName server-logs Name of the auto-created channel (reserved — users can't create a channel with this name) ServerLogs:MinRole Mod Minimum server role that can see and join the room (Member, Mod, Admin, Owner) ServerLogs:MinLevel Information Minimum log level streamed to the room (Verbose, Debug, Information, Warning, Error, Fatal) — affects only the room, not the file/console sinks ServerLogs:BacklogLines 100 Recent log lines replayed from file when someone opens the room ServerLogs:LogDirectory logs Directory holding the rolling log files (must match the Serilog file sink path below) ServerLogs:LogFilePattern echohub-server-*.log Filename glob for the rolling log files inside LogDirectory Logging EchoHub uses Serilog for structured logging — console output + daily rolling files with 14-day retention by default. Key Default Description Serilog:MinimumLevel:Default Information Global log level (Debug, Information, Warning, Error) Serilog:MinimumLevel:Override:Microsoft Warning Suppress noisy framework logs Serilog:MinimumLevel:Override:Microsoft.AspNetCore Warning Suppress request pipeline logs Serilog:MinimumLevel:Override:Microsoft.EntityFrameworkCore Warning Suppress database query logs Log files are written to logs/echohub-server-YYYY-MM-DD.log. To change the path or retention, edit the Serilog:WriteTo section in appsettings.json. Want more verbose output for debugging? Set the minimum level to Debug: # via environment variable export Serilog__MinimumLevel__Default=Debug # or command line ./EchoHub.Server --Serilog:MinimumLevel:Default=Debug"
  },
  "articles/docker.html": {
    "href": "articles/docker.html",
    "title": "Docker | EchoHub Documentation",
    "summary": "Docker Quick Start cp .env.example .env # create your config docker compose up -d # start the server On first run the server automatically generates JWT and encryption keys, creates the database, and seeds a #general channel. Connect with the EchoHub client to http://localhost:5000. Using a Pre-built Image Instead of building locally, you can pull from GHCR. In docker-compose.yml, replace the build block: services: echohub-server: image: ghcr.io/huebyte/echohub-server:latest # build: # context: ./src # dockerfile: EchoHub.Server/Dockerfile Configuration All settings are configured through the .env file. These are environment variables that override appsettings.json — the __ (double underscore) maps to JSON nesting levels. For example, Server__Name overrides the Server:Name key in appsettings.json. See the Configuration guide for the full reference of all available settings and how the override hierarchy works. Common Docker-relevant variables: Variable Default Description Server__Name My EchoHub Server Display name for your server Server__Admins__0 (empty) Admin username (use __1, __2 for more) Irc__Enabled false Enable the IRC gateway Serilog__MinimumLevel__Default Information Log level (Debug, Warning, etc.) Persistent Data All server state lives in a single Docker volume mounted at /app/data: /app/data/ ├── appsettings.json # generated config with JWT/encryption keys ├── echohub.db # SQLite database ├── uploads/ # uploaded files and avatars └── logs/ # rolling log files (14-day retention) Backup # stop the server to ensure a consistent snapshot docker compose stop # copy the data volume to a local directory docker cp echohub-server:/app/data ./backup docker compose start IRC Gateway To enable IRC, set these in your .env: Irc__Enabled=true Then uncomment the port in docker-compose.yml: ports: - \"5000:5000\" - \"6697:6697\" # IRC (TLS encrypted, preferred) For TLS, also set: Irc__TlsEnabled=true Irc__TlsCertPath=/app/data/cert.pfx Irc__TlsCertPassword=your_password Mount your certificate into the data volume or bind-mount it directly. IRC users must have an existing EchoHub account. See Getting Started for client connection examples. Updating # if using pre-built images docker compose pull docker compose up -d # if building locally docker compose build docker compose up -d Data persists across updates since it lives in the named volume. Troubleshooting Port already in use -- Another process is using port 5000. Change the host port in docker-compose.yml: ports: - \"8080:5000\" # access via http://localhost:8080 Permission denied on volume -- The container runs as a non-root echohub user (UID 999). If using bind mounts instead of named volumes, ensure the directory is writable. View logs -- Check the container output: docker compose logs -f echohub-server File-based logs are also available inside the volume at /app/data/logs/."
  },
  "articles/encrypted-rooms.html": {
    "href": "articles/encrypted-rooms.html",
    "title": "Encrypted Rooms (Password-Protected Channels) | EchoHub Documentation",
    "summary": "Encrypted Rooms (Password-Protected Channels) An encrypted room is a channel whose entire content — every message and every file — is end-to-end encrypted with a key derived from a shared passphrase. Only people who know the passphrase can read the room. Not even the server owner can read the content, yet the server can still gate who joins, and it can count and measure what's stored (message count, file sizes, timestamps) without ever seeing the plaintext. This is a stronger guarantee than the transport and at-rest encryption described elsewhere, where the server decrypts every message to process it. Here the server is treated as untrusted for content: it holds only ciphertext and wrapped keys. The passphrase is the only key. There is no recovery. If everyone who knows a room's passphrase forgets it, that room's history is permanently unreadable — by design. What the server can and cannot see The server can see The server cannot see That the channel is encrypted Message text Message count and timestamps File contents Who sent each message (sender identity) Image previews (ASCII art) Each attachment's file name and byte size The passphrase, the room key, or the key-encryption key The estimated total size (via /meta) Anything that would let it decrypt the above File names are stored in plaintext so the file list stays usable — treat a file name itself as non-secret. Everything inside the file is encrypted. Key hierarchy Three keys are derived from one passphrase. The passphrase, the key-encryption key, and the room content key never leave the client. flowchart TD P[Passphrase] -->|PBKDF2-SHA256, 210k iterations, per-room salt| OKM[64-byte output] OKM --> AK[\"Auth key (first 32 bytes)\"] OKM --> KEK[\"Key-encryption key / KEK (last 32 bytes)\"] AK -->|sent as lowercase hex| SRV1[\"Server: BCrypt-hash as the join gate\"] RCK[\"Room Content Key (random 256-bit)\"] -->|encrypts all content| CONTENT[Messages + files + previews] RCK -->|AES-256-GCM wrap under KEK| WRAP[\"Wrapped room key\"] WRAP -->|stored| SRV2[\"Server: stores wrapped key + salt only\"] Auth key — the join credential. Derived from the passphrase, sent to the server as hex, and stored only as a BCrypt hash. Proving knowledge of it is what lets you join; it reveals nothing about the content key. Key-encryption key (KEK) — never sent. Used locally to wrap (encrypt) and unwrap the room content key. Room Content Key (RCK) — a random 256-bit key generated once, at room creation. It encrypts every message and file. The server stores it only in wrapped form, so it can hand the wrapped key to a joiner but can never unwrap it itself. All content encryption is AES-256-GCM with a random 12-byte nonce and a 16-byte authentication tag per item, so identical inputs never produce identical ciphertext, and any tampering is detected. Room-encrypted text carries a self-describing prefix so clients and the server can tell it apart from plaintext: $RC1$base64(nonce || tag || ciphertext) Creating a room The client does all the cryptography locally, then hands the server only what it needs to gate joins and store (but not read) the content. sequenceDiagram participant Client participant Server Client->>Client: generate salt + random Room Content Key (RCK) Client->>Client: DeriveKeys(passphrase, salt) → auth key + KEK Client->>Client: wrap RCK under KEK Client->>Server: create channel { authKey(hex), salt, wrappedRoomKey } Server->>Server: BCrypt-hash auth key, store salt + wrapped key Note over Server: Server never receives passphrase, KEK, or RCK Joining a room sequenceDiagram participant Client participant Server Client->>Server: GET /crypto → { isEncrypted, salt } Client->>Client: DeriveKeys(passphrase, salt) → auth key + KEK Client->>Server: join { authKey(hex) } Server->>Server: BCrypt-verify against stored hash alt correct passphrase Server->>Client: history (ciphertext) + wrapped room key Client->>Client: unwrap RCK with KEK, then decrypt everything locally else wrong passphrase Server->>Client: rejected (join gate fails) end A wrong passphrase fails the BCrypt gate, so the server never even hands out the wrapped key. Even if it did, an attacker without the KEK cannot unwrap it. What gets encrypted When you send a message or attach files to an encrypted room, the client encrypts each part with the room content key before uploading: Message text → $RC1$… ciphertext. Files (any kind) → the whole blob is AES-256-GCM encrypted client-side; the server stores an opaque ciphertext blob. Image ASCII previews → rendered on the client, then room-encrypted. The server never sees the rendered art. The server records each attachment's kind, file name, and byte size (of the ciphertext blob) as metadata, and broadcasts the ciphertext to other members, who decrypt locally. Changing the passphrase /passwd <old> <new> rotates the passphrase. Because only the wrapping of the room content key changes — not the RCK itself — all existing history stays readable: The client proves knowledge of the old passphrase (old auth key). It unwraps the RCK with the old KEK, then re-wraps it under the new KEK (new salt). It uploads the new auth key + salt + re-wrapped key. The content is never re-encrypted. Inspecting a room Use /meta in any channel to see what the server knows about it, including encrypted rooms: Room info for #private-room: Room ID 3f2a…-…-… Created 7/16/2026 2:31 PM Messages 128 Unique users 4 Est. size 42.5 MB Protection end-to-end encrypted Est. size is the sum of stored attachment blob sizes plus message text length — an estimate of the room's footprint, computed entirely from metadata the server holds without reading any content. Limitations & security notes No recovery. A lost passphrase means unrecoverable history. Keep it safe; there is no reset. File names are plaintext. They stay readable so the file list works — don't put secrets in a file name. IRC is disabled for encrypted rooms. The IRC gateway forwards plaintext and cannot participate in the room's key scheme, so encrypted channels are not bridged to IRC. Metadata is visible. Message counts, timestamps, sender identities, file names, and sizes are intentionally readable so the server can moderate at the metadata level and report /meta. Endpoint trust. End-to-end encryption protects content from the server and the network, not from a compromised client device that already holds the passphrase. Related Message Encryption — transport ($ENC$v1$) and optional at-rest database encryption, where the server does decrypt content for processing. Encrypted rooms are a separate, stronger layer that sits on top."
  },
  "articles/encryption.html": {
    "href": "articles/encryption.html",
    "title": "Message Encryption | EchoHub Documentation",
    "summary": "Message Encryption EchoHub uses application-layer AES-256-GCM encryption to protect message content in transit between clients and the server. This is an additional layer on top of TLS, protecting against ISPs, proxies, and any middleman that can inspect HTTPS traffic (e.g. corporate proxies with trusted root CA certificates). How It Works sequenceDiagram participant Sender as TUI Client (Sender) participant Server participant Receiver as TUI Client (Receiver) participant IRC as IRC Client Sender->>Sender: encrypt(plaintext) Sender->>Server: $ENC$v1$... (SignalR) Server->>Server: decrypt → validate/sanitize Server->>Server: fetch embeds on plaintext Server->>Server: (optional) encrypt for DB storage Server->>Server: encrypt(plaintext) with fresh nonce Server->>Receiver: $ENC$v1$... (SignalR broadcast) Receiver->>Receiver: decrypt → display Server->>Server: decrypt for IRC Server->>IRC: plaintext (IRC PRIVMSG) Client encrypts the message before sending it over SignalR Server decrypts to validate content, sanitize newlines, and fetch link embeds Server re-encrypts with a fresh nonce and broadcasts to all connected SignalR clients Clients decrypt the broadcast and display the plaintext IRC clients receive plaintext automatically (the IRC broadcaster decrypts before forwarding) Each encryption uses a random 12-byte nonce, so the same message produces different ciphertext every time. Encryption Key A 256-bit AES key is auto-generated on first server startup and saved to appsettings.json: { \"Encryption\": { \"Key\": \"base64-encoded-32-byte-key\", \"EncryptDatabase\": false } } The key is generated by FirstRunSetup using RandomNumberGenerator.GetBytes(32). If the key is missing or empty when the server starts, a new one is created automatically. Clients fetch the key after login via an authenticated endpoint (GET /api/server/encryption-key). No manual configuration is needed on the client side. Encrypted Content Format Encrypted content uses a self-describing format: $ENC$v1${nonce_base64}${ciphertext+tag_base64} $ENC$v1$ — version prefix (allows future algorithm changes) Nonce — 12 bytes, Base64-encoded, randomly generated per message Ciphertext + Tag — AES-256-GCM output with 16-byte authentication tag appended The authentication tag ensures both confidentiality and integrity — any tampering with the ciphertext is detected during decryption. Database Encryption (Optional) By default, messages are stored as plaintext in the database. Transport encryption still protects everything on the wire, but the SQLite file itself contains readable messages. To encrypt messages at rest, enable the setting: { \"Encryption\": { \"Key\": \"...\", \"EncryptDatabase\": true } } Behavior by Setting Setting DB Storage Transport Key Rotation Risk false (default) Plaintext Encrypted None - stored data is unaffected true Encrypted Encrypted Changing the key makes old messages unreadable Mixed Content The server handles mixed encrypted/plaintext content in the database gracefully. When reading messages: Content starting with $ENC$v1$ is decrypted Everything else is treated as plaintext This means you can safely toggle EncryptDatabase at any time. Old messages remain readable regardless of the current setting. Enabling Encryption at Rest When you set EncryptDatabase: true, only new messages are encrypted going forward. Existing plaintext messages in the database are not retroactively encrypted. This is intentional — it keeps key rotation safe and avoids irreversible bulk changes. Key Rotation Changing the encryption key is safe: Plaintext messages — always readable regardless of key Messages encrypted with the old key — will show [encrypted message — decryption failed] New messages — encrypted with the new key going forward If you need to recover old encrypted messages, restore the original key from a backup of appsettings.json. Since plaintext messages are never retroactively encrypted, you'll never lose access to your entire history from a key change. Security Considerations What This Protects Against Passive network sniffing — messages are encrypted even if captured off the wire ISP/proxy inspection — content is encrypted at the application layer, independent of TLS Database theft (when EncryptDatabase: true) — SQLite file contains only ciphertext Limitations TLS-inspecting proxies — if a corporate proxy terminates TLS with a trusted root CA, it can intercept the key exchange (GET /api/server/encryption-key) and read all traffic. A future upgrade to ECDH key exchange would address this. Server has full access — the server decrypts all messages for processing. This is not end-to-end encryption between users; it's transport encryption between client and server. For true end-to-end encryption where the server cannot read content, use encrypted rooms. IRC clients receive plaintext — IRC is an open protocol and third-party clients cannot participate in the encryption scheme. Troubleshooting Messages show [encrypted message — decryption failed, try re-logging to fetch the latest key] The client's encryption key doesn't match the server's. This happens when: The server's encryption key was rotated while the client was connected The client cached a stale key Fix: Disconnect and reconnect (re-login). The client fetches the current key on each login. Messages show [encrypted message — decryption failed] in channel history The server cannot decrypt messages stored in the database. This happens when: EncryptDatabase was enabled, and the key was changed afterwards Fix: Restore the original key from a backup. There is no way to recover messages encrypted with a lost key."
  },
  "articles/getting-started.html": {
    "href": "articles/getting-started.html",
    "title": "Getting Started | EchoHub Documentation",
    "summary": "Getting Started Install the Client Windows (Chocolatey) choco install echohub Linux / macOS curl -sSfL https://raw.githubusercontent.com/HueByte/EchoHub/master/scripts/install.sh | sh To install a specific version or to a custom directory: curl -sSfL .../install.sh | sh -s -- --version 0.2.14 curl -sSfL .../install.sh | sh -s -- --install-dir /opt/echohub Manual Download Grab a self-contained binary from Releases -- no runtime needed. Host a Server Docker The quickest way to host a server: cp .env.example .env docker compose up -d See the Docker guide for configuration, pre-built images, and more. From Source dotnet run --project src/EchoHub.Server Requires .NET 10 SDK. On first run, the server automatically: Creates appsettings.json from the example config Generates a secure JWT secret Creates the SQLite database with a #general channel Usage After installing the client, run echohub (or dotnet run --project src/EchoHub.Client from source). Connect to a server, register an account, and start chatting. Connect via IRC Enable the IRC gateway in the server's appsettings.json: { \"Irc\": { \"Enabled\": true, \"Port\": 6667 } } Then connect with any standard IRC client: irssi -c localhost -p 6667 -w <password> -n <username> Your nick is your EchoHub username and the server password is your account password (PASS/NICK/USER or SASL PLAIN). Connecting with a new username registers the account. Messages flow bidirectionally between IRC and TUI clients. For TLS, set TlsEnabled: true, TlsPort: 6697, and provide a PKCS#12 certificate path. See the IRC Gateway guide for command mapping, attachment rendering, and limitations, or Architecture for how the gateway integrates with the chat service. Configuration Server configuration is in appsettings.json (auto-generated on first run). You can also use environment variables or command-line arguments to override settings. See the Configuration guide for the full reference and how it all works. Build from Source dotnet build src/EchoHub.slnx"
  },
  "articles/irc-gateway.html": {
    "href": "articles/irc-gateway.html",
    "title": "IRC Gateway | EchoHub Documentation",
    "summary": "IRC Gateway Every EchoHub server can expose a second door: a built-in IRC gateway that speaks the classic IRC protocol on port 6667. Any standard IRC client — irssi, WeeChat, HexChat, Halloy — can join the same channels as TUI users, see the same messages, and chat with the same accounts. Under the hood both protocols call the same chat service, so a message sent from IRC appears instantly in the TUI and vice versa (see Architecture). Enabling the gateway The gateway is off by default. Enable it in appsettings.json (or Irc__Enabled=true as an environment variable): { \"Irc\": { \"Enabled\": true, \"Port\": 6667, \"TlsEnabled\": false, \"TlsPort\": 6697, \"TlsCertPath\": \"\", \"ServerName\": \"echohub\", \"Motd\": \"Welcome to EchoHub IRC Gateway!\" } } The plaintext listener always starts on Port. The TLS listener on TlsPort starts only when TlsEnabled is true and TlsCertPath points to a PKCS#12 (.pfx) certificate. See the configuration reference for every option. Connecting & authentication Your IRC nick is your EchoHub username and your server password is your account password. Two flows are supported: # classic PASS/NICK/USER — most clients call this the \"server password\" irssi -c chat.example.com -p 6667 -w <password> -n <username> or SASL PLAIN (advertised via CAP LS), where the SASL username/password are the account credentials. A few things worth knowing: Connecting auto-registers. If the username doesn't exist yet, the gateway creates the account with that password (usernames: 3–50 chars of a-z 0-9 _ -; passwords: 6+ chars). The very first account ever created on a server becomes the Owner. Because of that, a typo'd password for an existing account fails with Username is already taken — the gateway tried to log in, couldn't, then tried to register the name. If you see that error, re-check your password. Connecting without a password is rejected: Password required. Use PASS command or SASL PLAIN. What maps to what IRC EchoHub JOIN #room Join a channel (history is replayed on join) JOIN #room <key> Join a password-protected (+k) channel PART / QUIT Leave channel / disconnect LIST Public channels only (password-protected ones show a [+k] hint) TOPIC Read or set the channel topic (permission-checked) NAMES / WHO Online users in the channel WHOIS Profile: display name, channels, idle time, away status AWAY [message] Sets your EchoHub status to Away / back to Online MODE #room +k <key> / -k Set / clear the channel password Private (unlisted) channels don't appear in LIST, but members who know the exact name can still JOIN them. Channels are not auto-created from IRC — create them from the TUI first. How messages look Attachments arrive as labeled link lines — [Image: photo.png] https://…, ♪ [Audio: song.mp3] https://…, [File: report.pdf] https://… — and image attachments additionally render their ASCII-art preview using truecolor ANSI escapes, so a modern terminal IRC client shows actual picture previews. Link embeds are appended as │-prefixed text lines. Long messages are split at word boundaries into IRC-safe lines (~400 bytes each); incoming messages may be up to 2,000 characters like any EchoHub message. Your own messages aren't echoed back (standard IRC convention). Moderation actions surface natively: kicks arrive as KICK, bans and channel nukes as server NOTICEs. Limitations The gateway bridges what IRC can express — and deliberately refuses what it can't: No end-to-end encrypted rooms. Joining an encrypted room fails with \"Cannot join channel — end-to-end encrypted, use the EchoHub client.\" Bridging one would require the server to hold the room key, breaking the zero-knowledge design. No private messages. PRIVMSG to a nick is rejected; EchoHub is channel-based. Usernames, not display names. Messages are attributed to the account username; a user's display name is visible via WHOIS/WHO (realname field). No client features. Uploading attachments, profiles, themes, and reactions to status changes are TUI-client features. Other users' status changes aren't pushed to IRC — discover them with WHOIS/WHO. How IRC users appear to TUI users Users connected only through the gateway are tagged [irc] in the users panel — a hint that they can't receive encrypted content or use client-side features. Someone connected with both an IRC client and the TUI shows untagged."
  },
  "articles/messages-and-attachments.html": {
    "href": "articles/messages-and-attachments.html",
    "title": "Messages & Attachments | EchoHub Documentation",
    "summary": "Messages & Attachments An EchoHub message is text content plus up to 10 attachments, Discord-style. A plain chat line is just a message with no attachments; a photo dump is one message with several files and an optional caption. This page explains how to attach files, what happens to them on the way to the server, and how other clients receive them. Message basics Limit Value Max message length 2,000 characters Max newlines per message 30 (no blank-line runs) Max attachments per message 10 Link embeds per message first 3 URLs Multiline messages are written with Ctrl+N for a newline; Enter sends. URLs in a message get link embeds (title, description, theme color) fetched by the server. Attaching files All of these end up in the same place — the staging tray — and are sent together as one message the next time you press Enter, with whatever you've typed as the caption: Paste a copied file — copy one or several files in your file manager and press Ctrl+V in the input. All of them are staged at once. Paste an image from the clipboard — copy an image in a browser (right-click → Copy image), take a screenshot (Win+Shift+S), or copy from an image editor, then Ctrl+V. The image is attached directly as a PNG named image.png — no saving to disk first. On Linux this uses wl-paste or xclip; on macOS it requires pngpaste (brew install pngpaste). Drag & drop — drop a file onto the terminal window; the client recognizes the dropped path and stages the file. /send <filepath> — stage a file by path (quote paths containing spaces). The input frame's title shows what's currently staged. /clear drops all staged attachments without sending. Sending with an empty input is fine — the message is just the attachments. ┌ Message (2 attached: report.pdf, image.png) ──────────────┐ │ here's the summary and a screenshot_ │ └────────────────────────────────────────────────────────────┘ URL sends are different: /send <https://…> sends an image URL immediately as its own message — nothing is staged, and it isn't available in end-to-end encrypted rooms (the server would have to fetch the image, which would defeat the encryption). Attachment kinds The kind is detected per attachment, not per message: Kind Detected by Renders as Default size limit Image Magic bytes: JPEG, PNG, GIF, WebP ASCII-art preview in chat 10 MB Audio Extension: .mp3 .wav .ogg .flac .aac .m4a .wma Playable row (▶) 10 MB File Everything else Downloadable row 100 MB Limits are per file and server-configurable — see the Uploads section in the configuration guide (MaxImageSizeMB, MaxAudioSizeMB, MaxFileSizeMB, MaxAttachmentsPerMessage). Image previews (ASCII art) Images are rendered in chat as half-block ASCII art. You pick the rendering size: Flag Size Feel -s / /size s 40 × 40 compact -m / /size m 80 × 80 default -l / /size l 120 × 120 detailed /size with no argument opens a picker; the choice persists as your default. A one-off -s|-m|-l flag on /send applies to that message. Receiving attachments Right-click a message (or press F6 to select one with the arrow keys) for actions: Images → save to disk Audio → play (in-client playback) Files → download Downloads go to your configured download folder — set it with /downloadpath (no argument opens a native folder picker, or pass a path directly). Attachments in encrypted rooms In an end-to-end encrypted room every attachment is encrypted client-side before upload: flowchart LR F[File bytes] -->|AES-256-GCM with room key| B[Ciphertext blob] F -->|if image: render ASCII locally| A[ASCII preview] A -->|room-encrypt| AP[\"$RC1$… preview\"] B --> S[Server stores blob + name + size] AP --> S The server never sees the file contents or the rendered preview — it stores an opaque blob and broadcasts it to members, who decrypt locally. File names and sizes remain visible to the server so the file list stays usable; don't put secrets in a file name. Pasted clipboard images go through exactly the same pipeline. Deleting messages with attachments Deleting a message also removes its uploaded attachment files from the server. You can always delete your own messages; moderators can delete others' — see Moderation & Roles."
  },
  "articles/moderation.html": {
    "href": "articles/moderation.html",
    "title": "Moderation & Roles | EchoHub Documentation",
    "summary": "Moderation & Roles Every EchoHub server has a four-tier role hierarchy. Moderation is strictly hierarchical: acting on another user requires outranking them — equal rank is never enough — and a few invariants protect the server owner from lockouts. Roles Role Rank Users panel glyph How it's granted Owner 3 ★ The first account ever registered on the server Admin 2 ♦ Assigned by the Owner Mod 1 ❀ Assigned by an Admin or the Owner Member 0 — Everyone else Assign roles with /role <user> <admin|mod|member>. Two rules apply: You can only assign roles strictly below your own — an Admin can promote to Mod but cannot create another Admin; only the Owner can. Owner is not assignable and not demotable. There is exactly one Owner (the first account), nobody can be promoted to it, and the Owner's role can't be changed. Actions Command Minimum role Effect /kick <user> [reason] Mod Disconnects the user. Not persistent — they can reconnect immediately. /ban <user> [reason] Admin Persistent: flags the account banned and disconnects it. Banned accounts are rejected at login. /unban <user> Admin Lifts a ban. /mute <user> [minutes] Mod Blocks the user from sending messages or uploading files. Without a duration the mute is indefinite; with one it auto-expires (checked every ~15 seconds). /unmute <user> Mod Lifts a mute early. /role <user> <role> Admin Assign a role (see rules above). /nuke Mod Deletes the entire history of the current channel, including all attachment files on disk. Channel-wide — no per-user check. Kick, ban, and mute all enforce the hierarchy: the target's role must be strictly lower than yours. A Mod cannot kick another Mod; nobody can kick, ban, mute, or demote the Owner. Deleting messages Deletion has its own, slightly different rule set: Your own messages — always deletable, whatever your role. Right-click a message → Delete message, or press F6, pick the message, and hit Delete. Someone else's messages — requires Mod or higher and strictly outranking the author. A Mod can delete a Member's message, but not another Mod's. Deleting a message also purges its uploaded attachment blobs from the server's disk, and the removal is broadcast live — the message disappears from everyone's chat immediately. How actions surface Everyone in the channel sees moderation happen: TUI clients show system messages — \"alice was kicked (reason)\", \"bob was banned\", \"Channel history has been cleared by a moderator.\" The kicked or banned user themselves gets a dialog with the reason, then the client disconnects. IRC clients get native protocol events: kicks arrive as a real KICK command, bans as a server NOTICE. (See the IRC Gateway guide.) Muted users aren't announced; they simply receive \"You are muted and cannot send messages.\" when they try to speak. Design notes All checks run server-side in the moderation API — the client commands are conveniences, and the same rules bind IRC users and any direct API caller. Bans are account-level, not IP-level. A banned person can register a fresh account; pair bans with registration hygiene on public servers. In end-to-end encrypted rooms moderation still works at the metadata level — messages can be deleted and users muted/kicked by identity — but no moderator can read the content, including the Owner."
  },
  "articles/notification-sounds.html": {
    "href": "articles/notification-sounds.html",
    "title": "Notification Sounds | EchoHub Documentation",
    "summary": "Notification Sounds EchoHub can play a notification sound when someone @mentions you. This is disabled by default and must be enabled in your profile settings. Enabling Notifications Open your profile (/profile) and check the \"Notification sound on @mention\" checkbox, then save. You can also adjust the Volume (0-100, default 30). All settings are persisted in ~/.echohub/config.json. Customizing the Sound The client ships with a default Notification.mp3 in the Assets folder. To use your own notification sound, replace the file at: <app-directory>/Assets/Notification.mp3 The file must be a valid .mp3 or .wav audio file. The replacement takes effect on the next app launch. Alternatively, set a custom path in ~/.echohub/config.json: { \"notifications\": { \"enabled\": true, \"volume\": 30, \"soundFile\": \"/path/to/your/sound.mp3\" } } When soundFile is set, EchoHub uses that file instead of the bundled default. Disabling Notifications Uncheck the option in your profile, or edit the config directly: { \"notifications\": { \"enabled\": false } }"
  },
  "articles/tui-guide.html": {
    "href": "articles/tui-guide.html",
    "title": "TUI Guide | EchoHub Documentation",
    "summary": "TUI Guide Everything you can do in the EchoHub terminal client: keyboard shortcuts, mouse actions, slash commands, themes, and the everyday behaviors (unread markers, auto-join, scrollback) that make it feel like a proper IRC-era client with modern comforts. Layout ┌ Menu bar ──────────────────────────────────────────────────┐ │ ┌ Channels ─┐ ┌ Messages ────────────────────┐ ┌ Users ──┐ │ │ │ #general 3│ │ 12:01 <alice> hi │ │ ★ alice │ │ │ │ #dev │ │ ── new messages ── │ │ ❀ bob │ │ │ │ #random*~ │ │ 12:04 <bob> anyone around? │ │ carol │ │ │ └───────────┘ └──────────────────────────────┘ │ d [irc] │ │ │ ┌ Message │ Enter=send │ Tab=complete │ … ────┐ └─────────┘ │ │ │ _ │ │ │ └─────────────────────────────────────────────┘ │ │ Status: Connected │ v0.2.14 │ alice │ Act: #dev │ └─────────────────────────────────────────────────────────────┘ Channel list markers: * = password-protected, ~ = private (unlisted), plus unread counts (orange when you were @mentioned). Users panel glyphs: ★ Owner, ♦ Admin, ❀ Mod, [irc] for IRC-gateway-only users; status icons ●/○/◐/◌ for online/offline/away/dnd. Keyboard shortcuts In the message input Key Action Enter Send the message (also sends staged attachments with the text as caption) Ctrl+N Insert a newline (multiline message) Tab Autocomplete a slash command (/th → /theme) Ctrl+V (or Ctrl+Y) Paste — copied files and images become attachments, text pastes normally (details) Ctrl+C / Ctrl+X Copy / cut in the input Ctrl+W Delete the word left of the cursor Ctrl+K Open the search palette F6 Move focus into the message list In the message list (after F6) Key Action ↑ / ↓ Select a message Enter Activate: play/download/save an attachment, open an @mention's profile, join a #channel, or open the sender's profile Delete / Backspace Delete the selected message (with confirmation; permission rules) F6 Return focus to the input Anywhere Key Action F2 Toggle the users panel Ctrl+K Search palette Alt+Q Quit The search palette (Ctrl+K) A command-palette that searches channels and app actions — type to filter, ↓ to navigate, Enter to jump. Actions include Connect, Disconnect, Logout, My Profile, Set Status, Create/Delete Channel, Saved Servers, Toggle Users Panel, Check for Updates, and Quit. Ctrl+K again closes it. Mouse Right-click a message for the context menu: save image / play audio / download file (depending on the attachment), *Mention @user*, View profile, Copy text, Copy message ID, Delete message. Left-click a message does the most useful thing for that line: attachments play/download/save, @mentions and the sender open profiles, #channel references join that channel. Click a user in the users panel to open their profile; click a channel to switch. Slash commands Type /help in any channel for the full list. The highlights: Command What it does /status <online\\|away\\|dnd\\|invisible> or /status <message> Presence / status message /nick <name>, /color <#hex>, /avatar <url or path> Display name, nick color, avatar /theme <name> Switch theme /send, /clear, /size, /downloadpath Attachments — see Messages & Attachments /join <channel> [password], /leave, /topic <text> Channel membership and topic /passwd <old> <new> Rotate an encrypted room's passphrase /profile [user], /users, /meta Profiles, online users, room info /kick, /ban, /mute, /role, /nuke, … Moderation /servers, /quit Saved servers, exit Emoji shortcodes (:smile: style) are replaced live as you type. Themes 14 built-in themes: Default, Transparent, TransparentLight, Classic, Light, Hacker, Solarized, Dracula, Monokai, Nord, Gruvbox, Ocean, HighContrast, RosePine — switch from the User menu or /theme <name>. The two Transparent themes use no background color at all, so your terminal's own background (and any blur/acrylic) shows through. You can add your own: drop a theme JSON into ~/.echohub/themes/ and it appears in the list (names that collide with a built-in are skipped). Everyday behaviors Unread markers — a ── new messages ── rule marks where you left off in each channel, irssi-style. Read positions are persisted per server, so the marker survives reconnects and restarts. The status bar's Act: segment lists channels with activity (orange when you were @mentioned), and day boundaries draw a date rule. Auto-join — connecting joins #general plus every channel you're a member of, so unread counts and mentions accumulate everywhere. Channels you /leave stay left, and password-protected or encrypted rooms are never auto-prompted — join those explicitly. #general is the home channel and can't be left or deleted. Scrollback — history loads 100 messages at a time; scrolling to the top of a channel fetches the next page and keeps your position (no jump). Drag & drop — dropping a file onto the window stages it as an attachment."
  },
  "changelog/index.html": {
    "href": "changelog/index.html",
    "title": "Changelog | EchoHub Documentation",
    "summary": "Changelog Release history for EchoHub. Releases v0.2.16 - Periodic Server-Stats Report, Upload & Moderation Logging & Quieter Connection Logs v0.2.15 - Invite Codes, Data Export & Deletion, /me, /banner, Replies, Open Images In Browser & IRC Image Links v0.2.14 - Clipboard Image & Multi-File Paste, E2E Room Unlock Fixes, Encrypted Key Cache & IRC Gateway Polish v0.2.13 - Chat Visual Overhaul, Auto-Join All Channels & Persistent Read Positions v0.2.12 - End-to-End Encrypted Channels, IRC Channel Keys, Image Save & Ctrl+W Crash Fix v0.2.11 - EchoHubSpace Auth, Live Directory Updates & Server Browser Metadata v0.2.10 - Command Palette, Infinite History Scroll & Auto-Updater Fixes v0.2.9 - Install Script & Chocolatey Fixes v0.2.8 - Docker Support, IRC Account Creation & BOM Fix v0.2.7 - User List Fix & Terminal.Gui NuGet Migration v0.2.6 - Major Refactoring & Code Organization v0.2.5 - Session Persistence, Auto-Updates, Audio & Transparent Theme v0.2.4 - E2E Message Encryption v0.2.3 - Moderation, Embeds & UI Overhaul v0.2.2 - Startup & Shutdown Fixes v0.2.1 - Shutdown & CI Fixes v0.2.0 - IRC Gateway v0.1.1 - Directory Connection Self-Healing v0.1.0 - Initial Release"
  },
  "changelog/v0.1.0.html": {
    "href": "changelog/v0.1.0.html",
    "title": "v0.1.0 - Initial Release | EchoHub Documentation",
    "summary": "v0.1.0 - Initial Release Features Real-time messaging via SignalR JWT authentication with access and refresh tokens Channel management (create, set topic, delete) File and image uploads with magic-byte validation Image-to-ASCII conversion for terminal display Presence tracking (online, away, DND, invisible) 6 built-in TUI themes Rate limiting on auth, upload, and general endpoints Self-contained binary releases for Windows, Linux, and macOS"
  },
  "changelog/v0.1.1.html": {
    "href": "changelog/v0.1.1.html",
    "title": "v0.1.1 - Directory Connection Self-Healing | EchoHub Documentation",
    "summary": "v0.1.1 - Directory Connection Self-Healing Fixes Server now reconnects to the EchoHubSpace directory indefinitely when the API goes down, using exponential backoff (2s → 30s cap) If automatic reconnect is exhausted, the connection is rebuilt from scratch automatically Initial connection attempts also use exponential backoff instead of a fixed 30s delay"
  },
  "changelog/v0.2.0.html": {
    "href": "changelog/v0.2.0.html",
    "title": "v0.2.0 - IRC Gateway | EchoHub Documentation",
    "summary": "v0.2.0 - IRC Gateway Features IRC protocol gateway -- native IRC clients (irssi, WeeChat, HexChat, etc.) can connect to EchoHub servers Cross-protocol messaging -- messages flow bidirectionally between IRC and TUI clients in real time SASL PLAIN authentication -- IRC clients can authenticate via SASL or traditional PASS/NICK/USER Full IRC command support -- JOIN, PART, PRIVMSG, QUIT, NAMES, TOPIC, WHO, WHOIS, AWAY, LIST, MODE, MOTD TLS support -- optional encrypted IRC connections on port 6697 Image-to-IRC formatting -- images appear as ASCII art line-by-line with download URLs Message splitting -- long messages automatically split at word boundaries (~400 byte chunks) Configurable MOTD -- server message of the day for IRC clients Architecture Changes Extracted shared business logic from ChatHub into protocol-agnostic IChatService Introduced IChatBroadcaster pattern for multi-protocol event fan-out ChatHub refactored to thin adapter delegating to IChatService ChannelsController updated to use IChatService for broadcasts New EchoHub.Server.Irc project for clean separation of concerns IChatService and IChatBroadcaster interfaces live in EchoHub.Core/Contracts"
  },
  "changelog/v0.2.1.html": {
    "href": "changelog/v0.2.1.html",
    "title": "v0.2.1 - Shutdown & CI Fixes | EchoHub Documentation",
    "summary": "v0.2.1 - Shutdown & CI Fixes Fixes Fixed server hanging on Ctrl+C when the directory server is unreachable — StopAsync now cancels background services before disposing connections Fixed IRC gateway shutdown blocking indefinitely on unresponsive clients — send operations are now bounded to 2 seconds Fixed CI release workflow not having full git history for building release notes (fetch-depth: 0) Fixed workflow_dispatch trigger breaking change detection when github.event.before is empty Improvements GitHub releases now include a commit list and version diff link instead of generic auto-generated notes GitHub releases link to the full changelog on the docs site"
  },
  "changelog/v0.2.10.html": {
    "href": "changelog/v0.2.10.html",
    "title": "v0.2.10 | EchoHub Documentation",
    "summary": "v0.2.10 Follow-up patch release for v0.2.9 addressing auto-updater regressions, adding a command palette, infinite-scroll message history, and input polish. New Features Command palette — press Ctrl+K from the message input (or anywhere in the main window) to open a searchable dialog for navigating channels and triggering app actions (connect, disconnect, logout, profile, status, create/delete channel, saved servers, toggle users panel, check for updates, quit). Fuzzy matches against both the label and the underlying key so typing ch surfaces channel actions alongside #channel entries Scroll-to-load message history — scrolling to the top of a channel now fetches the next batch of older messages in the background (previously only the most recent 100 messages were available). Duplicate messages are filtered by ID, a per-channel guard prevents concurrent fetches, and the scroll position is preserved after the prepend so your reading position doesn't jump Bug Fixes Fix update progress dialog freezing / not repainting — progress callbacks now run on the UI thread so the download and extraction percentage actually updates while an update is in progress Fix pre-update backup failing when a Serilog-held log file is locked — UpdateBackupService now enumerates files manually, skips the logs/ directory and .log files, and logs-and-continues on IOException/UnauthorizedAccessException instead of aborting the whole backup Simplify update progress dispatch — remove redundant Application.Invoke wrappers around progress updates that are already called from the UI thread (introduced while fixing the freeze above) Fix cursor position being reset to the start of the line when auto-completing commands in the CLI app — insertion point is now moved to the end of the completed text Fix notification sounds crashing or being silently dropped when several arrive in quick succession — playback is now serialized through a semaphore that's held for the duration of each sound (using PlaybackFinished with a 10s safety timeout) and always released in finally, so back-to-back notifications queue up and play in order instead of racing the underlying audio player (fixes #20) Fix client crashing on startup with No Serilog:Using configuration section is defined under single-file publish — pass ConfigurationReaderOptions with the Serilog.Sinks.File assembly explicitly so Serilog can resolve sinks without scanning the filesystem for .dlls (which don't exist in a bundled exe) Refactoring Move search-dialog dispatch out of MainWindow into AppOrchestrator — MainWindow now just raises OnSearchRequested, keeping the view dumb and letting the orchestrator own navigation/action routing ChatHub.GetChannelHistory and IChatService.GetChannelHistoryAsync gain an additional offset parameter for paginated history loading (defaults to 0 — existing callers are unaffected) ValidationConstants.MaxHistoryCount raised from 100 to 200 so power users and paginated fetches can request larger batches; DefaultHistoryCount stays at 100"
  },
  "changelog/v0.2.11.html": {
    "href": "changelog/v0.2.11.html",
    "title": "v0.2.11 | EchoHub Documentation",
    "summary": "v0.2.11 EchoHubSpace directory protocol overhaul: authenticated server registration with persistent claim tokens, near-real-time user-count updates, and richer server metadata (tags, multi-host, version). Coordinated cutover with the EchoHubSpace directory deploy. New Features EchoHubSpace claim-token authentication — the directory issues a per-server claim token on first registration, persisted atomically alongside the SQLite database (chmod 0600 on Unix). Subsequent reconnects authenticate with the token instead of relying on raw hostname-squatting protection. Token survives both client and directory restarts; lost tokens require an admin-side DELETE /api/servers/{id} on the directory to recover Server tags — public servers can advertise topic tags via the new Server:Tags config array, surfacing as filter facets in the EchoHubSpace browser Multi-host advertisement — a single server can register multiple hostnames (e.g. apex domain, IPv6, alias domains) by listing them in Server:PublicHosts. All hosts route to the same directory row Server version sent to directory — the EchoHubSpace browser shows what version each public server is running, pulled from the server's assembly informational version Operator-facing GET /api/server/directory endpoint (Admin role required) — returns ServerId, IsRegistered, LastRegisteredAt, LastError, and any ConflictingHosts for support tickets. Never exposes the claim token itself, only a HasClaimToken boolean Refactoring Replace 30s polling with event-driven directory updates — PresenceTracker now raises UserCountChanged only when the distinct user count actually changes (multi-tab/multi-connection users no longer trigger). ServerDirectoryService consumes via a single-slot Channel<int> (latest-wins coalesces bursts) with a 1-second min-interval throttle. Directory reflects user-count changes within ~1s instead of up to 30s stale Wrap directory hub responses in a Response<T> envelope with IsSuccess/Data/Errors/Version shape — protocol version is pinned client-side (currently 1.0); mismatches trigger a permanent-failure stop with operator-facing log Stop attempting re-registration after permanent failures (HostAlreadyClaimed, InvalidToken, HostConflict, InvalidInput) — the directory no longer terminates the connection on these errors, so the client suppresses re-register on Reconnected to avoid tight retry loops. Operator must restart the server after fixing config Configuration Breaking: Server:PublicHost (string) renamed to Server:PublicHosts (string array). Public servers must update appsettings.json — single-host deployments use a one-element array New Server:Tags (string array) — defaults to empty New optional Server:DirectoryClaimPath — overrides the path of the persisted claim file. Defaults to a directory-claim.json next to the SQLite database. Treat the file as a secret; back it up alongside the database"
  },
  "changelog/v0.2.12.html": {
    "href": "changelog/v0.2.12.html",
    "title": "v0.2.12 | EchoHub Documentation",
    "summary": "v0.2.12 Private channels are now genuinely private: password-protected channels are end-to-end encrypted, so the server (and its operators) can gate joins and measure storage but cannot read message or file contents. The IRC gateway grows real MODE/TOPIC support and channel keys, and the client gets image \"save original\", a transparent-light theme, drag-and-drop file sending, Ctrl+V paste, and a fix for the intermittent Ctrl+W crash. New Features End-to-end encrypted channels — creating a channel with a password now provisions a zero-knowledge room: The passphrase never leaves the client. It derives (PBKDF2-SHA256, 210k iterations) two keys: an auth key sent to the server as the join credential, and a key-encryption key that never leaves the machine. A random room content key encrypts every message and file with AES-256-GCM. The server only ever stores the room key wrapped under the passphrase, so it can gate joins and report a channel's message count, storage size, and attachments — but cannot decrypt any of it. Even the server owner cannot read a private room's contents. Members' clients cache the derived room key locally (in the per-server config, like saved sessions) so the passphrase isn't retyped every launch; joining on a new device prompts for it once. Change the passphrase with /passwd <old> <new> (channel creator only). The room key is re-wrapped, not rotated, so existing history stays readable and members who join later with the new passphrase can still read older messages. Files and images are encrypted client-side before upload; for images the ASCII-art preview is rendered on the client and stored room-encrypted too. Sending images by URL is disabled in encrypted channels (the server can't fetch-and-render without the key). End-to-end encrypted channels cannot be joined over the IRC gateway (that would require the server to hold the room key) — IRC JOIN returns 475 directing users to the EchoHub client. Password-protected channels — set an optional password when creating a channel (masked field in the Create Channel dialog, password on POST /api/channels). Passwords are BCrypt-hashed server-side; the join gate applies on first join only (existing members and the creator are unaffected). Protected channels show a * marker in the channel list and +k in the status bar Save original images — image messages now show a clickable \"[↓ save original]\" line under the ASCII-art preview that downloads the full-resolution original to your Downloads folder (decrypting locally in encrypted channels) Messages with attachments (Discord-style) — a message is now text plus a list of attachments instead of being either text or a single file. One message can carry a caption and several files (images, audio, docs) together: Compose with a staging tray: /send <file> or dropping files onto the terminal stages them (shown on the input bar); the next Enter sends your typed caption and all staged files as one message. /clear drops staged files. /send <URL> still posts an image immediately. Each image attachment renders its own ASCII preview with its own \"save original\" action; audio/file attachments each get their own play/download line. In encrypted channels every attachment is encrypted individually (blob + ASCII preview), and the caption is room-encrypted — the server still stores only ciphertext and can report count/size but not contents. Up to 10 attachments per message. Right-click message menu — right-click any message for a context menu: save/download/play its attachment, mention the sender, view their profile, copy the text, copy the message ID (for linking or command arguments), or delete the message. (Keyboard: F6 focuses the message list for arrow-key selection + Delete.) The selected message is now highlighted while the list is focused. Message deletion — press Delete on a selected message to remove it. You can always delete your own messages; moderators and above can delete others' messages, but only from users below their own role (a mod can't delete an admin's or owner's message). Deleting a message also removes its attachment blobs from server storage. Customizable download folder — /downloadpath opens your OS-native folder picker (Windows Explorer / macOS Finder / Linux GTK or KDE) to choose where downloaded attachments and saved images go; /downloadpath <path> sets it directly (the fallback when no native picker is available). Downloaded files now land in that folder (with automatic (n) de-duplication) instead of a temp directory. /join <channel> [password] — join protected channels inline, or let the client prompt: joining a protected channel without a password opens a masked prompt that re-prompts on a wrong password IRC channel keys — JOIN #room <key> works against room passwords (RFC 1459 comma-paired key lists supported); keyless or wrong-key joins get 475 ERR_BADCHANNELKEY IRC MODE implemented — MODE #chan reports +k/+, MODE #chan +k <key> sets and -k clears the room password (channel creator or admin only), ban-list probes get a clean empty reply, and CHANMODES is advertised in ISUPPORT IRC TOPIC set support — the channel creator can change the topic from IRC; the change broadcasts to connected TUI clients (previously topic changes were rejected with a stub error) Attach a file by drag & drop or by pasting — drop a file onto the terminal, or copy a file in your file manager and press Ctrl+V, to stage it as an attachment (the next Enter sends it with your caption). Multiple files at once are supported. Ctrl+V still pastes text when the clipboard holds text; Ctrl+Y is a paste alias. On Windows the copied-file paste reads the clipboard's file list directly (Windows Terminal never pastes copied files as text), with xclip/wl-paste used on Linux Pick ASCII-art size for attached images — /size opens a Small/Medium/Large picker (40×40 / 80×80 / 120×120) with descriptions, /size <s|m|l> sets it directly, and /send <file> -l sets it for that message. The choice is a saved preference and applies to copy-paste/drag-drop images (which have no per-file flag); the current size is shown in the staging tray New TransparentLight theme — dark characters on a transparent background, for light terminal color schemes (/theme transparentlight) /meta command — shows a summary of the current room: room id, created date, message count, unique participant count, estimated storage size, and protection level (open / password-protected / end-to-end encrypted). Works for encrypted rooms too, since these are all metadata the server tracks without reading content. (/info is an alias.) Configurable upload limits — a new Uploads section in the server settings sets the maximum size per file, image, audio clip, and avatar, plus the maximum attachments per message. Absent values fall back to the previous built-in limits, so existing servers are unaffected until they opt in. Updater progress bar — the self-updater now draws a real progress bar on the console (Downloading [████████░░] 62%) for each step, replacing the plain status lines, so a large download shows visible progress. Timestamps in messages are culture-aware and now always include the date: today's messages show a compact date + short time, and older messages show the general short date/time. Bug Fixes Transparent themes no longer draw an opaque box behind the message input. The input TextView renders with the Editable visual role, which Terminal.Gui derives as an opaque color when a theme leaves it unset; the themes now pin Editable/ReadOnly to their base colors so the input matches its (transparent) background. Attachments whose files have been pruned (retention cleanup deletes blobs older than Storage:RetentionDays but left the message rows) no longer render a dead download/preview. When channel history loads, the server checks which attachment blobs still exist: missing ones are dropped from the message, and an attachment-only message whose files are all gone is removed from the database. Fixed intermittent crash on Ctrl+W — Terminal.Gui binds Ctrl+W to clipboard-cut, and Windows clipboard contention (another app holding the clipboard) threw an unhandled Win32Exception that took the app down. Ctrl+W now deletes the previous word (readline behavior, no clipboard), and all clipboard shortcuts (Ctrl+X/C/V/Y) are guarded so transient clipboard failures log a warning instead of crashing Fixed emoji shortcode replacement permanently disabling itself if a cursor update threw mid-replacement Fixed new messages showing no date — today's messages rendered time-only, and the \"is it today?\" check compared the server's UTC date against the local date, so the classification could also be wrong near midnight. Timestamps are now converted to local time first and always include the date. Fixed the self-updater hanging at \"extracting\" — the update ran while the Terminal.Gui main loop still owned the console, so the old and new processes deadlocked over it. The update now runs after the TUI shuts down, on a clean console IRC LIST no longer leaks private channels; protected channels are marked [+k] API Changes ChannelDto gains isProtected and isEncrypted; CreateChannelRequest gains optional password, encryptionSalt, and wrappedRoomKey; SignalR JoinChannel takes an optional second password argument and JoinChannelResult gains passwordRequired, encryptionSalt, and wrappedRoomKey (older clients must update to join over SignalR) New endpoints: GET /api/channels/{channel}/crypto (public crypto metadata — salt only, never the wrapped key) and POST /api/channels/{channel}/rekey (creator-only passphrase change) New endpoint GET /api/channels/{channel}/meta returns a ChannelMetaDto (room id, name, topic, encrypted/protected flags, message count, unique user count, estimated size, created date) backing the /meta command New server Uploads configuration section (MaxFileSizeMB, MaxImageSizeMB, MaxAudioSizeMB, MaxAvatarSizeMB, MaxAttachmentsPerMessage); the message-upload endpoint's request-body ceiling is now derived from these values at runtime rather than from compile-time constants The upload endpoint accepts type and content form fields for encrypted channels, where the client supplies the declared message type and room-encrypted content ImageToAsciiService and FileValidationHelper moved from EchoHub.Server to EchoHub.Core so the client can render ASCII art and detect file types for encrypted uploads Message shape change: MessageDto drops Type/AttachmentUrl/AttachmentFileName/AttachmentFileSize and gains Attachments (a list of AttachmentDto { Kind, Url, FileName, FileSize, AsciiPreview }, null/empty for plain text). New Attachment entity + table with a cascade FK to Message New endpoint POST /api/channels/{channel}/messages (multipart: content + N files, plus kind/preview per file for encrypted channels) replaces the single-file upload endpoint; DELETE /api/moderation/messages/{id} now enforces the own-or-higher-role rule New EF migrations AddChannelPasswordHash, AddChannelEncryptionEnvelope, and AddMessageAttachments (applied automatically on server start); a one-time startup data migration folds legacy single-attachment messages into the new model"
  },
  "changelog/v0.2.13.html": {
    "href": "changelog/v0.2.13.html",
    "title": "v0.2.13 | EchoHub Documentation",
    "summary": "v0.2.13 A visual overhaul of the chat client — WeeChat-style column layout, per-user nick colors, date rules, unread markers, an activity status segment, and an ASCII welcome banner — plus reliable cross-channel notifications: the client now joins all your channels at connect so unread counts and @mentions light up everywhere, and read positions persist across restarts so activity that happened while you were offline still shows. Modern polish, old IRC soul. New Features Aligned message layout (WeeChat-style) — every message renders as HH:mm nickname │ text: a dim timestamp, the nick right-aligned in a fixed 12-column gutter, and a dim │ rail separating names from content. Word-wrapped continuation lines, multi-line messages, attachments, ASCII-art image previews, and link embeds all align under the text column and extend the rail, so conversations read as a clean two-column grid. Over-long nicks are truncated with … in the gutter only — profiles, mentions, and the users panel always use the full name. System and status lines (joined, is now Away, …) show -- in the nick column, replacing the old ** prefix. Deterministic nickname colors — users who haven't picked a /color now get an automatic, stable color derived from their nick (12-entry palette chosen to stay readable on dark and light backgrounds). The same user is the same color in message headers and the online-users panel, on every client, every session. An explicitly chosen nickname color still takes priority. Date separator rules — a full-width ── Wed, Jul 16 2026 ───── rule is inserted whenever the calendar day changes (live, in loaded history, and when scrolling back through older messages). Timestamps are now a compact HH:mm — the date lives in the rules instead of being repeated on every line. \"New messages\" marker — the first message that arrives in a channel you're not viewing gets an orange ── new messages ── rule above it (irssi-style). Switch to the channel and the marker shows exactly where you left off; it survives the history reload on channel switch and is consumed when you move away again. Status bar activity segment — channels with unread messages appear in the status bar as │ Act: #dev,#random (up to 4, +n overflow). Channels where you were @mentioned show in orange, others in cyan. Connecting spinner — transitional connection states (Connecting, Reconnecting, Authenticating…) now animate a braille spinner in the status bar instead of sitting on static text. Mention-aware channel list — a channel where you were @mentioned turns orange in the channel list (name and unread badge), escalating above the cyan plain-unread highlight. The highlight clears when you view the channel. Welcome banner — with no channel selected (fresh start, or after disconnecting) the chat pane shows a gold-gradient ASCII \"ECHOHUB\" logo with the version and key hints, instead of an empty box. Narrow panes get a compact variant. Rounded frame borders — the channels, chat, input, and users panels draw with rounded corners. Auto-join all channels at connect — the client now joins every channel it lists for you (public channels and prior memberships) as part of connecting, so message events flow for all of them: unread badges, @mention highlights, and the status-bar activity segment work without having to open each channel first. Protected channels you've never entered are skipped silently (joining them stays a prompted, manual action), and channels you /leave are remembered and excluded until you join them again. Read positions survive restarts — the client persists the last message you read per channel (locally, per server). On the next connect it compares that against fetched history, so messages that arrived while you were offline still produce unread counts, mention highlights, and a correctly placed \"new messages\" marker. Theme-tinted frame borders — themes can now color the window frame borders independently of text via a new optional border section (hex values supported). The Transparent and TransparentLight themes use it to draw dim gray borders instead of stark white ones, for a subtler, glassier look. Custom theme JSON files without a border section keep their base colors. Bug Fixes The @mention highlight no longer drops off the second and later lines of a long mention message — wrapped continuation lines now inherit the highlight. The unread-position marker is re-anchored after a channel switch reloads history, instead of being wiped by the reload before you could see it. Cached room keys for end-to-end encrypted channels no longer disappear from the config on reconnect. Saving the server entry after a successful connect replaced it wholesale, wiping the cached keys (and forcing a passphrase re-entry on the next launch); the entry is now updated in place. A failed channel rejoin after a reconnect (e.g. a channel deleted while you were away) no longer aborts rejoining the remaining channels. Client config file access is now serialized across threads (token refresh, room-key cache, and read-position checkpoints all write it), preventing rare config corruption."
  },
  "changelog/v0.2.14.html": {
    "href": "changelog/v0.2.14.html",
    "title": "v0.2.14 | EchoHub Documentation",
    "summary": "v0.2.14 A reliability and security pass over end-to-end encrypted rooms: locked channels now offer the passphrase prompt instead of dead-ending on the \"rejoin to unlock\" placeholder, the client can no longer be tricked into sending plaintext into an encrypted room, and cached room keys are encrypted at rest instead of sitting in the config as base64. Ctrl+V grows up too — images copied from a browser or screenshot tool paste straight into the chat as attachments, and copying several files pastes them all into one message. Plus a set of IRC gateway fixes — decrypted image previews, display-name plumbing, and user/channel indicators. New Features Paste images straight from the clipboard — copy an image from a browser, a screenshot tool (Win+Shift+S), or an image editor and Ctrl+V it into the input: it's attached as a PNG (image.png), no saving to disk first, Discord-style. Transparency is preserved when the source provides PNG data; plain clipboard bitmaps are converted automatically. On Linux this uses wl-paste/xclip; on macOS it requires pngpaste. In end-to-end encrypted rooms pasted images go through the same client-side encryption as any other attachment. Multi-file paste — copying several files in your file manager and pasting attaches them all to a single message (up to the 10-attachment cap), staged as one batch alongside anything you type as the caption. Previously each pasted file was routed through its own /send, which could misbehave on large batches. Room keys encrypted at rest — the per-channel room keys cached so you don't retype a passphrase every launch are no longer stored as plain base64 in config.json. On Windows they're protected with DPAPI (current-user scope); on Linux/macOS with AES-GCM under a per-user key file created with 0600 permissions next to the config. Existing plain entries migrate to the encrypted format automatically on first load. The passphrase itself is never stored in any form. [irc] tag in the users panel — users online only through the IRC gateway are tagged [irc], useful context since IRC clients lack encryption, attachments, and profiles. Someone also running the TUI shows untagged. ~ marker for private channels — the channel list now marks private (unlisted) channels with a trailing ~, alongside the existing * for password-protected ones (#room*~ when both apply). Bug Fixes Locked encrypted channels now prompt for the passphrase. Auto-joining your channels at connect silently entered end-to-end encrypted rooms you're a member of without running the unlock flow — on a new device (or after a cancelled prompt) the room showed only [encrypted — rejoin this channel with its passphrase to unlock] placeholders, and only a manual /join recovered it. Selecting the channel now offers the passphrase prompt; entering it unlocks history and live messages in place. Cancelling is remembered for the session so reselecting the channel doesn't nag — /join or trying to send always re-offers the prompt. A stale cached room key no longer beats a fresh one. If an encrypted channel was deleted and recreated under the same name, a client that still had the old key cached kept encrypting messages nobody else could read. Typing the passphrase on join now always adopts the key from the server's current envelope, replacing the stale cache. IRC clients no longer get flooded with ciphertext for image messages. The gateway forwarded image ASCII previews without stripping transport encryption, spamming IRC clients with one enormous $ENC$v1$… line per image. Previews are now decrypted before formatting, and any that still can't be read (e.g. end-to-end room ciphertext the server cannot decrypt) are skipped in favor of the plain [Image: name] url line. Display names now show on chat messages. Messages only carried the sender's username, so a configured display name appeared in the user list but not on the messages themselves. Live messages, history, and attachment messages now all carry it; mention and profile lookups stay keyed to the username. Security No plaintext can leak into an encrypted room. Previously, a client without the room key silently sent unencrypted text into an end-to-end encrypted channel (and other members saw it as a normal message, none the wiser it went over the wire readable by the server). All send paths — typed messages, staged file attachments, and URL sends — are now blocked while a room is locked: the client offers the unlock prompt, keeps staged files in the tray, and refuses to transmit until the key is present, with a hard guard at the connection layer as backstop."
  },
  "changelog/v0.2.15.html": {
    "href": "changelog/v0.2.15.html",
    "title": "v0.2.15 | EchoHub Documentation",
    "summary": "v0.2.15 A big one. Server owners can gate registration behind invite codes, users can export or delete everything the server holds about them, and chat gets three classics — /me actions that interop natively with IRC, /banner ASCII text, and message replies with quote rendering and jump-to-original. Images become properly shareable: every image gets an [open] action that views it in your browser (or, in end-to-end encrypted rooms, decrypts and opens it locally). The IRC gateway stops painting ANSI art nobody asked for and posts plain image links any IRC client can open or auto-preview. And a long-standing dual-session annoyance is fixed: messages you send from the TUI now reach your own connected IRC client instantly. New Features Invite-gated registration — set Server:Registration to \"invite\" (or \"closed\") in appsettings.json and new accounts require a valid invite code; the default \"open\" keeps today's behavior. Admins mint codes with /invite [uses] [hours] (single-use, never-expiring by default), list them with /invite list, and revoke with /invite revoke <code>. Codes are unguessable (K7QM-3XPF style), stored only in your server's own database, and consumed atomically so a code's last use can't be taken twice. The IRC gateway respects the gate too: unknown nicknames connecting to an invite-only server get a clear error instead of a silent auto-registered account. The very first account (server owner bootstrap) is always allowed. Data export — /export downloads everything the server stores about you (profile, your messages, attachment metadata) as JSON into your download folder. Content from end-to-end encrypted rooms appears as ciphertext, because the server never had the plaintext — \"you own the data\", demonstrably. Account deletion — /deleteaccount permanently removes your account after a typed confirmation and password re-check: profile, sessions, and every file you uploaded are deleted. Your messages remain (deleting them would gut other people's conversations) but are re-attributed to a reserved deleted-user name. The last remaining Owner can't self-delete. /me actions — /me waves renders as * nick waves. On the wire it's a real CTCP ACTION, so irssi/WeeChat users see a native action line, and their /me renders properly in the TUI. Works in encrypted rooms (the marker encrypts with the text). /banner <text> — renders short text (up to 20 characters) as a 5-row block-letter banner. Rendered locally with a built-in font — no network, no dependencies — and sent as plain message content, so it works over IRC and in encrypted rooms. Message replies — right-click a message (or F6-select) and pick Reply: the input shows a \"Replying to\" strip (Esc cancels), and the sent message renders with a dim ┌ nick: snippet quote line. Activating the quote line jumps to the original message. Being replied to lights up the channel like an @mention. IRC clients see the familiar > nick: snippet | text convention. In encrypted rooms the quoted snippet is decrypted client-side with the room key — the server still never reads room content. [open] images without saving them — every image attachment now shows [open] [↓ save original] beneath its preview. Open views the image in your default browser straight from the server; in end-to-end encrypted rooms (where a browser would only see ciphertext) the client downloads, decrypts with the room key, and opens the image in your OS viewer from a temp file instead. Both actions are individually clickable, Enter on the line opens, and the right-click menu carries both. Attachment links work in a browser — GET /api/files/{id} is now a capability URL: the unguessable GUID in the link is the access token (Discord-CDN style), so attachment links can be opened directly in a browser or shared to IRC without a login token. Images and audio are served inline so the browser displays them instead of forcing a download. Blobs from encrypted rooms remain ciphertext, so their links reveal nothing. IRC gets image links instead of terminal art — the gateway no longer floods IRC clients with truecolor-ANSI ASCII art for images. Each attachment is now a single line — [Image: photo.png] https://your-server/api/files/… — the convention every IRC client understands, and ones like TheLounge or IRCCloud auto-preview. Set the new Irc:PublicBaseUrl option (e.g. \"https://chat.example.com\") so those links come out absolute; unset, they fall back to relative paths as before. Spam protection — per-user message flood and duplicate-message limits, join and channel-creation throttles, and automatic escalation: enough rejected sends in a few minutes earns a timed auto-mute through the normal mute system (moderators see it, and it expires on its own). One guard covers every ingress — TUI and IRC clients hit the same limits. Mods and above are exempt, everything is configurable under the new Spam section (Spam:Enabled master switch, lenient defaults a fast typist won't trip), and the guard only ever sees stored content — encrypted-room messages stay ciphertext. Improvements Strict /status — /status garbage is now an error instead of silently becoming your status message (and resetting you to Online). Status messages moved to /status msg <text>, which preserves your current status; /status msg with no text clears it. The server also rejects out-of-range status values sent by misbehaving clients. Registration dialog — gained Invite Code support, and the Display Name field you could already type into is now actually sent with registration. Bug Fixes Messages sent from the TUI now reach your own IRC session. With the same account online via both the TUI and an IRC client, messages sent from the TUI never appeared in the IRC client until it reconnected (which replayed history). The gateway suppressed the sender's echo by nickname, which swallowed the message for every IRC connection on that account — it now excludes only the exact connection a message originated from, so all your other sessions (a second IRC client included) receive it immediately. Channel deleted event — when a channel is deleted, all connected clients are now immediately notified via the new ChannelDeleted SignalR event. The channel is removed from the channel list. Previously only the deleting client was able to delete the channel. Other clients would see the channel linger until the next manual refresh. Deleted accounts don't break history — channel history now left-joins users, so messages from deleted accounts render (as deleted-user) instead of silently disappearing. Notes for server operators New config key: Server:Registration — \"open\" (default), \"invite\", or \"closed\". New config section: Spam — flood/duplicate/join/create thresholds and auto-mute duration; see appsettings.example.json. On by default with lenient limits; Spam:Enabled: false turns it all off. Note Spam:MaxJoinsPerWindow counts only first-time channel joins — keep it above your public channel count so a new member's first connect isn't throttled. New REST endpoints: POST/GET/DELETE /api/invites (Admin+), GET /api/users/me/export, DELETE /api/users/me. One new database migration (AddInvitesAndReplies) applies automatically on startup."
  },
  "changelog/v0.2.16.html": {
    "href": "changelog/v0.2.16.html",
    "title": "v0.2.16 | EchoHub Documentation",
    "summary": "v0.2.16 A logging and observability pass built on the new server-logs room. Busy servers stop drowning in connect/disconnect noise, the events operators actually care about — uploads, moderation actions — now get logged, and a new periodic report summarizes server activity as pretty-printed JSON that streams into the logs room and is saved to the database for history. New Features Periodic server-stats report — every 6 hours (configurable), the server logs an aggregate activity snapshot as pretty-printed JSON and saves it to the database. Each report covers the window since the previous one: messages sent, files uploaded (and total bytes), new members, active members (distinct senders), session connections/disconnections, kicks, bans, total registered members, users online now, and peak concurrent online. Reports also stream into the live server-logs room, and old ones are pruned past a configurable retention. New Stats config section (Stats:Enabled, Stats:IntervalHours = 6, Stats:RetentionDays = 90). Upload logging — every file, image, and audio upload is now logged with its resource URL (/api/files/{id}), kind, size, uploader, and channel. In end-to-end encrypted rooms the filename is client-side ciphertext, so it's logged as [encrypted] — the server still never sees room content. Moderation & user-action logging — role changes, kicks, bans, unbans, mutes, unmutes, moderator message removals, and channel nukes are now logged with the actor, target, and reason. Bans and channel nukes log at Warning level so they stand out. Improvements Quieter connection logs — user connect, disconnect, join-channel, and leave-channel events (over both SignalR and IRC) dropped from Information to Debug. On a busy server these fired constantly and buried everything else; the aggregate counts now live in the periodic stats report instead. They still show in Development, where the default level is Debug. Config examples stay in sync — .env.example gained the ServerLogs and Stats sections (the former had been missing), alongside appsettings.json / appsettings.example.json. Notes for server operators New config section: Stats — Enabled (default true), IntervalHours (default 6), RetentionDays (default 90, 0 keeps reports forever). See appsettings.example.json and .env.example. A non-positive interval falls back to 6h; positive values are honored down to a 1-second floor. One new database migration (AddServerStatsReports) applies automatically on startup. Connect/disconnect/join/leave now log at Debug — if you relied on those Information lines, lower Serilog:MinimumLevel:Default (or ServerLogs:MinLevel for the room) to Debug, or read the periodic stats report for aggregates."
  },
  "changelog/v0.2.2.html": {
    "href": "changelog/v0.2.2.html",
    "title": "v0.2.2 - Startup & Shutdown Fixes | EchoHub Documentation",
    "summary": "v0.2.2 - Startup & Shutdown Fixes Fixes Fixed server hanging on startup when IRC gateway is enabled — circular DI dependency between IrcGatewayService → IChatService → IChatBroadcaster → IrcBroadcaster caused the DI container to deadlock Fixed SignalRBroadcaster eagerly resolving IHubContext<ChatHub> during DI construction, which could deadlock on some platforms — now lazy-resolves via IServiceProvider on first use Simplified IRC service registration to use standard AddSingleton<IChatBroadcaster, IrcBroadcaster> instead of manual factory, breaking the circular resolution chain Fixed server hanging on Ctrl+C — replaced await using with explicit dispose bounded to 3 seconds, so a stuck HubConnection can no longer block shutdown Reduced host shutdown timeout from 30s (default) to 5s Caught OperationCanceledException in the directory service reconnect loop so cancellation exits immediately instead of propagating through dispose Refactoring Replaced primary constructors with standard constructor injection across all server classes for consistency"
  },
  "changelog/v0.2.3.html": {
    "href": "changelog/v0.2.3.html",
    "title": "v0.2.3 - Moderation, Embeds & UI Overhaul | EchoHub Documentation",
    "summary": "v0.2.3 - Moderation, Embeds & UI Overhaul Features Moderation System Added server roles: Owner, Admin, Mod, Member — first registered user is automatically Owner New /kick, /ban, /unban, /mute, /unmute, /role, /nuke commands for moderators and admins ModerationController with full REST API for role assignment, kicks, bans, mutes, message deletion, and channel nuking Mutes support optional duration (auto-expire) and blocked users cannot log in Role claim included in JWT tokens; role badges shown in the online users panel Kicked and banned users are forcibly disconnected in real time — server cleans up presence, broadcasts departures, and signals client disconnect Works for both SignalR and IRC connections; client shows an error dialog with the reason Private Channels Channels can be created as public or private via a checkbox in the Create Channel dialog Public channels are visible to all users; private channels only appear for members who joined them Persistent channel membership tracked in the database (ChannelMembership table) GET /api/channels returns the combined list: public channels + user's joined private channels Channel creators are automatically added as members OpenGraph Link Embeds Messages containing URLs now show a rich preview below the message text Multiple URLs per message supported (up to 3) — each gets its own embed Server-side fetching: detects URLs in a message, fetches each page, and parses OpenGraph meta tags (og:title, og:description, og:site_name) Embeds are persisted in the database as a JSON array and included in channel history TUI client renders embeds with a ▏ left border bar — site name and border in blue, title in white, description in gray; text word-wraps at actual viewport width IRC gateway receives a text-only embed preview (site name, title, description) Falls back to <title> tag when no OG tags are present; gracefully skips if no useful metadata is found 5-second fetch timeout ensures message delivery is never significantly delayed SSRF protection rejects private/loopback IP addresses before fetching Notification Sounds Incoming messages play a notification sound when the terminal is not focused Embedded MP3 asset with cross-platform playback support Online Users Panel Collapsible right-side panel showing online users in the current channel (toggle with F2) Users displayed with status indicators, role badges, and their custom nickname colors Panel updates on join, leave, and status change events @mention Highlighting @username text rendered in orange accent color in all messages Messages mentioning the current user get a full-line amber background highlight Works across multi-line messages and continuation lines ASCII Art Improvements Half-block character rendering (▀/█) with separate foreground + background colors for 2x vertical resolution Switched from ANSI escape codes to printable color tags ({F:RRGGBB}, {B:RRGGBB}, {X}) — no control bytes in stored content Optional size parameter for /send command: -s (40x40), -m (80x80, default), -l (120x120) IRC gateway converts color tags back to ANSI for IRC client compatibility Client UI Version number shown in the status bar Custom colored rendering for channel list (active indicator, unread count badges) Avatar upload field added to the profile edit dialog (file path or URL) Profile avatar now renders with full color tag support in the profile view dialog Update check notification on connect — shows a system message if a newer GitHub release exists Chat messages no longer show selection/focus highlight Exit shortcut changed from Ctrl+C to Alt+Q — frees Ctrl+C for copy Default history increased from 50 to 100 messages on channel join Fixes Fixed #general channel not visible after adding the IsPublic column — migration default changed to true and startup service ensures it Fixed channels disappearing when creating a new channel — replaced full re-fetch with incremental updates Wired OnChannelUpdated SignalR event so new public channels appear for all connected users in real time Fixed color tag parser using wrong regex group numbers (6,7,8 instead of 1,2,3) — new ASCII art was rendering without colors Full Unicode/emoji support — renderers use Terminal.Gui v2 grapheme cluster API (GraphemeHelper, AddStr) for proper wide character handling Emoji-to-text shortcode conversion for consistent cross-platform rendering Fixed /send and /avatar commands not handling file paths with spaces correctly, even when quoted Server-side newline spam protection — consecutive blank/whitespace-only lines collapsed to 1 and total lines capped at 30 Fixed OG tag regex truncating descriptions containing apostrophes (e.g. \"HueByte's portfolio\" was cut to \"HueByte\") — switched to backreference-based quote pairing Infrastructure New LinkEmbedService on the server — URL detection, HTML fetching (first 64KB), OG tag parsing via compiled regex EmbedDto record added to shared Core DTOs; MessageDto.Embeds list for multiple embeds per message EmbedJson nullable column on the Message table stores serialized embed data as JSON array (max 8KB); DataMigrationService auto-migrates old single-object format Dedicated \"OgFetch\" named HttpClient with bot User-Agent header and 5-second timeout PresenceTracker.ForceRemoveUser() for atomic user cleanup on kick/ban IChatBroadcaster.ForceDisconnectUserAsync() and IEchoHubClient.ForceDisconnect for force-disconnect signaling NotificationSoundService for cross-platform audio playback of embedded notification sounds Startup DataMigrationService automatically converts old ANSI-format messages to the new color tag format on server boot, logging the count of migrated records EmojiHelper utility for emoji-to-shortcode conversion Heartbeat handling in ServerDirectoryService for connection health checks Four new EF Core migrations: AddModerationRoles, AddChannelIsPublic, AddChannelMembership, AddMessageEmbed ChannelMembership table with cascade delete on both channel and user removal"
  },
  "changelog/v0.2.4.html": {
    "href": "changelog/v0.2.4.html",
    "title": "v0.2.4 - E2E Message Encryption | EchoHub Documentation",
    "summary": "v0.2.4 - E2E Message Encryption Features E2E Message Encryption Application-layer AES-256-GCM encryption for all message content between client and server Protects against ISPs, proxies, and any middleman reading chat messages — even if TLS is compromised 256-bit encryption key auto-generated on first server startup and saved to appsettings.json Client fetches the encryption key automatically after login via GET /api/server/encryption-key Client encrypts messages before sending via SignalR; server decrypts for validation and processing Server broadcasts encrypted content to SignalR clients; client decrypts transparently — no user action required Optional database encryption at rest via Encryption:EncryptDatabase setting (disabled by default) When enabled: new messages encrypted before DB storage (existing plaintext messages are not retroactively encrypted) When disabled: messages stored as plaintext, no risk of data loss from key rotation Reads handle mixed content (encrypted + plaintext) regardless of setting — safe to toggle at any time Key rotation is safe: old plaintext stays readable, new messages use the new key IRC gateway automatically decrypts messages before forwarding to IRC clients (plaintext over IRC) Encrypted content format: $ENC$v1${nonce}${ciphertext+tag} (Base64, 12-byte nonce, 16-byte auth tag) Server strips $ENC$ prefix from user-typed messages to prevent format spoofing Graceful fallback: if decryption fails, shows [encrypted message — decryption failed, try re-logging to fetch the latest key] Infrastructure IMessageEncryptionService interface in Core; MessageEncryptionService server implementation and ClientEncryptionService client implementation EncryptionKeyResponse DTO and GET /api/server/encryption-key endpoint (authenticated, rate-limited) FirstRunSetup.EnsureEncryptionKey() auto-generates AES-256 key on first server run Encryption:EncryptDatabase server setting (default false) controls whether messages are encrypted at rest DB column max lengths increased for encrypted content: Message.Content 2000 → 16000, Message.EmbedJson 8000 → 32000 EF Core migration: AddEncryptionSupport Encryption test suite: server-side, client-side, and cross-compatibility tests Documentation article: docs/articles/encryption.md"
  },
  "changelog/v0.2.5.html": {
    "href": "changelog/v0.2.5.html",
    "title": "v0.2.5 - Session Persistence, Auto-Updates, Audio & Transparent Theme | EchoHub Documentation",
    "summary": "v0.2.5 - Session Persistence, Auto-Updates, Audio & Transparent Theme Features Audio Message Support New Audio message type — uploaded audio files (.mp3, .wav, .ogg, .flac, .aac, .m4a, .wma) are automatically detected and categorized TUI client renders audio messages with ♪ [Audio: filename] (Enter to play) indicator Press Enter on an audio message to open the Audio Player dialog with animated wave visualization, play/pause/stop controls, and volume slider Per-type upload limits: 10 MB images, 10 MB audio, 100 MB generic files (with Kestrel request size configured to match) AudioPlaybackService enhanced with pause/resume, volume control, and playback-finished events Fixed: wrapped audio/file messages now remain clickable on all lines (attachment metadata propagated through word-wrap) IRC gateway formats audio messages as ♪ [Audio: filename] url Server detects audio files by extension via FileValidationHelper.IsAudioFile() File Downloads Press Enter on a file message in the TUI client to download and open it with the system default application ApiClient.DownloadFileAsync() streams file downloads from the server File and audio messages in the chat list now show colored indicators with interaction hints File Cleanup Service FileCleanupService (BackgroundService) periodically removes old uploaded files Configurable via Storage:CleanupIntervalHours (default 1h) and Storage:RetentionDays (default 30d) Data Migration Service DataMigrationService runs at startup to handle schema/data evolution Ensures #general channel and pre-existing channels are marked public Migrates legacy ANSI escape codes in image messages to printable color tags ({F:RRGGBB}, {B:RRGGBB}, {X}) Migrates legacy single-object EmbedJson to array format Promotes usernames listed in Server:Admins config to Admin role True Transparent Background Transparent theme now uses the terminal's native background instead of solid black Powered by Color.None (alpha=0) which emits ANSI CSI 49m (default background) instead of explicit RGB Terminal transparency, acrylic, wallpaper effects now show through the TUI Dialogs retain solid DarkGray background for readability Uses local Terminal.Gui fork (submodule) with transparent color support pending upstream merge (gui-cs/Terminal.Gui#4234) Enhanced Status Bar Status bar now shows EchoHub branding at the start Connection state is color-coded: green (Connected), red (Disconnected), yellow (transitional states like Connecting, Reconnecting, Authenticating) Current channel shows its type: #channel - public or #channel - private Remember Me (Session Persistence) \"Remember me\" checkbox in the connect dialog — saves a 30-day refresh token so users can reconnect without entering their password Saved servers with active sessions show [session] indicator in the connect dialog and saved servers list Token-based login: selecting a saved server with a session lets you click Login with an empty password Graceful expiry handling: if the saved session is expired or revoked, shows an error and prompts for password Refresh token rotation: rotated tokens are automatically persisted to config so the session stays valid across refreshes New \"Logout\" menu item (Server menu): revokes the refresh token server-side and clears the saved session Removed dead SavedServer.Token field (stored 15-min access token that was never read back) Automatic Update Checking UpdateChecker rewritten to use the AlwaysUpToDate library for self-updating Polls the EchoHub version manifest hourly (active only in RELEASE builds) UpdateConfirmDialog shows current vs. available version with Update/Cancel buttons UpdateProgressDialog displays real-time download progress bar Errors are logged via Serilog instead of being silently swallowed Windows Installer New Inno Setup script (installer/Installer.iss) to build a Windows installer Auto-reads product version from the built EXE User-level install by default (no admin required), with admin override option Creates desktop and quick-launch shortcuts (optional) Supports English and German languages Application Icons New hue_icon branding assets (ICO, PNG, SVG) in assets/ Application icon set on both Client and Server projects #general Channel Protection #general channel is now auto-recreated if somehow missing (both in GetChannels endpoint and JoinChannel flow) Users cannot /leave the #general channel (client-side guard) Connecting while already connected now prompts to disconnect first instead of silently leaking the previous connection Fixes IRC Gateway Fixed: IRC JOIN history replay showed encrypted gibberish — IrcCommandHandler.HandleJoinAsync now decrypts channel history before formatting for IRC clients (history was encrypted for SignalR transport but sent raw to IRC) Fixed: JoinedChannels race condition — IrcClientConnection.JoinedChannels replaced with thread-safe methods (JoinChannel, LeaveChannel, IsInChannel, GetJoinedChannels) using lock synchronization; prevents crashes when broadcaster threads read while the command handler writes Fixed: RequireRegistered fire-and-forget — converted from sync bool to async Task<bool> (RequireRegisteredAsync) so the error reply is properly awaited before the handler returns ChannelService Extraction Extracted channel management logic from ChannelsController and ChatService into a dedicated IChannelService / ChannelService ChannelsController is now a thin adapter — delegates CRUD operations to IChannelService and maps ChannelError to HTTP status codes ChatService.JoinChannelAsync delegates channel validation + membership to IChannelService.EnsureChannelMembershipAsync() New ChannelOperationResult result type with ChannelError enum for typed error handling across service boundaries IRC gateway uses IChannelService for topic queries and channel listing (instead of IChatService) EchoHub Branding Status bar \"EchoHub\" text now uses golden color (218, 165, 32) Extracted ChatMessageManager from MainWindow — message storage, formatting, and mutation logic now in a dedicated class, reducing MainWindow complexity Infrastructure Audio MIME types in FilesController (mp3, wav, ogg, flac, aac, m4a, wma) CI workflows updated to handle Terminal.Gui submodule nuget.config workaround (rm -f step) Root nuget.config added for package source management Terminal.Gui formatting excluded from CI format checks Recursive submodule fetching enabled in CI workflows Release workflow: use --notes-file instead of inline --notes interpolation to prevent shell expansion of special characters in commit messages Server:Admins config array in appsettings.example.json for designating admin usernames Storage:CleanupIntervalHours and Storage:RetentionDays added to appsettings.example.json FakeChannelService test helper added for IRC unit tests Test suites: ChatLine, CommandHandler, DataMigrationService, FileValidationHelper, ImageToAsciiService, IrcMessageFormatter, JwtTokenService, LinkEmbedService IRC abstraction layer test suite: IrcMessage parsing, IrcMessageFormatter, IrcClientConnection, IrcCommandHandler, IrcBroadcaster Test helpers: TestDuplexStream, TestIrcConnectionFactory, FakeChatService, FakeChannelService, FakeEncryptionService for IRC unit testing without network I/O 346 total tests"
  },
  "changelog/v0.2.6.html": {
    "href": "changelog/v0.2.6.html",
    "title": "v0.2.6 | EchoHub Documentation",
    "summary": "v0.2.6 Refactoring Extracted ChatMessageManager from MainWindow — message storage, formatting, and mutation logic now in a dedicated class, reducing MainWindow complexity Split ChatRenderer.cs (8 classes, 548 lines) into 7 individual files: ChatSegment, ChatLine, ChatListSource, ChannelListSource, UserListSource, ChatColors, ColorHelper, RenderHelpers Extracted ConnectionManager from AppOrchestrator — connection lifecycle, authentication, SignalR event wiring, and channel tracking now in a dedicated service Extracted AvatarHelper — deduplicated avatar upload logic previously duplicated in /avatar command and profile edit dialog Consolidated ProfileEditDialog.ParseHexToTrueColor into shared ColorHelper.ParseHexToColor Extracted UserSession — session state (Username, Status, StatusMessage) now in a dedicated class instead of scattered fields Replaced 21 inline command handler lambdas with named HandleCmd* methods for improved readability Extracted shared CleanupConnectionAsync to deduplicate disconnect/logout cleanup logic Reorganized flat UI/ folder (19 files) into subfolders: Chat/, Dialogs/, ListSources/, Helpers/ with matching namespaces Moved AsyncRunner from project root to Services/ with updated namespace Renamed ColorHelper → HexColorHelper to avoid namespace collision with Terminal.Gui's ColorHelper NuGet dependency Moved hue_icon.ico to Client/Assets/, removed duplicate from Server (Server now references shared icon via relative path) Add hex color parsing helper and implement custom list sources for channels and users Add dialogs for connection, channel creation, profile editing, and status management Infrastructure Release workflow: use --notes-file instead of inline --notes interpolation to prevent shell expansion of special characters in commit messages"
  },
  "changelog/v0.2.7.html": {
    "href": "changelog/v0.2.7.html",
    "title": "v0.2.7 | EchoHub Documentation",
    "summary": "v0.2.7 Bug Fixes Fix user list empty on initial connect — FetchAndUpdateOnlineUsers was called before InvokeUI set the current channel, causing an early return Infrastructure Switch Terminal.Gui from local fork submodule back to NuGet package (2.0.0-develop.5039) — transparent color PR merged upstream Remove Terminal.Gui submodule, .gitmodules, and root nuget.config workaround Remove rm -f submodule nuget.config steps and submodules: recursive from all 3 CI workflows (ci, docs, release) Remove --exclude src/Terminal.Gui/ from format check"
  },
  "changelog/v0.2.8.html": {
    "href": "changelog/v0.2.8.html",
    "title": "v0.2.8 | EchoHub Documentation",
    "summary": "v0.2.8 Bug Fixes Fix memory leak — HttpResponseMessage objects never disposed in ApiClient, leaking TCP connections and content buffers on every API call (especially on failed connection attempts) Fix 401 retry leak — AuthenticatedGetAsync/AuthenticatedRequestAsync leaked the original response when retrying after token refresh Fix connection failure cleanup — ConnectionManager.ConnectAsync now properly disposes ApiClient and EchoHubConnection on any failure path (previously only cleaned up on saved-token auth failures) Fix IRC gateway sending UTF-8 BOM on first message, breaking CAP negotiation and SASL auth for all clients Handle AUTHENTICATE * (SASL abort) instead of crashing on invalid base64 Fix userlist not refreshing when creating a new channel — now fetches online users after channel creation Fix unmute timer not working — add MuteExpirationService background job that proactively unmutes users when their timed mute expires (previously only checked on message send) Fix missing space between mod/admin role icon and username in the userlist Fix invisible users still visible in the userlist — GetOnlineUsersAsync now filters invisible users; server skips UserJoined broadcast for invisible users Fix invisible→online transition — user reappears in cached userlist when switching from invisible back to a visible status Fix thread safety — _channelUsers presence cache now protected by Lock to prevent races between SignalR events and background fetches Fix @mention regex matching email addresses and #channel regex matching hex colors / issue numbers — both now use lookbehind and letter-requirement guards Fix ParseThemeColor accepting non-hex characters — now validates [0-9a-fA-F] digits Fix ghost channel when trying to join a channel that doesn't exist New Features Add Docker support for EchoHub.Server — docker compose up -d for easy self-hosting with persistent volume for database, uploads, and logs IRC account creation — connecting with a new username auto-registers the account (PASS and SASL PLAIN) Auto-updater rollback — pre-update backup created automatically before each update; restore via File > Rollback menu or --rollback CLI flag Update failure recovery — if an update fails mid-extraction, offers to restore from the backup immediately Defensive Unix permission check — verify execute permission on startup after auto-update (defense-in-depth) Clickable usernames — press Enter on a username in the userlist or message sender to view their profile Clickable @mentions — press Enter on a message containing @username to open that user's profile Clickable #channels — press Enter on a message containing #channel to join/switch to that channel; #channel references are now highlighted in chat Embed theme colors — embed vertical border line now uses the source site's theme-color meta tag when available Stateful userlist — user presence is cached per channel and updated incrementally via SignalR events instead of re-fetching the full list on every join/leave/status change Security Restrict file auto-open — only safe file types (video, PDF, text) are opened with the system default app; all other files are downloaded to temp without executing (prevents script execution via .bat, .exe, etc.) Refactoring Extract IUserService/UserService — consolidate user registration, authentication, and profile management into a dedicated service, eliminating duplicated logic between AuthController and ChatService IRC gateway now checks ban status during authentication (previously skipped) EchoHubSpace directory updates — server now only sends user count when it actually changes instead of every 30 seconds ChatHub.JoinChannel now returns JoinChannelResult instead of List<MessageDto> to allow for better error handling Distribution Add Chocolatey package — choco install echohub for Windows users, auto-published from CI on each release Add Linux/macOS install script — curl -sSfL .../install.sh | sh with automatic OS/arch detection Documentation Add Flows section — Mermaid sequence diagrams documenting all major request/event flows (auth, connection, messaging, channels, moderation, file upload, link embeds, server directory) with inline code references CI Add Docker workflow — builds and pushes multi-arch (amd64/arm64) server image to GHCR on release Add linux-arm64 builds to release pipeline — server and client binaries for ARM Linux (Raspberry Pi, cloud ARM instances) Automate Chocolatey package publishing in release workflow (checksum calculation, pack, push)"
  },
  "changelog/v0.2.9.html": {
    "href": "changelog/v0.2.9.html",
    "title": "v0.2.9 | EchoHub Documentation",
    "summary": "v0.2.9 Bug Fixes Fix Linux/macOS client install — enable single-file publish so the install script copies one self-contained binary instead of just the native host (which failed with \"does not exist: EchoHub.Client.dll\") Fix Chocolatey install path on Windows — chocolateyInstall.ps1 was joining the install directory and executable name into a single segment, producing an invalid target path Fix Chocolatey package metadata — corrected GitHub repository URLs and documentation URLs in echohub.nuspec that pointed at the wrong location Fix double-click on \"Public\" checkbox in the Create Channel dialog accidentally submitting the dialog — checkbox toggle commands no longer bubble up to the dialog's default button Documentation Add a dedicated configuration guide (docs/articles/configuration.md) covering server settings, client settings, and environment overrides Refresh README badges and reorganize the articles table of contents for better discoverability Polish Docker, getting-started, and flow docs to match the current configuration surface Dependencies Bump Terminal.Gui to 2.0.0-develop.5043 (from 5039) CI Release workflow now publishes a single-file self-contained client binary for Linux and macOS so the install script works out-of-the-box Chocolatey publishing step now triggers only when the package source actually changes and performs a proper version check against the feed before pushing"
  },
  "flows/authentication.html": {
    "href": "flows/authentication.html",
    "title": "Authentication | EchoHub Documentation",
    "summary": "Authentication User Registration A new user creates an account on a server. The client sends credentials via REST, the server hashes the password, issues JWT tokens, and the client stores the refresh token for \"Remember Me\" sessions. sequenceDiagram participant UI as ConnectDialog participant AO as AppOrchestrator participant CM as ConnectionManager participant API as ApiClient participant Auth as AuthController participant US as UserService participant JWT as JwtTokenService participant DB as SQLite UI->>AO: ConnectDialogResult(IsRegister: true) AO->>CM: ConnectAsync(dialogResult) CM->>API: RegisterAsync(username, password) API->>Auth: POST /api/auth/register Auth->>US: RegisterUserAsync(username, password, displayName) US->>US: Validate (regex, length, uniqueness) US->>DB: INSERT User (BCrypt hash) US-->>Auth: UserOperationResult.Success Auth->>JWT: GenerateAccessToken(user) JWT-->>Auth: (token, expiresAt) [15 min] Auth->>JWT: GenerateRefreshToken() JWT-->>Auth: Base64 random (64 bytes) Auth->>DB: INSERT RefreshToken (SHA256 hash) Auth-->>API: LoginResponse API->>API: SetTokens() — store in memory + set Bearer header API-->>CM: LoginResponse CM->>CM: Wire OnTokensRefreshed for config persistence CM->>CM: Continue to connection setup (see Connection Flow) User Login Returning user authenticates with username/password or a saved refresh token. sequenceDiagram participant UI as ConnectDialog participant CM as ConnectionManager participant API as ApiClient participant Auth as AuthController participant US as UserService participant DB as SQLite alt Saved refresh token (Remember Me) UI->>CM: ConnectDialogResult(SavedRefreshToken: \"...\") CM->>API: LoginWithRefreshTokenAsync() API->>Auth: POST /api/auth/refresh Auth->>DB: Lookup token by SHA256 hash Auth->>DB: Revoke old token, issue new pair Auth-->>API: LoginResponse (rotated tokens) else Username + Password UI->>CM: ConnectDialogResult(IsRegister: false) CM->>API: LoginAsync(username, password) API->>Auth: POST /api/auth/login Auth->>US: AuthenticateUserAsync(username, password) US->>DB: Fetch user, BCrypt.Verify(password, hash) US->>DB: Update LastSeenAt US-->>Auth: UserOperationResult.Success Auth-->>API: LoginResponse end API->>API: SetTokens() Token Refresh Access tokens expire after 15 minutes. The client auto-refreshes transparently before requests and on 401 responses. Refresh tokens are rotated on each use. sequenceDiagram participant SR as SignalR / HTTP Request participant API as ApiClient participant Auth as AuthController participant DB as SQLite participant Config as config.json SR->>API: GetValidTokenAsync() or HTTP 401 API->>API: Token expires within 60s? alt Proactive refresh (SignalR token provider) API->>Auth: POST /api/auth/refresh (old refresh token) else Reactive refresh (HTTP 401 retry) API->>Auth: POST /api/auth/refresh (old refresh token) end Auth->>DB: Lookup by SHA256 hash Auth->>DB: Revoke old refresh token Auth->>DB: INSERT new RefreshToken Auth-->>API: LoginResponse (new token pair) API->>API: SetTokens() — update Bearer header API-->>API: Fire OnTokensRefreshed event API-->>Config: Persist new refresh token (if Remember Me) API->>SR: Retry original request with new token"
  },
  "flows/channels.html": {
    "href": "flows/channels.html",
    "title": "Channels | EchoHub Documentation",
    "summary": "Channels Channel Creation Channels are created via the REST API. Public channels are broadcast to all connected clients. sequenceDiagram participant Client as Client (TUI/API) participant CC as ChannelsController participant ChS as ChannelService participant CS as ChatService participant DB as SQLite participant SRB as SignalRBroadcaster participant IRCB as IrcBroadcaster Client->>CC: POST /api/channels {name, topic, isPublic} CC->>ChS: CreateChannelAsync(creatorId, name, topic, isPublic) ChS->>ChS: Normalize name (lowercase, trim) ChS->>ChS: Validate format (2-100 chars, regex) ChS->>DB: Check duplicate ChS->>DB: INSERT Channel + ChannelMembership (creator auto-added) ChS-->>CC: ChannelDto alt Channel is public CC->>CS: BroadcastChannelUpdatedAsync(channel) CS->>SRB: SendChannelUpdatedAsync(channelDto) SRB->>SRB: Notify all SignalR clients CS->>IRCB: SendChannelUpdatedAsync(channelDto) end CC-->>Client: 201 Created (ChannelDto) Channel Deletion Only the channel creator (or admin) can delete a channel. The default channel is protected. sequenceDiagram participant Client as Client participant CC as ChannelsController participant ChS as ChannelService participant DB as SQLite Client->>CC: DELETE /api/channels/{channel} CC->>ChS: DeleteChannelAsync(channelName, callerId) ChS->>ChS: Reject if default channel ChS->>DB: Lookup channel ChS->>ChS: Verify caller is creator or admin ChS->>DB: DELETE Channel (cascade: messages, memberships) ChS-->>CC: Success CC-->>Client: 204 No Content Joining a Channel Both SignalR and IRC clients join channels through ChatService. The presence tracker determines if this is a genuinely new join (vs. a second connection) and broadcasts accordingly. sequenceDiagram participant Client as Client (SignalR or IRC) participant Entry as ChatHub / IrcCommandHandler participant CS as ChatService participant ChS as ChannelService participant PT as PresenceTracker participant DB as SQLite participant SRB as SignalRBroadcaster participant IRCB as IrcBroadcaster Client->>Entry: JoinChannel / JOIN #channel Entry->>CS: JoinChannelAsync(connectionId, userId, username, channel) CS->>ChS: EnsureChannelMembershipAsync(userId, channel) ChS->>DB: INSERT ChannelMembership (if not exists) CS->>PT: JoinChannel(username, channel) PT-->>CS: isNewJoin? alt First connection in this channel CS->>DB: Fetch UserPresenceDto CS->>CS: BroadcastToAllAsync(SendUserJoinedAsync) par CS->>SRB: SendUserJoinedAsync(channel, user, excludeConn) and CS->>IRCB: SendUserJoinedAsync(channel, user) end end CS->>DB: Fetch message history CS-->>Entry: (history, error) Entry-->>Client: History messages Leaving a Channel sequenceDiagram participant Client as Client participant Entry as ChatHub / IrcCommandHandler participant CS as ChatService participant PT as PresenceTracker participant SRB as SignalRBroadcaster participant IRCB as IrcBroadcaster Client->>Entry: LeaveChannel / PART #channel Entry->>CS: LeaveChannelAsync(connectionId, username, channel) CS->>PT: LeaveChannel(username, channel) CS->>CS: BroadcastToAllAsync(SendUserLeftAsync) par CS->>SRB: SendUserLeftAsync(channel, username) and CS->>IRCB: SendUserLeftAsync(channel, username) end"
  },
  "flows/connection.html": {
    "href": "flows/connection.html",
    "title": "Connection | EchoHub Documentation",
    "summary": "Connection SignalR Client Connection After authentication, the TUI client establishes a SignalR WebSocket, registers event handlers, joins the default channel, and loads history. sequenceDiagram participant CM as ConnectionManager participant EHC as EchoHubConnection participant Hub as ChatHub participant CS as ChatService participant PT as PresenceTracker participant DB as SQLite CM->>CM: Fetch encryption key (GET /api/server/encryption-key) CM->>EHC: new EchoHubConnection(apiClient, encryption) EHC->>EHC: Build HubConnection (URL + JWT token provider + auto-reconnect) EHC->>EHC: RegisterHandlers() — wire ReceiveMessage, UserJoined, etc. EHC->>Hub: ConnectAsync() → WebSocket handshake Hub->>CS: UserConnectedAsync(connectionId, userId, username) CS->>PT: UserConnected(connectionId, userId, username) CS->>DB: Update user: Status=Online, LastSeenAt=now CM->>CM: Fetch channel list (GET /api/channels) CM->>EHC: JoinChannelAsync(\"general\") EHC->>Hub: InvokeAsync(\"JoinChannel\", \"general\") Hub->>CS: JoinChannelAsync(connectionId, userId, username, \"general\") CS->>DB: EnsureChannelMembership CS->>PT: JoinChannel(username, \"general\") CS->>CS: BroadcastToAllAsync → UserJoined CS->>DB: Fetch message history Hub-->>EHC: List<MessageDto> (encrypted) EHC-->>CM: Decrypted history IRC Client Connection IRC clients connect via TCP, authenticate with PASS/NICK/USER or SASL PLAIN, and auto-join channels. New usernames are auto-registered. sequenceDiagram participant IRC as IRC Client participant GW as IrcGatewayService participant CH as IrcCommandHandler participant US as UserService participant CS as ChatService participant PT as PresenceTracker IRC->>GW: TCP connect (:6667 or :6697 TLS) GW->>GW: Accept + create IrcClientConnection GW->>CH: new IrcCommandHandler(connection, services) GW->>CH: RunAsync() — start read loop alt SASL PLAIN IRC->>CH: CAP REQ :sasl CH-->>IRC: CAP ACK :sasl IRC->>CH: AUTHENTICATE PLAIN CH-->>IRC: AUTHENTICATE + IRC->>CH: AUTHENTICATE <base64(\\0user\\0pass)> CH->>US: AuthenticateUserAsync(user, pass) alt Auth fails → auto-register CH->>US: RegisterUserAsync(user, pass) end CH-->>IRC: 903 :SASL authentication successful else PASS/NICK/USER IRC->>CH: PASS <password> IRC->>CH: NICK <nickname> IRC->>CH: USER <username> 0 * :<realname> CH->>US: AuthenticateUserAsync(nick, pass) alt Auth fails → auto-register CH->>US: RegisterUserAsync(nick, pass) end end CH->>CS: UserConnectedAsync(irc-{guid}, userId, username) CS->>PT: UserConnected(irc-{guid}, userId, username) CH-->>IRC: 001-004 RPL_WELCOME burst + MOTD Note over IRC,CH: Client is now ready for JOIN/PART/PRIVMSG User Disconnect & Presence When a client disconnects, the presence tracker determines if the user has any remaining connections. If not, status is set to Invisible and all channels are notified. sequenceDiagram participant Client as Client participant Entry as ChatHub / IrcGatewayService participant CS as ChatService participant PT as PresenceTracker participant DB as SQLite participant SRB as SignalRBroadcaster Client->>Entry: Disconnect / TCP close Entry->>CS: UserDisconnectedAsync(connectionId) CS->>PT: Get username + channels (before removal) CS->>PT: UserDisconnected(connectionId) PT->>PT: Remove connection from tracking alt No remaining connections for user CS->>DB: Update user: Status=Invisible, LastSeenAt=now loop For each channel user was in CS->>CS: BroadcastToAllAsync(SendUserStatusChangedAsync) CS->>SRB: Notify channel members end end"
  },
  "flows/flows.html": {
    "href": "flows/flows.html",
    "title": "Flows | EchoHub Documentation",
    "summary": "Flows This section documents the major request/event flows in EchoHub, showing how data moves between the TUI client, IRC client, server, and database. Each diagram includes code references so you can jump straight to the implementation."
  },
  "flows/media.html": {
    "href": "flows/media.html",
    "title": "Media & Services | EchoHub Documentation",
    "summary": "Media & Services File Upload Files are uploaded via REST, validated by magic bytes, stored with GUID filenames, and broadcast as a message with a download link. sequenceDiagram participant Client as Client participant CC as ChannelsController participant FV as FileValidationHelper participant FS as FileStorageService participant CS as ChatService participant DB as SQLite Client->>CC: POST /api/channels/{channel}/upload (multipart) CC->>CC: Check file size limits CC->>FV: IsValidImage(stream) — magic byte check FV->>FV: Read header: JPEG(FFD8FF) / PNG(89504E47) / GIF / WebP(RIFF+WEBP) FV-->>CC: true/false CC->>FS: SaveFileAsync(stream, extension) FS->>FS: Generate GUID filename, write to uploads/ FS-->>CC: fileId (GUID) CC->>CC: Determine MessageType (Image/Audio/File) CC->>CS: SendMessageAsync (with file URL + optional ASCII art) CS->>DB: INSERT Message CS->>CS: BroadcastToAllAsync → fan out to clients Link Embed Resolution When a message contains URLs, the server fetches OpenGraph metadata for preview embeds. sequenceDiagram participant CS as ChatService participant LE as LinkEmbedService participant Web as External Website CS->>LE: TryGetEmbedsAsync(messageContent) LE->>LE: Extract URLs via regex LE->>LE: Filter: max URLs per message, skip duplicates loop For each URL LE->>LE: Validate: http(s) only, block private IPs LE->>Web: GET URL (timeout + size limit) Web-->>LE: HTML response LE->>LE: Parse og:title, og:description, og:site_name LE->>LE: Parse theme-color meta tag LE->>LE: Fallback to <title> if no og:title end LE-->>CS: List<EmbedDto> (or null) Note over CS: Attached to MessageDto before broadcast Server Directory Registration Public servers register with the EchoHubSpace directory for discoverability. sequenceDiagram participant SDS as ServerDirectoryService participant Dir as EchoHubSpace Directory participant PT as PresenceTracker SDS->>SDS: Check Server:PublicServer config SDS->>Dir: SignalR connect (echohub.voidcube.cloud/hubs/servers) SDS->>Dir: RegisterServer(name, description, host, userCount) Dir-->>SDS: Registered loop Every 30 seconds SDS->>PT: Get online user count alt Count changed SDS->>Dir: UpdateUserCount(count) end end Dir->>SDS: Ping SDS->>Dir: Heartbeat Note over SDS,Dir: Exponential backoff on disconnect (2s → 30s max)"
  },
  "flows/messaging.html": {
    "href": "flows/messaging.html",
    "title": "Messaging | EchoHub Documentation",
    "summary": "Messaging Sending a Message (SignalR) A message typed in the TUI travels through encryption, the SignalR hub, ChatService validation, database storage, and fan-out to both SignalR and IRC clients. sequenceDiagram participant UI as MainWindow (TUI) participant AO as AppOrchestrator participant EHC as EchoHubConnection participant Hub as ChatHub participant CS as ChatService participant LE as LinkEmbedService participant DB as SQLite participant SRB as SignalRBroadcaster participant IRCB as IrcBroadcaster participant Clients as Other Clients UI->>AO: OnMessageSubmitted(channel, text) AO->>AO: IsCommand(text)? → No AO->>EHC: SendMessageAsync(channel, text) EHC->>EHC: Encrypt(text) → ciphertext EHC->>Hub: InvokeAsync(\"SendMessage\", channel, ciphertext) Hub->>CS: SendMessageAsync(userId, username, channel, ciphertext) CS->>CS: Decrypt(ciphertext) → plaintext CS->>CS: Validate (length, newlines, channel exists) CS->>DB: Check mute status CS->>LE: TryGetEmbedsAsync(plaintext) LE->>LE: Extract URLs, fetch OG tags LE-->>CS: List<EmbedDto> (or null) CS->>DB: INSERT Message (encrypted at rest) CS->>CS: Re-encrypt plaintext for transport CS->>CS: Build MessageDto with embeds par Fan-out to all broadcasters CS->>SRB: SendMessageToChannelAsync(channel, dto) SRB->>Clients: HubContext.Group(channel).ReceiveMessage(dto) and CS->>IRCB: SendMessageToChannelAsync(channel, dto) IRCB->>IRCB: Decrypt → format as PRIVMSG lines IRCB->>Clients: Send to each IRC conn (skip sender) end Sending a Message (IRC) Messages from IRC clients follow the same ChatService path but enter as plaintext (no app-layer encryption). sequenceDiagram participant IRC as IRC Client participant CH as IrcCommandHandler participant CS as ChatService participant DB as SQLite participant SRB as SignalRBroadcaster participant IRCB as IrcBroadcaster IRC->>CH: PRIVMSG #channel :Hello world CH->>CH: Parse target + content CH->>CH: IrcToEchoHubChannel(\"#channel\") → \"channel\" CH->>CS: SendMessageAsync(userId, username, \"channel\", \"Hello world\") CS->>CS: Decrypt(\"Hello world\") → passthrough (no $ENC$ prefix) CS->>CS: Validate, check mute, fetch embeds CS->>DB: INSERT Message CS->>CS: Encrypt plaintext for SignalR transport par Fan-out CS->>SRB: SendMessageToChannelAsync (encrypted for SignalR) and CS->>IRCB: SendMessageToChannelAsync (decrypt → PRIVMSG) IRCB->>IRCB: Skip sender (IRC echo suppression) end Receiving a Message (TUI Client) When a message arrives via SignalR, the client decrypts it, adds it to the chat view, and optionally plays a notification sound for @mentions. sequenceDiagram participant SRB as SignalRBroadcaster participant EHC as EchoHubConnection participant AO as AppOrchestrator participant MM as ChatMessageManager participant UI as MainWindow SRB->>EHC: ReceiveMessage(MessageDto) EHC->>EHC: Decrypt(message.Content) EHC-->>AO: OnMessageReceived(decrypted dto) AO->>AO: InvokeUI (thread-safe) AO->>MM: AddMessage(message) MM->>UI: Render in chat ListView alt Message contains @username AO->>AO: PlayAsync() notification sound end Command Execution Slash commands (/status, /nick, /kick, etc.) are parsed client-side and dispatched to appropriate handlers, which call REST APIs or SignalR methods. sequenceDiagram participant UI as MainWindow participant AO as AppOrchestrator participant CMD as CommandHandler participant API as ApiClient participant EHC as EchoHubConnection participant Server as Server (API/Hub) UI->>AO: OnMessageSubmitted(channel, \"/kick baduser\") AO->>CMD: IsCommand(\"/kick baduser\")? → true AO->>CMD: HandleAsync(\"/kick baduser\") CMD->>CMD: Parse → command=\"kick\", args=\"baduser\" alt API command (kick, ban, mute, nick, etc.) CMD-->>AO: Fire OnKickRequested(\"baduser\") AO->>API: KickUserAsync(\"baduser\") API->>Server: POST /api/moderation/kick/baduser else Hub command (status, join, leave, etc.) CMD-->>AO: Fire OnSetStatus / OnJoinChannel / etc. AO->>EHC: UpdateStatusAsync() / JoinChannelAsync() / etc. EHC->>Server: SignalR Invoke else Local command (theme, help, quit) CMD-->>AO: Fire OnThemeChanged / etc. AO->>UI: Apply locally (no server call) end AO->>UI: AddSystemMessage(result) Available commands: Command Type Handler /status <status> [message] Hub UpdateStatusAsync /nick <name> API UpdateProfileAsync /color <hex> API UpdateProfileAsync /join <channel> Hub JoinChannelAsync /leave Hub LeaveChannelAsync /topic <text> API UpdateChannelTopicAsync /kick <user> API POST /api/moderation/kick/{user} /ban <user> API POST /api/moderation/ban/{user} /unban <user> API POST /api/moderation/unban/{user} /mute <user> [mins] API POST /api/moderation/mute/{user} /unmute <user> API POST /api/moderation/unmute/{user} /role <user> <role> API PUT /api/moderation/role/{user} /nuke API DELETE /api/channels/{channel}/messages /send <file> API POST /api/channels/{channel}/upload /profile [user] Local Show profile dialog /avatar API POST /api/users/avatar /theme <name> Local ThemeManager.SetTheme() /servers API GET /api/serverdir/servers /users Local Show userlist /help Local Show help text /quit Local Exit application"
  },
  "flows/moderation.html": {
    "href": "flows/moderation.html",
    "title": "Moderation | EchoHub Documentation",
    "summary": "Moderation Kick / Ban / Mute Moderators and admins can kick, ban, or mute users via REST API or slash commands. These actions force-disconnect the target and broadcast the event. sequenceDiagram participant Mod as Moderator participant MC as ModerationController participant CS as ChatService participant PT as PresenceTracker participant DB as SQLite participant SRB as SignalRBroadcaster participant IRCB as IrcBroadcaster Mod->>MC: POST /api/moderation/kick/{user} alt Kick MC->>CS: Get user's channels MC->>CS: BroadcastToAllAsync(SendUserKickedAsync) MC->>CS: ForceDisconnectAndCleanupAsync(user) else Ban MC->>DB: Set user.IsBanned = true MC->>CS: BroadcastToAllAsync(SendUserBannedAsync) MC->>CS: ForceDisconnectAndCleanupAsync(user) else Mute MC->>DB: Set user.IsMuted = true, MutedUntil = now + duration Note over DB: MuteExpirationService auto-unmutes when timer expires end CS->>PT: ForceRemoveUser(username) PT->>PT: Remove from all connections + channels CS->>SRB: ForceDisconnectUserAsync(connectionIds, reason) CS->>IRCB: ForceDisconnectUserAsync(connectionIds, reason) CS->>DB: Set Status=Invisible, LastSeenAt=now"
  },
  "index.html": {
    "href": "index.html",
    "title": "EchoHub Documentation | EchoHub Documentation",
    "summary": "EchoHub Documentation Welcome to the EchoHub documentation. EchoHub is a decentralized, IRC-style chat platform. Self-hosted, terminal-first, with a built-in IRC gateway so native IRC clients can connect alongside the TUI client. Website: echohub.voidcube.cloud | Public Servers: Server Directory Quick Links Getting Started - Set up and run EchoHub TUI Guide - Keyboard shortcuts, slash commands, and everyday usage Messages & Attachments - Attaching, pasting, and receiving files Encrypted Rooms - End-to-end encrypted channels IRC Gateway - Connect with any IRC client Architecture - System design and IRC gateway API Reference - Generated C# API documentation Changelog - Release history"
  },
  "todo.html": {
    "href": "todo.html",
    "title": "TODO | EchoHub Documentation",
    "summary": "TODO [ ] fix the chat trailing; when user scrolls up, and somebody sends a message – the chat instantly \"teleports\" to the very bottom [x] disable the autorun of files (maybe keep for mp4? gotta do some sec research on it) [x] when user creates a new channel, he gets moved to that channel; but the userlist does not refresh the state on that – it refreshes when user re-enters the channel again [x] password protected rooms (end-to-end encrypted — server cannot read contents) [ ] better audio lib, current one (NetCoreAudio) does not support seek or other audio actions [ ] Use options pattern for both client & server ref: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-10.0 [x] The vertical line of embeds should be the same colour as theme-color meta tag of the source [x] change text wrapping to honor the offset of the user. (Preferably wrap whole words if they fit in on line. Basically how the CSS \"text-wrap-mode: wrap;\" works) [x] make usernames in messages, mentions and in the user list clickable to open the user profile [ ] Add tags support for public servers & add filters on echohubspace for those tags in the server \"browser\" [ ] when trying to /join a channel that doesn't exist a client side-ghost channel gets created that does not work [x] unmute timer does not seem to work [ ] add IRC network like support that means basically multiple servers linked, so users can chat cross-server in this network [x] when users clicks public -> private -> public checkbox in the channel creation, it ends up creating the channel on 3rd check switch [ ] add keyboard only controls | at least for most important parts and the rest might be accessible with: (down) [x] add search bar / search modal – that will allow users to instantly navigate to room / focus on app element & etc [ ] Actually smart data management – cache messages, lazy load messages on scroll (currently hardcoded 100msgs fetched + new ones) [x] Another thing would be stateful userlist – basically fetch once and listen for userlist updates [x] Send to EchohubSpace only state changes, currently we send user count periodically, instead of updating it on update [x] space between mod|admin \"icon\" and username [ ] Embeds still incorrectly display colors"
  }
}