Summary Update.
This commit is contained in:
@@ -2,11 +2,24 @@ using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `channel_message_edits` table. One row per historical version of
|
||||
/// an edited message — written by HandleEditMessage BEFORE overwriting the live row.
|
||||
///
|
||||
/// Encrypted with the channel AES key (same as ChannelMessages), so HandleGetEditHistory
|
||||
/// can decrypt + re-encrypt per requester.
|
||||
/// </summary>
|
||||
public class ChannelMessageEdits : Record
|
||||
{
|
||||
/// <summary>"channel_messages:abc" — which live message this version belonged to.</summary>
|
||||
public required string MessageId { get; set; }
|
||||
|
||||
/// <summary>Base64 AES-GCM ciphertext of the JSON-serialised previous ChatMessageContent.</summary>
|
||||
public required string CipherText { get; set; }
|
||||
|
||||
public required string Nonce { get; set; }
|
||||
public required string Tag { get; set; }
|
||||
|
||||
/// <summary>When this version was the current text (i.e. when it was replaced).</summary>
|
||||
public required DateTime EditedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,14 +2,36 @@ using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `channel_messages` table. One row per message.
|
||||
///
|
||||
/// Encryption: CipherText/Nonce/Tag use the channel AES key (ChannelDbKey), NOT any user's
|
||||
/// RSA keypair. This means the server can decrypt for history queries; the per-recipient
|
||||
/// RSA wrapping happens at delivery time in DeliverToServerMembers.
|
||||
/// </summary>
|
||||
public class ChannelMessages : Record
|
||||
{
|
||||
/// <summary>"channels:xyz" — which channel this belongs to.</summary>
|
||||
public required string ChannelId { get; set; }
|
||||
|
||||
/// <summary>"users:keeper317" — who wrote it. Lowercased to match CoreClientService's id format.</summary>
|
||||
public required string SenderUserId { get; set; }
|
||||
|
||||
/// <summary>Base64 AES-GCM ciphertext of the JSON-serialised ChatMessageContent.</summary>
|
||||
public required string CipherText { get; set; }
|
||||
|
||||
/// <summary>Base64 AES-GCM 96-bit nonce. Different every message.</summary>
|
||||
public required string Nonce { get; set; }
|
||||
|
||||
/// <summary>Base64 AES-GCM 128-bit authentication tag.</summary>
|
||||
public required string Tag { get; set; }
|
||||
|
||||
/// <summary>UTC timestamp of original send. Drives history ordering.</summary>
|
||||
public required DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>UTC timestamp of last edit. Null = never edited. Drives the (edited) bubble footer.</summary>
|
||||
public DateTime? EditedAt { get; set; }
|
||||
|
||||
/// <summary>Soft-delete flag. Tombstones in history responses; bubbles show "deleted" placeholder.</summary>
|
||||
public bool IsDeleted { get; set; }
|
||||
}
|
||||
|
||||
@@ -3,13 +3,38 @@ using RelayShared.Services;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `channels` table. One row per channel.
|
||||
///
|
||||
/// Lifecycle: created by HandleCreateChannel (or seeded by ServerBootstrapService at boot).
|
||||
/// Soft-deleted by HandleDeleteChannel (IsDeleted flipped, row stays for audit).
|
||||
/// </summary>
|
||||
public class Channels : Record
|
||||
{
|
||||
/// <summary>Sidebar display name. Lowercased and dash-separated for new channels.</summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>Creation timestamp. Drives sidebar sort order.</summary>
|
||||
public required DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>Drives client rendering and server routing — Text/Voice/File/Forum/Stage.</summary>
|
||||
public ChannelType Type { get; set; } = ChannelType.Text;
|
||||
|
||||
/// <summary>Sidebar category header (e.g. "General"). Empty means default group.</summary>
|
||||
public string Group { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// True for announcement-style channels (#welcome, #files). Non-admins are blocked from
|
||||
/// posting via PermissionService.CanSendMessagesAsync.
|
||||
/// </summary>
|
||||
public bool IsReadOnly { get; set; }
|
||||
|
||||
/// <summary>Soft-delete flag. Filtered out of channel-list builds in BuildChannelListForUser.</summary>
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record id of a File channel ("channels:xyz"). When set, ChatSocketBehavior's
|
||||
/// MirrorAttachmentIfNeeded auto-copies non-gif attachments into the linked channel.
|
||||
/// </summary>
|
||||
public string? LinkedFileChannelId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
using SurrealDb.Net.Models;
|
||||
using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `client_public_keys` table. Stores the RSA public key each user
|
||||
/// has registered. Written by HandleRegisterKey, read by DeliverToServerMembers and history
|
||||
/// fetches to encrypt outbound messages per recipient.
|
||||
///
|
||||
/// When a client reinstalls and regenerates a keypair, the existing row is updated rather
|
||||
/// than duplicated (ClientKeyService.RegisterOrUpdateKeyAsync).
|
||||
/// </summary>
|
||||
public class ClientPublicKeys : Record
|
||||
{
|
||||
/// <summary>Mixed-case username as the user registered it. Used as the lookup key.</summary>
|
||||
public required string Username { get; set; }
|
||||
|
||||
/// <summary>Base64 SubjectPublicKeyInfo (DER) of the user's RSA public key.</summary>
|
||||
public required string PublicKey { get; set; }
|
||||
|
||||
/// <summary>When the user first registered.</summary>
|
||||
public required DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>When the key was last updated (key rotation, reinstall).</summary>
|
||||
public required DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,28 @@ using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `server_encryption_keys` table. Stores both:
|
||||
/// - The server's RSA keypair (for receiving encrypted client→server payloads).
|
||||
/// - The single AES-256 key used to encrypt channel_messages at rest.
|
||||
///
|
||||
/// Generated once on first boot by ServerBootstrapService. Loaded into static fields on
|
||||
/// ChatSocketBehavior at boot so handlers can use them without a DB round-trip.
|
||||
/// </summary>
|
||||
public class ServerEncryptionKeys : Record
|
||||
{
|
||||
/// <summary>Base64 AES-256 key used by ChannelCryptoService for at-rest message encryption.</summary>
|
||||
public required string KeyBase64 { get; set; }
|
||||
|
||||
/// <summary>Base64 SubjectPublicKeyInfo of the server's RSA public key. Sent to clients on GetServerKey.</summary>
|
||||
public required string PublicKey { get; set; }
|
||||
|
||||
/// <summary>Base64 PKCS8 of the server's RSA private key. Never leaves the server.</summary>
|
||||
public required string PrivateKey { get; set; }
|
||||
|
||||
/// <summary>When the keys were generated.</summary>
|
||||
public required DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>When the keys were last rotated. Currently same as CreatedAt — rotation isn't implemented.</summary>
|
||||
public required DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,24 @@ using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `channel_permissions` table. Per-(channel, role) override of a
|
||||
/// role's base permissions.
|
||||
///
|
||||
/// Allow and Deny are independent masks (NOT a tri-state). Deny wins over Allow when both
|
||||
/// have the same flag set. Bits not set in either fall through to the role's base permissions.
|
||||
/// </summary>
|
||||
public class ChannelPermissions : Record
|
||||
{
|
||||
/// <summary>"channels:xyz" — which channel this override applies in.</summary>
|
||||
public required string ChannelId { get; set; }
|
||||
|
||||
/// <summary>"roles:abc" — which role this override applies to.</summary>
|
||||
public required string RoleId { get; set; }
|
||||
|
||||
/// <summary>Permissions explicitly granted here (overrides "role doesn't have it" for this channel).</summary>
|
||||
public PermissionFlags Allow { get; set; }
|
||||
|
||||
/// <summary>Permissions explicitly denied here. Wins over Allow.</summary>
|
||||
public PermissionFlags Deny { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,6 +2,18 @@ using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// The permission bitfield. The whole permission model is just:
|
||||
///
|
||||
/// ServerMembers.IsOwner = true → unconditional Administrator
|
||||
/// roles.Permissions has Administrator flag → unconditional everything
|
||||
/// channel_permissions.Deny has a specific flag → that permission denied here
|
||||
/// channel_permissions.Allow has a specific flag → that permission allowed here
|
||||
/// roles.Permissions has the flag → fallback (channel-independent)
|
||||
///
|
||||
/// PermissionService.HasPermissionAsync walks that ladder in order. See that class for the
|
||||
/// authoritative implementation.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PermissionFlags
|
||||
{
|
||||
@@ -18,11 +30,21 @@ public enum PermissionFlags
|
||||
DeleteChannel = 1 << 9 // Delete a channel
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `roles` table. Defines a named permission bundle that can be
|
||||
/// assigned to users via UserRoles.
|
||||
/// </summary>
|
||||
public class Roles : Record
|
||||
{
|
||||
/// <summary>Display name ("Admin", "Moderator", "Member").</summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>Base permission bitfield. Channel-level overrides in ChannelPermissions can add or remove.</summary>
|
||||
public required PermissionFlags Permissions { get; set; }
|
||||
|
||||
/// <summary>When the role was seeded.</summary>
|
||||
public required DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>Tie-breaker for future multi-role-per-user scenarios. Lower = higher priority. Not used by the current ladder.</summary>
|
||||
public int Priority { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,9 +2,22 @@ using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `server_members` table. Membership list.
|
||||
/// Drives DeliverToServerMembers (the fan-out target list for every chat message) and the
|
||||
/// authoritative ownership flag for PermissionService.
|
||||
/// </summary>
|
||||
public class ServerMembers : Record
|
||||
{
|
||||
/// <summary>"users:keeper317" — references the Core users table by name convention.</summary>
|
||||
public required string UserId { get; set; }
|
||||
|
||||
/// <summary>When the user was added to this server.</summary>
|
||||
public required DateTime JoinedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Authoritative owner flag. Owner gets unconditional Administrator via
|
||||
/// PermissionService.IsServerOwnerAsync, independent of role assignments.
|
||||
/// </summary>
|
||||
public bool IsOwner { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,18 @@ using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `servers` table. Currently single-row (one server per deployment),
|
||||
/// but the schema supports multi-server in the future.
|
||||
/// </summary>
|
||||
public class Servers : Record
|
||||
{
|
||||
/// <summary>Display name (currently "Test Server" from bootstrap).</summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>"users:keeper317" — the owner. Mirrored as IsOwner=true on the matching ServerMembers row.</summary>
|
||||
public required string OwnerUserId { get; set; }
|
||||
|
||||
/// <summary>Server creation timestamp.</summary>
|
||||
public required DateTime CreatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,21 @@ using SurrealDb.Net.Models;
|
||||
|
||||
namespace RelayServer.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Surreal record for the `user_roles` table. Join table linking users to roles.
|
||||
///
|
||||
/// Invariant: ServerBootstrapService.SetUserRoleAsync guarantees exactly one row per user.
|
||||
/// Multi-role-per-user isn't currently supported by the permission ladder — adding it would
|
||||
/// just be a matter of removing the bootstrap's "delete stale rows" step.
|
||||
/// </summary>
|
||||
public class UserRoles : Record
|
||||
{
|
||||
/// <summary>"users:keeper317" — the assignee.</summary>
|
||||
public required string UserId { get; set; }
|
||||
|
||||
/// <summary>"roles:abc" — the role being granted.</summary>
|
||||
public required string RoleId { get; set; }
|
||||
|
||||
/// <summary>When the assignment was made.</summary>
|
||||
public required DateTime AssignedAt { get; set; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user