diff --git a/SharpChat/C2SPacketHandlers/AuthC2SPacketHandler.cs b/SharpChat/C2SPacketHandlers/AuthC2SPacketHandler.cs
index 4199060..e9369ee 100644
--- a/SharpChat/C2SPacketHandlers/AuthC2SPacketHandler.cs
+++ b/SharpChat/C2SPacketHandlers/AuthC2SPacketHandler.cs
@@ -1,5 +1,6 @@
 using SharpChat.Auth;
 using SharpChat.Bans;
+using SharpChat.Channels;
 using SharpChat.Configuration;
 using SharpChat.SockChat.S2CPackets;
 
@@ -8,14 +9,10 @@ namespace SharpChat.C2SPacketHandlers;
 public class AuthC2SPacketHandler(
     AuthClient authClient,
     BansClient bansClient,
-    Channel defaultChannel,
+    ChannelsContext channelsCtx,
     CachedValue<int> maxMsgLength,
     CachedValue<int> maxConns
 ) : C2SPacketHandler {
-    private readonly Channel DefaultChannel = defaultChannel ?? throw new ArgumentNullException(nameof(defaultChannel));
-    private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
-    private readonly CachedValue<int> MaxConnections = maxConns ?? throw new ArgumentNullException(nameof(maxConns));
-
     public bool IsMatch(C2SPacketHandlerContext ctx) {
         return ctx.CheckPacketId("1");
     }
@@ -75,7 +72,7 @@ public class AuthC2SPacketHandler(
                     );
 
                 // Enforce a maximum amount of connections per user
-                if(ctx.Chat.Connections.Count(conn => conn.User == user) >= MaxConnections) {
+                if(ctx.Chat.Connections.Count(conn => conn.User == user) >= maxConns) {
                     await ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.MaxSessions));
                     ctx.Connection.Dispose();
                     return;
@@ -93,7 +90,7 @@ public class AuthC2SPacketHandler(
                         await ctx.Connection.Send(new CommandResponseS2CPacket(0, LCR.WELCOME, false, line));
                 }
 
-                await ctx.Chat.HandleJoin(user, DefaultChannel, ctx.Connection, MaxMessageLength);
+                await ctx.Chat.HandleJoin(user, channelsCtx.DefaultChannel, ctx.Connection, maxMsgLength);
             } finally {
                 ctx.Chat.ContextAccess.Release();
             }
diff --git a/SharpChat/C2SPacketHandlers/SendMessageC2SPacketHandler.cs b/SharpChat/C2SPacketHandlers/SendMessageC2SPacketHandler.cs
index 234cde1..d757ecb 100644
--- a/SharpChat/C2SPacketHandlers/SendMessageC2SPacketHandler.cs
+++ b/SharpChat/C2SPacketHandlers/SendMessageC2SPacketHandler.cs
@@ -1,3 +1,4 @@
+using SharpChat.Channels;
 using SharpChat.Configuration;
 using SharpChat.Events;
 using SharpChat.Snowflake;
diff --git a/SharpChat/ClientCommandContext.cs b/SharpChat/ClientCommandContext.cs
index d53a9ba..3d58037 100644
--- a/SharpChat/ClientCommandContext.cs
+++ b/SharpChat/ClientCommandContext.cs
@@ -1,4 +1,6 @@
-namespace SharpChat;
+using SharpChat.Channels;
+
+namespace SharpChat;
 
 public class ClientCommandContext {
     public string Name { get; }
diff --git a/SharpChat/ClientCommands/CreateChannelClientCommand.cs b/SharpChat/ClientCommands/CreateChannelClientCommand.cs
index 03de911..d13cdba 100644
--- a/SharpChat/ClientCommands/CreateChannelClientCommand.cs
+++ b/SharpChat/ClientCommands/CreateChannelClientCommand.cs
@@ -1,3 +1,4 @@
+using SharpChat.Channels;
 using SharpChat.SockChat.S2CPackets;
 
 namespace SharpChat.ClientCommands;
@@ -35,28 +36,23 @@ public class CreateChannelClientCommand : ClientCommand {
 
         string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
 
-        if(!Channel.CheckName(createChanName)) {
+        try {
+            Channel channel = ctx.Chat.Channels.CreateChannel(
+                createChanName,
+                temporary: !ctx.User.Permissions.HasFlag(UserPermissions.SetChannelPermanent),
+                rank: createChanHierarchy,
+                ownerId: ctx.User.UserId
+            );
+
+            foreach(User ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
+                await ctx.Chat.SendTo(ccu, new ChannelCreateS2CPacket(channel.Name, channel.HasPassword, channel.IsTemporary));
+
+            await ctx.Chat.SwitchChannel(ctx.User, channel, channel.Password);
+            await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_CREATED, false, channel.Name));
+        } catch(ChannelNameFormatException) {
             await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NAME_INVALID));
-            return;
-        }
-
-        if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) {
+        } catch(ChannelExistsException) {
             await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_ALREADY_EXISTS, true, createChanName));
