This post introduces haskell-butler, a virtual operating system that runs multi-players applications on the web. In two parts, I present:
- The project’s overview, and,
- A motivating use-case; a remote desktop that looks like this:
Hobby Project
Before I begin, let me set the expectations. Butler is a hobby project inspired by the SerenityOS project. I am working on it on my free time as a challenge and learning experience. Moreover I am considering alternative runtimes and protocols. Therefore the implementation is subject to change.
That being said, the project reached a point where it is now enjoyable, and I’m happy to share its current state with you.
Modular Operating System
Butler’s goal is to implement a virtual operating system with a strong focus on concurrency. Butler is not a computer hardware operating system. Instead, Butler is an application environment that runs on top of an existing system.
Thus I created the following modules:
Starting with the Core
layer, I designed low level interfaces for the host system:
-
Process
for thread management, powered by the ki library. -
Pipe
for inter-process communication using stm. -
File
for directory and file objects. -
Network
to serve Web Application Interface wai.
Then in the Main
layer, I implemented higher level interfaces on top of the Core
primitive:
-
Scheduler
for the process hierarchy and supervision. -
Display
for graphical user interfaces. -
Session
for users. -
Frame
for the a data exchange format.
Finally, the App
layer provides the application environment:
-
Window
for app instance. -
Cursor
for user pointer. -
Sound
to produce and receive audio. -
Agent
to forward local agent.
I find it valuable to model these APIs after regular operating system constructs. They are available through a single top level Butler module. The next section introduces the application environment.
Application Actor
Butler applications are implemented as standalone processes that operate a AppEvent pipe. Checkout the App definition. Applications start with a AppContext provided by the display.
In order to run multiple application concurrently, Butler features a data exchange format to share a single socket per client. Applications output:
-
HTMX payload, by suffixing the application id. For example the UI are mounted on:
-
Binary payload, by encoding the application id at the beginning of the buffer:
| $pid | server-data |
Applications receive:
-
HTMX trigger, by suffixing the trigger name with the application id:
{"HEADERS": {"HX-Trigger": "$trigger-$pid"}}
- Binary payload, using the