Back-end
8 minute read

NodeOS: The JavaScript Based Operating System

Danny is a PHP developer with a proven knack for coding efficiently and solving problems rapidly.

I am sure most programmers have heard of Node.js, but what about NodeOS? Yes, NodeOS, an operating system written in Node.js. Well, kind of. NodeOS uses the Linux kernel for most performance critical stuff like, for example, hardware interactions, but for everything else it uses Node.js. NodeOS development started two years ago and was created by people who shared a simple, but intriguing, idea: “Is it possible to create an operating system using only Node.js?”

Darwin’s evolution chart

Is it possible to create an operating system using only Node.js?

What is the big deal?

First of all, think about the progress Node.js has made in the short time it’s been around. Now, think about the same thing happening with an operating system. Then, let’s add some cool stuff to it.

Per-user independent and isolated root filesystem

NodeOS introduced an interesting paradigm: If all users have an isolated filesystem, it gives them a simple filesystem hierarchy to work with. Since their “home folder” is, in fact, the root of their own filesystem hierarchy, they can install packages globally without requiring special permissions and not need to configure anything since they are installed in their home directory by default. Also, it provides a good deal of security; if a hacker finds a way to get inside a particular account in the operating system, the only partition that s/he can access is the partition of that user. The end result is hacker cannot compromise the whole system.

Node.js and NPM

If you think about it, an operating system that uses Node.js means that any package available in NPM is, at the same time, also a NodeOS package. At the time of writing, there are 210,735 packages; since the number of NPM packages grows every minute, it would not be strange if, in a few years, NodeOS has a million applications.

It is based on the Linux kernel

This might not seem like a big deal, but Linux is the most-used server operating system. Since NodeOS is based on the Linux kernel, you could run every application written for other Linux distributions with minimal changes.

The downsides

As much as I would like a finished NodeOS, it is not there, yet. It’s still missing many key functions for a server operating system. For example, the whole BASH toolset is missing, including ps, tail, nano and grep. Furthermore, you cannot run it as a desktop operating system since it has no GUI. Sure, you can implement some of the missing features fairly easily using a bit of JavaScript, but the fact all the mentioned features are not available by default, is not good.

So, how can I try out NodeOS?

Using Docker

The easiest and quickest way to try out NodeOS is by using the following:

  1. A computer with either Mac OSX or Linux. It might work with Windows, but I did not try it.
  2. Docker.

Once you installed Docker, running an instance of NodeOS is easy. All you need to do is execute the following command, and Docker does all the magic:

sudo docker run -t -i nodeos/nodeos

The easiest and quickest way to try out NodeOS is by using Docker

The easiest and quickest way to try out NodeOS is by using Docker.

When you run the aforementioned command, Docker automatically downloads the disk image for NodeOS from a repository and performs the installation of NodeOS inside a virtual machine. Once the installation is completed, it opens an SSH session to the NodeOS shell.

Without docker

There are some reasons why you would want to avoid using Docker, and one being the latest NodeOS version. At the time of writing, the last change to the Docker image of NodeOS was performed two months ago, while the development version was updated six days ago. So, if you want to use the latest version you should definitely get the source code. That’s not too difficult, but it takes a long time. You will need:

  1. A computer with Linux. You can compile it on OS X, but it will take longer since it has to make a cross compilation. The same goes for Windows.
  2. The Linux build utilities (make, g++, gcc, autoconf).
  3. Qemu.
  4. Time. Seriously, a lot.

Once you have everything, you can proceed with the source code compilation:

  1. Download the project source code: bash git clone [email protected]:NodeOS/NodeOS.git.
  2. Compile it by running following commands: cd NodeOS and npm install.
  3. I am going to quote, word by word, the official documentation: “Pick some microwave popcorn and go to see a movie. No, really, do it.”. Yes, it will take that much time, so do something interesting in the meantime.
  4. Run bash npm start to run NodeOS inside Qemu.

Is it working?

Once the installation is completed, we can check if it is working by executing inside the NodeOS shell the command ls. An output like this should show up:

[ 'etc', 'lib', 'lib64', 'root', 'bin', 'sys', 'usr', 'share', 'proc' ]

If it does, it means some basic commands are working. But, what if we want to display the network card’s IP address? Under Linux, there’s a command called ifconfig that does just that; let’s try it:

command not found: ifconfig

It looks like the command ifconfig is missing. That is because NodeOS does not have a default ifconfig command. Now, what? Well, it is simple; NodeOS has an integrated package manager (like apt or yum), called npkg, which is based on Node’s NPM and is easy to use. Installing ifconfig is as simple as running the following command:

npkg install bin-ifconfig

If all went well, the command ifconfig should now be available in the shell. If we try to execute it again, an output like this shows up (I put a random MAC address and IP address for obvious reasons):

eth0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
  ether 01:23:45:67:89:ab 
  inet6 f0cd::ef01:0203:0405:181%en1 prefixlen 64 scopeid 0x5 
  inet 192.168.0.21 netmask 0xffffff00 broadcast 192.168.0.21
  nd6 options=1<PERFORMNUD>
  media: autoselect
  status: active