-            return;
         }
-
-        Channel createChan = new(
-            createChanName,
-            isTemporary: !ctx.User.Permissions.HasFlag(UserPermissions.SetChannelPermanent),
-            rank: createChanHierarchy,
-            ownerId: ctx.User.UserId
-        );
-
-        ctx.Chat.Channels.Add(createChan);
-        foreach(User ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
-            await ctx.Chat.SendTo(ccu, new ChannelCreateS2CPacket(createChan.Name, createChan.HasPassword, createChan.IsTemporary));
-
-        await ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
-        await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_CREATED, false, createChan.Name));
     }
 }
diff --git a/SharpChat/ClientCommands/DeleteChannelClientCommand.cs b/SharpChat/ClientCommands/DeleteChannelClientCommand.cs
index 68f2c83..3c7aed4 100644
--- a/SharpChat/ClientCommands/DeleteChannelClientCommand.cs
+++ b/SharpChat/ClientCommands/DeleteChannelClientCommand.cs
@@ -1,3 +1,4 @@
+using SharpChat.Channels;
 using SharpChat.SockChat.S2CPackets;
 
 namespace SharpChat.ClientCommands;
@@ -19,7 +20,7 @@ public class DeleteChannelClientCommand : ClientCommand {
         }
 
         string delChanName = string.Join('_', ctx.Args);
-        Channel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
+        Channel? delChan = ctx.Chat.Channels.GetChannel(delChanName);
 
         if(delChan == null) {
             await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, delChanName));
diff --git a/SharpChat/ClientCommands/JoinChannelClientCommand.cs b/SharpChat/ClientCommands/JoinChannelClientCommand.cs
index d2b9364..3a05b13 100644
--- a/SharpChat/ClientCommands/JoinChannelClientCommand.cs
+++ b/SharpChat/ClientCommands/JoinChannelClientCommand.cs
@@ -1,3 +1,4 @@
+using SharpChat.Channels;
 using SharpChat.SockChat.S2CPackets;
 
 namespace SharpChat.ClientCommands;
@@ -10,7 +11,7 @@ public class JoinChannelClientCommand : ClientCommand {
     public async Task Dispatch(ClientCommandContext ctx) {
         long msgId = ctx.Chat.RandomSnowflake.Next();
         string joinChanStr = ctx.Args.FirstOrDefault() ?? "Channel";
-        Channel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
+        Channel? joinChan = ctx.Chat.Channels.GetChannel(joinChanStr);
 
         if(joinChan is null) {
             await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, joinChanStr));
diff --git a/SharpChat/ClientCommands/RankChannelClientCommand.cs b/SharpChat/ClientCommands/RankChannelClientCommand.cs
index fa98c88..5f51644 100644
--- a/SharpChat/ClientCommands/RankChannelClientCommand.cs
+++ b/SharpChat/ClientCommands/RankChannelClientCommand.cs
@@ -22,7 +22,7 @@ public class RankChannelClientCommand : ClientCommand {
             return;
         }
 
-        await ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy);
+        await ctx.Chat.UpdateChannel(ctx.Channel, rank: chanHierarchy);
         await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_HIERARCHY_CHANGED, false));
     }
 }
