A wireframe diagram of the orb

Introduction

Welcome! This book serves as the spot that the Worldcoin Project hosts its documentation for developing software for the orb.

It also provides an inside look at our development practices, which helps increase transparency of the project and provide foundations for other contributors to the project to build on top of.

Who is this for?

The target audience of this documentation is for contributors to the orb-software repo or any of the other open source repos that pertain to the orb. We plan to accept contributions at a later date, but do not have bandwidth to review PRs currently.

Likewise, we are providing source code and documentation for the benefit of the community, but cannot commit to any SemVer or API stability guarantees. Be warned: we may change things in a backwards-incompatible way at any time!

Where is the code?

See the orb-software repo for the code.

This is a living document. If you are a contributor and wish to improve the documentation, simply open a PR! The source code for the mdBook lives here.

However, spam/noise/typo PRs will be ignored. Especially those from bots. Do not farm green github squares from us.

Initial repo setup

To be able to build the code, there is some first-time setup required.

Set up nix + direnv (the developer environment)

We use nix to manage all of the dependencies during development. While most of the software can use convnetional rust tools like cargo, we do have a few additional dependencies. Instead of apt-installing them, we use nix as the package manager, and use direnv to handle automatically activating a developer shell. The process to install and configure these two tools is as follows:

  1. Install nix. This works for both mac and linux, if you are using a windows machine, you must first set up WSL2.
  2. Ensure that you have these lines in your ~/.config/nix/nix.conf or /etc/nix/nix.conf. This is done automatically by the above installer:
    experimental-features = nix-command flakes
    max-jobs = auto
    
    You can check that things work by running nix run nixpkgs#hello
  3. Install direnv: nix profile install nixpkgs#direnv
  4. Hook direnv into your shell.
  5. Set up your personalized .envrc file by running cp .envrc.example .envrc. You can customize this file if you wish. We recommend filling in your cachix token if you have one. If prompted, dont run direnv allow yet, follow step 6 first. Otherwise you'll get a bunch of errors.
  6. Follow the instructions on vendoring proprietary SDKs in the subsequent section.
  7. Run direnv allow in the repository's root directory. Direnv will then automatically use the .envrc file you set up any time you cd into the directory.
  8. If you are on macos, run the following:
    brew install dbus
    brew services start dbus
    

Vendoring proprietary SDKs

Although all of Worldcoin's code in the orb-software repo is open source, some of the sensors on the orb rely on proprietary SDKs provided by their hardware vendors. Luckily, these are accessible without any cost, they are just annoying to get and are not themselves open source.

To get started, you will need to download these SDKs. The process for this depends on if you are officially affiliated with Worldcoin.

If you have access to Worldcoin private repos

  1. Create a personal access token from github to allow you to use private git repos over HTTPS.
  2. Append the following to your ~/.config/nix/nix.conf:
    access-tokens =
    github.com=github_pat_YOUR_ACCESS_TOKEN_HERE
    
  3. Test everything works so far by running nix flake metadata github:worldcoin/priv-orb-core. You should see a tree of info. If not, you probably don't have your personal access token set up right - post in slack for help.

If you don't have access to Worldcoin private repos

  1. Go to https://developer.thermal.com and create a developer account. Getting the SDK can take several days for Seek Thermal to approve access. In the meantime, you can skip steps 2 and 3.
  2. Download the 4.1.0.0 version of the SDK (its in the developer forums).
  3. Extract its contents, and note down the dir that contains the Seek_Thermal_SDK_4.1.0.0 dir.
  4. modify your .envrc like this: use flake --override-input seekSdk "PATH_FROM_STEP_3". If you don't yet have access to the SDK, just provide a path to an empty directory.

How to develop and build code

Make sure you followed the first time setup instructions.

Building

We use cargo zigbuild for most things. The following cross-compiles a binary in the foobar crate to the orb. Replace foobar with the crate that you wish to build:

cargo zigbuild --target aarch64-unknown-linux-gnu --release -p foobar

You can also build code faster with cargo check, since it skips the linking step. Try running cargo check -p foobar.

Testing the code

Unlike building the code, tests are expected to run on the same target as the host. But not all tests are possible on every target.

IF it is supported, you can run cargo test -p foobar like normal. But support varies from crate to crate.

Running the code

For binaries that are intended to run on the orb, you can take your cross-compiled binary, and scp it onto the orb. You can either use teleport (if you have access) via tsh scp or you can get the orb's ip address and directly scp it on, with the worldcoin user.

If you choose the scp route without teleport, you will need to know the password for the worldcoin user. Note that this password is only for dev orbs, orbs used in prod are not accessible without teleport and logging in with a password is disabled.

Debugging

Tokio Console

