Quick start

curl bootstrap.f0cal.com/master | python3 - git -v ${VENV_DIR} --my-device
source ${VENV_DIR}/bin/activate
f0cal my-device image install ubuntu/latest@f0cal/stable \
  --device-type ${DEVICE_TYPE_ID} \
  --kwarg sdcard=/dev/sda1


Every device vendor has a slighly different way of getting operating systems and vendor-provided software onto their devices. Some of the variation is due to constraints imposed by the hardare design itself. Many cases, however, can be chalked up to business decisions that don’t put developer productivity at the top of the list. (Ahem, you know who you are.)

f0cal/my-device brings developer productivity back to front and center.

In this repo you’ll find the front-end to a device installer. The “back end” to the installer, such as it is, is a collection of publicly maintained recipes for getting OSes onto devices automatically. It’s “one CLI to rule them all!”


The f0cal.my-device namespace implements a special client for pulling down operating system binaries – “images” in our terminology – alongside the recipes for installing them. Under the hood, all of the image and recipe management functionality is provided by the excellent conan.io project.

The installer recipes themselves are provided as salt states.

FØCAL’s contribution is to tie conan and salt together to provide lean, clean CLI workflows for getting devices up and running quickly.

Getting operating systems onto devices - a primer

First, a few words about the general mechanics of getting devices up and running.

In general, there are three kinds of software on devices of interest:

  • Firmware – Very low-level code that performs startup tasks specific to the device’s core hardware components. Typically located on small – kilobytes – persistent storage chips on the device. (BIOS is one type of firmware.)

  • Bootloader – Receives flow control when the firmware picks a peripheral to boot from. This could be a disk, the network, or otherwise.

  • Operating system – The operating system kernel is loaded into memory by the bootloader. It then initializes device drivers, starts core OS services, and hands control off to user-facing terminals.

With very few exceptions, getting any of these onto a device requires both physical access to the device and a companion computer. If the device has removable storage media (like an SD card), then it’s sufficient to plug the removable media into the companion computer and create an OS-specific startup disk. If the device’s storage media can’t be removed, the companion computer needs to be tethered to the device directly, generally by USB. A vendor-specific installer or install procedure will then write the system software directly into place.

Due to these “companion logistics,” many of the my-device workflows don’t refer to the devices directly, but instead to the path where the companion computer has mounted the device storage. For example:

f0cal my-device image install ubuntu/latest@f0cal/stable \
  --device-type rpi3bp \
  --kwarg sdcard=/dev/sda1

There are two things to note here:

  • --image ubuntu/latest@f0cal/stable – This is the typical way of expressing a conan package. The tokens are {pkg}/{version}@{user}/{channel}

  • --kwarg sdcard=/dev/sda1 – This is the path on the companion filesystem where an SD card is typically mounted.


If you’re already familiar with conan, you know that there are two really great things about it:

  1. Reproducibility – The entire purpose of conan is to make builds reproducible through dependency mangement.

  2. Sharability – Like git, conan remote allows you to specify network endpoints that artifacts can be pulled from and pushed to.

Given that f0cal/my-device images are backed by conan packages, does this mean that images can be pulled from non-f0cal endpoints? Yes. In future sections, we’ll explore how use conan to build and share custom images.

f0cal/farm images

{% include_relative api_key_note.md %}

Without any additional infrastructure, you can pull private images from f0cal/farm for use on local devices.

f0cal my-device remote add farm https://farm.f0cal.com
f0cal my-device image install my-yocto/latest@${USER}/${CHANNEL} \
  --device-type rpi3bp \
  --remote farm \
  --kwarg sdcard=/dev/sda1


  • ${USER} is the alphumerical user ID found on your account page.

  • ${CHANNEL} is a helpful tag that the user can define from f0cal farm. The default is “farm”.

  • --remote farm forces the CLI to check farm.f0cal.com for a candidate image.

You can also share images with coworkers and collaborators from the f0cal/farm CLI:

f0cal farm instance create my_pi \
  --image ubuntu/latest \
  --device-type rpi3bp
f0cal farm instance connect my_pi \
  --ssh # Make tweaks by hand
f0cal farm instance stop my_pi
f0cal farm image create my-ubuntu/latest \
  --instance my_pi
f0cal farm image export my-ubuntu/latest \
  --user ${FRIEND}

Now on friend’s side:

f0cal my-device remote add farm https://farm.f0cal.com
f0cal my-device image install my-ubuntu/latest@${USER}/farm \
  --device-type rpi3bp \
  --remote farm \
  --kwarg sdcard=/dev/sda1

Again, ${USER} and ${FRIEND} are alphumeric IDs found on your mutual accounts pages.