diff --git a/SharpChat/ClientCommands/WhoClientCommand.cs b/SharpChat/ClientCommands/WhoClientCommand.cs
index c60f6dd..d037ce9 100644
--- a/SharpChat/ClientCommands/WhoClientCommand.cs
+++ b/SharpChat/ClientCommands/WhoClientCommand.cs
@@ -1,3 +1,4 @@
+using SharpChat.Channels;
 using SharpChat.SockChat.S2CPackets;
 using System.Text;
 
@@ -30,7 +31,7 @@ public class WhoClientCommand : ClientCommand {
 
             await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_SERVER, false, whoChanSB));
         } else {
-            Channel? whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr));
+            Channel? whoChan = ctx.Chat.Channels.GetChannel(whoChanStr);
 
             if(whoChan is null) {
                 await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
diff --git a/SharpChat/Context.cs b/SharpChat/Context.cs
index 3354cb9..423a7f8 100644
--- a/SharpChat/Context.cs
+++ b/SharpChat/Context.cs
@@ -1,3 +1,4 @@
+using SharpChat.Channels;
 using SharpChat.Events;
 using SharpChat.Messages;
 using SharpChat.Snowflake;
@@ -15,7 +16,7 @@ public class Context {
     public SnowflakeGenerator SnowflakeGenerator { get; } = new();
     public RandomSnowflake RandomSnowflake { get; }
 
-    public HashSet<Channel> Channels { get; } = [];
+    public ChannelsContext Channels { get; } = new();
     public HashSet<Connection> Connections { get; } = [];
     public HashSet<User> Users { get; } = [];
     public MessageStorage Messages { get; }
@@ -59,7 +60,7 @@ public class Context {
                         true
                     ));
             } else {
-                Channel? channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
+                Channel? channel = Channels.GetChannel(mce.ChannelName);
                 if(channel is not null)
                     await SendTo(channel, new ChatMessageAddS2CPacket(
                         mce.MessageId,
@@ -118,8 +119,7 @@ public class Context {
     }
 
     public Channel[] GetUserChannels(User user) {
-        string[] names = GetUserChannelNames(user);
-        return [.. Channels.Where(c => names.Any(n => c.NameEquals(n)))];
+        return [.. Channels.GetChannels(GetUserChannelNames(user))];
     }
 
     public string[] GetChannelUserIds(Channel channel) {
@@ -241,7 +241,7 @@ public class Context {
             await conn.Send(new ContextMessageS2CPacket(msg));
 
         await conn.Send(new ContextChannelsS2CPacket(
-            Channels.Where(c => c.Rank <= user.Rank)
+            Channels.GetChannels(user.Rank)
                 .Select(c => new ContextChannelsS2CPacket.Entry(c.Name, c.HasPassword, c.IsTemporary))
         ));
 
@@ -294,9 +294,6 @@ public class Context {
     }
 
     public async Task ForceChannelSwitch(User user, Channel chan) {
-        if(!Channels.Any(c => c.NameEquals(chan.Name)))
-            return;
-
         Channel oldChan = UserLastChannel[user.UserId];
 
         long leaveId = RandomSnowflake.Next();
@@ -354,7 +351,7 @@ public class Context {
     }
 
     public async Task SendToUserChannels(User user, S2CPacket packet) {
-        IEnumerable<Channel> chans = Channels.Where(c => IsInChannel(user, c));
+        IEnumerable<Channel> chans = Channels.GetChannels(c => IsInChannel(user, c));
         IEnumerable<Connection> conns = Connections.Where(conn => conn.IsAlive && conn.User is not null && ChannelUsers.Any(cu => cu.UserId == conn.User.UserId && chans.Any(chan => chan.NameEquals(cu.ChannelName))));
         foreach(Connection conn in conns)
             await conn.Send(packet);
@@ -373,18 +370,18 @@ public class Context {
         await SendTo(user, new UserChannelForceJoinS2CPacket(chan.Name));
     }
 
-    public async Task UpdateChannel(Channel channel, bool? temporary = null, int? hierarchy = null, string? password = null) {
-        if(!Channels.Any(c => c.NameEquals(channel.Name)))
-            throw new ArgumentException("Provided channel is not registered with this manager.", nameof(channel));
-
-        if(temporary.HasValue)
-            channel.IsTemporary = temporary.Value;
-
-        if(hierarchy.HasValue)
-            channel.Rank = hierarchy.Value;
-
-        if(password != null)
-            channel.Password = password;
+    public async Task UpdateChannel(
+        Channel channel,
+        bool? temporary = null,
+        int? rank = null,
+        string? password = null
+    ) {
+        Channels.UpdateChannel(
+            channel,
+            temporary: temporary,
+            rank: rank,
+            password: password
+        );
 
         // TODO: Users that no longer have access to the channel/gained access to the channel by the hierarchy change should receive delete and create packets respectively
         foreach(User user in Users.Where(u => u.Rank >= channel.Rank))
@@ -392,20 +389,13 @@ public class Context {
     }
 
     public async Task RemoveChannel(Channel channel) {
-        if(channel == null || Channels.Count < 1)
-            return;
-
-        Channel? defaultChannel = Channels.FirstOrDefault();
-        if(defaultChannel is null)
-            return;
-
         // Remove channel from the listing
-        Channels.Remove(channel);
+        Channels.RemoveChannel(channel.Name);
 
         // Move all users back to the main channel
         // TODO: Replace this with a kick. SCv2 supports being in 0 channels, SCv1 should force the user back to DefaultChannel.
         foreach(User user in GetChannelUsers(channel))
-            await SwitchChannel(user, defaultChannel, string.Empty);
+            await SwitchChannel(user, Channels.DefaultChannel, string.Empty);
 
         // Broadcast deletion of channel
         foreach(User user in Users.Where(u => u.Rank >= channel.Rank))
diff --git a/SharpChat/SockChatServer.cs b/SharpChat/SockChatServer.cs
index 11b75ae..3e9b6de 100644
--- a/SharpChat/SockChatServer.cs
+++ b/SharpChat/SockChatServer.cs
@@ -2,6 +2,7 @@ using Fleck;
 using SharpChat.Auth;
 using SharpChat.Bans;
 using SharpChat.C2SPacketHandlers;
+using SharpChat.Channels;
 using SharpChat.ClientCommands;
 using SharpChat.Configuration;
 using SharpChat.Messages;
@@ -35,8 +36,6 @@ public class SockChatServer : IDisposable {
 
     private static readonly string[] DEFAULT_CHANNELS = ["lounge"];
 
-    private Channel DefaultChannel { get; set; }
-
     public SockChatServer(
         AuthClient authClient,
         BansClient bansClient,
@@ -63,20 +62,14 @@ public class SockChatServer : IDisposable {
                 if(string.IsNullOrWhiteSpace(name))
                     name = channelName;
 
-                Channel channelInfo = new(
+                Context.Channels.CreateChannel(
                     name,
                     channelCfg.SafeReadValue("password", string.Empty)!,
                     rank: channelCfg.SafeReadValue("minRank", 0)
                 );
-
-                Context.Channels.Add(channelInfo);
-                DefaultChannel ??= channelInfo;
             }
 
-        if(DefaultChannel is null)
-            throw new Exception("The default channel could not be determined.");
-
-        GuestHandlers.Add(new AuthC2SPacketHandler(authClient, bansClient, DefaultChannel, MaxMessageLength, MaxConnections));
+        GuestHandlers.Add(new AuthC2SPacketHandler(authClient, bansClient, Context.Channels, MaxMessageLength, MaxConnections));
 
         AuthedHandlers.AddRange([
             new PingC2SPacketHandler(authClient),
diff --git a/SharpChat/Channel.cs b/SharpChatCommon/Channels/Channel.cs
similarity index 68%
rename from SharpChat/Channel.cs
rename to SharpChatCommon/Channels/Channel.cs
index cfdd371..56bf40a 100644
--- a/SharpChat/Channel.cs
+++ b/SharpChatCommon/Channels/Channel.cs
@@ -1,4 +1,4 @@
-namespace SharpChat;
+namespace SharpChat.Channels;
 
 public class Channel(
     string name,
@@ -7,15 +7,18 @@ public class Channel(
     int rank = 0,
     string ownerId = ""
 ) {
-    public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name));
-    public string Password { get; set; } = password ?? string.Empty;
-    public bool IsTemporary { get; set; } = isTemporary;
-    public int Rank { get; set; } = rank;
-    public string OwnerId { get; set; } = ownerId;
+    public string Name { get; internal set; } = name;
+    public string Password { get; internal set; } = password ?? string.Empty;
+    public bool IsTemporary { get; internal set; } = isTemporary;
+    public int Rank { get; internal set; } = rank;
+    public string OwnerId { get; internal set; } = ownerId;
 
     public bool HasPassword
         => !string.IsNullOrWhiteSpace(Password);
 
+    public bool IsPublic
+        => !HasPassword && Rank < 1;
+
     public bool NameEquals(string name) {
         return string.Equals(name, Name, StringComparison.InvariantCultureIgnoreCase);
     }
diff --git a/SharpChatCommon/Channels/ChannelExistsException.cs b/SharpChatCommon/Channels/ChannelExistsException.cs
new file mode 100644
index 0000000..751b2ba
--- /dev/null
+++ b/SharpChatCommon/Channels/ChannelExistsException.cs
@@ -0,0 +1,4 @@
+namespace SharpChat.Channels;
+
+public class ChannelExistsException(string argName)
+    : ArgumentException("A channel with that name already exists.", argName) {}
diff --git a/SharpChatCommon/Channels/ChannelIsDefaultException.cs b/SharpChatCommon/Channels/ChannelIsDefaultException.cs
new file mode 100644
index 0000000..6a6294d
--- /dev/null
+++ b/SharpChatCommon/Channels/ChannelIsDefaultException.cs
@@ -0,0 +1,4 @@
+namespace SharpChat.Channels;
+
+public class ChannelIsDefaultException(string argName)
+    : ArgumentException("You cannot delete the default channel.", argName) {}
diff --git a/SharpChatCommon/Channels/ChannelNameFormatException.cs b/SharpChatCommon/Channels/ChannelNameFormatException.cs
new file mode 100644
index 0000000..f29cae9
--- /dev/null
+++ b/SharpChatCommon/Channels/ChannelNameFormatException.cs
@@ -0,0 +1,4 @@
+namespace SharpChat.Channels;
+
+public class ChannelNameFormatException(string argName)
+    : ArgumentException("Channel name contains unsupported characters.", argName) { }
diff --git a/SharpChatCommon/Channels/ChannelNotFoundException.cs b/SharpChatCommon/Channels/ChannelNotFoundException.cs
new file mode 100644
index 0000000..0c8a818
--- /dev/null
+++ b/SharpChatCommon/Channels/ChannelNotFoundException.cs
@@ -0,0 +1,4 @@
+namespace SharpChat.Channels;
+
+public class ChannelNotFoundException(string argName)
+    : ArgumentException("No channel with that name exists.", argName) {}
diff --git a/SharpChatCommon/Channels/ChannelsContext.cs b/SharpChatCommon/Channels/ChannelsContext.cs
new file mode 100644
index 0000000..ac0b7c6
--- /dev/null
+++ b/SharpChatCommon/Channels/ChannelsContext.cs
@@ -0,0 +1,126 @@
+namespace SharpChat.Channels;
+
+public class ChannelsContext {
+    private readonly List<Channel> Channels = [];
+
+    public int Count => Channels.Count;
+
+    private Channel? DefaultChannelValue;
+    public Channel DefaultChannel {
+        get {
+            if(DefaultChannelValue is not null) {
+                if(Channels.Contains(DefaultChannelValue))
+                    return DefaultChannelValue;
+
+                DefaultChannelValue = null;
+            }
+
+            return GetChannel(c => c.IsPublic && !c.IsTemporary) ?? throw new NoDefaultChannelException();
+        }
+
+        set => DefaultChannelValue = value;
+    }
+
+    public bool ChannelExists(Func<Channel, bool> predicate) {
+        return Channels.Any(predicate);
+    }
+
+    public bool ChannelExists(string name) {
+        return ChannelExists(c => c.NameEquals(name));
+    }
+
+    public Channel? GetChannel(Func<Channel, bool> predicate) {
+        return Channels.FirstOrDefault(predicate);
+    }
+
+    public Channel? GetChannel(string name) {
+        return GetChannel(c => c.NameEquals(name));
+    }
+
+    public IEnumerable<Channel> GetChannels(Func<Channel, bool> predicate) {
+        return Channels.Where(predicate);
+    }
+
+    public IEnumerable<Channel> GetChannels(IEnumerable<string> names) {
+        return GetChannels(c => names.Any(n => c.NameEquals(n)));
+    }
+
+    public IEnumerable<Channel> GetChannels(int maxRank) {
+        return GetChannels(c => c.Rank <= maxRank);
+    }
+
+    public Channel CreateChannel(
+        string name,
+        string password = "",
+        bool temporary = false,
+        int rank = 0,
+        string ownerId = ""
+    ) {
+        if(!Channel.CheckName(name))
+            throw new ChannelNameFormatException(nameof(name));
+        if(ChannelExists(name))
+            throw new ChannelExistsException(nameof(name));
+
+        Channel channel = new(
+            name,
+            password ?? string.Empty,
+            temporary,
+            rank,
+            ownerId ?? string.Empty
+        );
+
+        Channels.Add(channel);
+
+        return channel;
+    }
+
+    public Channel UpdateChannel(
+        Channel channel,
+        string? name = null,
+        string? password = null,
+        bool? temporary = null,
+        int? rank = null,
+        string? ownerId = null
+    ) => UpdateChannel(channel.Name, name, password, temporary, rank, ownerId);
+
+    public Channel UpdateChannel(
+        string currentName,
+        string? name = null,
+        string? password = null,
+        bool? temporary = null,
+        int? rank = null,
+        string? ownerId = null
+    ) {
+        Channel channel = GetChannel(currentName) ?? throw new ChannelNotFoundException(nameof(currentName));
+
+        if(name is not null && currentName != name) {
+            if(!Channel.CheckName(name))
+                throw new ChannelNameFormatException(nameof(name));
+            if(ChannelExists(name))
+                throw new ChannelExistsException(nameof(name));
+        }
+
+        if(name is not null)
+            channel.Name = name;
+        if(password is not null)
+            channel.Password = password;
+        if(temporary.HasValue)
+            channel.IsTemporary = temporary.Value;
+        if(rank.HasValue)
+            channel.Rank = rank.Value;
+        if(ownerId is not null)
+            channel.OwnerId = ownerId;
+
+        return channel;
+    }
+
+    public void RemoveChannel(string name) {
+        Channel channel = GetChannel(name) ?? throw new ChannelNotFoundException(nameof(name));
+
+        Channel defaultChannel = DefaultChannel;
+        if(channel == defaultChannel || defaultChannel.NameEquals(channel.Name))
+            throw new ChannelIsDefaultException(nameof(name));
+
+        Channels.Remove(channel);
+    }
+}
diff --git a/SharpChatCommon/Channels/NoDefaultChannelException.cs b/SharpChatCommon/Channels/NoDefaultChannelException.cs
new file mode 100644
index 0000000..d50513f
--- /dev/null
+++ b/SharpChatCommon/Channels/NoDefaultChannelException.cs
@@ -0,0 +1,4 @@
+namespace SharpChat.Channels;
+
+public class NoDefaultChannelException()
+    : NullReferenceException("A public, non-temporary default channel could not be determined.") {}