Some of the binaries have support for tokio console. This is useful when debugging async code. Arguably the most useful thing to use it for is to see things like histograms of poll() latencies, which can reveal when one is accidentally blocking in async code. Double check that the binary you wish to debug actually supports tokio console - support has to be manually added, it isn't magically available by default.

To use tokio console, you will need to scp a cross-compiled tokio-console binary to the orb. To do this, just clone the repo and use cargo zigbuild --target aarch64-unknown-linux-gnu --release --bin tokio-console, then scp it over.

Note: tokio-console supports remote debugging via grpc, but I haven't figured out how to get the orb to allow that yet - I assume we have a firewall in place to prevent arbitrary tcp access, even in dev orbs.

Then, you must build the binary you want to debug unstable tokio features enabled. To do this, uncomment the line in .cargo/config.toml about tokio unstable.

Finally, make sure that the binary has the appropriate RUST_LOG level set up. try using RUST_LOG="info,tokio=trace,runtime=trace".

Finally, run your compiled binary and the compiled tokio-console binary on the orb. You should see a nice TUI.

Note that it is recommended but not required to have symbols present to improve the readability of debugging.

How we do open source

Worldcoin is committed to building a fully open source, decentralized ecosystem. In service of this goal, the entirety of the orb-software repo is open source under an MIT/Apache 2.0 dual license. You can read more about the project's open sourcing efforts here.

All source code for the Worldcoin Project lives under the worldcoin github organizaton.

Overview of private repos

To the maximum extent possible, we put all public code in the orb-software and orb-firmware repos. But we also maintain private repos, which contain code that is responsible for fraud detection or uses third party SDKs that we do not have a license to open source.

The most notable private repos are:

  • priv-orb-firmware. This repo contains the fraud sensitive parts of the firmware. It consumes the public orb-firmware repo as a dpenedency.
  • orb-internal. This repo contains the fraud sensitive parts of the user-space software. It consumes the public orb-software repo as a dependency.
  • priv-orb-core. This repo is the mainline branch of orb-core. Unlike the other repos, this is a fork of its public counterpart, orb-core. The public repo is therefore inherently less up to date and doesn't retain git history, as its code has all fraud-related codepaths manually deleted. One of the long-term goals is for these two repos to become un-forked, and most code consolidated into orb-software so that we can transition to developing orb-core directly in the open.
  • trustzone. This repo contains code related to the secure operating system OP-TEE that runs alongside linux inside ARM TrustZone. We plan to open source the OP-TEE CAs and TAs at some point in the future.-
  • orb-os. This repo is where we build the operating system image that runs on the orb. It consumes artifacts from all the other repos to assemble one final image.
  • orb-update-agent. Contains code for OTAing orbs. We plan to open source this by merging it into the orb-software repo.
  • orb-update-verifier. Contains code used during the OTA process to check that the update booted successfully. We plan to open source this by merging it into the orb-software repo.

Hardware In Loop

Developing for the orb generally requires access to an orb. To make life easy, as well as to enable automated tests, we use an x86 linux machine as a host, which attaches to an orb via a number of hardware peripherals. We created the orb-hil cli tool to leverage this known hardware setup to perform a number of common, useful actions.

Getting an x86 Linux Machine

Technically, any x86 linux machine will do. However, we recommend using an ASUS/Intel NUC due to its compact form factor.

The Linux installation needs:

  • Access to the various attached usb devices without sudo, i.e. udev rules configured
  • Access to serial without sudo
  • Various packages (awscli2, usbutils, etc)
  • Teleport
  • Github self-hosted runner (if using this in CI).

To make this setup easy, we have a nix config that sets all of this up. BUT you could use regular ubuntu, or some other linux distro instead.

For the NixOS approach, see the nixos setup. If you use NixOS, we can manage all the machines in one git repo, so this is the prefrred option, even though the initial setup is a bit more hassle (for now).

orb-hil cli

There is a CLI tool to facilitate hardware-in-loop operations. This tool lives in the orb-software repo and releases can be downloaded here.

It is a single, statically linked CLI tool with lots of features helpful for development:

  • Rebooting orbs into either normal or recovery mode
  • Flashing orbs (including downloading from S3, extraction, etc)
  • Executing commands over serial
  • Automating the login process over serial

Required peripherals

Different orb-hil subcommands require different hardware peripherals. We strongly recommend at least getting an x86 linux machine and a serial adapter. See the hardware setup page for more detailed info.

Here are the different hardware peripherals necessary for the different subcommands of orb-hil:

  • orb-hil flash: x86 linux machine
  • orb-hil reboot: Serial adapter.
  • orb-hil login: Serial adapter.
  • orb-hil cmd: Serial adapter.

Logging in to AWS

The flash subcommand can download S3 urls. To set this up, we recommend putting the following into ~/.aws/config:

