Extensions (Alpha)

💡 This page provides context and development guidance for Extensions. For installing and using Extensions please refer to the Extensions Manager section of the Advanced Usage page.

Context

One of the primary aims of BlueOS is to be a platform that is readily extendable to the needs of each user. For maximum utility it should be easy to add support for custom hardware, add components to the user interface, and share developments with other BlueOS users, without compromising the base BlueOS experience.

Accordingly, BlueOS has been designed with a containerised architecture, so the core functionality is kept separate from Extensions, and each can be distributed and updated independently of the other. The built in Extension system allows developers and users to find and install add-on software packages to BlueOS, and manage updates, permissions, and resource limits of those Extensions through the Extensions Manager.

For some additional context and discussion, please see this forum thread about the BlueOS Extensions architecture. Feel free to contribute ideas or ask questions there as relevant.

Implementation

⚠️ The BlueOS Extensions system is currently still in an alpha stage of development, so significant implementation changes are expected in the near future.

Components: Anatomy of an Extension

At heart, a BlueOS Extension is some functionality (optionally with a web interface) packaged into a Docker Image, and combined with some metadata that allows it to be found, shared, and managed. Extensions may interface with existing services provided by BlueOS (or other Extensions), and some Extensions may create persistent logs and/or make use of data or files provided by the user.

Once installed, an Extension Package can be run as a Docker Container, which normally occurs automatically when the vehicle turns on, but can also be manually disabled/enabled via the Extensions Manager. When running, Extensions can have custom permissions assigned, which can limit resource-usage and/or allow access to parts of the host computer's hardware.

Docker

Docker is a lightweight containerisation system that allows packaging one or more software programs into a single executable that can be easily shared across systems. A static package is referred to as a "Docker Image", and when it is being run it takes the form of a "Docker Container".

A Docker Container generally acts like an isolated mini operating system, but it's possible to enter a running Container from the host computer to see (and modify) what the programs inside are doing, and access things like logging output. Importantly, changes in a Container do not affect its Image, so they're not persistent1 and it's possible to "start fresh" by simply restarting the Container (which creates a new Container from the Image, without any changes from previous instances).

1

While changes within/to the Container are not persistent, it is possible to make persistent changes to the host's file-system if the Container has access to it (usually via "HostConfig": "Binds" in the metadata permissions).

Metadata (Dockerfile)

The process of packaging a set of programs and files into a Docker Image requires a Dockerfile, which can be thought of as recipe for building the Image.

For an Extension to be properly managed and shareable, the Dockerfile it's created from should include relevant metadata2 via the following labels:

  • LABEL permisions='{}'
    • This can be used to set resource limits, including allowing access to specific components of the underlying hardware
    • This must be a valid JSON (use \ to break lines), which follows the configuration described in the Docker Api
    • For reference, existing permissions can be retrieved from a running Docker Container by running the command docker inspect <container_name>
      • Do not copy the whole output to use in your Extension - only the minimal requirements for the Extension to work
    • Common uses of the permissions label include:
      • Mapping/mounting a folder from the host device into the running Container
        "HostConfig":\
          {\
            {\
              {\
                "Binds":["/var/lib/zerotier-one:/var/lib/zerotier-one"]\
              }\
            }\
          }
        
      • Automatically mapping port 80 of the Container to a free port in the host
        LABEL permissions='\
        {\
         "ExposedPorts": {\
           "80/tcp": {}\
          },\
          "HostConfig": {\
            "Binds":["/root/.config:/root/.config"],\
            "PortBindings": {\
              "80/tcp": [\
                {\
                  "HostPort": ""\
                }\
              ]\
            }\
          }\
        }'
        
      • Constraining CPU/memory usage (half a core)
        • This is important if your Extension is CPU or memory-hungry, as we need to ensure it doesn't affect the autopilot's operation
        {
          "HostConfig":{
            "CpuPeriod":100000,
            "CpuQuota":20000,
            "Memory":209715200,
          }
        }
        
    • Our Extensions Examples Repository has some useful examples
  • LABEL version="1.0.0"
    • Public Extension releases should use a SemVer-compliant version
    • We recommend using alpha/beta versions for non-stable (development) releases (e.g. 1.0.1-beta.16)
  • LABEL authors
    • The individual author(s) who have developed the Extension
    • Useful for giving credit, and as a potential support backup
    • This should be a list of JSON dicts, each containing a "name" and "email" key with corresponding values
    LABEL authors='[\
        {\
            "name": "Me",\
            "email": "me@email.com"\
        },\
        {\
            "name": "Me Too",\
            "email": "me2@email.com"\
        }\
    ]'
    
  • LABEL company (becoming maintainer)
    • The person or company responsible for maintaining the Extension
    • JSON object in the form
      {
          "about": "brief description",
          "name": "Company/Person Name",
          "email": "email@company.com"
      }
      
  • LABEL readme
    • A URL pointing to a markdown-based README file
      • Allows using {tag} to find versioned readme files without manually updating the URLs
      • e.g. "https://https://raw.githubusercontent.com/Williangalvani/BlueOS-examples/{tag}/example4-vue-backend/Readme.md"
  • LABEL links
    • An arbitrary collection of relevant links, in a JSON dict
    • Recommended link type examples include
      {
          "website": "https://...",
          "github": "https://github.com/...",
          "support": "mailto:support@company.com",
          "documentation": "https://docs.company.com/cool-extension/",
          "phone": "tel:+1234567890"
      }
      
  • LABEL requirements
    • SemVer-compliant dependency requirements for this Extension to work correctly (with BlueOS and/or other Extensions)
    • format not yet finalised
      • will likely be something like repo/extension-name >= version
  • LABEL type
    • Useful for broad-strokes filtering when searching for/browsing Extensions
    • Not yet implemented in the Extensions Manager
    • Should be one of the following strings:
      • "device-integration"
      • "theme"
      • "other"
      • "example" (for Extension examples)
  • LABEL tags
    • Useful for finer-grained filtering to help find relevant Extensions
    • Not yet implemented in the Extensions Manager
    • Must be alpha-numeric (no capital letters, no symbols except dashes)
    • Limited to 10 tags per extension
    • Some examples (focused around possible Extension functions):
      • "positioning"
      • "navigation"
      • "mapping"
      • "data-collection" (e.g. scientific sensor integrations)
      • "communication" (e.g. integration of an acoustic modem)
      • "interaction" (e.g. integration of a gripper / robot arm / brush)