If your output looks kind of like that, then it is working. You’ve successfully installed your first NodeOS application: ifconfig.

It is working. However, what do we do now with the OS?

What is the point of having an operating system written in Node.js if you can do the same things (or even fewer things) that you can do on Ubuntu or any other Linux distribution? Well, the whole point is everything is developed by using nothing more than Node.js. It also means we can develop our applications using nothing more than Node.js. For example, NodeOS does not have a default implementation for the command man, which is used on Linux to display manual pages of other commands. Fear not, implementing it is easy. How, I hear you ask? Simple.

Building a NodeOS application with Node.js

First, let’s install a text editor called Hipster so we can create and edit files by executing the following command: npm install -g [email protected]. This file editor is simple, and definitely not something I would use as an editor for anything else, but it is good enough in this case. Creating files with Hipster is really simple, just run hip filename, like hip package.json; to save press Ctrl + s and to exit press Ctrl + q. For this example I am going to use a code developed by one of the main NodeOS developers, I have not actually implemented it, myself. The original code for our example can be found in the node-bin-man Git repository.

Let’s get back to creating our first NodeOS application. As with every Node.js application (or NPM package), we start by creating a package.json file, as in the following example:

{
  "name": "bin-man",
  "version": "0.0.1",
  "description": "Format and display manual pages",
  "bin": {
    "man": "man.js"
  },
  "repository": "https://github.com/groundwater/node-bin-man",
  "author": "groundwater",
  "license": "MIT",
  "dependencies": {
    "blessed": "~0.0.22"
  }
}

The parameters name, version, author, repository, license, and description are self-explanatory. The bin collection is a JSON key/value object containing the command name and an associated JavaScript file. In our example, the man command is associated with the file man.js. The collection, dependencies, contains a list of NPM packages that are needed to use this application. In our example, the author of the code included Blessed, a curses-like library with a high-level terminal interface API for Node.js.

Now let’s go to the main part, the actual code.

#!/usr/bin/env node

This part is called “shebang”. It is not actually required by NodeOS, but it tells an operating system how to execute the following code. In our case, it tells the interpreter that everything needs to be executed with the command /usr/bin/env node.

var fs = require('fs');
var blessed = require('blessed');

Just like in Node.js, the function require() loads the selected package into memory and saves it to the specified variable.

var arg = process.argv[2] || 'bin-man';

The standard behavior of a man command is to tell a manual about itself if no other command is specified. Our code example is doing the same: If no argument is specified for the second parameter (the first being man itself), it defaults to bin-man.

var path = process.env.HOME + "/lib/node_modules/" + arg + "/README.md";

try{
  var readme = fs.readFileSync(path, 'utf-8');
}catch(e){
  console.log('No README.md for Package ',arg);
  process.exit(-1);
}

At this point, the program checks if a readme file exists for the given application. In NodeOS, the installation path of every application is the home directory (or /) followed by the directory lib/node_modules. If the file README.md exists, it saves its content inside the variable readme. Otherwise, it shows an error and exits the process.

// Create a screen object.
var screen = blessed.screen();

var box = blessed.box({
  content: readme,
  alwaysScroll:true,
  scrollable: true,
});

// Append our box to the screen.
screen.append(box);

Blessed has a really simple API; showing the content of a file is as easy as telling it to create a box and loading the content.

screen.key(['escape', 'q', 'C-c'], function(ch, key) {
  return process.exit(0);
});

Now, let’s create a way to exit the man application. We combine the keys escape, q or the emacs-style combination C-c to exit the process.

screen.key(['space','f','j','n'], function(ch, key) {
  box.scroll(box.height);
  screen.render();
});

screen.key(['down'], function(ch, key) {
  box.scroll(1);
  screen.render();
});

screen.key(['up'], function(ch, key) {
  box.scroll(-1);
  screen.render();
});

screen.key(['b','k','p'], function(ch, key) {
  box.scroll(-box.height);
  screen.render();
});

We use the directional keys to scroll up or down one row and the keys space, f, j or n to scroll down a page (as many lines as there are on the screen). Use b, k or p to do the reverse.

box.focus();
screen.render();

Finally, we tell the application to focus on the box we created and render the whole thing. Let’s put those files in the directory /lib/node_modules/bin-man and let’s add a simple README.md, something like this:

# Man

Author: @groundwater

## Install

npkg install bin-man

## Usage

```
Usage: man PKGNAME

Display a packages README.md file
```

We are almost done with our first custom application for NodeOS. Only one last small step remains; we need to create a config file since it is a requirement for NodeOS applications. It is as simple as creating a file in the path /etc/bin-man/config.json with the following content: {}, an empty JSON object. Now, we can try our new application. Running man inside the NodeOS shell should show the readme file we created earlier.

Conclusion

As you can see with our simple code example, implementing anything in NodeOS is an easy task, you only need to know Node.js.

NodeOS has plenty of potential, and I think it could become a great operating system once additional features are implemented. It still needs work, but as a result of the popularity of the whole Node.js-based ecosystem, I would not be surprised if it quickly becomes a popular operating system quickly. What do you think about it? Let me know in the comments.