[default]
sso_session = hil
sso_account_id = 510867353226
sso_role_name = ViewOnlyAccess
[profile hil]
sso_session = hil
sso_account_id = 510867353226
sso_role_name = ViewOnlyAccess
[sso-session hil]
sso_start_url = https://d-90676ede48.awsapps.com/start/#
sso_region = us-east-1
sso_registration_scopes = sso:account:access

You can now chose the appropriate aws profile for the hil by passing the AWS_PROFILE=hil env var in any aws-related tasks. This works with both the aws cli tool, and orb-hil.

To actually log in and get a fresh set of credentials:

AWS_PROFILE=hil aws sso login

HIL NixOS Setup

Eventually, we will support making installation media from the NixOS config directly, including setup scripts to fully automate this process. But for now, one first needs to do a lot of manual bootstrapping.

Installing NixOS to a liveusb

On the ASUS NUCs, they don't support MBR partitioned live usbs. But for some inexplicable reason the official NixOS installer only exists as a MBR partitioned disk. This means we need to build our own GPT/UEFI based NixOS live usb ;(

TODO: Describe how to make a UEFI-compatible minimal nixos live usb. The TLDR is that you use nixos-generators with the raw-efi format.

Use the liveusb to install NixOS

Booting from the liveusb

This is the same as any other linux liveusb. Get into your boot menu using the function keys at boot, and select the USB from the boot options. If it doesn't show up, make sure you are using a GPT/UEFI based liveusb. You will likely need to disable UEFI secure boot as well.

Setting up Partitions

You need three partitions:

  • EFI (512MB, format as FAT32)
  • Swap (Make it 32GB or size of ram, whichever is bigger. Format as linux-swap)
  • Rootfs (Rest of disk, format as ext4)

TODO: Describe how to do this from parted. See also here and here

After you finish this step, the rootfs partition should be mounted to to /mnt and the EFI boot partition to /mnt/boot.

Install NixOS from the liveusb

  1. Make sure that the new partitions are mounted under /mnt and /mnt/boot.

  2. Run sudo nixos-generate-config --root /mnt. This will create a new nixos config for the NUC.

  3. Edit the NUC's NixOS config at /mnt/etc/nixos/configuration.nix to be the following: TODO: Make sure this is all that is needed, and just use this to generate an image instead of them typing it in.

    /mnt/etc/nixos/configuration.nix:

    { config, pkgs, lib, ... }:
    let
      username = "worldcoin";
      hostname = "my-hostname-here";
      hashedPassword = ""; # paste output of mkpasswd here
    in
    {
      networking.hostName = "${hostname}";
      
      environment.systemPackages = with pkgs; [
        curl
        git
        neovim
        parted
        usbutils
        vim
      ];
    
      # Enable the OpenSSH daemon.
      services.openssh = {
        enable = true;
        passwordAuthentication = false;
      };
    
      # Enable the X11 windowing system.
      services.xserver.enable = true;
      # Enable the KDE Plasma Desktop Environment.
      services.xserver.displayManager.sddm.enable = true;
      services.xserver.desktopManager.plasma5.enable = true;
    
      # Enable networking
      networking.networkmanager.enable = true;
    
      users.users."${username}" = {
        isNormalUser = true;
        description = "${username}";
        hashedPassword = hashedPassword;
        extraGroups = [
          "networkmanager"
          "wheel" # Gives sudo
          "plugdev"
          "dialout"
        ];
      };
    
      # use the latest Linux kernel
      boot = {
        kernelPackages = pkgs.linuxPackages_latest;
        # Needed for https://github.com/NixOS/nixpkgs/issues/58959
        supportedFilesystems = lib.mkForce [ "btrfs" "reiserfs" "vfat" "f2fs" "xfs" "ntfs" "cifs" ];
      };
    }
    
  4. Make sure that your liveusb is connected to the internet. If its not, you can use nmtui to connect.

  5. cd /mnt && sudo nixos-install

  6. sudo shutdown -h now, and remove the liveusb.

  7. Boot the freshly installed NixOS (you may need to select it from the boot menu).

  8. Make sure that all of the following is true:

  • You can boot into it.
  • You have internet access (you can connect to wifi with nmtui).
  • You have sudo rights

Switch to the full NixOS config.

Now that NixOS is installed on the NUC, we need to upgrade it to the full blown config that we use. Luckily nix makes this really easy.

  1. Clone the nix config.
  2. Customize the config to add an entry for your new machine. Be sure you set the hostname to be the same as what the current hostname is. You can ask @thebutlah to do this for you or look at the existing config to figure it out. Eventually we will make this really easy. Be sure that you add a ssh key for your account so that you can still access it in the case that teleport doesn't work.
  3. (only if creating a self-hosted runner) Create a /etc/worldcoin/secrets/gh-runner-token file and populate it with the orb-os-self-hosted-runner token from 1Password.
  4. Clone the nix config to ~/nix.
  5. Run sudo nixos-rebuild --impure --flake ~/nix
  6. Install teleport. Ask in slack for how to do this, its a bit involved, since it requires manually editing the shell script, as well as requesting access.

