2024-02-04
I just wanted to make an IRC client/server library
If you only read one paragraph: I wanted IRC. I got BedrockSlim, PulseFlow, Channels-in-DI, storage abstractions, CronJobs, workflows, a torrent client, and a game engine-shaped distraction. IRC is still a future fantasy. Skill issue? Partly. Microsoft gave me a machine gun of foot-guns? Also partly.
IRC has been around a long time. It is simple, it is still used, and it is a good way to learn networking and string handling. It is also a good way to learn security, because it has none. The documents I keep open are RFC 1459 and RFC 2812. Transport security is out of band unless you bolt it on. IRCv3 is the capability work on top. The baseline is still cleartext until you prove otherwise. The socket primitives I ended up on are the Kestrel / connection ones, which is a different problem from IRC grammar.
I wanted it in .NET, on NuGet, cross-platform, close to out of the box, and alse a framework you can extend. Server and client in different packages.
I looked at the existing libraries. A few are .NET. None of them were close to what I wanted. I wanted dependency injection, Channel<>, IHostedService, and cron, mostly my own code, everything async. Easy peasy, right? WRONG!
I love this stack. Microsoft has done a great job with .NET Core, but its microsoft, and so they don't give you A gun to shoot yourself in the foot, they give you a machine gun, or at least a box of guns. I looked at System.Net.Sockets and thought, oh, this is easy. Then I looked at lifecycle management and thought, oh, this is not. Is this skillissues? Yes, but also the .NET team hasn't made sockets easy. I sank so low I asked on Reddit, r/dotnet, and David Fowler answered. He pointed me at a demo on his GitHub, Bedrock.Framework. I copied it, stripped it down, didn't like it, then stepped back. He was showing how to use Microsoft.AspNetCore.Connections.Abstractions. I pivoted, and that became Frank.BedrockSlim. Async, DI, a bare minimum of ASP.NET Core. The server has a framework reference. The client does not. I am happy with it. I still don't have an IRC library.
Servers need data. Sessions, users, channels, messages. A database, even SQLite, is not always the answer. A profile can be a JSON blob, logs can be a file, messages might belong in a database. Frank.DataStorage is the common interface, a few implementations, and DI. Five months in. Still no IRC library. I had something cool, though.
Channel<> might be the coolest thing added to .NET Core in the last few years. One to many, many to one, async. A lot of this is workflow-shaped. Maybe I should make a workflow library? Forshadowing? Channels are also a good in-memory pub/sub, so I made Frank.PulseFlow. Then I needed Channel<> in DI somewhere else, so I made Frank.Channels.DependencyInjection. You register the type as a channel and inject the channel, the reader, or the writer. Looking at resource use its like nothing. I'm getting a bit off track here, wasn't I making an IRC client/server library?
I have contributed to CronQuery. It parses a cron expression and starts DI jobs. I wanted that without living in Microsoft.Extensions.Configuration, so I made Frank.CronJobs. I copied the license header from the original, linked it, and rewrote a lot of the complex parts. I'm not just a copycat.
I have built workflow engines before. Most of the time a workflow is a linear sequence of steps, and the tools still make that complicated. Frank.WorkflowEngine is the simple one, on DI, channels, hosted services, and cron.
Then I remembered. IRC, no security. Text over TCP, not encrypted, not authenticated. So I built Frank.Security. Password hashing, passprashe generation, a few other things. (There's a pattern to my naming scheme that is REALLY hard to deduce.) How does that help IRC? Well, he he. Securing IRC is more than I thought. I would rather say there is no end-to-end encryption than do it wrong. Low-level networking cryptography is not my strong suit, and I don't want to do it badly.
No security, and openness about it, is usually exponentially better than bad security. A bad implementation of encryption is worse than no encryption. It gives a false sense of security, and it opens you up to liability.
How did I end up with a torrent client? I wanted to dowload a pile of data-science files, and I didn't want to install anything. The libraries I found were old. I forked one, spent a long time getting it usable, and called it Frank.TorrentClient. DI, modern .NET, and UI projects on Avalonia and WPF. It needs a lot of work. It is usable.
A game framework? Seriously?
I used to be a game developer (Unity), but I was always fighting the engine. I wanted to buold my own framework. Frank.GameEngine is interchangeable parts. Swap the renderer, swap the physics, and it should just work. Its pretty jankey, and it needs a lot of work, especially the collision detection.
Frank.GameEngine was the janky start. I did not stop there. That line of work is Novolis now. Physics and simulation stay headless, rendering is swappable, and the studios are Avalonia, not me losing another fight with Unity. Still no IRC library. Of course.
There is a Frank.IRC repo. It is a learning exercise. It is not the library.
I still want the library. Code generation would be benefical, because IRC is a lot of commands and events. A moderation bot is the other idea I have not let go of. For now I keep making stuff and see where it goes.