Baresoil Programming Model

This section describes the Baresoil programming model at a more detailed technical level. Readers who prefer a more hands-on approach may prefer to skip to the walkthrough of a minimal example app instead.

Project Structure


A Baresoil application consists of a server component and a client component. Both are optional, but at least one is required.

  • The client component consists of a set of static files that are served at the root URL of a domain, sometimes called a web distribution. Baresoil serves all files in this component as a typical webserver like Apache or nginx would, i.e., by serving a file called index.html at the root of the domain, and then mapping URL paths to files. The top-level URL path /__bs__/ is reserved for system use.
  • The server component consists of a node.js package that is loaded to serve each new web client. It is written from the perspective of a program that is created to serve exactly one connected client, from within a restricted container process. The Baresoil runtime provides a realtime, bi-directional, secured link between web clients and your server code, with a 1-to-1 mapping from clients to containers.

Generally, both client and server components are required for webapps and hybrid mobile apps. If the server directory is empty, then the project becomes a simple static website. If the client directory is empty, the project becomes an API-only service.

Once an application has been deployed to Baresoil, it is available at a user-selected subdomain of the top-level domain baresoil.cloud (if the demo server is being used; otherwise, use your own top-level domain).

Clients


A client is any device that establishes a WebSocket connection to the application subdomain (specifically, at the WebSocket URL path /__bs__/live).

WebSockets are now standardized, widely supported, and allow easy, TCP-like, two-way communication between client and server, while tunneling over existing HTTP(s) infrastructure. They allow a range of client/server programming models to be used over the same shared channel, such as remote procedure calls and server-sent events.

The following is a partial list of possible client types:

  • A webapp in a browser, possibly using the official Javascript client library. Depending on how the app is coded, a single browser tab may open one or more connections, with each connection serving as a new client.
  • A native mobile application, using any platform-specific, standards-compliant WebSocket library.
  • A command-line tool, possibly using the official Javascript client library. The Baresoil SDK's baresoil command is an example of this.
  • A Raspberry Pi microcomputer embedded in a fridge, or other exotic, WebSocket-comaptible setup.

In other words, the web-hosted "client" portion of Baresoil projects is merely a convenience for webapps.

Once connected, clients send and receive line-delimited JSON messages in UTF-8 encoding, using a small, open protocol. This format is chosen to maximize compatibility with clients written in different languages, since JSON parsing and UTF-8 support are both commonly supported features. Individual messages are automatically compressed at the WebSocket layer, when standards-compliant WebSocket client libraries are used.

This also means that simple tools like wscat can be used to directly send and receive messages from a Baresoil server.

Sandboxes


Once a client application connects to the /__bs__/live WebSocket endpoint of your subdomain, Baresoil creates a lightweight, security-restricted virtual machine called a sandbox to serve that client, and only that client. The sequence of steps taken for each new client is the following:

  1. Allocate a fresh sandbox for the client.
  2. Copy your project's deployed server directory into the sandbox.
  3. Execute the sandbox driver program to interface with the client.
  4. Automatically destroy the sandbox once the client disconnects, or if your server program crashes.

Each sandbox is created only for the lifetime of a single client connection. Once the client disconnects, the sandbox is destroyed.

Under the hood, the sandbox is an enhanced Ubuntu Linux 16.04 LTS container, with limited CPU and memory resources, and a privilege-free sandbox. Outgoing HTTP and HTTPS network access, however, is permitted. The container includes multiple current versions of node.js, as well as other preinstalled software that can be spawned as child processes.

The SandboxDriver program is designed to allow server-side code that is concise, flexible, and easy to write. It is based around a collection of server-side handler functions, which are node.js functions that are executed at the server. The results of evaluating the functions are then passed back to the client.

All function evaluations for a single client connection occur inside the same process, so handler functions are free to interact with each other in any way required, and to use process-global variables if required (for example).

In practical terms, SandboxDriver provides the context and interface for handler functions, and serves as an interface to the Baresoil runtime. It is written in ES2016 Javascript and executed inside the sandbox.

Once invoked, it performs the following steps to "launch" your server program once it is invoked.

  1. Loads your server program using node's standard require() function.
  2. Looks for and asynchronously executes a function called $websocket on the start of new WebSocket connections, or $http to process HTTP PUT/POST/DELETE requests.

The source code for SandboxDriver is part of the baresoil-server repository. The file main.js is the entry point invoked by the runtime.

The default SandboxDriver can be replaced by an alternative written in any other language, or by an opaque binary, by setting the server.driver key in baresoil.json to the command line to execute.


Next: Read a walkthrough tutorial of a simple application.