Hardware Setup

The HIL leverages the use of several hardware peripherals to control the orb. Most important is the serial adapter and the x86 linux machine. While any x86 linux machine will do, we recommend purchasing an ASUS/Intel NUC due to the convenient form factor.

Required Parts

We break down the parts into common/shared items, and items per-hil.

Note: Not all of these parts are required. In the most minimal case, we recommend at least getting some electrical tape, an x86 linux machine, the Micro USB cable, and a serial adapter. This will cover 90% of the use cases. Note also, no soldering is required!

Shared

  • Electrical Tape
  • Some Male-Male Jumper Wires (but Male-Female or Female-Female as needed).
  • Display for initial bringup
  • A flash-drive

Per HIL Rig

The Essentials

Flashing the microcontrollers

Connecting the parts to the orb

Flashing support

  1. Connect the microusb cable to the microusb port of the orb.
  2. Use the USB Power blocker to ensure that this cable is data-only. This will ensure that the main mcu doesn't get power from the cable. This is an optional but highly recommended step.
  3. Connect the usb power blocker to the NUC directly. Do not use the usb dock.

Serial and Reboot support

  1. Ensure the serial adapter is configured for 3.3v. There is a jumper on the serial adapter that controls this setting.
  2. Add electrical tape behind the TX and RX pins, to prevent short circuiting the pins against the chassy.
  3. Connect the GND pin of the serial adapter to the GND pin on the orb (there is one at the top).
  4. Connect TX and RX pins to the orb. TX (blue) on bottom, RX (green) on top. picture of serial pins
  5. Connect CTS pin to the BTN pin. Its located below the top GND pin. This allows controlling the power button of the orb. picture of CTS pin
  6. Connect the RTS pin to the bottom of the two pins on the recovery mode jumper on the right side of the orb. This allows controlling recovery mode of the orb. picture of recovery
  7. Double check that nothing is going to short circuit. It is ok for the GND pin to touch the chassy, but it is NOT ok for any of the other pins to touch anything else.
  8. Plug the serial adapter into the USB dock and connect the dock to the NUC.

(Optional) Recovery from a bricked microcontroller

For each microcontroller that you want to be able to recover without, you should attach the ST-Link. For setups where you don't want to physically have to access the orb, we recommend keeping both ST-Links always attached to their microcontrollers. You can do this by:

  1. Plug the tag-connect adapter into the port. There are two - one for the main mcu on the front center of the orb, and one for the security mcu on the back of the orb.
  2. Connect the other end of the tag-connect to the ST-Link.
  3. Plug the st-link into the NUC (via the dock or directly).

Software components of the orb

The orb consists of several software components. This section of the book provides a place to describe all of these different components.

See the individual sections in the table of contents to read more.

Orb Core

orb-core contains the core rust application responsible for verifying users' World IDs.

The binaries controlling the orb are found in src/bin/:

  • src/bin/orb-core.rs: the production binary, which runs verifications in the field.
  • src/bin/orb-backend-connect.rs: a binary to ensure backend connectivity by scanning a WiFi QR code and establishing the WiFi connection as long as the backend is not reachable.

Code Overview

Overview of the do_signup function:

flowchart TD
    A[Start Signup] --> B{Scan Operator QR Code}
    B -->|Success| C{Scan User QR Code}
    B -->|Failure| Z[End Signup]
    C -->|Success| D{Detect Face}
    C -->|Failure| Z
    D -->|Detected| E[Start Image Notary]
    D -->|Not Detected| Z
    E --> F[Biometric Capture]
    F --> G[Stop Image Notary]
    G --> H[Biometric Pipeline]
    H --> I{Detect Fraud}
    I -->|No Fraud| J[Enroll User]
    I -->|Fraud Detected| K[Mark as Fraud]
    J -->|Success| L[Mark Signup as Successful]
    J -->|Failure| M[Mark Signup as Failed]
    K --> N[Upload Debug Report and Opt-in Images]
    L --> N
    M --> N
    N --> Z

State of open sourcing

There are two orb-cores: the public one at worldcoin/orb-core and the private one at worldcoin/priv-orb-core. Today, the public repo is a manually stripped down version of the private one. This is done to remove code paths that could reveal the types of fraud detection that we perform. Long term, we plan to un-fork these two repos such that the public code lives in worldcoin/orb-software and the private code is only minimal additional code, which consumes the public code as a dependency. This will ensure that most code we develop is done directly in the open, where main lives in a public repo.

See how we do open source for more context.