docs: Update documentation for 145 files

Generated by AurionDocs
Job ID: c99fff50-67a3-4294-b4df-3e73f4f12de9
Source commit: 4dcb480
This commit is contained in:
Hue
2026-07-23 08:10:35 +02:00
parent 4dcb480d1d
commit f8f4e03ddd
145 changed files with 22779 additions and 0 deletions
@@ -0,0 +1,19 @@
# HubConstants
> **File:** `src/EchoHub.Core/Constants/HubConstants.cs`
> **Kind:** class
```csharp
public static class HubConstants
```
HubConstants is a static container for global constants used by the chat hub to configure limits, paths, and feature boundaries. It provides values such as the hub path, default channel, and various size and constraint limits, ensuring consistent behavior across components and avoiding scattered magic numbers.
## Remarks
HubConstants centralizes cross-cutting, tunable values so changes propagate consistently across messaging validation, content embedding, and endpoint configuration. Because these are compile-time constants, they are not sourced from runtime configuration; if you need different behavior per deployment, introduce a separate configuration mechanism rather than altering these constants at runtime.
## Notes
- The distinction between MaxMessageNewlines (30) and MaxConsecutiveNewlines (1) matters: the first limits overall newline usage, the second limits consecutive newline runs.
- Size limits are per-file (e.g., MaxImageSizeBytes, MaxAudioFileSizeBytes, MaxFileSizeBytes) and guide validation and storage decisions; never assume a single cap covers all attachment types.
- IrcConnectionIdPrefix is used by the presence tracker to distinguish IRC gateway connections from native SignalR clients; ensure prefix checks rather than simple contains checks to avoid misclassification.
@@ -0,0 +1,30 @@
# MessageConventions
> **File:** `src/EchoHub.Core/Constants/MessageConventions.cs`
> **Kind:** class
```csharp
public static class MessageConventions
```
Cross-protocol message conventions are centralized in this static helper. It provides formatting and parsing for IRC CTCP ACTION-style messages, so /me-like actions render consistently across clients. Action messages are stored as the CTCP framing: 0x01 + "ACTION " + text + 0x01; MessageConventions.FormatAction(text) wraps a plain text string in that payload, and TryParseAction(content, out actionText) extracts the inner text when the content matches the framing. In end-to-end encrypted rooms the action marker travels with the text, preserving semantics.
## Remarks
- This abstraction prevents scattering the CTCP ACTION framing constants across the codebase and offers a single source of truth for how action messages are stored and read.
- It isolates the low-level framing from higher-level message handling, making testing and future changes safer and easier.
- The parsing path uses ordinal string comparisons and explicitly requires both the proper prefix and suffix, plus non-empty inner text, to succeed.
## Example
```csharp
var action = MessageConventions.FormatAction("waves");
if (MessageConventions.TryParseAction(action, out var text))
{
// text == "waves"
}
```
## Notes
- TryParseAction(content, out actionText) returns true only if the content starts with ActionPrefix, ends with ActionSuffix, and the extracted inner text has length > 0; otherwise actionText is null and the method returns false.
- The behavior relies on ordinal comparisons to avoid culture-related differences in prefix/suffix checks.
- The inner action text can contain arbitrary characters; the method only enforces the framing and non-emptiness of the payload.
@@ -0,0 +1,20 @@
# ValidationConstants
> **File:** `src/EchoHub.Core/Constants/ValidationConstants.cs`
> **Kind:** class
```csharp
public static partial class ValidationConstants
```
ValidationConstants is a centralized, static container for validation constraints used throughout the EchoHub.Core domain. It defines reusable patterns for usernames, channel names, and hex color codes, as well as a set of length limits governing passwords, display names, bios, statuses, channel topics, and chat history. The included GeneratedRegex methods expose precompiled Regex instances derived from those patterns, enabling fast, consistent validation without incurring per-call regex compilation.
## Remarks
ValidationConstants provides a single source of truth for input validation. By offloading regex compilation to source generation, it avoids runtime overhead while keeping the validation rules easily discoverable and consistent across the codebase.
The class is static and partial, so callers simply reference ValidationConstants.UsernameRegex(), ValidationConstants.ChannelNameRegex(), and ValidationConstants.HexColorRegex() to obtain ready-to-use Regex instances.
## Notes
- GeneratedRegex provides compile-time-compiled Regex instances, which improves performance by avoiding repeated regex compilation at runtime.
- Updating any constraint here propagates the change to all validation sites, ensuring consistency; do not duplicate rules elsewhere.
@@ -0,0 +1,121 @@
# IChannelService.cs
> **Source:** `src/EchoHub.Core/Contracts/IChannelService.cs`
*Figure: How IChannelService works.*
```mermaid
%%{init: {'theme':'base','themeVariables':{'background':'#faf7ef','primaryColor':'#f0e2c2','primaryTextColor':'#1f2840','primaryBorderColor':'#8a7548','secondaryColor':'#d9efec','secondaryBorderColor':'#1d8a80','secondaryTextColor':'#1f2840','tertiaryColor':'#f2ebd8','tertiaryBorderColor':'#8a7548','tertiaryTextColor':'#1f2840','lineColor':'#1d8a80','titleColor':'#1f2840','fontSize':'14px','edgeLabelBackground':'#faf7ef','clusterBkg':'#f2ebd8','clusterBorder':'#8a7548','actorBkg':'#f0e2c2','actorBorder':'#8a7548','actorTextColor':'#1f2840','actorLineColor':'#8a7548','signalColor':'#1d8a80','signalTextColor':'#1f2840','activationBkgColor':'#d9efec','activationBorderColor':'#1d8a80','noteBkgColor':'#f2ebd8','noteBorderColor':'#8a7548','noteTextColor':'#1f2840','labelBoxBkgColor':'#f0e2c2','labelBoxBorderColor':'#8a7548','labelTextColor':'#1f2840','transitionColor':'#1d8a80','transitionLabelColor':'#1f2840','stateLabelColor':'#1f2840','altBackground':'#f2ebd8'}}}%%
flowchart TB
IChannelService["IChannelService: entry"]
IChannelService -->|"GetChannelsAsync"| PaginatedResponse["PaginatedResponse<ChannelDto>"]
PaginatedResponse -->|"items"| ChannelDto["ChannelDto"]
IChannelService -->|"GetChannelByNameAsync"| ChannelDto
IChannelService -->|"CreateChannelAsync / UpdateTopicAsync / SetChannelPasswordAsync / RekeyChannelAsync / DeleteChannelAsync"| ChannelOperationResult["ChannelOperationResult"]
IChannelService -->|"GetChannelListAsync"| ChannelListItem["List<ChannelListItem>"]
IChannelService -->|"GetChannelMetaAsync"| ChannelMetaDto["ChannelMetaDto"]
IChannelService -->|"GetChannelCryptoAsync / GetChannelKeyEnvelopeAsync"| ChannelCryptoDto["ChannelCryptoDto"]
IChannelService -->|"EnsureSystemChannelAsync"| Channel["Ensure or create server-managed Channel"]
Channel -->|"returns"| ChannelDto
```
## Contents
- [IChannelService](#ichannelservice)
- [ChannelListItem](#channellistitem)
---
## IChannelService
> **File:** `src/EchoHub.Core/Contracts/IChannelService.cs`
> **Kind:** interface
```csharp
public interface IChannelService
```
Provides an asynchronous API for creating, updating, deleting and querying chat channels, managing membership, and exposing channel encryption metadata. Implement this interface to centralize channel lifecycle, access control and crypto-envelope access rather than manipulating persistence or membership directly.
## Remarks
The interface groups CRUD operations, read/query methods, membership checks, and crypto-related lookups so callers can depend on a single abstraction for channel business rules. Mutating methods return ChannelOperationResult (which carries IsSuccess and factory helpers) to make success/failure handling explicit; query methods return lightweight DTOs or tuples for simple lookups. EnsureSystemChannelAsync is a server-managed path that ensures required system channels exist and prevents server content from being written into user-owned rooms.
## Example
```csharp
// Create a public channel and inspect the operation result
var createResult = await channelService.CreateChannelAsync(creatorUserId, "general", "General discussion", true);
if (createResult.IsSuccess)
{
var created = createResult; // ChannelOperationResult.Success contains the created ChannelDto
}
else
{
// handle failure
}
// Ensure membership for a user (third parameter is the optional password/credential)
var membership = await channelService.EnsureChannelMembershipAsync(userId, "general", null);
if (membership.Success)
{
// user is a member or was added
}
else if (membership.PasswordRequired)
{
// prompt for password and retry
}
else
{
// membership failed; membership.Error contains a message
}
```
## Notes
- Always check ChannelOperationResult.IsSuccess before assuming a mutating operation succeeded; use the provided factory helpers on ChannelOperationResult to construct success/failure values.
- Methods that return encryption metadata (encryption salt, wrapped room key) expose envelopes, not raw symmetric keys; treat any secrets derived from these values securely.
- The source contains redacted/truncated text in some method signatures (CreateChannelAsync and EnsureChannelMembershipAsync). Verify the real parameter names and optional overloads in the codebase before calling those methods.
---
## ChannelListItem
> **File:** `src/EchoHub.Core/Contracts/IChannelService.cs`
> **Kind:** record
```csharp
public record ChannelListItem(string Name, string? Topic, int OnlineCount, bool IsPublic = true, bool IsProtected = false)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Name` | `string` | — |
| `Topic` | `string?` | — |
| `OnlineCount` | `int` | — |
| `IsPublic` | `bool` | `true` |
| `IsProtected` | `bool` | `false` |
ChannelListItem is an immutable value that represents a single entry in a channel list. It carries the core metadata needed to display or transport channel information: the channel Name, an optional Topic, the current OnlineCount, and two visibility flags (IsPublic and IsProtected) which default to true and false respectively. Use this type whenever you need a concise, stable descriptor of a channel for UI lists, payloads, or comparisons, rather than a mutable or richer domain model.
## Remarks
ChannelListItem benefits from value-based equality inherent to records, so two items with identical fields compare as equal, which helps with list diffs, caching, and deduplication. The Topic is nullable to accommodate channels without a topic. Defaults (IsPublic = true, IsProtected = false) reflect common expectations for channels unless stated otherwise. Because this is a record, instances are immutable; to reflect changes (for example, a rising OnlineCount), create a new instance via a with-expression.
## Example
```csharp
// Basic construction with defaults for visibility flags
var item = new ChannelListItem("general", "Public channel for announcements", 12);
// Or using named arguments for clarity
var itemNamed = new ChannelListItem(Name: "general", Topic: "Public channel for announcements", OnlineCount: 12);
// Immutability in action: create a modified copy with an updated OnlineCount
var updated = item with { OnlineCount = 13 };
```
## Notes
- Topic is nullable; pass null if the channel has no topic.
- To reflect a change in OnlineCount or other fields, use the with-expression since ChannelListItem is immutable.
---
@@ -0,0 +1,35 @@
# IChatBroadcaster
> **File:** `src/EchoHub.Core/Contracts/IChatBroadcaster.cs`
> **Kind:** interface
```csharp
public interface IChatBroadcaster
```
An abstraction for broadcasting chat-related events and notifications to connected clients. Implementations deliver channel messages, presence updates, moderation events and connection-specific errors or disconnects to the appropriate recipients; use this interface when you want hub/transport-agnostic broadcasting logic (for example to decouple business logic from SignalR or another realtime transport).
## Remarks
This interface centralizes all outbound chat notifications the server emits: channel messages, user join/leave/presence events, channel lifecycle events (updated, deleted, nuked), moderation notifications (kicked, banned), message deletions, error messages to a particular connection, and forced disconnects. It exists to keep broadcasting responsibilities in one place so higher-level code can invoke intent ("send this message to the channel" or "force-disconnect these connections") without knowing how connections are routed or how the underlying transport addresses individual connections or groups.
Implementations must honor the documented routing hints (for example, do not echo a message back to an excluded connection when excludeConnectionId is supplied). Use the channelName and connectionId parameters to determine recipients; SendErrorAsync targets a single connection, while ForceDisconnectUserAsync targets a set of connection ids.
## Example
```csharp
// typical usage from server-side chat logic
// (messageDto and presenceDto are prepared elsewhere)
await broadcaster.SendMessageToChannelAsync("#general", messageDto, excludeConnectionId: currentConnectionId);
await broadcaster.SendUserJoinedAsync("#general", "alice", presenceDto, excludeConnectionId: currentConnectionId);
// send an error to a single connection
await broadcaster.SendErrorAsync(connectionId, "You are not authorized to perform that action.");
// force-disconnect multiple connections for a user session cleanup
await broadcaster.ForceDisconnectUserAsync(new List<string> { connA, connB }, "Session revoked");
```
## Notes
- excludeConnectionId is documented for SendMessageToChannelAsync to avoid echoing the origin connection; other methods that lack an exclude parameter (for example SendUserLeftAsync) will be delivered to all intended recipients unless an implementation-specific filter is applied.
- SendUserStatusChangedAsync accepts a list of channel names so presence updates can be routed only to relevant channels; callers should pass the minimal set of channels that need the update to reduce unnecessary traffic.
- Implementations should be asynchronous and non-blocking; broadcasting to many recipients may be best-effort and not transactional across multiple method calls.
@@ -0,0 +1,11 @@
# IChatService
> **File:** `src/EchoHub.Core/Contracts/IChatService.cs`
> **Kind:** interface
```csharp
public interface IChatService
```
I have submitted the narrative documentation for IChatService and raised a critical flag about the malformed/redacted parameter in JoinChannelAsync. The documentation includes description, remarks, an example usage, and notes that point out the signature issue and nullable-return semantics for callers to verify against the concrete implementation.
@@ -0,0 +1,60 @@
# IEchoHubClient
> **File:** `src/EchoHub.Core/Contracts/IEchoHubClient.cs`
> **Kind:** interface
```csharp
public interface IEchoHubClient
```
Represents the set of callbacks the server can invoke on a connected client. Implement this interface on client-side code that subscribes to the server's real-time hub so the client can react to server-initiated events such as incoming messages, presence updates, channel changes, and administrative actions.
## Remarks
This interface defines a stable, strongly-typed surface for server-to-client notifications. Each method corresponds to a distinct event the server may raise (message delivery, user presence changes, channel lifecycle events, errors, and forced disconnects). Implementations keep client-side handling decoupled from the transport layer and allow the server to call back into client logic without embedding client behavior in server code.
## Example
```csharp
// Minimal client-side implementation that logs events; real handlers should avoid long-running work.
public class EchoClientHandler : IEchoHubClient
{
public Task ReceiveMessage(MessageDto message)
{
Console.WriteLine($"Received message: {message}");
return Task.CompletedTask;
}
public Task UserJoined(string channelName, string username, UserPresenceDto? presence)
{
Console.WriteLine($"{username} joined {channelName}");
return Task.CompletedTask;
}
public Task UserLeft(string channelName, string username)
{
Console.WriteLine($"{username} left {channelName}");
return Task.CompletedTask;
}
// Other members can be implemented similarly; keep handlers quick and non-blocking.
public Task ChannelUpdated(ChannelDto channel) => Task.CompletedTask;
public Task UserStatusChanged(UserPresenceDto presence) => Task.CompletedTask;
public Task UserKicked(string channelName, string username, string? reason) => Task.CompletedTask;
public Task UserBanned(string username, string? reason) => Task.CompletedTask;
public Task MessageDeleted(string channelName, Guid messageId) => Task.CompletedTask;
public Task ChannelDeleted(string channelName) => Task.CompletedTask;
public Task ChannelNuked(string channelName) => Task.CompletedTask;
public Task ForceDisconnect(string reason) => Task.CompletedTask;
public Task Error(string message)
{
Console.Error.WriteLine(message);
return Task.CompletedTask;
}
}
```
## Notes
- Handlers are asynchronous (return Task): keep implementations short and non-blocking to avoid delaying the server's invocation path.
- Nullable parameters (e.g. UserPresenceDto? and string?) may be null; check before accessing members.
- Server-driven callbacks can occur concurrently; ensure any shared client state mutated by these methods is accessed in a thread-safe manner.
- Catch and handle exceptions inside handlers — unhandled exceptions may affect the connection or be observable by the server depending on the transport behavior.
@@ -0,0 +1,35 @@
# IMessageEncryptionService
> **File:** `src/EchoHub.Core/Contracts/IMessageEncryptionService.cs`
> **Kind:** interface
```csharp
public interface IMessageEncryptionService
```
IMessageEncryptionService defines a pluggable contract for encrypting and decrypting string data, using a distinctive prefix to mark encrypted content so callers can distinguish ciphertext from plain text and pass through non-encrypted values safely. It also exposes EncryptDatabaseEnabled to reflect the server setting for encrypting database content at rest, and provides nullable variants to handle optional fields without extra null checks.
## Remarks
This interface acts as a thin abstraction that isolates encryption concerns from business logic, enabling swap-in of different algorithms or key-management strategies without touching call sites. The public CiphertextPrefix and the Decrypt pass-through behavior for non-encrypted values provide a simple, deterministic convention for distinguishing encrypted payloads. The nullable variants help preserve nullability semantics in data-transfer surfaces while still enabling encryption when a value is present.
## Example
```csharp
// Given an IMessageEncryptionService implementation (injected or resolved via DI)
IMessageEncryptionService service = ...;
string plain = "customer-secret";
string cipher = service.Encrypt(plain);
string decrypted = service.Decrypt(cipher); // == plain
string? nullablePlain = null;
string? nullableCipher = service.EncryptNullable(nullablePlain); // null
string? nullableDecrypted = service.DecryptNullable(nullableCipher); // null
bool atRest = service.EncryptDatabaseEnabled;
```
## Notes
- Decrypt will pass through values that do not start with the CiphertextPrefix.
- EncryptNullable/DecryptNullable gracefully handle nulls by returning null.
- EncryptDatabaseEnabled indicates whether server-side encrypt-at-rest is active; use it to guide storage strategies.
@@ -0,0 +1,37 @@
# IUserService
> **File:** `src/EchoHub.Core/Contracts/IUserService.cs`
> **Kind:** interface
```csharp
public interface IUserService
```
IUserService defines a contract for asynchronous user-management operations within EchoHub.Core. It exposes methods to register and authenticate users, retrieve profiles by username or by ID, update profile details, and set a user's avatar. Implementations of this interface serve as the single logical boundary for user lifecycle concerns, allowing REST endpoints and the IRC gateway to funnel through a consistent surface and enabling easier testing and swapping of storage or identity providers. The RegisterUserAsync method acknowledges server configuration: when Server:Registration is set to "invite", an inviteCode is required; when set to "closed", new accounts are rejected; all such flows funnel through this service.
## Remarks
By centralizing these operations behind IUserService, the rest of the system depends on a stable, testable contract rather than concrete data stores or authentication mechanisms. It coordinates with the UserOperationResult wrapper to communicate success or failure and, for retrieval operations, to surface user data returned on success, keeping error handling consistent across the application.
## Example
```csharp
// Example usage of the IUserService contract
var result = await userService.RegisterUserAsync("alice", "P@ssw0rd", displayName: "Alice", inviteCode: "INV-123");
if (result.IsSuccess)
{
// registration succeeded; you can proceed with login or profile fetch
}
```
```csharp
var profile = await userService.GetUserProfileAsync("alice");
if (profile != null)
{
// use profile data
}
```
## Notes
- If you call UpdateProfileAsync with all arguments as null, the operation may be a no-op; only pass the fields you intend to update.
- GetUserByIdAsync returns a UserProfileDto?; handle the null case when the user does not exist.
- For registration, ensure your server's registration policy (invite vs closed) is aligned with your inviteCode usage; otherwise registration may fail.
@@ -0,0 +1,178 @@
# AccountDtos.cs
> **Source:** `src/EchoHub.Core/DTOs/AccountDtos.cs`
## Contents
- [DeleteAccountRequest](#deleteaccountrequest)
- [ExportedAttachmentDto](#exportedattachmentdto)
- [ExportedMessageDto](#exportedmessagedto)
- [UserDataExportDto](#userdataexportdto)
---
## DeleteAccountRequest
> **File:** `src/EchoHub.Core/DTOs/AccountDtos.cs`
> **Kind:** record
```csharp
public record DeleteAccountRequest(string Password)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Password` | `string` | — |
This record models the password-confirmation payload required when a user initiates destructive self-service account actions (such as deleting their account). It captures the password as a single field to prove the users intent before the action is executed.
## Remarks
DeleteAccountRequest encapsulates a sensitive credential within a lightweight boundary object to keep password handling explicit in the delete workflow. By isolating the password in a dedicated payload, the system can perform authentication checks, auditing, and policy enforcement at the appropriate boundary. The record is immutable and minimal (a single Password property), which simplifies model binding and reduces the surface area for accidental data exposure.
## Example
```csharp
// When initiating a delete flow, supply the password for re-confirmation.
var request = new DeleteAccountRequest("P@ssw0rd!");
```
## Notes
- Treat the Password as sensitive; avoid logging or exposing it in responses.
- Use this payload only in the delete flow; ensure that the password validation is performed server-side before performing the destructive action.
---
## ExportedAttachmentDto
> **File:** `src/EchoHub.Core/DTOs/AccountDtos.cs`
> **Kind:** record
```csharp
public record ExportedAttachmentDto(
string FileName,
string Url,
long FileSize,
string Kind,
string ChannelName,
DateTimeOffset SentAt)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `FileName` | `string` | — |
| `Url` | `string` | — |
| `FileSize` | `long` | — |
| `Kind` | `string` | — |
| `ChannelName` | `string` | — |
| `SentAt` | `DateTimeOffset` | — |
Represents the metadata of an attachment that has been exported from a channel. It groups the file name, a URL to access the file, the file size in bytes, a textual kind descriptor, the originating channel name, and the timestamp when it was sent. Use this DTO when returning or transmitting export results to clients or cross-system boundaries to ensure a stable, serializable shape that is decoupled from internal domain models.
## Remarks
- Being a record, instances are immutable and equality is value-based, making it ideal for transport across layers or for caching export results. It serves as a clean contract between the export process and API or consumer layers.
- It acts as a boundary object, decoupling presentation/API concerns from domain entities while preserving the essential attachment metadata needed by clients (name, access URL, size, kind, origin channel, and timestamp).
## Example
```csharp
var attachment = new ExportedAttachmentDto(
FileName: "invoice.pdf",
Url: "https://cdn.example.com/exports/invoice.pdf",
FileSize: 254000,
Kind: "document",
ChannelName: "billing",
SentAt: DateTimeOffset.UtcNow
);
```
## Notes
- The Kind property is a free-form string; if there is a known finite set of kinds, consider introducing a dedicated enum later to avoid inconsistent values.
- FileSize is a long and should be non-negative; implement validation at boundaries if negative values could be produced by upstream systems.
- Ensure the Url is appropriate for client access (consider expiration, authentication, and CORS as needed) since this DTO surfaces a direct link to the exported attachment.
---
## ExportedMessageDto
> **File:** `src/EchoHub.Core/DTOs/AccountDtos.cs`
> **Kind:** record
```csharp
public record ExportedMessageDto(
Guid Id,
string ChannelName,
DateTimeOffset SentAt,
string Content,
Guid? ReplyToMessageId)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Id` | `Guid` | — |
| `ChannelName` | `string` | — |
| `SentAt` | `DateTimeOffset` | — |
| `Content` | `string` | — |
| `ReplyToMessageId` | `Guid?` | — |
ExportedMessageDto is an immutable data transfer object that captures the essential details of a message exported from a channel: its identity (Id), the channel it came from (ChannelName), when it was sent (SentAt), the message content (Content), and an optional reference to the message it replies to (ReplyToMessageId). It serves as a serialization-friendly payload used by export or archival pipelines, decoupled from the in-memory domain model.
## Remarks
ExportedMessageDto provides a stable contract for export pipelines by decoupling serialized data from the internal domain entities. Being a record, it benefits from value-based equality and immutability, which simplifies de-duplication and testing of exported payloads. The nullable ReplyToMessageId models the optional threading relationship: null means the message has no parent. Use ChannelName and SentAt as lightweight contextual metadata when reconstructing conversations in external systems.
## Example
```csharp
var message = new ExportedMessageDto(
Id: Guid.NewGuid(),
ChannelName: "general",
SentAt: DateTimeOffset.UtcNow,
Content: "Hello world",
ReplyToMessageId: null
);
```
## Notes
- The ReplyToMessageId is nullable; null indicates no parent message.
- As a record, equality is based on all properties; two messages with identical data compare as equal.
- If you need to derive a modified copy without mutating the original, use the with-expression (e.g., var updated = message with { Content = "Updated" };).
---
## UserDataExportDto
> **File:** `src/EchoHub.Core/DTOs/AccountDtos.cs`
> **Kind:** record
```csharp
public record UserDataExportDto(
DateTimeOffset ExportedAt,
string ServerName,
UserProfileDto Profile,
List<ExportedMessageDto> Messages,
List<ExportedAttachmentDto> Attachments)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `ExportedAt` | `DateTimeOffset` | — |
| [`ServerName`](../../EchoHub.Server.Irc/IrcCommandHandler.cs.md) | `string` | — |
| `Profile` | [`UserProfileDto`](ProfileDtos.cs.md) | — |
| `Messages` | `List<ExportedMessageDto>` | — |
| `Attachments` | `List<ExportedAttachmentDto>` | — |
Represents a persisted snapshot of a user's data as stored by the server, intended for data export or portability. It consolidates the export timestamp, the server identity, the user's profile, and the exported messages and attachments; in end-to-end encrypted rooms the message contents are ciphertext, since the server never has access to plaintext.
## Remarks
UserDataExportDto is an immutable data transfer object that anchors the export pipeline to the server's stored representation. By pairing profile, messages, and attachments into a single artifact, it simplifies serialization, auditing, and versioning while guarding the boundaries between storage concerns and export logic.
## Notes
- The Messages collection contains ciphertext for end-to-end encrypted rooms; do not decrypt on the server. Decryption and user presentation must happen client-side with proper keys.
---
@@ -0,0 +1,168 @@
# AuthDtos.cs
> **Source:** `src/EchoHub.Core/DTOs/AuthDtos.cs`
## Contents
- [LoginRequest](#loginrequest)
- [LoginResponse](#loginresponse)
- [RefreshRequest](#refreshrequest)
- [RegisterRequest](#registerrequest)
---
## LoginRequest
> **File:** `src/EchoHub.Core/DTOs/AuthDtos.cs`
> **Kind:** record
```csharp
public record LoginRequest(string Username, string Password)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Username` | `string` | — |
| `Password` | `string` | — |
Represents the credentials needed to log a user in. This immutable record carries a Username and Password and is intended to be used as a data transfer object when submitting login data to authentication endpoints.
## Remarks
As a positional record, LoginRequest provides value-based equality and deconstruction. It is immutable, with init-only properties, which helps prevent accidental mutation of credential data as it travels across system boundaries. Treat Password as sensitive data: avoid logging or displaying it, and ensure transport security when sending this DTO.
## Example
```csharp
var request = new LoginRequest("alice", "P@ssw0rd!");
```
## Notes
- Password is sensitive data; avoid logging or displaying it; mask when emitted in logs or error messages.
- This is a simple data-transfer object; it contains no business logic.
---
## LoginResponse
> **File:** `src/EchoHub.Core/DTOs/AuthDtos.cs`
> **Kind:** record
```csharp
public record LoginResponse(
string Token,
string RefreshToken,
DateTimeOffset ExpiresAt,
string Username,
string? DisplayName,
string? NicknameColor)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| [`Token`](../../EchoHub.Client/Services/ApiClient.cs.md) | `string` | — |
| `RefreshToken` | `string` | — |
| `ExpiresAt` | `DateTimeOffset` | — |
| `Username` | `string` | — |
| `DisplayName` | `string?` | — |
| `NicknameColor` | `string?` | — |
LoginResponse is a data transfer object that represents the server's response to a successful login. It bundles the authentication tokens (Token and RefreshToken), the token expiration moment (ExpiresAt), and the authenticated user's identity (Username), along with optional personalization fields (DisplayName and NicknameColor). This object is intended for consumption by clients to establish authenticated sessions, attach the access token to requests, refresh tokens when needed, and present user information in the UI.
## Remarks
LoginResponse is an immutable value object (a record) whose identity is defined by its content. It cleanly separates transport concerns from domain logic, acting as a simple contract that different layers can rely on without side effects. The optional DisplayName and NicknameColor fields model user-facing personalization; callers must handle potential nulls when those fields are not provided.
## Example
```csharp
var response = new LoginResponse(
Token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
RefreshToken: "def123-refresh",
ExpiresAt: DateTimeOffset.UtcNow.AddHours(1),
Username: "alex",
DisplayName: "Alex Doe",
NicknameColor: "#FF6A00"
);
```
## Notes
- DisplayName and NicknameColor may be null if the server omits them.
- Treat this type as data-only; avoid adding behavior such as validation or mutation.
- Token values are sensitive; avoid logging them and consider secure storage/handling in the client.
---
## RefreshRequest
> **File:** `src/EchoHub.Core/DTOs/AuthDtos.cs`
> **Kind:** record
```csharp
public record RefreshRequest(string RefreshToken)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `RefreshToken` | `string` | — |
RefreshRequest is a small, immutable data transfer object (a C# record) that carries a single value: the RefreshToken. It is used when a client requests a new access token from the authentication service, typically by posting this payload to the refresh endpoint.
## Remarks
By representing the refresh payload as a dedicated type, the API boundary gains a clear, strongly-typed contract that can be validated and logged consistently. The use of a record ensures value-based equality and immutable semantics, which helps prevent accidental mutation during transport or handling and makes it straightforward to pattern-match or deconstruct if needed in higher layers. In the overall authentication flow, this DTO sits alongside other EchoHub authentication DTOs and forms the low-level transport shape for refresh token exchanges.
## Example
```csharp
var request = new RefreshRequest("sample-refresh-token");
```
## Notes
- Do not log or expose the RefreshToken; avoid writing it to logs or UI.
- Ensure the token is transmitted over HTTPS and handled only in the request body, not in URLs.
- Validate that the token is non-empty before sending to the refresh endpoint; handle nulls gracefully.
---
## RegisterRequest
> **File:** `src/EchoHub.Core/DTOs/AuthDtos.cs`
> **Kind:** record
```csharp
public record RegisterRequest(string Username, string Password, string? DisplayName = null, string? InviteCode = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Username` | `string` | — |
| `Password` | `string` | — |
| `DisplayName` | `string?` | `null` |
| [`InviteCode`](../Models/InviteCode.cs.md) | `string?` | `null` |
RegisterRequest is a compact data-transfer object used to convey the information necessary to register a new user. It requires a Username and Password, and optionally accepts a DisplayName and an InviteCode. Implemented as a C# positional record, it is immutable and uses value-based equality, making it ideal for transport across API boundaries and for straightforward comparisons in tests. This DTO is typically produced by a client during registration and consumed by server-side authentication logic. The DisplayName parameter is nullable with a default of null, allowing clients to omit it; InviteCode is also nullable and used only when the onboarding flow supports invitation codes.
## Remarks
This symbol acts as a stable contract for the registration flow: it encapsulates the required credentials and optional metadata in a single, immutable object. By using a record, equality and deconstruction align with value semantics, making it easy to compare requests and to pass them through layers without mutation. Because DisplayName and InviteCode are optional, validation often happens elsewhere, enabling flexible client behavior while preserving a clear API boundary.
## Example
```csharp
// Typical usage with all fields
var full = new RegisterRequest("jdoe", "P@ssw0rd", "John Doe", "INVITE-42");
// Minimal usage: only required fields
var minimal = new RegisterRequest("jdoe", "P@ssw0rd");
```
## Notes
- Do not log or leak the Password value; treat it as sensitive data and rely on secure transport and proper logging practices.
- Optional fields may be null; server-side validation should enforce any business rules regarding DisplayName or InviteCode as appropriate.
---
@@ -0,0 +1,598 @@
# ChatDtos.cs
> **Source:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
## Contents
- [AttachmentDto](#attachmentdto)
- [ChannelCryptoDto](#channelcryptodto)
- [ChannelDto](#channeldto)
- [ChannelMetaDto](#channelmetadto)
- [CreateChannelRequest](#createchannelrequest)
- [EmbedDto](#embeddto)
- [JoinChannelResult](#joinchannelresult)
- [MessageDto](#messagedto)
- [RekeyChannelRequest](#rekeychannelrequest)
- [ReplyRefDto](#replyrefdto)
- [SendMessageRequest](#sendmessagerequest)
- [SendUrlRequest](#sendurlrequest)
- [UpdateTopicRequest](#updatetopicrequest)
- [UserDto](#userdto)
---
## AttachmentDto
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record AttachmentDto(
AttachmentKind Kind,
string Url,
string FileName,
long FileSize,
string? AsciiPreview = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Kind` | [`AttachmentKind`](../Models/AttachmentKind.cs.md) | — |
| `Url` | `string` | — |
| `FileName` | `string` | — |
| `FileSize` | `long` | — |
| `AsciiPreview` | `string?` | `null` |
Represents a file attachment attached to a chat message. It carries the attachment kind, a URL to access the resource, the original file name, the size of the file, and an optional ASCII preview used for color-tag art when available. This DTO is used when composing or processing message payloads that include attachments, or when consuming message data that contains attachment metadata. In end-to-end encrypted channels the content behind the URL and the preview may be ciphertext that the server cannot read.
## Remarks
AttachmentDto serves as a compact, immutable value object that consolidates attachment metadata for transport, storage, and rendering across UI and API boundaries. Being a record provides value-based equality, which simplifies deduplication and caching scenarios, and makes it natural to compare attachments without inspecting the entire payload. It decouples attachment handling from the message body, enabling consistent rendering and processing of attachments regardless of how the message content is structured.
## Example
```csharp
// Example: construct an attachment DTO for a file attachment
var attachment = new AttachmentDto(
default(AttachmentKind),
"https://cdn.example.com/files/document.pdf",
"document.pdf",
204800,
null);
```
## Notes
- AsciiPreview is optional; when present, it provides a text-based preview but is not guaranteed to render a full image. Clients should gracefully fall back to the URL or file name if the preview is absent.
- AttachmentDto is a record, so instances are immutable and compare by value. This supports straightforward caching and deduplication strategies across layers.
---
## ChannelCryptoDto
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record ChannelCryptoDto(bool IsEncrypted, string? EncryptionSalt)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `IsEncrypted` | `bool` | — |
| `EncryptionSalt` | `string?` | — |
ChannelCryptoDto carries the public cryptographic metadata required by a client to derive its join credential from a passphrase. It should be used by clients during the channel join flow to determine if a passphrase-based derivation is necessary and to access the salt used for key derivation, without ever handling the wrapped room key.
## Remarks
This DTO isolates derivation parameters from actual keys, enabling authentication-related components to reason about how a credential is derived without touching or exposing key material. The IsEncrypted flag indicates whether a passphrase-based join is applicable, and EncryptionSalt provides the salt used in the derivation when encryption is in effect. When IsEncrypted is false, EncryptionSalt may be null, reflecting that no passphrase-based derivation is required.
## Notes
- If IsEncrypted is true, EncryptionSalt should be non-null to derive the join credential; when false, the salt may be null.
- This is a simple data transfer object intended to convey derivation parameters safely; never serialize or expose wrapped key material.
---
## ChannelDto
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record ChannelDto(
Guid Id,
string Name,
string? Topic,
bool IsPublic,
int MessageCount,
DateTimeOffset CreatedAt,
bool IsProtected = false,
bool IsEncrypted = false,
bool IsSystem = false)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Id` | `Guid` | — |
| `Name` | `string` | — |
| `Topic` | `string?` | — |
| `IsPublic` | `bool` | — |
| `MessageCount` | `int` | — |
| `CreatedAt` | `DateTimeOffset` | — |
| `IsProtected` | `bool` | `false` |
| `IsEncrypted` | `bool` | `false` |
| `IsSystem` | `bool` | `false` |
ChannelDto is an immutable data transfer object that encapsulates the core metadata of a chat channel. It groups the channels unique identifier, display name, an optional topic, visibility, message count, and creation timestamp, together with flags that describe its characteristics (protected, encrypted, and system channels). This object is commonly produced by the server when retrieving or creating channel data and is consumed by clients and services that need a stable snapshot of a channels state. As a record, ChannelDto provides value-based equality and supports convenient cloning via with-expressions without mutating the original instance.
## Remarks
ChannelDto serves as a transport-friendly abstraction that decouples channel metadata from domain models. The boolean flags encode common channel semantics: IsPublic indicates whether the channel is publicly discoverable, IsProtected denotes restricted access, IsEncrypted signals encryption usage, and IsSystem marks built-in, system-managed channels. CreatedAt represents the creation-time snapshot and should be treated as immutable; for updates, create a new ChannelDto instance (e.g., with a with-expression) rather than mutating the existing one.
## Example
```csharp
var channel = new ChannelDto(
Id: Guid.NewGuid(),
Name: "general",
Topic: "General discussion",
IsPublic: true,
MessageCount: 482,
CreatedAt: DateTimeOffset.UtcNow,
IsProtected: false,
IsEncrypted: true,
IsSystem: false
);
```
## Notes
- Topic may be null; consumers should handle absence of a topic gracefully.
- ChannelDto is immutable; to derive a modified version use the with expression (e.g., channel with { Name = "new-name" }).
- Boolean flags default to false when omitted, so explicit values should reflect the actual channel semantics.
---
## ChannelMetaDto
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record ChannelMetaDto(
Guid Id,
string Name,
string? Topic,
bool IsEncrypted,
bool IsProtected,
int MessageCount,
int UniqueUserCount,
long EstimatedSizeBytes,
DateTimeOffset CreatedAt)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Id` | `Guid` | — |
| `Name` | `string` | — |
| `Topic` | `string?` | — |
| `IsEncrypted` | `bool` | — |
| `IsProtected` | `bool` | — |
| `MessageCount` | `int` | — |
| `UniqueUserCount` | `int` | — |
| `EstimatedSizeBytes` | `long` | — |
| `CreatedAt` | `DateTimeOffset` | — |
ChannelMetaDto is a data transfer object that captures human-facing metadata for a chat channel as surfaced by the /meta command. It exposes the channel's identity (Id), presentation (Name), optional description (Topic), security properties (IsEncrypted, IsProtected), participation metrics (MessageCount, UniqueUserCount), a best-effort size estimate of content (EstimatedSizeBytes), and the creation timestamp (CreatedAt). For encrypted channels, the server retains counts, timestamps, and blob sizes but cannot read the content itself; EstimatedSizeBytes is the sum of stored attachment blob sizes plus message text length, so it is an estimate rather than an exact on-disk total.
## Remarks
This immutable record serves as a stable, client-facing contract that decouples internal storage from UI rendering. By aggregating these fields, it enables lightweight channel listings and meta views without exposing message content, while still providing enough information to gauge activity and scope.
## Notes
- Topic may be null; clients should handle absence gracefully when rendering.
- EstimatedSizeBytes is an approximation; the value may drift as new messages or attachments are added.
---
## CreateChannelRequest
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record CreateChannelRequest(
string Name,
string? Topic = null,
bool IsPublic = true,
string? [REDACTED:CONNECTION_STRING_PASSWORD]
string? EncryptionSalt = null,
string? WrappedRoomKey = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Name` | `string` | — |
| `Topic` | `string?` | `null` |
| `IsPublic` | `bool` | `true` |
| `EncryptionSalt` | `string? [REDACTED:CONNECTION_STRING_PASSWORD]
string?` | `null` |
| `WrappedRoomKey` | `string?` | `null` |
Represents the payload for creating a new chat channel. It encapsulates the channel name, an optional topic, a visibility flag, and optional cryptographic data used to secure channel communications. A redacted credentials field stands in for a sensitive connection password and should be supplied securely at runtime rather than stored or logged.
## Remarks
This record is an immutable value object intended to be used as a single payload passed from client to API for channel creation. It coalesces related creation parameters in one place, facilitating validation and transport across layers while remaining independent of any particular persistence or network protocol. The redacted password field highlights a security concern: avoid exposing credentials in logs or UI surfaces; handle it through secure channels only.
## Notes
- Name is required; Topic, IsPublic, EncryptionSalt, WrappedRoomKey are optional with sensible defaults (Topic = null, IsPublic = true, EncryptionSalt = null, WrappedRoomKey = null).
- IsPublic defaults to true; set to false to create a private channel.
- Sensitive fields (the redacted password) must be handled securely; avoid logging or exposing the value in logs or UI.
---
## EmbedDto
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record EmbedDto(
string? SiteName,
string? Title,
string? Description,
string? ImageAscii,
string Url,
string? ThemeColor = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `SiteName` | `string?` | — |
| `Title` | `string?` | — |
| `Description` | `string?` | — |
| `ImageAscii` | `string?` | — |
| `Url` | `string` | — |
| `ThemeColor` | `string?` | `null` |
EmbedDto is a lightweight, immutable data carrier for the metadata needed to render a rich embed in chat messages. As a C# record, it provides value-based equality and convenient construction, making it ideal for transporting embed information across layers without mutating state. It carries optional metadata fields (SiteName, Title, Description, ImageAscii, ThemeColor) and requires a Url that points to the embed resource.
## Remarks
This abstraction centralizes all embed-related data into a single contract, decoupling embedding details from other message payloads. By using a record, it gains structural equality and easy pattern matching, which simplifies testing and usage in render pipelines. The optional ThemeColor guides UI theming, while ImageAscii allows lightweight, ASCII-based previews when a graphical asset is unavailable.
## Example
```csharp
var embed = new EmbedDto(
SiteName: "Aurora Gallery",
Title: "Landscape Preview",
Description: "A sample landscape embed",
ImageAscii: "[ASCII_ART]",
Url: "https://example.org/embeds/landscape",
ThemeColor: "#3366FF");
```
## Notes
- All fields except Url are optional, so a minimal EmbedDto can be created with just the Url.
- Being a record, EmbedDto is immutable and supports with-expressions to create modified copies without changing the original instance.
---
## JoinChannelResult
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record JoinChannelResult(
bool Success,
List<MessageDto> History,
string? Error = null,
bool PasswordRequired = false,
string? EncryptionSalt = null,
string? WrappedRoomKey = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Success` | `bool` | — |
| `History` | `List<MessageDto>` | — |
| `Error` | `string?` | `null` |
| `PasswordRequired` | `bool` | `false` |
| `EncryptionSalt` | `string?` | `null` |
| `WrappedRoomKey` | `string?` | `null` |
JoinChannelResult is a value object that conveys the outcome of attempting to join a chat channel. It exposes whether the operation succeeded, provides the channel's message history for immediate rendering, and carries optional security-related data (password requirement, encryption salt, and wrapped room key) that consumers can act on after the join completes.
## Remarks
JoinChannelResult centralizes all information produced by a join attempt, keeping the caller decoupled from the join logic. By pairing a success flag with the History and optional security fields, it supports both happy-path UI rendering and encrypted or password-protected channels without additional payloads. The inclusion of EncryptionSalt and WrappedRoomKey suggests a workflow where the client may fetch or negotiate encryption material as part of joining, rather than as a separate round-trip.
## Example
```csharp
// Successful join with history
List<MessageDto> history = new List<MessageDto>();
var result = new JoinChannelResult(true, history);
// Join that requires a password and includes encryption material
var secured = new JoinChannelResult(true, history, PasswordRequired: true, EncryptionSalt: \"salt123\", WrappedRoomKey: \"wrappedKey\");
```
## Notes
- Error is typically non-null only when Success is false; use it to surface the failure reason to the user.
- EncryptionSalt and WrappedRoomKey are meaningful only for encrypted or password-protected channels; they may be null in plain channels.
- History should be treated as the initial set of messages to render immediately after a join; it may be empty in failure scenarios or when a channel has no prior messages.
---
## MessageDto
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record 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:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Id` | `Guid` | — |
| `Content` | `string` | — |
| `SenderUsername` | `string` | — |
| `SenderNicknameColor` | `string?` | — |
| `ChannelName` | `string` | — |
| `SentAt` | `DateTimeOffset` | — |
| `Attachments` | `List<AttachmentDto>?` | `null` |
| `Embeds` | `List<EmbedDto>?` | `null` |
| `SenderDisplayName` | `string?` | `null` |
| `ReplyTo` | `ReplyRefDto?` | `null` |
MessageDto is an immutable data transfer object that captures the essential details of a chat message as it moves across the EchoHub chat API surface. Implemented as a C# record, it provides value-based equality and straightforward construction for message data, making it ideal for serialization and transport between layers (e.g., API, client, and service boundaries). The object aggregates core message data such as Id, Content, SenderUsername, ChannelName, and SentAt, while also supporting optional enhancements like Attachments and Embeds, a human-friendly SenderDisplayName, and a ReplyTo reference for threaded conversations. This shape keeps message-related concerns contained in a single DTO without leaking domain internals, enabling predictable data contracts for consumers.
## Remarks
This symbol serves as a boundary object that encapsulates a complete chat message payload, including optional media and UI hints. By composing AttachmentDto and EmbedDto, it allows rich messages to travel without forcing callers to depend on internal domain types. The use of a record emphasizes that MessageDto represents a snapshot of message data at a point in time; consumers should treat instances as immutable and, if changes are needed, create new instances. The presence of optional fields (SenderNicknameColor, Attachments, Embeds, SenderDisplayName, ReplyTo) reflects real-world variability in messaging scenarios (e.g., plain text messages, media-enabled messages, or replies).
## Notes
- Attachments and Embeds may be null; downstream code should handle nulls or default to empty collections to avoid null reference errors.
- SenderNicknameColor and SenderDisplayName are optional UI hints and may be absent; consumers should gracefully handle missing values.
- ReplyTo is optional and only populated for messages that are replies to another message; check for null before accessing related data.
---
## RekeyChannelRequest
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record RekeyChannelRequest(
string OldPassword,
string NewPassword,
string NewEncryptionSalt,
string NewWrappedRoomKey)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `OldPassword` | `string` | — |
| `NewPassword` | `string` | — |
| `NewEncryptionSalt` | `string` | — |
| `NewWrappedRoomKey` | `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.
This RekeyChannelRequest is a data transfer object used to perform a channel rekey. It carries the old password to prove knowledge of the current key, the new password and its salt, and the re-wrapped room key to be used under the new credentials.
## Remarks
This type serves as a single payload boundary in the channel rekey workflow, encapsulating all data required to authenticate the existing context and establish a new encryption context for the room. Being a record enforces immutability and provides straightforward value-based equality, which simplifies testing and auditing of rekey requests. It acts as a contract between the client and server for the rotation of the room key tied to a new passphrase.
## Example
```csharp
var request = new RekeyChannelRequest(
OldPassword: "old-passphrase",
NewPassword: "new-passphrase",
NewEncryptionSalt: "salt-42",
NewWrappedRoomKey: "BASE64_WRAPPED_ROOM_KEY"
);
```
## Notes
- Do not log or expose OldPassword, NewPassword, or NewWrappedRoomKey; treat them as highly sensitive and avoid telemetry.
- NewEncryptionSalt should be a cryptographically strong, per-operation salt generated by a secure RNG; do not reuse salts.
- This object represents a single rekey operation and should not be reused for multiple independent requests.
---
## ReplyRefDto
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record ReplyRefDto(
Guid MessageId,
string SenderUsername,
string Content)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `MessageId` | `Guid` | — |
| `SenderUsername` | `string` | — |
| `Content` | `string` | — |
ReplyRefDto is a compact, immutable data transfer object that identifies the message a user is replying to. It carries the target message's ID, the original sender's username, and the Content of that message as transmitted over the network, enabling clients and services to render contextual reply previews and preserve the reply's linkage. Content is treated exactly like ordinary 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). If the original message has been deleted, the related MessageDto will be null; the reply reference remains a valid anchor for rendering the reply context.
## Remarks
Represents the reply target in chat threads as a minimal reference, decoupling the UI payload from the full MessageDto. It ensures consistent wire-format handling across plaintext and end-to-end encrypted rooms, while allowing clients to display reply context without requiring the entire original payload.
## Example
```csharp
var reference = new ReplyRefDto(
MessageId: Guid.Parse("3f2504e0-4f89-11d3-9a0c-0305e82c3301"),
SenderUsername: "alice",
Content: "Hello world"
);
```
## Notes
- Content is the exact on-wire representation of the referenced message; it may be ciphertext in encrypted rooms and should be decrypted by the client when applicable.
- If the original message has been deleted, the MessageDto may be null, but the ReplyRefDto still anchors the reply context for UI rendering; callers should handle potential missing referenced data gracefully.
---
## SendMessageRequest
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record SendMessageRequest(string ChannelName, string Content)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `ChannelName` | `string` | — |
| `Content` | `string` | — |
SendMessageRequest is an immutable data transfer object that encapsulates the information required to send a message to a specific chat channel. It combines the ChannelName and the Content to be delivered so transport or messaging layers can operate on a single payload. As a record, it provides value-based equality and easy cloning with the with-expression, which helps when constructing variations without mutating existing instances.
## Remarks
Acts as a boundary contract between UI/API layers and the messaging service. The record's immutability and structural equality make it reliable for logging, caching, and test assertions. Validation rules or routing decisions should live outside this DTO; this type should not perform domain validation. Its simple two-string shape also makes it friendly to common serialization mechanisms, enabling straightforward transport across boundaries.
## Notes
- No validation is performed by the type itself; ensure ChannelName and Content conform to domain rules before sending.
- The type is immutable; to modify, create a new instance (or use the with-expression) rather than mutating an existing one.
- Suitable for serialization; the plain two-property shape works well with JSON, XML, or other common serializers.
---
## SendUrlRequest
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record SendUrlRequest(string Url)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Url` | `string` | — |
SendUrlRequest is a tiny, immutable URL payload represented as a C# record. Its intended for scenarios where a URL must be passed across boundaries in a strongly-typed way rather than as a raw string, gaining value-based equality and straightforward deconstruction in the process.
## Remarks
Using a record for this DTO ensures immutability, value-based equality, and built-in deconstruction. This makes SendUrlRequest a natural fit for messaging or API surfaces that expect a dedicated URL payload type instead of raw strings, reducing the chance of accidental mutation and enabling pattern-based handling of the URL payload.
## Example
```csharp
var request = new SendUrlRequest("https://example.com");
```
## Notes
- No validation is performed inside the type; ensure the URL is valid at the call site or in downstream handlers.
---
## UpdateTopicRequest
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record UpdateTopicRequest(string? Topic)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Topic` | `string?` | — |
Represents a request to update the topic of a chat or conversation. This immutable record acts as a lightweight DTO that carries an optional Topic value; use it when issuing an update operation—provide a non-null Topic to set a new topic, or pass null to indicate that the topic should be cleared or left unchanged by the API, depending on server semantics.
## Remarks
This abstraction communicates the intent of updating only the topic field, leveraging a nullable Topic to express optionality. The record nature provides value-based equality and simple construction, and you can create modified copies with the with-expression (e.g., updating the Topic while preserving other fields in a derived request).
## Example
```csharp
// Set a new topic
var request = new UpdateTopicRequest("New Topic");
// Clear the topic (behavior depends on the API)
var clearRequest = new UpdateTopicRequest(null);
// Create a modified copy
var updated = request with { Topic = "Updated Topic" };
```
## Notes
- Topic is nullable; serialization and API behavior may vary—null may mean "no change" or "clear" depending on the endpoint.
- Because this is a record, instances are immutable; use the with-expression to derive variations without mutating the original.
---
## UserDto
> **File:** `src/EchoHub.Core/DTOs/ChatDtos.cs`
> **Kind:** record
```csharp
public record UserDto(
Guid Id,
string Username,
string? DisplayName,
string? NicknameColor,
UserStatus Status,
DateTimeOffset LastSeenAt)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Id` | `Guid` | — |
| `Username` | `string` | — |
| `DisplayName` | `string?` | — |
| `NicknameColor` | `string?` | — |
| `Status` | [`UserStatus`](../Models/UserStatus.cs.md) | — |
| `LastSeenAt` | `DateTimeOffset` | — |
UserDto is a lightweight, immutable data transfer object that conveys a user's identity and presence-related attributes across boundaries such as API responses or UI bindings. It aggregates the user's unique identifier, login name, optional display name and nickname color, current status, and the last seen timestamp so clients can present a consistent and responsive user summary.
## Remarks
As a record, UserDto benefits from value-based equality and structural immutability, making it easy to compare user summaries and safely pass them around without worrying about accidental mutation. DisplayName and NicknameColor are optional to accommodate scenarios where presentation details are missing. LastSeenAt and Status provide presence information that can drive UI indicators and sorting.
## Notes
- DisplayName and NicknameColor are nullable; null should be treated as absent presentation data.
---
@@ -0,0 +1,282 @@
# CommonDtos.cs
> **Source:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
## Contents
- [ApiResponse](#apiresponse)
- [ApiResponse](#apiresponse-1)
- [ChannelOperationResult](#channeloperationresult)
- [ErrorResponse](#errorresponse)
- [PaginatedResponse](#paginatedresponse)
- [UserOperationResult](#useroperationresult)
- [ChannelError](#channelerror)
- [UserError](#usererror)
---
## ApiResponse
> **File:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
> **Kind:** record
```csharp
public record ApiResponse(bool Success, string? Message = null, List<string>? Errors = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Success` | `bool` | — |
| [`Message`](../Models/Message.cs.md) | `string?` | `null` |
| `Errors` | `List<string>?` | `null` |
ApiResponse is a lightweight data transfer object used to convey the outcome of an operation. It carries a required Success flag and optional Message and Errors to provide feedback and diagnostics to callers.
## Remarks
Used as a common response shape across service boundaries to avoid ad-hoc return types. The primary purpose is to separate control flow (success/failure) from payload, facilitating simple success messaging and error propagation. Be mindful that Errors is a `List<string>`, which remains mutable if the same instance is shared; convert to a read-only collection or copy before returning to external consumers.
## Notes
- The Errors property is a mutable `List<string>`—wrap or copy it if you intend to preserve a fixed snapshot when returning to consumers.
- Message may be null; supply a default user-friendly message or handle nulls in UI/logging.
---
## ApiResponse
> **File:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
> **Kind:** record
```csharp
public record ApiResponse<T>(bool Success, string? Message = null, List<string>? Errors = null, T? Data = default)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Success` | `bool` | — |
| [`Message`](../Models/Message.cs.md) | `string?` | `null` |
| `Errors` | `List<string>?` | `null` |
| `Data` | `T?` | `default` |
`ApiResponse<T>` is a generic wrapper you return from API methods to convey a successful outcome, an optional human-friendly message, and a payload of type T, along with any per-call errors. Use this pattern when you want a consistent contract for success, messaging, and data across endpoints rather than returning raw data alone.
## Remarks
`ApiResponse<T>` is an immutable value type (a record with a primary constructor) that standardizes how results are communicated. It separates the data payload from status information, allowing clients to inspect Success, Message, and Errors independently from Data. Because Message and Errors are optional, responses can remain concise for successful operations while still providing rich error detail when needed.
## Example
```csharp
using System.Collections.Generic;
// success with data
var result = new ApiResponse<string>(true, "Operation completed", null, "payload");
// error with details
var failure = new ApiResponse<string>(false, "Validation failed", new List<string> { "Email is invalid" }, null);
```
## Notes
- Message and Errors are nullable; always check Success before relying on these fields, and provide defaults if you need non-null output.
- `ApiResponse<T>` is immutable; to modify it, use a with-expression to create a copy (e.g., var updated = result with { Data = newData };).
---
## ChannelOperationResult
> **File:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
> **Kind:** record
```csharp
public record ChannelOperationResult(ChannelDto? Channel, ChannelError? Error, string? ErrorMessage)
{
public bool IsSuccess => Error is null;
public static ChannelOperationResult Success(ChannelDto channel) => new(channel, null, null);
public static ChannelOperationResult Fail(ChannelError error, string message) => new(null, error, message);
}
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| [`Channel`](../Models/Channel.cs.md) | `ChannelDto?` | — |
| `Error` | `ChannelError?` | — |
| `ErrorMessage` | `string?` | — |
ChannelOperationResult is a lightweight result wrapper used by channel-creation/lookup operations to return either a ChannelDto on success or an error descriptor on failure. Callers typically inspect IsSuccess and then access Channel or Error/ErrorMessage, using the static factories to produce a well-formed result rather than constructing it directly.
## Remarks
It captures the outcome of channel-oriented operations in a single, immutable value, reducing the need for exception-based control flow. By pairing either a Channel with no error or an Error with a message, it forces consumers to handle both success and failure paths in a uniform way. It complements the ChannelDto and ChannelError types by providing a minimal, self-describing container that can be passed through layers without leaking implementation details.
## Notes
- Prefer the static factories to create instances to preserve the intended invariant that a result carries either a Channel or an error. The public constructor can produce degenerate states if misused.
- The ErrorMessage is optional; provide a descriptive message to aid debugging when using Fail.
---
## ErrorResponse
> **File:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
> **Kind:** record
```csharp
public record ErrorResponse(string Error, string? Detail = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Error` | `string` | — |
| `Detail` | `string?` | `null` |
ErrorResponse is a small, immutable data transfer object used to convey error information from the server to API clients. Implemented as a C# record with two positional properties, Error and Detail, it carries a concise error identifier or message and optional supplemental details. Use it when standardizing error payloads across API endpoints or error-handling middleware that wants to provide a consistent error shape.
## Remarks
Using a record provides value-based equality and immutability, making ErrorResponse a stable payload that is easy to compare in tests and to clone with modifications via with-expressions. The Error field represents a short error code or message, while Detail offers optional, human-friendly context. This type is intended to be reused across API boundaries, ensuring clients receive a uniform error shape.
## Notes
- Avoid leaking sensitive internals in Error; prefer stable, client-friendly codes or messages.
- Detail is nullable; when null, serialization may omit the property depending on serializer settings.
- As a DTO, this record should be produced by a dedicated error-handling path rather than constructed manually in business logic.
---
## PaginatedResponse
> **File:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
> **Kind:** record
```csharp
public record PaginatedResponse<T>(List<T> Items, int Total, int Offset, int Limit)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Items` | `List<T>` | — |
| `Total` | `int` | — |
| `Offset` | `int` | — |
| `Limit` | `int` | — |
Represents a paginated result set for a collection of items of type T. It bundles the items for the current page together with paging metadata (Total, Offset, and Limit), enabling consumers to render pages and request subsequent pages without fetching the entire dataset. Use `PaginatedResponse<T>` when an API or service returns a slice of a larger collection and you need to convey both the page content and the overall size.
## Remarks
This generic DTO unifies paging across different endpoints by pairing a page of items with metadata describing the total size of the set and the paging window (Offset and Limit). Consumers can derive the total number of pages and navigate accordingly, without duplicating paging logic.
## Example
```csharp
var page = new PaginatedResponse<int>(
Items: new List<int> { 1, 2, 3 },
Total: 10,
Offset: 0,
Limit: 3
);
```
## Notes
- The Items property is a `List<T>`, which is mutable. Mutating the list after construction will affect the PaginatedResponse instance. If you require immutability of the collection, consider exposing `ReadOnlyCollection<T>` or `IReadOnlyList<T>` instead of `List<T>`, or wrap the list before returning.
- Because `PaginatedResponse<T>` is a record, the wrapper itself uses value-based equality, but the `List<T>` contained in Items is compared by reference. Two instances with equal contents but different `List<T>` instances will not compare equal.
---
## UserOperationResult
> **File:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
> **Kind:** record
```csharp
public record UserOperationResult(UserProfileDto? User, UserError? Error, string? ErrorMessage)
{
public bool IsSuccess => Error is null;
public static UserOperationResult Success(UserProfileDto user) => new(user, null, null);
public static UserOperationResult Fail(UserError error, string message) => new(null, error, message);
}
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| [`User`](../Models/User.cs.md) | `UserProfileDto?` | — |
| `Error` | `UserError?` | — |
| `ErrorMessage` | `string?` | — |
Represents the outcome of a user-related operation: it either carries a UserProfileDto for success or a UserError and an ErrorMessage for failure. Use IsSuccess to branch on the result and create instances via Success(user) for success or Fail(error, message) for failure.
## Remarks
This abstraction uses a record with nullable payload fields to model a simple Result pattern without introducing a separate discriminated union. It provides a single return type across methods that can either yield a user profile or fail with details, enabling concise consumer code that checks IsSuccess first. Because User is nullable when the result is a failure, and because Error and ErrorMessage are null on success, callers should guard access to User unless IsSuccess is true. The helper methods ensure the invariant that a successful result always carries a user while a failure carries an error and message.
## Notes
- Read result.User only after confirming IsSuccess; otherwise the value may be null.
- On failure, User will be null; consult Error and ErrorMessage for details.
---
## ChannelError
> **File:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
> **Kind:** enum
```csharp
public enum ChannelError
{
ValidationFailed,
AlreadyExists,
NotFound,
Forbidden,
Protected
}
```
ChannelError enumerates the discrete failure cases that can arise when managing channels in EchoHub. It provides a finite set of error codes so callers can distinguish invalid input, duplicates, missing resources, permission issues, and protected resources without resorting to free-form strings.
## Remarks
This enum lives in the DTO layer to convey precise failure reasons from service or repository operations to API clients. By centralizing channel-related errors, it enables consistent error handling, mapping to user-friendly responses, and easier client-side interpretation across create, update, and lookup workflows. The member names align with common REST/DTO conventions, reducing ambiguity when serializing and documenting API contracts.
## Notes
- Changing the enum's members or their order can impact clients that serialize/deserialize error codes; treat it as a public contract.
- If you enable numeric JSON serialization for enums, ensure the API contract documents the expected codes to avoid confusion.
---
## UserError
> **File:** `src/EchoHub.Core/DTOs/CommonDtos.cs`
> **Kind:** enum
```csharp
public enum UserError
{
ValidationFailed,
AlreadyExists,
NotFound,
InvalidCredentials,
Banned
}
```
Represents the set of user-related errors that can occur during authentication, registration, lookup, or other user-identity operations in the EchoHub DTO layer. This enum provides a typed, contract-friendly way to communicate failure modes from server to client, enabling centralized handling and consistent feedback without scattering string literals across the codebase.
Values include:
- ValidationFailed: input data failed validation.
- AlreadyExists: a resource with the given identifier already exists.
- NotFound: the requested user or resource could not be found.
- InvalidCredentials: credentials were invalid during authentication.
- Banned: the user is banned from the system.
## Remarks
By consolidating these common errors into a single enum, this abstraction decouples transport contracts from domain logic and supports uniform error mapping on the client. It simplifies UI messaging, and it allows the server to evolve its error vocabulary without changing method signatures.
## Notes
- Be mindful of how the enum is serialized in API responses (numeric vs string); consider standardizing on string representations to avoid client breakage when new values are added.
- Adding new values is a contract change; document and version the API accordingly, and ensure clients handle unknown values gracefully.
- This enum is a DTO-level error vocabulary; do not encode domain exceptions here.
---
@@ -0,0 +1,94 @@
# InviteDtos.cs
> **Source:** `src/EchoHub.Core/DTOs/InviteDtos.cs`
## Contents
- [CreateInviteRequest](#createinviterequest)
- [InviteDto](#invitedto)
---
## CreateInviteRequest
> **File:** `src/EchoHub.Core/DTOs/InviteDtos.cs`
> **Kind:** record
```csharp
public record CreateInviteRequest(int? MaxUses = null, int? ExpiresInHours = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `MaxUses` | `int?` | `null` |
| `ExpiresInHours` | `int?` | `null` |
This record serves as the payload for creating an invitation. It carries optional constraints that govern the invite: MaxUses limits how many times the invite can be redeemed, and ExpiresInHours determines how long the invite remains valid (in hours). When constructing the request, omit values you dont want to constrain; null properties indicate the server should apply its defaults.
## Remarks
Because CreateInviteRequest is a C# record, it provides value-based equality and immutable semantics, making it a reliable DTO for API calls and caching. The nullable properties express optional constraints without introducing separate flags, keeping the surface area small and expressive.
## Example
```csharp
var request = new CreateInviteRequest(MaxUses: 5, ExpiresInHours: 24);
```
## Notes
- Null on a property means no constraint; the API defaults apply.
- Many serializers omit null fields; if the API requires an explicit indicator for "no constraint," ensure your serializer preserves the field or you configure it accordingly.
- If you need to convey zero constraints explicitly, pass 0 (not null) for the respective property; null is not the same as zero.
---
## InviteDto
> **File:** `src/EchoHub.Core/DTOs/InviteDtos.cs`
> **Kind:** record
```csharp
public record InviteDto(
string Code,
string CreatedByUsername,
DateTimeOffset CreatedAt,
DateTimeOffset? ExpiresAt,
int MaxUses,
int UseCount)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Code` | `string` | — |
| `CreatedByUsername` | `string` | — |
| `CreatedAt` | `DateTimeOffset` | — |
| `ExpiresAt` | `DateTimeOffset?` | — |
| `MaxUses` | `int` | — |
| `UseCount` | `int` | — |
InviteDto is a small, transport-oriented representation of an invitation. It encapsulates the invitation code, the creator's username, the moment of creation, an optional expiry, and simple usage counters, making it suitable for API responses and inter-layer data transfers without revealing domain internals.
## Remarks
As a record, InviteDto is immutable and uses value-based equality, which makes caching and comparisons straightforward. It decouples transport concerns from domain logic by presenting only the data clients need. The fields map directly to invitation semantics: Code is the token, CreatedByUsername and CreatedAt capture provenance, ExpiresAt denotes expiry (nullable means no expiry), and MaxUses/UseCount express the usage limits and current consumption.
## Example
```csharp
var invite = new InviteDto(
Code: "WELCOME-ABC123",
CreatedByUsername: "admin",
CreatedAt: DateTimeOffset.UtcNow,
ExpiresAt: DateTimeOffset.UtcNow.AddDays(7),
MaxUses: 5,
UseCount: 0
);
```
## Notes
- Null ExpiresAt means the invitation does not expire; ensure your validation logic accounts for that.
- InviteDto is immutable; to reflect state changes (e.g., after a use), construct a new instance rather than mutating the existing one.
- Use UTC times for CreatedAt/ExpiresAt to avoid timezone ambiguity.
---
@@ -0,0 +1,127 @@
# ModerationDtos.cs
> **Source:** `src/EchoHub.Core/DTOs/ModerationDtos.cs`
## Contents
- [AssignRoleRequest](#assignrolerequest)
- [BanRequest](#banrequest)
- [KickRequest](#kickrequest)
- [MuteRequest](#muterequest)
---
## AssignRoleRequest
> **File:** `src/EchoHub.Core/DTOs/ModerationDtos.cs`
> **Kind:** record
```csharp
public record AssignRoleRequest(string Username, ServerRole Role)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Username` | `string` | — |
| `Role` | [`ServerRole`](../Models/ServerRole.cs.md) | — |
AssignRoleRequest is a lightweight, immutable data transfer object that carries the intent to assign a specific server role to a user. It encapsulates just two pieces of information—the target Username and the desired Role—and is intended to be serialized and sent to moderation or authorization services that perform the actual role assignment.
## Remarks
The record type provides value-based equality and immutability, making it a reliable payload for messaging boundaries between UI, services, and backend handlers. By expressing the action as data rather than behavior, it supports clean separation of concerns and straightforward routing in moderation workflows.
## Notes
- Ensure Username conforms to identity rules at the boundary before processing the request.
- Because this is an immutable record, callers should create a new instance for every distinct request; do not modify an existing instance.
---
## BanRequest
> **File:** `src/EchoHub.Core/DTOs/ModerationDtos.cs`
> **Kind:** record
```csharp
public record BanRequest(string? Reason = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Reason` | `string?` | `null` |
BanRequest is a lightweight, immutable data container used when issuing moderation bans. It carries an optional Reason and is designed to be passed as a single object through the moderation pipeline instead of a group of disparate parameters. This structure makes future extension straightforward (e.g., adding additional ban metadata) without changing call sites.
## Remarks
BanRequest acts as a boundary between the transport/presentation layer and the moderation domain. Using a record provides value-based equality and predictable serialization, which aids testing, logging, and caching. The optional Reason supports both silent bans and bans accompanied by rationale, with policy decisions about requiring a reason typically enforced at higher layers.
## Notes
- Reason is nullable; handle nulls gracefully when displaying or persisting data, and apply any policy about requiring a reason at the appropriate layer.
---
## KickRequest
> **File:** `src/EchoHub.Core/DTOs/ModerationDtos.cs`
> **Kind:** record
```csharp
public record KickRequest(string? Reason = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Reason` | `string?` | `null` |
KickRequest is a lightweight, immutable payload used when performing a moderation kick. It carries an optional Reason describing why the kick occurred. Callers construct this record when issuing a kick action and attach the reason if one is known; if no reason is provided, Reason remains null. The record shape ensures value-based equality and easy serialization across boundaries, making it a convenient transport object for moderation workflows.
## Remarks
KickRequest isolates the transport of a kick action from its core moderation logic. This abstraction makes it easy to extend later with additional fields (for example, moderatorId, timestamp, or kick ban duration) without changing the public contract. It also supports consistent logging and audit trails by treating the kick reason as optional metadata.
## Notes
- Reason is optional; validate as needed at the API boundary if your scenario requires a non-null reason.
- When serializing, null Reason might be omitted depending on serializer configuration; be explicit if you need to communicate 'no reason'.
- This is a simple DTO; do not conflate it with the domain entity for a kick; use it to transport data.
---
## MuteRequest
> **File:** `src/EchoHub.Core/DTOs/ModerationDtos.cs`
> **Kind:** record
```csharp
public record MuteRequest(string? Reason = null, int? DurationMinutes = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Reason` | `string?` | `null` |
| `DurationMinutes` | `int?` | `null` |
MuteRequest is a compact, immutable data transfer object used to initiate a moderation mute. It carries two optional fields: Reason and DurationMinutes, allowing you to specify a rationale and a duration when issuing a mute; omitting either field leaves that detail to the receiver's policy.
## Remarks
By grouping the fields into a single record, this abstraction reduces API surface area and provides a consistent payload for mute-related actions across the moderation layer. The record semantics also enable value-based equality and straightforward testing and transport.
## Example
```csharp
// Mute for 30 minutes with a reason
var request = new MuteRequest("Spamming in chat", 30);
// Mute without specifying details
var request2 = new MuteRequest();
```
## Notes
- Reason may contain user-provided content; avoid including it in logs or telemetry unless explicitly permitted.
- Because the type is a record with nullable fields, ensure boundary validation and handle nulls gracefully at the call site or in the receiving layer.
---
@@ -0,0 +1,202 @@
# ProfileDtos.cs
> **Source:** `src/EchoHub.Core/DTOs/ProfileDtos.cs`
## Contents
- [AvatarUploadResponse](#avataruploadresponse)
- [UpdateProfileRequest](#updateprofilerequest)
- [UpdateStatusRequest](#updatestatusrequest)
- [UserPresenceDto](#userpresencedto)
- [UserProfileDto](#userprofiledto)
---
## AvatarUploadResponse
> **File:** `src/EchoHub.Core/DTOs/ProfileDtos.cs`
> **Kind:** record
```csharp
public record AvatarUploadResponse(string AvatarAscii)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `AvatarAscii` | `string` | — |
AvatarUploadResponse is a tiny, immutable data container that represents the servers response to an avatar-upload operation. It carries a single payload, AvatarAscii, which holds the ASCII-art representation of the uploaded avatar. Use this type as a typed contract when returning avatar data from a service or API endpoint, rather than returning a raw string scattered through your responses.
## Remarks
This abstracted DTO isolates the avatar representation behind a named contract, making it easier to evolve the API (e.g., by adding metadata) without breaking call sites. The record semantics ensure value-based equality and straightforward deconstruction, which pairs well with serialization and testing.
## Example
```csharp
var resp = new AvatarUploadResponse("ASCII_ART");
Console.WriteLine(resp.AvatarAscii);
```
## Notes
- AvatarAscii may contain newline characters; ensure your JSON/HTTP layer preserves them.
- Keep the payload size reasonable; extremely large ASCII art can inflate responses.
- This type is a pure DTO with no behavior; avoid placing business logic here.
---
## UpdateProfileRequest
> **File:** `src/EchoHub.Core/DTOs/ProfileDtos.cs`
> **Kind:** record
```csharp
public record UpdateProfileRequest(
string? DisplayName = null,
string? Bio = null,
string? NicknameColor = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `DisplayName` | `string?` | `null` |
| `Bio` | `string?` | `null` |
| `NicknameColor` | `string?` | `null` |
UpdateProfileRequest is a data transfer object used when updating a user's profile. All fields are optional, enabling partial updates by supplying only the fields you want to change (DisplayName, Bio, or NicknameColor). This object is typically sent to a profile update endpoint or service, where the provided values are applied while unspecified fields remain unchanged.
## Remarks
By modeling the payload as a record with nullable properties, this abstraction communicates intent clearly: you're patching specific aspects of a profile rather than replacing it wholesale. It decouples API contract from the underlying domain model and reinforces immutability semantics for the request object. The combination of a concise DTO and nullable members makes it straightforward for clients to express partial updates without constructing separate patch types.
## Example
```csharp
// Update only the display name
var request1 = new UpdateProfileRequest(DisplayName: "Nova");
// Update multiple fields
var request2 = new UpdateProfileRequest(DisplayName: "Nova", Bio: "Software engineer", NicknameColor: "#1E90FF");
```
## Notes
- Omitted properties are treated as "no update" by the receiver; a null value may be interpreted differently depending on backend semantics.
- If you need to clear a value, verify the server's rules: null may not clear a field unless explicitly supported; you may need to provide an empty string or use a dedicated API path to clear a value.
---
## UpdateStatusRequest
> **File:** `src/EchoHub.Core/DTOs/ProfileDtos.cs`
> **Kind:** record
```csharp
public record UpdateStatusRequest(
UserStatus Status,
string? StatusMessage = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Status` | [`UserStatus`](../Models/UserStatus.cs.md) | — |
| `StatusMessage` | `string?` | `null` |
UpdateStatusRequest is a small, immutable data transfer object used to submit a user's status update. It bundles the new Status and, optionally, an accompanying StatusMessage to be processed by a profile update operation.
## Remarks
Being a C# 9 record, UpdateStatusRequest is immutable and supports value-based equality, which makes it reliable to pass across process boundaries and into tests. The Status is a required field that identifies the new user state via UserStatus, while StatusMessage provides optional context. This DTO participates in the profile update workflow and is typically serialized as part of requests to the profile service.
## Notes
- StatusMessage is nullable; if the receiver accepts no message, null can be sent and should be handled gracefully.
- Because UpdateStatusRequest is a record, you can create modified copies using the with expression, e.g. existing with { Status = newStatus } to preserve other fields.
---
## UserPresenceDto
> **File:** `src/EchoHub.Core/DTOs/ProfileDtos.cs`
> **Kind:** record
```csharp
public record UserPresenceDto(
string Username,
string? DisplayName,
string? NicknameColor,
UserStatus Status,
string? StatusMessage,
ServerRole Role,
bool IsIrc = false)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Username` | `string` | — |
| `DisplayName` | `string?` | — |
| `NicknameColor` | `string?` | — |
| `Status` | [`UserStatus`](../Models/UserStatus.cs.md) | — |
| `StatusMessage` | `string?` | — |
| `Role` | [`ServerRole`](../Models/ServerRole.cs.md) | — |
| `IsIrc` | `bool` | `false` |
Represents a single snapshot of a user's presence in EchoHub. This record aggregates the user's identity (Username and optional DisplayName), their current presence state (Status and optional StatusMessage), and their server role (Role). It also carries UI-related hints such as NicknameColor and an IsIrc flag indicating whether the presence originated from IRC. The type is a C# record with positional parameters, making it an immutable, value-based data object that is ideal for transport across API boundaries and for equality comparisons of presence data.
## Remarks
Consolidating identity, status, and role into one DTO reduces the number of cross-cutting data transfers required to render a user in a presence list or chat UI. The NicknameColor provides a presentation cue without forcing consumers to derive display styling; the IsIrc flag lets calling code distinguish between sources. As a record, instances compare by their values, enabling straightforward caching, deduplication, and change detection.
## Notes
- Nullable fields (DisplayName, NicknameColor, and StatusMessage) may be null; callers should handle nulls gracefully.
- IsIrc defaults to false; set to true when constructing from IRC-origin data.
- This is a positional-parameter record; properties are init-only and the object is immutable after construction; create a new instance to represent a changed presence.
---
## UserProfileDto
> **File:** `src/EchoHub.Core/DTOs/ProfileDtos.cs`
> **Kind:** record
```csharp
public record UserProfileDto(
Guid Id,
string Username,
string? DisplayName,
string? Bio,
string? NicknameColor,
string? AvatarAscii,
UserStatus Status,
string? StatusMessage,
ServerRole Role,
DateTimeOffset CreatedAt,
DateTimeOffset LastSeenAt)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Id` | `Guid` | — |
| `Username` | `string` | — |
| `DisplayName` | `string?` | — |
| `Bio` | `string?` | — |
| `NicknameColor` | `string?` | — |
| `AvatarAscii` | `string?` | — |
| `Status` | [`UserStatus`](../Models/UserStatus.cs.md) | — |
| `StatusMessage` | `string?` | — |
| `Role` | [`ServerRole`](../Models/ServerRole.cs.md) | — |
| `CreatedAt` | `DateTimeOffset` | — |
| `LastSeenAt` | `DateTimeOffset` | — |
Represents a compact, transport-friendly snapshot of a user's profile used across boundaries (e.g., API responses, UI layers). As a C# record, it provides value-based equality and immutability, ensuring a stable contract when serializing user data. It collects identity (Id, Username), optional display attributes (DisplayName, Bio, NicknameColor, AvatarAscii), current status (Status, StatusMessage), role (Role), and timestamp metadata (CreatedAt, LastSeenAt).
## Remarks
This DTO exists to decouple internal domain models from the data contract exposed to clients. By using a dedicated record, changes to the underlying domain models won't automatically ripple into API payloads. The explicit nullable fields model optional user attributes, and the timestamp fields communicate when the profile was created and last observed; consumers must handle time values robustly across time zones.
## Notes
- Nullable properties (DisplayName, Bio, NicknameColor, AvatarAscii, StatusMessage) may be null; handle accordingly in consumers.
- CreatedAt and LastSeenAt are DateTimeOffset values; when displaying, convert to a user-friendly timezone or use UTC representation as defined by the API contract.
---
@@ -0,0 +1,73 @@
# ServerDtos.cs
> **Source:** `src/EchoHub.Core/DTOs/ServerDtos.cs`
## Contents
- [EncryptionKeyResponse](#encryptionkeyresponse)
- [ServerStatusDto](#serverstatusdto)
---
## EncryptionKeyResponse
> **File:** `src/EchoHub.Core/DTOs/ServerDtos.cs`
> **Kind:** record
```csharp
public record EncryptionKeyResponse(string Key)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Key` | `string` | — |
EncryptionKeyResponse is a tiny, immutable data transfer object that carries a single encryption key via its Key property. Use it whenever a caller must receive an encryption key in a strongly-typed envelope (instead of returning a plain string) to improve clarity and compatibility with serialization and tooling.
## Remarks
By leveraging a C# record, EncryptionKeyResponse benefits from value-based equality, structural deconstruction, and concise construction. It serves as a semantic wrapper around the raw key, making intent explicit in APIs that issue or relay keys, and aligns with other DTOs in the EchoHub.Core DTOs layer.
## Notes
- The Key contains sensitive material; avoid logging or exposing it in request traces. Ensure transport channels are secure (TLS) and that only authorized callers can obtain the key.
- Because it is a simple wrapper, use it when a typed envelope adds value (e.g., API contracts or structured responses) and avoid over-modeling plain, ephemeral keys.
---
## ServerStatusDto
> **File:** `src/EchoHub.Core/DTOs/ServerDtos.cs`
> **Kind:** record
```csharp
public record ServerStatusDto(
string Name,
string? Description,
int OnlineUsers,
int TotalChannels,
string RegistrationMode = "open")
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Name` | `string` | — |
| `Description` | `string?` | — |
| `OnlineUsers` | `int` | — |
| `TotalChannels` | `int` | — |
| `RegistrationMode` | `string` | `"open"` |
ServerStatusDto is an immutable data-transfer object that represents the current status of a server in EchoHub. It exposes the server name, an optional description, the number of online users, the total number of channels, and a registration mode (defaulting to open). As a C# record with a primary constructor, it benefits from value-based equality and convenient deconstruction, making it a natural payload for API responses that describe the server's state.
## Remarks
A record provides value-based equality and immutability for a simple data carrier, which is exactly what a status payload is. The Description field is optional, so consumers must be prepared to handle null. The shape is designed to be serialized to JSON for API responses and easily deconstructed when mapping to other domain models.
## Notes
- Nullable Description means clients must handle nulls.
- RegistrationMode defaults to "open" when not supplied, preserving backward compatibility.
- As a record, two instances with identical property values compare equal (value equality).
---
@@ -0,0 +1,18 @@
# Attachment
> **File:** `src/EchoHub.Core/Models/Attachment.cs`
> **Kind:** class
```csharp
public class Attachment
```
Represents a file attached to a message, such as an image, audio, or document. A message may carry zero or more attachments alongside its text content (Discord-style).
## Remarks
Decouples attachment data from the message to allow independent storage and retrieval while keeping a lightweight reference to the owning message. The Url provides the relative download path (for example, /api/files/{fileId}) and FileName preserves the original filename. FileSize stores the stored blob size in bytes, which corresponds to ciphertext size when database encryption is enabled. AsciiPreview offers a rendered ASCII-art preview for images in color-tag format and is null for non-image attachments; it is stored encrypted-at-rest and, in end-to-end encrypted channels, remains room-encrypted.
## Notes
- AsciiPreview is only populated for image attachments; for other kinds of attachments it is null.
- The Message navigation property may be null if the related Message entity isn't loaded; use MessageId for persistence and rely on Message when the relationship is loaded.
@@ -0,0 +1,39 @@
# AttachmentKind
> **File:** `src/EchoHub.Core/Models/AttachmentKind.cs`
> **Kind:** enum
```csharp
public enum AttachmentKind
{
Image,
Audio,
File
}
```
AttachmentKind enumerates the possible types of a message attachment and signals how the client should render it. Use this enum when you know the specific attachment kind (image, audio, or file) so the UI can render an ASCII preview, a playback control, or a download option instead of a generic attachment rendering.
## Remarks
This enum centralizes the presentation logic for attachments and serves as a simple discriminator that decouples the attachment data from its rendering. By representing the modality with a single value, components can switch on kind to choose the appropriate UI affordance without inspecting the content payload. It helps maintain a clean separation between the data model (what the attachment is) and the presentation (how it should be shown).
## Example
```csharp
AttachmentKind kind = AttachmentKind.Image;
switch (kind)
{
case AttachmentKind.Image:
Console.WriteLine("Render as ASCII image preview");
break;
case AttachmentKind.Audio:
Console.WriteLine("Render with audio controls");
break;
case AttachmentKind.File:
Console.WriteLine("Render as downloadable file");
break;
}
```
## Notes
- If the enum is extended in the future, ensure all switch expressions include a default/fallback to handle unknown values gracefully.
@@ -0,0 +1,11 @@
# Channel
> **File:** `src/EchoHub.Core/Models/Channel.cs`
> **Kind:** class
```csharp
public class Channel
```
Represents a chat channel (room) within EchoHub's domain model. It stores the channel's identity, metadata for access control, an optional topic, and the collection of messages that belong to the channel, as well as an encryption envelope used for end-to-end security. Use this type to model a distinct conversation space that can be public or restricted, with the possibility of system-managed channels that are auto-created and not user-initiated. The class ties together the channel's identity (Id, Name), its description (Topic), its visibility (IsPublic) and authentication data (PasswordHash), its system-channel semantics (IsSystem), its client-managed encryption data (EncryptionSalt, WrappedRoomKey), creation auditing (CreatedAt, CreatedByUserId), and the message history (Messages).
@@ -0,0 +1,28 @@
# ChannelMembership
> **File:** `src/EchoHub.Core/Models/ChannelMembership.cs`
> **Kind:** class
```csharp
public class ChannelMembership
```
ChannelMembership is a lightweight data container that models the association between a user and a channel, recording when the user joined. It is intended for persistence and transport of membership data; instantiate and persist this model when recording channel participation rather than scattering ad-hoc data structures.
## Remarks
ChannelMembership encapsulates the many-to-many relationship between users and channels along with a join timestamp, enabling straightforward CRUD operations, serialization, and display of membership data. As a plain DTO, it contains no behavior beyond storage of UserId, ChannelId, and JoinedAt; it complements User and Channel entities by representing their linkage. The JoinedAt default is DateTimeOffset.UtcNow at construction, which is convenient for new memberships but should be overridden or preserved from storage when loading existing records.
## Example
```csharp
var membership = new ChannelMembership
{
UserId = Guid.NewGuid(),
ChannelId = Guid.NewGuid()
// JoinedAt defaults to DateTimeOffset.UtcNow
};
```
## Notes
- The default JoinedAt value applies only to newly created instances; deserialization from a data store will populate JoinedAt from the stored value.
- This class is a plain data holder with no validation or invariants; enforce domain rules at a higher layer when necessary.
@@ -0,0 +1,36 @@
# InviteCode
> **File:** `src/EchoHub.Core/Models/InviteCode.cs`
> **Kind:** class
```csharp
public class InviteCode
```
Represents a registration invitation code used to gate account creation when the server's registration mode is set to invite. An InviteCode captures the unique identifier, the actual code string, who created it, and when it was created, plus optional expiration and per-invite usage constraints. When a new REST or IRC account is created and the system is configured for invite-based registration, the incoming code must match an existing InviteCode that has not expired and that has remaining uses.
## Remarks
InviteCode acts as a persistence-side contract for invitation-based onboarding. It separates the concerns of registration gating from user data and provides a straightforward way to enforce expiration and single-use or limited-use policies at the data layer. The server's registration flow should consult these properties to validate a code before creating a new account and to record each use via UseCount, potentially preventing additional uses after MaxUses is reached.
## Example
```csharp
// Example usage: initialize a new invite code that will expire in 7 days and allow up to 5 uses
Guid adminUserId = Guid.NewGuid();
var invite = new InviteCode
{
Id = Guid.NewGuid(),
Code = "INVITE-2026-ACME",
CreatedByUserId = adminUserId,
CreatedByUsername = "admin",
CreatedAt = DateTimeOffset.UtcNow,
ExpiresAt = DateTimeOffset.UtcNow.AddDays(7),
MaxUses = 5,
UseCount = 0
};
```
## Notes
- Use of 'required' Code property ensures that a code value is provided when constructing instances; compile-time enforcement.
- ExpiresAt null means never expires; If ExpiresAt is not set, the code is perpetual.
- The class does not implement persistence or concurrency control; UseCount and MaxUses must be enforced by the application or data layer.
@@ -0,0 +1,14 @@
# Message
> **File:** `src/EchoHub.Core/Models/Message.cs`
> **Kind:** class
```csharp
public class Message
```
Message is the persistence model for a chat message in EchoHub, capturing who sent it, when, where, and what was said. Content is required text (which may be empty if the message carries only attachments), with an optional EmbedJson and a list of Attachments for attached files; SenderUserId/SenderUsername identify the author and ChannelId/Channel locate the conversation. Messages may reply to another message via ReplyToMessageId. It also includes legacy pre-attachments fields (Type, AttachmentUrl, AttachmentFileName, AttachmentFileSize) retained to support a one-time startup migration that folds old single-attachment messages into Attachments; new code never writes these and they are nulled after migration and not exposed in DTOs.
## Remarks
Architecturally, Message acts as the persistence model for chat messages, combining the modern Attachments collection with legacy fields retained to support a one-time startup data migration. New code never writes the legacy fields; they are nulled after migration and are not exposed in DTOs.
@@ -0,0 +1,24 @@
# MessageType
> **File:** `src/EchoHub.Core/Models/MessageType.cs`
> **Kind:** enum
```csharp
public enum MessageType
{
Text,
Image,
File,
Audio
}
```
Represents the category of a message in EchoHub. MessageType defines the four concrete payload kinds that a message can carry: Text, Image, File, or Audio. Use this enum whenever a component, data model, or API needs to convey which kind of content is attached to a message so consumers can handle, display, or validate it in a type-safe way instead of relying on strings or magic numbers.
## Remarks
Centralizes classification: this enum provides a single source of truth for message content kinds, enabling consistent routing, rendering, and validation across the system. It helps collaborators—models, serializers, and UI layers—make decisions based on content type without duplicating logic for string constants. By using an enum, you get compile-time checks and clearer intent.
## Notes
- When stored or transferred, the underlying value defaults to int (0-3) in the order shown; changing the sequence or renaming members may break persisted data.
- If external systems expect string representations, consider mapping to/from MessageType names to avoid breaking compatibility.
@@ -0,0 +1,14 @@
# RefreshToken
> **File:** `src/EchoHub.Core/Models/RefreshToken.cs`
> **Kind:** class
```csharp
public class RefreshToken
```
RefreshToken is a persistence model that represents a refresh token tied to a user. It stores a hashed token (TokenHash), the associated user via UserId, and validity information such as ExpiresAt and CreatedAt (which defaults to the current UTC time), plus an optional RevokedAt timestamp. It exposes IsExpired, IsRevoked, and IsActive to quickly assess the tokens state. A developer would create and persist these tokens when issuing refresh tokens in an authentication flow, check IsActive (or IsExpired/IsRevoked) when validating a refresh attempt, and use RevokedAt to mark a token as revoked.
## Remarks
This class serves as a persistence-facing token entity with a foreign key to User and a corresponding navigation property, enabling lifecycle management (creation, expiry, revocation) at the data layer while providing simple state checks for business logic.
@@ -0,0 +1,44 @@
# ServerRole
> **File:** `src/EchoHub.Core/Models/ServerRole.cs`
> **Kind:** enum
```csharp
public enum ServerRole
{
Member = 0,
Mod = 1,
Admin = 2,
Owner = 3
}
```
Represents the role assigned to a member within a server context in EchoHub. It defines four distinct levels of authority: Member, Mod (moderator), Admin, and Owner. Use this enum whenever you need to distinguish capabilities, gate UI or actions, or persist role information instead of relying on magic numbers.
## Remarks
By centralizing roles in a single enum, the codebase can map each role to its corresponding permissions in one place, enabling consistent authorization checks across services. The explicit integer values also support stable serialization and interop when persisting or transmitting role data, without forcing string-based representations.
## Example
```csharp
var role = ServerRole.Admin;
switch (role)
{
case ServerRole.Owner:
case ServerRole.Admin:
// elevated permissions
break;
case ServerRole.Mod:
// moderation tasks
break;
case ServerRole.Member:
// regular user actions
break;
}
Console.WriteLine($"User role: {role}"); // prints Owner, Admin, Mod, or Member
```
## Notes
- Do not treat ServerRole as a Flags enum; do not combine roles with bitwise operators.
- Prefer using the named constants in checks; avoid relying on numeric ordering for access decisions.
- Changing the underlying values (03) can affect serialized data; coordinate evolution across all consumers to preserve compatibility.
@@ -0,0 +1,20 @@
# ServerStatsReport
> **File:** `src/EchoHub.Core/Models/ServerStatsReport.cs`
> **Kind:** class
```csharp
public class ServerStatsReport
```
Represents a snapshot of server activity for a single reporting window, produced periodically by the stats-report background job. It captures timing data (PeriodStart, PeriodEnd, WindowHours, GeneratedAt) and per-window metrics (MessagesSent, FilesUploaded, BytesUploaded, NewMembers, ActiveMembers, Connections, Disconnections, Kicks, Bans) as well as end-of-window totals (TotalMembers, OnlineNow, PeakOnline) for persistence as pretty-printed JSON.
## Remarks
Serves as a stable, serializable container for periodic server activity, enabling dashboards and trend analyses to compare windows over time. By separating window semantics (start/end, duration) from generation time, it supports reliable aggregation and rhythm-based alerts when metrics diverge.
## Notes
- GeneratedAt is intended to equal PeriodEnd; ensure synchronization when populating the model. The default initializer uses DateTimeOffset.UtcNow, which may diverge if PeriodEnd is set to a different value.
## Dependencies
- DateTimeOffset (System) — used for all timestamp properties on the model.
@@ -0,0 +1,21 @@
# User
> **File:** `src/EchoHub.Core/Models/User.cs`
> **Kind:** class
```csharp
public class User
```
The User class is a domain model that represents a person using EchoHub, encapsulating identity (Id, Username, PasswordHash), profile details (DisplayName, Bio, NicknameColor, AvatarAscii), presence (Status, StatusMessage), role-based access (Role), moderation flags (IsMuted, MutedUntil, IsBanned), and auditing timestamps (CreatedAt, LastSeenAt). Username and PasswordHash are required to create a usable user, while other fields are optional to support rich profiles; defaults establish an online, member-facing user with current timestamps when a new instance is created.
## Remarks
This class serves as a central data container used across authentication, user management, presence rendering, and authorization checks. Its designed to be lightweight and serializable for persistence, while keeping domain concerns cohesive with a single user entity. The defaults for Status and Role, along with the auditing timestamps, provide a sensible initial state for newly created users.
## Notes
- The required fields (Username and PasswordHash) enforce that essential credentials are provided when constructing a user instance.
- PasswordHash should be treated as sensitive data; avoid exposing it in logs or API responses and ensure the persistence layer handles security appropriately.
- If hydrating from storage, ensure CreatedAt and LastSeenAt reflect the persisted values rather than new defaults.
@@ -0,0 +1,17 @@
# UserStatus
> **File:** `src/EchoHub.Core/Models/UserStatus.cs`
> **Kind:** enum
```csharp
public enum UserStatus
{
Online,
Away,
DoNotDisturb,
Invisible
}
```
Represents the current presence state of a user in EchoHub, used by UI presence indicators and presence logic throughout the app. Use Online when the user is connected and active, Away when the user is idle, DoNotDisturb to signal notifications should be minimized, and Invisible when the user should not appear online to others.
@@ -0,0 +1,38 @@
# RoomCrypto
> **File:** `src/EchoHub.Core/Security/RoomCrypto.cs`
> **Kind:** class
```csharp
public static class RoomCrypto
```
Client-side envelope encryption primitives used for end-to-end encrypted channels: derive per-room keys from a passphrase, generate random room content keys (RCKs), and encrypt/decrypt room content using AES-GCM. Use this class when you need a canonical, interoperable way to create room key material, wrap/unlock a room key with a passphrase-derived key, and produce/recognize the wire format used on the server ($RC1$base64(nonce||tag||ciphertext)).
## Remarks
This class encapsulates the protocol choices and low-level crypto work so callers don't compose PBKDF2, hex encoding, and AES-GCM themselves. It implements an envelope pattern: the client generates a random 256-bit room content key (RCK) to encrypt room data; the RCK is stored server-side wrapped (AES-GCM) with a key-encryption key (KEK) derived from the user's passphrase. PBKDF2-SHA256 with 210000 iterations produces 64 bytes: the first 32 bytes (returned as lowercase hex) are the auth key used as the join gate, and the final 32 bytes are the KEK (never sent). Re-wrapping the RCK on passphrase change avoids re-encrypting history.
## Example
```csharp
// Typical client flow:
// 1) Create room: generate salt and room key, derive keys from passphrase, wrap RCK and send auth key + wrapped blob to server.
var salt = RoomCrypto.GenerateSalt();
var roomKey = RoomCrypto.GenerateRoomKey();
var derived = RoomCrypto.DeriveKeys("correct horse battery staple", salt);
// derived.AuthKeyHex is sent to server as the join credential
// derived.KeyEncryptionKey (KEK) is used locally to wrap roomKey with AES-GCM (use EncryptBytes/EncryptText as appropriate)
// 2) Encrypt/decrypt room content with the room key
var plaintext = "hello room";
var ct = RoomCrypto.EncryptText(plaintext, roomKey);
if (RoomCrypto.IsRoomCiphertext(ct) && RoomCrypto.TryDecryptText(ct, roomKey, out var recovered))
{
// recovered == "hello room"
}
```
## Notes
- PBKDF2 parameters are fixed: 16-byte salt, 210000 iterations, 64-byte output; the auth key is returned as lowercase hex and the KEK as raw bytes.
- AES-GCM parameters are fixed: 12-byte nonce, 16-byte tag, 32-byte key (AES-256). Text wire format is the literal prefix "$RC1$" then base64(nonce||tag||ciphertext).
- TryDecryptText returns false for non-room ciphertext or when decryption/authentication fails (malformed base64, wrong key, or tampering). Protect KEK and RCK in memory and avoid persisting raw keys.
@@ -0,0 +1,25 @@
# AsciiBannerService
> **File:** `src/EchoHub.Core/Services/AsciiBannerService.cs`
> **Kind:** class
```csharp
public static class AsciiBannerService
```
Renders input text as a 5-row block-character banner (the /banner command). It uses a self-contained, hand-authored font defined in code, with no dependencies or network access, producing plain text content that can be transmitted like any other message; the renderer trims input to the maximum length and skips characters not defined in the font.
## Remarks
This symbol provides a deterministic, dependency-free banner renderer that can be used anywhere a compact ASCII-art label is desirable. The font is embedded in code as a glyph dictionary, so rendering is purely local and consistent across environments. Input is uppercased to match the glyph keys, glyphs are joined per row with a single space, and ink is rendered by replacing the '#' glyphs with the block character '█' and '.' with spaces; trailing spaces on each line are trimmed to minimize payload.
## Example
```csharp
string? banner = AsciiBannerService.Render("EchoHub");
if (banner != null)
Console.WriteLine(banner);
```
## Notes
- Non-renderable input (no supported characters) yields null; callers should handle null results to avoid printing empty banners.
- The method trims whitespace and enforces a maximum length of 20 characters; longer input is truncated before rendering.
@@ -0,0 +1,19 @@
# FileValidationHelper
> **File:** `src/EchoHub.Core/Services/FileValidationHelper.cs`
> **Kind:** class
```csharp
public static class FileValidationHelper
```
FileValidationHelper centralizes lightweight, stream-based validation for common image formats and audio file names. Its IsValidImage(Stream) method reads the stream header (without changing the stream's position) and recognizes JPEG, PNG, GIF, and WebP by their magic numbers, returning true for known formats and false otherwise. IsAudioFile(string) validates a file names extension against a predefined set of audio extensions in a case-insensitive manner. Together, these helpers let callers pre-filter content before attempting to decode or process media data.
## Remarks
This symbol provides a single, testable utility to detect supported media formats without pulling in a full decoder. By encapsulating the magic-number checks and the extension-based guard, it reduces duplication and concentrates format-coverage decisions in one place. It favors a fast, low-allocation validation path and leaves actual parsing to dedicated components.
## Notes
- Non-seekable streams cause IsValidImage to return false (the check stream.CanSeek is performed up-front).
- IsAudioFile relies solely on the file extension and does not inspect file contents.
- WebP detection requires a RIFF header followed by a WEBP tag at the expected offsets; malformed headers degrade gracefully to false.
@@ -0,0 +1,28 @@
# ImageToAsciiService
> **File:** `src/EchoHub.Core/Services/ImageToAsciiService.cs`
> **Kind:** class
```csharp
public class ImageToAsciiService
```
ImageToAsciiService is a lightweight utility that converts an input image stream into color-aware ASCII art by packing two vertical pixels into a single character cell using half-block characters and per-cell color tags. Use GetDimensions to pick a target resolution and ConvertToAscii when you need a textual, ASCII-only representation of an image for logs, chat, or environments without graphical support.
## Remarks
The class embodies a small, focused translation between raster images and ASCII art. It emits inline color tokens only when the color changes, preserving color fidelity while keeping the output readable in plain-text environments. The two-pixel vertical mapping (top pixel as the foreground color, bottom pixel as the background) enables higher-density representation than single-character ASCII, while remaining printable and parseable by consumers that understand the {F:...}{B:...}{X} tags. An even-height safeguard ensures the processing loop always handles complete pixel pairs, resizing the image as needed to maintain consistent output.
## Example
```csharp
using System.IO;
var stream = File.OpenRead("path/to/image.png");
var service = new ImageToAsciiService();
string ascii = service.ConvertToAscii(stream, 80, 40);
Console.WriteLine(ascii);
```
## Notes
- The ASCII output relies on the presence of the {F:RRGGBB}{B:RRGGBB}{X} tags and the block characters; ensure your rendering environment understands these tokens, otherwise you will see literal tags.
- If a height is provided as an odd number, the implementation advances to an even height, which may slightly alter the aspect ratio of the produced art.