https://tristancacqueray.github.io/blog/introducing-butler * + Blog o Tristan de Cacqueray Introducing Butler Virtual Operating System 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: butler-demo 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: butler-intro 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 same application id encoding: | $pid | client-data | The key design is that clients are dumb terminals and the state is controlled server side. A good example is the text editor app named Noter. This app leverages the whole application environment to implement an etherpad like text editor. Haskell RunTime System In this section I explain the choice of using Haskell. Beyond the language's common features, here are two reasons for using Haskell: * The language lets you break down any problem into smaller chunks, using let or where bindings. I find this syntax and way of thinking very helpful for simplifying complex software. * The REPL evaluates modification almost instantaneously. I run development code through ghcid, and thanks to a simple client's reconnect handler, the services are hot reloaded in milliseconds when the source changes. However, even though the Haskell ecosystem is quickly improving, it is not a mainstream language, and it can be rough around the edges. Here are a couple of alternative languages that I am also considering: * Rust, because it is very similar to Haskell. Though it can be more complicated: for example, some expressions don't seem to be decomposable into sub expressions without triggering the borrow checker. And the lack of REPL to evaluate the code interactively is frustrating. * Gleam, because it leverages the BEAM virtual machine, which is a great fit for application actors. That being said, Haskell works great for Butler. So far I have only used its most basic features, and the project source is only 5k lines of code according to polyglot. HTML5 Graphical User Interface In this section I explain the choice of using HTML5 for the GUI. I choose HTML5 because it is an ubiquitous standard and the Web API offers a fully featured application environment. Thanks to the hypermedia paradigm enabled by htmx, it is a great fit for Butler. Though it would be interesting to investigate morphdom and rich client alternatives, such as Web GPU or Flutter. Application Demos Here is a list of demos I am using to validate the core implementation: * Butler.App.Noter: a text editor. * Butler.App.FileManager: a file manager. * Butler.App.Mumbler: a voice chat. * Butler.App.Tabletop: a free form tabletop simulator. * Butler.App.Terminal: a legacy terminal. * Butler.App.NoVnc: a VNC client. * And a few other system applications you can find in the Butler.App directory. I am mostly focusing on the desktop use-case, but I also helped my colleague fboucher use Butler to create a game named HazardHunter. Road-map A terminal and VNC client are already provided to access non Butler applications, and perhaps it is possible to re-implemented these legacy system as Butler native applications. For example, I am looking forward adding: * Programming REPL. * Media player named "Ampler". * IRC and Matrix client. * Agenda and calendar. * More video games. Moreover, I would like to develop the Butler APIs to include: * Application testing framework, to simulate actor's inputs and validate the outputs. * User's mailbox and notification system. * Application debugging strace. * External identity providers. * Authorization system. Finally, it would be interesting to investigate if and how an app registry could be implemented. For example as a list of packages that could be dynamically loaded/unloaded into a running instance. Conclusion The actor framework looks like a great solution for web applications, and I am quite happy with the Butler's implementation. Moreover, I am excited about the community discussion for moving Towards an actor framework for Haskell. My implementation is rather limited, and I would be happy to use a common library similar to the erlang/otp. Butler may sound like a crazy project, but I believe it is important to pursue fun goals that appear impossible. I would like to quote the author of SerenityOS Andreas Kling: "Never underestimate the power of a bunch of nerds building something just for fun." Finally I would like to thank elmiko for his early feedback and encouragement. Cheers! #blog #haskell #butler This work is licensed under a Creative Commons Attribution 4.0 International License [emanote-lo] [ ]