Add USB devices to Effects

This guide will show you how to use a USB device from within an Hercules CI Effect on NixOS.

Prerequisites:

  • You have set up an agent for the account that owns the repository, and you have access to deploy changes to its configuration.

  • You have added a repository to your Hercules CI installation.

  • You have a USB device that you want to use in your builds, plugged into the machine that runs the agent.

1. Configure the agent

By default, the NixOS module for Hercules CI Agent locks down the agent’s permissions, as most agents have no business accessing USB devices or other capabilities.

To allow the agent to access USB devices, you need to add the following to your NixOS agent configuration.

Instead of USER_NAME, replace it with the username of your agent service, typically hercules-ci-agent, or hci-<something>. Instead of SERVICE_NAME, replace it with the name of your agent service, typically hercules-ci-agent, or hercules-ci-agent-<something>. Omit the .service suffix.

users.extraGroups.my-hci-usb = { };
users.extraUsers.USER_NAME.extraGroups = [ "my-hci-usb" ];
systemd.services.SERVICE_NAME.serviceConfig.DeviceAllow = "char-usb_device rw";
# You may need the AF_NETLINK protocol for device discovery
systemd.services.SERVICE_NAME.serviceConfig.RestrictAddressFamilies = [ "AF_NETLINK" ];
services.udev.extraRules = ''
  # My USB device; replace with your device's vendor and product IDs
  ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="204f", MODE="660", GROUP="my-hci-usb"
'';
# Alternatively, if you're using an old style single agent configuration:
# services.hercules-ci-agent = {
services.hercules-ci-agents.NAME = {
  settings = {
    effectMountables =
    let
      # Allow access from any repository
      usbCondition = true;
      # Allow access only from a specific repository
      # usbCondition = { isRepo = "my-repo"; };
    in
    {
      "dev-bus-usb" = {
        source = "/dev/bus/usb";
        readOnly = false;
        condition = usbCondition;
      };
      "sys-bus-usb" = {
        source = "/sys/bus/usb";
        readOnly = false;
        condition = usbCondition;
      };
      # If your device is a serial device, you may need to add the following:
      "ttyACM0" = {
        source = "/dev/ttyACM0";
        readOnly = false;
        condition = usbCondition;
      };
      "ttyACM1" = {
        source = "/dev/ttyACM1";
        readOnly = false;
        condition = usbCondition;
      };
    };
  };
}

2. Deploy the configuration

  • Deploy the updated configuration to your agent machine, e.g. using nixos-rebuild switch.

  • Reload the udev rules, e.g. using udevadm control --reload && sudo udevadm trigger.

  • Restart the agent service, e.g. using systemctl restart SERVICE_NAME.

3. Use the USB device in an Effect

Now that the agent has access to the USB device, you can use it in your Effects.

Here’s an example of how you can use a USB device in an Effect:

hci-effects.modularEffect {
  mounts = {
    "/dev/bus/usb" = "dev-bus-usb";
    "/sys/bus/usb" = "sys-bus-usb";
    # If you need these:
    "/dev/ttyACM0" = "ttyACM0";
    "/dev/ttyACM1" = "ttyACM1";
  };
}

Troubleshooting

If you encounter issues, the strace command might give insight into which resource is unavailable in the sandbox. The agent logs may also contain useful information.

If you need help, feel free to reach out in the Hercules CI Matrix room or by email at [email protected].