2

The metadata from the Docker labels is used to populate the Repository Manifest, which is used to include Extensions in the Extensions Manager.

Web Interface (HTTP Server)

If the Extension needs a visual interface3, the recommended approach is to provide a webpage that's accessible via the existing BlueOS web interface.

To do so requires the Extension to run a HTTP server4, at which it must serve a register_service endpoint in the format of a JSON dict with the following keys:

  • "name"
  • "description"
  • "icon"
  • "company"
    • The name of the company/person in charge of maintaining the Extension
  • "version"
    • The SemVer-compliant version of the API that is accessible through the Extension's HTTP server
    • Not required to match the version of the Extension itself
      • e.g. the Extension may release a version with a new appearance, with no changes to the API/functionality
    • May be used in dependency requirement checks
  • "webpage"
    • The official URL for the extension
  • "api"
    • The official URL for the extension's API
  • "new_page" (optional)
    • Boolean (true/false) specifying whether to open the extension in a new page instead of in a BlueOS frame

As an example:

{
    "name": "BlinkLED",
    "description": "An intuitive application that blinks a LED.",
    "icon": "mdi-led-on",
    "company": "Lights End Darkness",
    "version": "1.0.1",
    "new_page": false,
    "webpage": "https://github.com/octocat/blink-led-BlueOS-extension",
    "api": "https://github.com/octocat/blink-led-BlueOS-extension/wiki/api"
}

Each registered Extension service will have an entry under the "Extensions" section of the BlueOS web interface sidebar menu:

sidebar display showing a registered service in the menu

3

You can choose to not provide a web interface if your extension operates with no user input. The VirtualHere Extension is a relevant example. 4:You can use programs like simple-http-server to serve static files for an Extension.

Functionality

Different types of Extensions have different functionality, and there are often a variety of tools available to provide that. When starting to develop extensions, we would generally recommend:

  • Python to run stuff
    • Frequently using a web framework like Starlite/Flask/FastAPI to provide the backend of an interface
  • Static HTML files to contain and display parts of an interface
  • Our Extensions Examples Repository has some useful examples

Examples: What do they Look like?

Device Integrations

Examples

Themes

  • No examples yet

Other

  • VirtualHere
    • USB over IP: used to present USB devices connected to the Onboard Computer as though they are plugged in to the Control Station Computer
  • ZeroTier
    • Network extension/sharing: used to create a virtual private network that allows managing and controlling a vehicle remotely over the internet

Where do they Live?

Online Packages

  • Extension Packages are expected to be hosted on Docker Hub
  • To appear on the Extensions Manager store they must be registered in the BlueOS Extensions Repository
    1. Extensions are added to the store by opening a Pull Request (PR) against the repository, including a metadata.json file and an icon
    2. Once the PR is merged, a continuous-integration job will automatically use the information from the provided metadata file to access Docker Hub and fetch the labels for all tags that are SemVer-compliant
    3. The fetched information is then compiled and published to the Manifest file in the gh-pages branch

Installed Extensions

Once installed on the Onboard Computer, Extensions are stored at /var/lib/docker in the file-system, but should be managed through the BlueOS Extensions Manager.

Ecosystem Attributes

Current Extensions Manager Features

  • Install Extensions from store
  • Runs installed Extensions on startup
  • Track CPU and memory usage (per Extension)
  • Manage/edit permissions (including limiting hardware resource access/usage)
  • View Extension logs
  • Disable Extensions
  • Uninstall Extensions that are no longer wanted
  • Manually update/rollback Extensions (select version)
  • Create a custom Extension configuration
  • Allow restarting Extensions

Planned Extensions Manager Features

  • Notify users of available updates
  • Support userdata file cleanup from Extensions (checks for dependencies)
  • Improve permissions interface (to show what's available/requested)
  • See more / track progress in the Kraken development tracker

Development Process

💡 Walkthrough coming soon!

Edit this page on GitHub