Skip to content

Build the install image

Gateways are installed from a zero-keystroke USB image: it installs OpenBSD, drops in the agent, and enrolls the device into the holding zone on first boot, with nobody at the keyboard. The build tooling lives in digilan/provisioning/openbsd-autoinstall (profile gateway).

What goes into the image

scripts/build-usb.sh takes the stock OpenBSD installXX.img and adds:

  • site<ver>.tgz built from profiles/gateway/site/, holding the edge-agent binary (/usr/local/sbin/edge-agent), its rc.d script, the digilan-status banner and root's .kshrc (with the ea alias);
  • /etc/edge-enroll.conf (mode 0600) with NEXUS_ADDR, REALTIME_ADDR and ENROLL_TOKEN;
  • auto_install.conf (the answer file) inside the bsd.rd ramdisk, via rdsetroot. The installer only looks for it in the ramdisk root; a copy on the media root is ignored and the installer stops at its prompt;
  • /etc/boot.conf on the media, pinning the console (see below).

The agent binary and edge-enroll.conf are generated at build time and are gitignored.

Build it

build-usb.sh must run on OpenBSD as root (it needs vnconfig, an FFS mount and rdsetroot). From macOS, scripts/build-image.sh does the whole thing: it fetches and checks the agent, starts the local QEMU OpenBSD VM from edge/gateway/dev/openbsd-vm.sh, runs build-usb.sh inside it and copies the image back.

cd openbsd-autoinstall
ENROLL_TOKEN=<bootstrap-token> \
AGENT_VERSION=v0.1.99 \
NEXUS_TOKEN=<system-admin API token> \
./scripts/build-image.sh
# -> gateway-installer-amd64-78.img        (CONSOLE=serial, the default)

On an OpenBSD host, call the low-level script directly with a release agent (fetch it with scripts/fetch-agent.sh on any machine, see below):

doas env NEXUS_ADDR=https://dev.nexus.digilan-edge.com \
         REALTIME_ADDR=wss://dev.realtime.digilan-edge.com \
         ENROLL_TOKEN=<bootstrap-token> \
         AGENT_BIN=/path/to/edge-agent-openbsd-amd64-v0.1.99 \
         CONSOLE=serial \
         ./scripts/build-usb.sh
Variable Default Meaning
ENROLL_TOKEN required Bootstrap token from Admin → Enrollment in the target environment
NEXUS_ADDR dev Nexus (build-image.sh); required for build-usb.sh Nexus URL the box enrolls with
REALTIME_ADDR dev Realtime (build-image.sh); required for build-usb.sh Realtime URL
AGENT_VERSION none build-image.sh only: release tag of the agent to fetch and bake in
AGENT_BIN none A release agent binary already on disk; required for build-usb.sh
EXPECT_SHA256, NEXUS_TOKEN none How AGENT_VERSION is verified; one is required, see below
ALLOW_UNSIGNED_AGENT 0 1 accepts an agent without the release key (bench only, see below)
DRY_RUN 0 build-image.sh only: 1 checks the agent and stops before the VM
CONSOLE serial serial or vga, see below
ROOT_PW locked (*) on serial, DEFAULT_VGA_ROOT_PW from config.sh on vga Root password on the installed system
OBSD_ARCH amd64 Target architecture
BASE_IMG downloaded A pre-downloaded installXX.img to start from
GATEWAY_REPO, OUT_DIR sibling edge/gateway checkout, repo root build-image.sh only

Point NEXUS_ADDR / REALTIME_ADDR at the environment the token was minted in; see the environment URL table.

The agent must be a release build

Only an agent built by the gateway tag pipeline carries the release signing key; without it the device refuses every over-the-air update until it is reflashed. Both build-image.sh and build-usb.sh read the key embedded in the agent binary and compare its sha256 with RELEASE_KEY_SHA256 in config.sh. An agent with no key (such as a make build-openbsd build) or another key stops the build before anything is written.

build-image.sh takes the agent from exactly one of:

Set Agent baked in
AGENT_VERSION=vX.Y.Z that release, fetched and verified by scripts/fetch-agent.sh
AGENT_BIN=/path a release binary you already have
ALLOW_UNSIGNED_AGENT=1 and neither of the above a make build-openbsd build of GATEWAY_REPO

With none of them it stops and says so.

scripts/fetch-agent.sh vX.Y.Z [arch] downloads edge-agent-openbsd-<arch> from the build:<arch> job of that tag's pipeline in edge/gateway (with glab, or GITLAB_TOKEN with read_api), into agents/, and checks:

  1. its sha256 against EXPECT_SHA256, or, when that is unset, against the release registry of NEXUS_ADDR (ListAgentReleases, which needs a system-admin API token in NEXUS_TOKEN); this is the sha256 Nexus serves to devices for that version;
  2. that it carries the release signing key;
  3. when go is installed, that it is a clean build of that tag.

Any failed check stops it. Use it on its own to get a binary for build-usb.sh.

Bench images

ALLOW_UNSIGNED_AGENT=1 lets the build continue with an agent that has no release key, and prints a BENCH IMAGE warning block. A device installed from that image can never be updated over the air. Do not flash it for a site.

The first run of build-image.sh installs the VM and takes 15 to 20 minutes; later runs are faster. The VM's own downloads stall under emulation, so place a copy of install78-amd64.img in edge/gateway/dev/.vm/ (or set BASE_IMG) first.

Serial or VGA console

The image pins, at build time, the one console the bootloader, the installer and the installed system all use. Picking the wrong one looks like a dead box: it installs and enrolls normally while showing nothing.

CONSOLE=serial (default) CONSOLE=vga
For headless box with a COM port box with a monitor and no usable COM port
Output file gateway-installer-<arch>-<ver>.img gateway-installer-<arch>-<ver>-vga.img
Media boot.conf stty com0 115200, set tty com0 default console (pc0)
Installed console com0 at 115200 baud VGA
Root console login locked unless ROOT_PW is set enabled with DEFAULT_VGA_ROOT_PW unless ROOT_PW is set

Both variants are otherwise identical. Root SSH login is disabled in both.

The VGA image has a known root password

A VGA build exists to debug a box that looks dead, so it ships a console login whose password is public (DEFAULT_VGA_ROOT_PW in config.sh, and printed at the end of the build). Do not leave a VGA-installed box at a customer site: build CONSOLE=serial, or pass an explicit ROOT_PW (ROOT_PW='*' locks it).

How to attach to each console: Write the image to a USB stick.

Firmware: UEFI or legacy

The answer file leaves partitioning unanswered on purpose, so the OpenBSD installer chooses from how the box booted: GPT with an EFI system partition under UEFI, whole-disk MBR under legacy/CSM. One image installs a bootable system in both modes.

On hardware you control, prefer UEFI with CSM disabled. In every case, put the internal disk ahead of USB in the boot order, or pull the stick at the first reboot.

What the box shows

  • First boot prints a banner and numbered steps: [1/3] waiting for network (up to 60 s for a default route), [2/3] enrolling, [3/3] starting edge_agent. A failure names the likely cause and the command to retry. A copy is kept in /var/log/digilan-enroll.log.
  • Every boot ends with a status box from /etc/rc.local: enrolled (with the gateway id), agent running, Nexus link, reachability of the Nexus host baked in at flash time, and the WAN address. Run digilan-status to print it again. On a VGA console, scroll back with Shift+PgUp.

Once enrolled, the device appears on Admin → Devices under Holding. Assign it there; see Device lifecycle.