BuildHive
CLI tool for building and managing Linux systems on NTL hardware. All builds run in Docker containers; build state persists in named Docker volumes.
| Platform | SoC | Build system | Rootfs |
|---|---|---|---|
| Hive-S | LiteX/VexRiscv RISC-V soft-SoC (Artix-7) | Buildroot | initramfs (cpio) |
| Hive-M | Xilinx ZynqMP | Yocto (bitbake) | squashfs, RAUC A/B |
Documentation
- Command reference - every command, flag, prerequisite
- GUI - pages, what each button runs, settings location
- Project configuration -
project.jsonschema - Hive-S internals - HW/SW/flash build flow
- Hive-M internals - Yocto flow, kernel development
- Update system - RAUC bundles, update archives, signing, legacy migration
- Secure boot - key generation, boot chain, eFUSE provisioning
- Development - repo layout, release process, template update mechanism
Installation
Windows
Run BuildHive_<x.y.z>_Setup.exe from the newest buildhive_v<x.y.z> release. Installs per user, optionally adds buildhive to PATH. Later versions: buildhive upgrade or the update button in the GUI.
Requirements
- Docker running (WSL backend on Windows) - required by almost every command
- Vivado on PATH - Hive-S only (
build hw,deploy flash) - VS Code with
codeCLI on PATH - only forbuildhive code - Company SSO account - build secrets (
BUILDSIGN_TOKEN,SSTATE_MIRROR_AUTH) come from the secrets server (secretsinproject.json), browser login on first use; env vars of the same name override - NTL VPN - only for signing update archives
Command names accept unique prefixes: buildhive dep fl = buildhive deploy flash.
Hive-S quickstart
[!IMPORTANT] Requires AMD/Xilinx Vivado in your PATH.
buildhive create -n PROJECT_NAME -t hive-s
cd PROJECT_NAME
buildhive build hw # generate SoC + IP cores (output: ip/)
# generate bitstream in Vivado, then:
buildhive configure bitstream -f PATH_TO_BITSTREAM # copies to images/bitstream.bin
buildhive build sw # UWM + Buildroot (kernel, rootfs, u-boot, opensbi)
buildhive build flash # assemble images/flash.bin
buildhive deploy flash # program SPI flash via Vivado/JTAG (device connected)
If multiple devices are connected, you are prompted to select one.
Hive-M quickstart
buildhive create -n PROJECT_NAME -t hive-m
cd PROJECT_NAME
# optional: replace hardware description
buildhive configure xsa -f PATH_TO_XSA # extracts .bit from XSA
buildhive configure bitstream -f PATH_TO_BIT # or supply .bit directly
buildhive configure dts -f PATH_TO_DTSI # custom device tree include
# optional: kernel / u-boot menuconfig
buildhive configure linux
buildhive configure uboot
buildhive build sw # bitbake aionyx-image-minimal -> images/sd.wic
buildhive build update # update archive + RAUC bundle, signed -> images/
Kernel development (Hive-M)
Fast out-of-tree module and kernel builds without full bitbake runs:
buildhive build kernel-devsrc # export kernel source + toolchain to Docker volume (once)
buildhive build module ntl_tsu # build one NTL driver out-of-tree -> oot/ntl_tsu/*.ko
buildhive build module ntl_tsu --clean
buildhive build kernel-oot # rebuild kernel Image without bitbake -> images/Image
buildhive build kernel-oot --clean
buildhive shell # interactive shell in build container
buildhive shell --module ntl_tsu # shell with kernel env, cd oot/ntl_tsu
buildhive code # VS Code attached to dev container
buildhive code --stop
Dev container mounts: /kernel-devsrc (kernel source + build tree, case-sensitive volume), /oot (host oot/), /drivers (host sources/drivers/).
Command reference
Commands are resolved against the project in the current working directory (project.json, not searched upward). Without a project, only the common commands exist. With one, the template field selects the Hive-S or Hive-M command set.
Any command segment may be abbreviated to a unique prefix (buildhive dep fl = buildhive deploy flash). Exact matches win over prefixes; ambiguous prefixes error.
Overview
| Command | hive-s | hive-m | Docker | Other prerequisites |
|---|---|---|---|---|
create | ✓ | ✓ | ✓ | - |
gui | ✓ | ✓ | - | - |
upgrade | ✓ | ✓ | - | secrets server login (GitHub token), Windows for the install |
login / logout | ✓ | ✓ | - | project with secrets (login only), browser |
info project | ✓ | ✓ | - | project |
info secrets | ✓ | ✓ | - | project |
info flash | ✓ | - | - | project, built .fbi files |
configure project | ✓ | ✓ | - | project |
configure linux | ✓ | ✓ | ✓ | interactive terminal |
configure buildroot | ✓ | - | ✓ | interactive terminal |
configure uboot | - | ✓ | ✓ | interactive terminal |
configure bitstream | ✓ | ✓ | - | .bit file |
configure xsa | - | ✓ | - | .xsa file |
configure dts | - | ✓ | - | .dtsi file |
build sw | ✓ | ✓ | ✓ | git access |
build hw | ✓ | - | ✓ | Vivado on PATH |
build flash | ✓ | - | - | images/bitstream.bin + built components |
build kernel | - | ✓ | ✓ | - |
build kernel-devsrc | - | ✓ | ✓ | - |
build kernel-oot | - | ✓ | ✓ | build kernel-devsrc first |
build module <name> | - | ✓ | ✓ | build kernel-devsrc first, drivers repo cloned |
build update | - | ✓ | ✓ | signing: buildsign config, BUILDSIGN_TOKEN, NTL VPN |
export sw | - | ✓ | ✓ | - |
packages list / add / remove | - | ✓ | - | project |
packages index | - | ✓ | ✓ | - |
packages search | - | ✓ | - | packages index first |
secure-boot keygen | - | ✓ | - | - |
secure-boot ppk-hash | - | ✓ | ✓ | keygen + prior build sw |
code | - | ✓ | ✓ | code CLI on PATH |
shell | - | ✓ | ✓ | interactive terminal |
deploy flash | ✓ | - | - | Vivado on PATH, connected device |
clean | ✓ | ✓ | ✓ | - |
Common commands
buildhive create
| Flag | Required | Description |
|---|---|---|
-n, --name | yes | Project name. Letters, digits, -, _; max 128 chars. |
-t, --template | yes | hive-s or hive-m. |
Creates ./<name>/: copies the default template, then the chosen template, writes project.json, hash manifest (.buildhive_manifest.json) and merge baseline (.buildhive_baseline/). Fails if the directory already contains a project.
buildhive gui
Launches the desktop GUI (PySide6); running the exe without arguments does the same. Pages, button-to-command mapping and settings location: gui.md.
buildhive login / buildhive logout
login: OIDC login to the secrets server from project.json secrets.url (see configuration.md). Opens the browser (company SSO), waits for the callback on http://localhost:8250/oidc/callback, caches the OpenBao token in the OS config dir (token.json, plain text, next to gui.json). Builds log in on demand when a secret is needed and no valid token is cached, so an explicit login is only needed to switch users. logout: deletes the cached token. BAO_TOKEN env (CI machine token) bypasses both.
buildhive upgrade
| Flag | Description |
|---|---|
--check | Only report installed vs. latest version. |
--silent | Installer without the wizard (Inno Setup /SILENT). |
Looks up the newest buildhive_v<x.y.z> release in NetTimeLogic/Tools (draft and pre-release skipped). The repo is private: the GitHub token comes from GITHUB_TOKEN env or the secrets server (buildhive/github, key GITHUB_TOKEN; browser login if no token is cached, no project needed). Newer release: downloads BuildHive_<x.y.z>_Setup.exe to the temp dir and starts it two seconds after the CLI exits, so the installer can replace bin/. The installer removes the old version and installs per user, no admin. Up to date: OK: BuildHive x.y.z is up to date. Linux/macOS: prints the releases page. Exit 1 on token, network or download errors. The GUI has the same check at start plus an update button, see gui.md.
buildhive info secrets
Prints the secrets server, the logged-in email and, per secret (SSTATE_MIRROR_AUTH, BUILDSIGN_TOKEN), the source a build would use: environment variable, <mount>/users/<email>/<secret>, <mount>/<secret> or not available (<reason>). Never prints values, never opens the browser.
buildhive info project
Prints name, ID, BuildHive version, template. Hive-S: Buildroot repo/branch. Hive-M: Yocto manifest repo/branch and additional clone repos.
buildhive configure project
| Flag | Description |
|---|---|
-n, --name | Rename the project. |
Hive-S commands
buildhive build hw
| Flag | Description |
|---|---|
-d, --debug | Append hw.debug_args (RISC-V JTAG debug). |
-v, --verbose | Show container output. |
Generates the LiteX/VexRiscv SoC in the HW container (make.py <hw.args>), rewrites the generated Vivado tcl to host paths, runs vivado -mode batch to build IP, copies the IP repo to ip/.
buildhive build sw
| Flag | Description |
|---|---|
--rebuild | Wipe work volumes, build from scratch. |
-v, --verbose | Show container output. |
Clones and builds UWM (out/hive-s_full_tls → images/uwm.xz, uwm-loader into the rootfs overlay), then Buildroot (ntl_defconfig) → kernel Image.lzma, rootfs.cpio.xz, u-boot.bin, opensbi.bin in images/.
buildhive build flash
| Flag | Description |
|---|---|
-v, --verbose | Also print the flash info table. |
No Docker. Wraps each flash mapping entry with a length+CRC32 header (.fbi), builds mapping_table.bin, assembles images/flash.bin (bitstream at offset 0, components at their src offsets, mapping table at 0x3F0000). See hive-s.md.
buildhive info flash
Prints the flash mapping table and image structure (offsets/sizes from .fbi files in images/).
buildhive configure linux / configure buildroot
Interactive make linux-menuconfig / make menuconfig inside the SW container.
buildhive configure bitstream
| Flag | Required | Description |
|---|---|---|
-f, --file | yes | Bitstream file, copied to images/bitstream.bin. |
buildhive deploy flash
Runs vivado -mode gui -source tools/flash_device.tcl to program the SPI flash over JTAG. Requires Vivado on PATH and a connected device.
buildhive clean
Removes and recreates the HW and SW Docker volumes. Fails if a container is using them.
Hive-M commands
All container-based Hive-M commands run an init script first: repo init/sync of the Yocto manifests (first run), source edf-init-build-env, layer sync, machine generation from the SDT, copy of sw/meta-ntl-hive into the build tree, secure-boot config toggle. Repos from sw.clone are pulled in parallel unless --no-update is given. See hive-m.md.
buildhive build sw
| Flag | Description |
|---|---|
--rebuild | Wipe SW volume + bitbake linux-xlnx -c cleansstate first. |
--no-update | Skip git pull of cloned repos. |
-v, --verbose | Show container output. |
bitbake aionyx-image-minimal → images/sd.wic.
buildhive build kernel
| Flag | Description |
|---|---|
--rebuild | bitbake linux-xlnx -c cleansstate first. |
-v, --verbose | Show container output. |
bitbake linux-xlnx → images/Image.
buildhive build kernel-devsrc
Builds linux-xlnx, resolves source/build paths via bitbake -e, copies kernel source, build artifacts and .config into the case-sensitive kdevsrc Docker volume, writes env.sh (ARCH, CROSS_COMPILE, toolchain PATH). Prerequisite for kernel-oot, module, shell --module, code.
buildhive build kernel-oot
| Flag | Description |
|---|---|
--clean | make clean instead of build. |
Rebuilds the kernel Image directly from /kernel-devsrc (make -j Image modules), no bitbake → images/Image.
buildhive build module <name>
| Flag | Description |
|---|---|
name (positional) | Driver under sources/drivers/staging/ntl/<name>/. C identifier, max 64 chars. |
--clean | Clean before build. |
Scaffolds oot/<name>/Makefile if absent, cross-compiles the module against /kernel-devsrc → oot/<name>/*.ko.
buildhive build update
| Flag | Default | Description |
|---|---|---|
--legacy-migration | off | Build hive-m-migration-*.tar.gz (0.3.9 upgrade path) instead. Unsigned. |
--include <csv> | all | Components: raucb,kernel,dtb,bootscr,bootbin,fitimage,bitstream,dtbo. |
--select | off | Interactive component picker. |
--bitstream <file> | - | PL bitstream (.bit/.bin) for uwm import via /data/firmware/import. |
--dtbo <file> | - | DT overlay (.dtso compiled with dtc, .dtbo as-is); requires --bitstream. |
--no-sign | off | Skip remote signing (archive not installable by uwm). |
--sign-only | off | Sign the newest existing images/hive-m-update-*.tar.gz, no build. |
--sign-timeout <s> | 3600 | Poll timeout for the signing server. |
--no-update | off | Skip repo pulls. |
-v, --verbose | off | Show container output. |
Selecting the bitstream/dtbo component (via --include/--select) without the matching flag opens a file-picker dialog instead; conversely, a given flag adds its component to the selection automatically.
bitbake update-archive → images/update.raucb + images/hive-m-update-<version>.tar.gz, then remote-signs the archive → images/signed_hive-m-update-<version>.bin. See updates.md.
buildhive export sw
bitbake aionyx-image-minimal -c populate_sdk - builds the cross SDK and copies the installer (*.sh) plus Image to images/.
| Flag | Default | Description |
|---|---|---|
--image | off | Build the SDK Docker image from the installer (sdk/Dockerfile). Tags <tag>:<version> and <tag>:latest. |
--push | off | docker push both tags. Implies --image. Needs docker login ghcr.io. |
--tag | ghcr.io/nettimelogic/hive-m-sdk | Image repository. |
--version | tool version | Image version tag, also written to the org.opencontainers.image.version label. |
--no-build | off | Skip populate_sdk, use the newest *.sh in images/. |
See hive-m.md for the image layout and usage.
buildhive configure xsa
| Flag | Required | Description |
|---|---|---|
-f, --file | yes | .xsa file. Extracts the .bit, validates the header, copies to sources/sdt/. |
buildhive configure bitstream
| Flag | Required | Description |
|---|---|---|
-f, --file | yes | .bit file. Validates the header, copies to sources/sdt/. |
buildhive configure dts
| Flag | Required | Description |
|---|---|---|
-f, --file | yes | DTS include, copied to sw/meta-ntl-hive/recipes-bsp/device-tree/files/ntl-custom.dtsi. |
buildhive configure linux / configure uboot
Interactive bitbake virtual/kernel|virtual/bootloader -c menuconfig; the resulting diffconfig fragment is saved as a timestamped user_*.cfg in meta-ntl-hive so it persists across builds.
buildhive packages list / add <name>... / remove <name>...
Edit IMAGE_INSTALL:append in sw/meta-ntl-hive/conf/layer.conf. Names: lowercase letters, digits, + . _ -. The block is rewritten in the template layout, the rest of the file is untouched, so info project updates still 3-way merge it. Applied on the next build sw.
buildhive packages index
| Flag | Description |
|---|---|
-v, --verbose | Show container output. |
Scans every .bb under /work/sources in the SW container (name, layer, SUMMARY, DESCRIPTION, SECTION, RDEPENDS, fields also taken from required .inc files) and writes build/recipe-index.json. Host-only recipes are skipped: *-native, *-cross*, nativesdk-* and anything inheriting native, nativesdk, cross, crosssdk, cross-canadian, image, core-image. Re-run after changing the Yocto manifest.
buildhive packages search [term]
| Flag | Description |
|---|---|
-n, --limit | Maximum results (default 30). |
-g, --packagegroups | Only packagegroup-* recipes. Without a term lists them all. |
-l, --layer | Only recipes from this layer, e.g. poky. Without a term lists the layer. |
Case-insensitive search over the index, ranked: exact name, term as a whole word in RDEPENDS (gcc lists packagegroup-core-buildessential right after gcc), name prefix, whole word in summary/description, substrings. Lists recipes, not split runtime packages. The GUI Configure page has the same list/search under "Image packages".
buildhive secure-boot keygen
| Flag | Description |
|---|---|
--force | Overwrite existing keys. Fused devices keyed to the old PSK become unbootable. |
Generates keys/ (gitignored): psk.pem, ssk.pem (RSA-4096, BOOT.bin auth), fit.key/fit.crt (RSA-2048, FIT signing). See secure-boot.md.
buildhive secure-boot ppk-hash
Runs bootgen -efuseppkbits against the deployed FSBL → images/ppk0_hash.txt for eFUSE provisioning. Requires keys and a prior build sw.
buildhive shell
| Flag | Description |
|---|---|
--module <name> | Source /kernel-devsrc/env.sh, cd oot/<name> (must exist). |
--no-update | Skip repo pulls. |
-v, --verbose | Show container output. |
Interactive bash in the SW container with the Yocto environment sourced.
buildhive code
| Flag | Description |
|---|---|
--stop | Stop and remove the background dev container. |
Starts a detached dev container (/kernel-devsrc, oot/, sources/drivers/ mounted) and attaches VS Code to it. Requires the code CLI on PATH.
buildhive clean
| Flag | Description |
|---|---|
-a, --all | Also remove the shared ntl_buildhive_sstate volume (Yocto sstate + downloads). |
Removes and recreates the project SW volume.
GUI
PySide6 desktop front end for the CLI. Same features as the command line except the interactive targets (configure linux|uboot|buildroot, shell, code), plus recipe search and in-place project updates.
Starting
| How | Notes |
|---|---|
Start menu BuildHive | Runs buildhive-gui.exe, no console window. Installed by BuildHive_Setup.exe. |
buildhive gui | From any terminal. |
buildhive.exe without arguments | Same as buildhive gui, keeps the console window. |
python src/main.py | Development checkout, commands run through main.py instead of buildhive.exe. |
The most recently opened project opens at start. Docker Desktop is needed for builds, indexing and create, not for opening the GUI, editing packages or updating a project.
Window

| Area | Content |
|---|---|
| Sidebar | Pages: Project, Configure, Build & Deploy, Settings. Tool version under the name. Update to x.y.z appears when a newer release exists, see BuildHive updates. |
| Header | Project name, template tag, path. Show folder opens the project directory in the file manager. Right: Docker daemon state, checked at start and every 10 s. Docker not running means every command that needs Docker fails until Docker Desktop is up; the GUI keeps working. |
| Page | Cards per topic. Each card names what it does under its title. |
| Output | Log of the running command, see below. The splitter above it is draggable. |
Output panel
Every button that runs a command spawns buildhive <command> in the project directory and streams its output line by line into the panel. The line $ buildhive ... marks the start.
| Element | Behavior |
|---|---|
| Header text | Command and elapsed time while running, then Done (m:ss) or Failed, exit N. |
| Clear | Empties the log. |
| Stop | Terminates the command and runs docker stop on the project's containers. Enabled only while a command runs. |
| Colors | $ orange, ERROR: red, WARNING: yellow, OK: green, INFO: blue, rest grey. ANSI codes are stripped. |
One command at a time. While one runs, the Project, Configure and Build & Deploy pages are disabled; Settings stays usable. Closing the window stops a running command first.

Project
| Item | Action |
|---|---|
| Open folder | Directory picker. The directory needs a project.json. Ctrl+O works on every page. |
| Recent | Last 10 opened projects, newest first. Double-click opens, right-click removes the entry. Missing directories are dropped. |
| New project | buildhive create -n <name> -t <template> in the chosen parent directory, then opens the new project. Templates: hive-m, hive-s. |
Project update
Opening a project whose buildhive_version is older than the tool opens a dialog. Cancel keeps the project closed. Yes runs the template update in the GUI (no terminal), the same merge the CLI does on the next command in the project directory.

| Section | Meaning |
|---|---|
| Updated | Template file changed, yours did not. Overwritten, backup as .bak. |
| Added | New template file. |
| Merged | Both changed, 3-way merged against the stored baseline. Backup as .bak. |
| Merged with conflicts | Both changed the same lines. The file contains <<<<<<< markers. |
| Conflicts - skipped | Both changed, not mergeable (binary or no merge base). Per-file choice in the dialog: keep my version or take the template version. |
| Kept your version | You changed it, the template did not. |
| Removed | Dropped from the template, you had not changed it. |
| Removed from template | Dropped from the template but you changed it. Stays in the project. |
Finish update is enabled once no file contains merge markers. Resolve the markers in an editor, then Re-check. Finish writes the tool version into project.json and adds sections newer templates need. Cancel after a merge leaves the merged files in place, the version stays old and the dialog appears again on the next open.
A project created by a newer BuildHive than the installed one is refused with a message to update BuildHive.
Configure
hive-m

Boot bitstream and device tree. Inputs for Build SW image. The bitstream ends up in BOOT.bin and the SD image, so this is what a device boots from the card.
| Row | Command | Effect |
|---|---|---|
| XSA | configure xsa -f <file> | Extracts the .bit from the Vivado archive into sources/sdt/, header validated. |
| Bitstream | configure bitstream -f <file> | Copies a .bit into sources/sdt/. Same target as XSA, use one of the two. |
| Device tree | configure dts -f <file> | Copies the include to sw/meta-ntl-hive/recipes-bsp/device-tree/files/ntl-custom.dtsi, merged into the kernel device tree. |
Browse fills the field, Apply runs the command. For a device already in the field use the Update archive on Build & Deploy instead, which imports the bitstream at runtime.
Image packages. Edits IMAGE_INSTALL:append in sw/meta-ntl-hive/conf/layer.conf directly, no command. Applied on the next Build SW image. Same file as buildhive packages, see commands.md.
| Element | Behavior |
|---|---|
| Installed list | Current entries. Select one or more, Remove selected. |
| Add field | Package name, Add or Enter. Allowed: lowercase letters, digits, + . _ -. Several names separated by spaces. |
| Filters | All recipes / Packagegroups only, and All layers / one layer. With filters set and an empty field the list shows everything matching. |
| Suggestions | Filtered while typing. Ranking: exact name, recipes whose runtime dependencies contain the term (that is how gcc finds packagegroup-core-buildessential), name prefix, summary, substrings. Click fills the field, double-click adds. Hover shows the recipe description. |
| Build index / Rebuild index | Runs buildhive packages index in the build container and writes build/recipe-index.json. The hint line shows how many recipes are indexed and when. Rebuild after changing the Yocto manifest. |
The index lists recipes, not the split runtime packages a recipe produces (gcc is listed, gcc-symlinks is not). Host-only recipes (-native, -cross, nativesdk-, images) are excluded.
Kernel, U-Boot and Buildroot menuconfig are terminal-only: buildhive configure linux | uboot | buildroot.
hive-s

Bitstream: configure bitstream -f <file>, copied into the project and used by Build flash image.
Build & Deploy
Builds run with -v so the container output shows in the panel. Output files land in images/.
hive-m

Build
| Button | Command | Options |
|---|---|---|
| Build SW image | build sw -v | Rebuild adds --rebuild (clean the build volumes first). Skip repo pulls adds --no-update (no git pull of the cloned repositories). |
| Build kernel | build kernel -v | Kernel Image only. |
| Export SDK | export sw -v | Cross toolchain installer for application development, see hive-m.md. |
Update archive. Builds images/update.raucb from the last Build SW image for devices in the field, see updates.md.
| Element | Command / flag |
|---|---|
| Only these components | --include <list> with the checked components. Unchecked: default set (everything except fitimage). |
| Bitstream | --bitstream <file>, .bit or .bin. Imported by uwm on the device and installed as the boot bitstream. |
| DT overlay | --dtbo <file>, .dtso or .dtbo. Needs a bitstream. |
| Build update | build update -v with the flags above. |
| Skip signing | Adds --no-sign. |
| Sign existing | build update --sign-only -v, signs the archive already in images/. |
Signing needs the buildsign token from the secrets server or from Settings.
hive-s

| Button | Command | Options |
|---|---|---|
| Build HW | build hw -v | Vivado on PATH. RISC-V JTAG debug adds --debug. |
| Build SW | build sw -v | Buildroot. |
| Build flash image | build flash -v | Needs the bitstream from Configure and built software. |
| Deploy flash | deploy flash -v | Vivado on PATH, device connected. |
Clean artifacts
buildhive clean after confirmation. Deletes the Docker build volumes, so the next build starts from scratch. Sources, images/ and the sstate cache stay (clean --all for the sstate cache is CLI-only).

Settings

| Card | Content |
|---|---|
| Secrets server | Login runs buildhive login: browser login with the company SSO, token cached next to the GUI settings. Logout deletes the token. Needs an open project because the server URL comes from project.json. |
| BuildHive updates | Check for updates queries the GitHub releases now, logging in to the secrets server first if needed. Result goes to the output panel. |
| Credentials | Overrides for BUILDSIGN_TOKEN (signing) and SSTATE_MIRROR_AUTH (user:password). Passed to every command the GUI runs. Empty fields mean the command fetches them from the secrets server. Show toggles masking, Save writes both. |
BuildHive updates
Releases: NetTimeLogic/Tools, tags buildhive_v<x.y.z>, asset BuildHive_<x.y.z>_Setup.exe. The repo is private, so the GUI reads a GitHub token with Contents: read from the secrets server (buildhive/github, key GITHUB_TOKEN; GITHUB_TOKEN in the environment overrides it).
| Step | Behavior |
|---|---|
| Start | Silent check with the cached secrets login. No login, offline or API error: nothing happens. |
| Newer release | INFO: BuildHive x.y.z is available in the output, Update to x.y.z in the sidebar. |
| Update button | Confirmation, then the installer downloads to the temp dir and starts. BuildHive closes; the installer removes the old version and installs the new one (per user, no admin). Refused while a command runs. On Linux/macOS the button opens the releases page. |
| Errors | ERROR: Update failed: ... in the output, the button stays. |
Same from a terminal: buildhive upgrade (commands.md).
Settings file: gui.json in the OS config dir, %LOCALAPPDATA%\buildhive\ on Windows, ~/.config/buildhive/ on Linux. Plain text: the credential overrides and the recent project list.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
Docker not running in the header | Start Docker Desktop. The header updates within 10 s. |
Command fails at once with Docker is not running or accessible | Same, the command checked Docker itself. |
Failed, exit N | Read the log, the last ERROR: line names the cause. Rerun from a terminal with the same command for an interactive session. |
| Nothing happens on double-click of the Start menu entry | The windowed exe has no console to print to. Run buildhive.exe gui from a terminal to see the error. |
layer.conf not found - update the project first | The project predates the meta-ntl-hive layer. Reopen it to get the update dialog. |
| Suggestions stay empty | No index yet, click Build index. Needs Docker and the first build environment setup (repo sync), a few minutes. |
| Project update shows conflicts every time | Merge markers still in a file. Resolve them, Re-check, Finish update. |
Implementation
| File | Content |
|---|---|
src/gui/app.py | QApplication, Fusion style, dark palette, stylesheet. |
src/gui/main_window.py | Sidebar, header, page stack, output panel, run_cli(), Docker check, project open and update flow. |
src/gui/pages.py | The four pages. card() = titled section, ActionRow = button, description, options, FileRow = path field with Browse. |
src/gui/runner.py | CommandRunner thread: Popen of buildhive.exe next to the GUI exe (or main.py in a checkout), CREATE_NO_WINDOW, credentials via environment. DockerCheck thread. |
src/gui/update.py | Project update dialog, calls Project.update() and finish_update() in-process. |
src/gui/app_update.py | UpdateCheck and UpdateInstall threads around src/selfupdate.py, see BuildHive updates. |
src/gui/settings.py | gui.json load/save. |
src/gui/style.py | Qt stylesheet, accent #ffaa28. |
tools/gui_screenshots.py | Renders the screenshots on this page into docs/images/gui/. |
buildhive.spec produces two exes from one analysis: buildhive.exe (console) and buildhive-gui.exe (console=False). Both run main.py; without arguments it starts the GUI, which spawns the console exe for commands. The windowed exe has no stdout or stderr, so nothing on the GUI path may exit on a failed print or on a missing Docker daemon: state.connect() runs only on the CLI path, stop_container() is a no-op without a connection.
Project configuration
Files in the project root
| File | Purpose |
|---|---|
project.json | Project configuration (schema below). Never touched by template updates. |
.buildhive_manifest.json | SHA-256 per template file; drives change detection on template updates. |
.buildhive_baseline/ | Pristine copies of text template files; base for 3-way merges. |
project.json is loaded from the current working directory only (no upward search). A directory counts as a project if project.json exists, parses as a JSON object and has a name key. Optional fields with value null are omitted on save (exclude_none).
Common fields (both templates)
| Field | Type | Default | Meaning |
|---|---|---|---|
id | string | generated | UUID hex, set on first load. Names the per-project Docker volumes/containers. |
name | string | - | Project name. Letters, digits, -, _; max 128 chars. |
template | string | - | hive-s or hive-m. Selects model and command set. |
buildhive_version | string | tool version | Version of the tool that last wrote the project. A mismatch with the running tool triggers the update flow (see development.md). |
Git object used below: { "url": string, "branch": string }.
Hive-S schema
{
"name": "...",
"template": "hive-s",
"sw": {
"buildroot": { "url": "...", "branch": "..." },
"uwm": { "url": "...", "branch": "..." }
},
"hw": {
"args": "--cpu-count=1 --with-rvc",
"debug_args": "--ntl-debug --with-privileged-debug --hardware-breakpoints 4"
},
"flash": {
"opensbi.bin": { "src": "0x...", "dst": "0x..." }
}
}
| Field | Meaning |
|---|---|
sw.buildroot | Buildroot repo, cloned in the SW container, built with ntl_defconfig. |
sw.uwm | UWM repo (private, SSH). Built before Buildroot; loader copied into the rootfs overlay. |
hw.args | Arguments for the LiteX SoC generator (make.py). |
hw.debug_args | Appended to args with build hw --debug. |
flash | Map of image filename → {src, dst} hex address strings (parsed base-0). src = flash offset, dst = RAM load address (optional; e.g. uwm.xz has none). Consumed by build flash / info flash. Empty map is fatal at build. |
Hive-M schema
{
"name": "...",
"template": "hive-m",
"secrets": {
"url": "https://secrets.ntl.dev",
"role": "dev"
},
"sw": {
"yocto_manifests": { "url": "...", "branch": "..." },
"clone": {
"uwm": { "url": "...", "branch": "master" },
"udm": { "url": "...", "branch": "master" },
"drivers": { "url": "...", "branch": "master" }
},
"buildsign": {
"url": "https://sign.ntl.dev/api",
"public_key": "<base64 Ed25519 key>"
},
"secure_boot": false,
"sstate_mirror": {
"url": "https://sstate.ntl.dev",
"hashserv": "wss://hashserv.ntl.dev"
}
}
}
| Field | Type/Default | Meaning |
|---|---|---|
secrets | optional | OpenBao server holding build secrets. url: API base. role: OIDC auth role (default dev). mount: KV v2 mount (default buildhive). Secrets read: <mount>/mirror key SSTATE_MIRROR_AUTH, <mount>/users/<login email>/<sw.buildsign.secret> then <mount>/<sw.buildsign.secret>, key BUILDSIGN_TOKEN. Lookup order per secret: env var of the same name, BAO_TOKEN env (CI), cached OIDC token, browser login (buildhive login). Server errors degrade: warning and mirror off, signing fatal. Without a secrets block only the env vars are used. |
sw.yocto_manifests | Git | repo init -u <url> -b <branch> on first container run. |
sw.clone | dict of Git, optional | Extra repos, cloned/pulled in parallel into sources/<name> and mounted at /custom_sources/<name>. Yocto recipes fetch uwm/udm from there (SRCREV=${AUTOREV}). --no-update skips the pull. |
sw.buildsign | optional | Signing server for update archives. url: API base. public_key: pinned base64 Ed25519 key (32 bytes) used to verify signatures locally; if unset, verification is skipped with a warning. secret: KV path under the secrets mount holding key BUILDSIGN_TOKEN (default buildsign). Lookup: users/<login email>/<secret> (personal token) first, then <secret> (shared). Without a buildsign block, build update requires --no-sign. |
sw.secure_boot | bool, false | true: requires keys/psk.pem (fatal otherwise), adds require .../secure-boot.conf to local.conf, switches the update archive to signed-FIT components. false: strips the require line. See secure-boot.md. |
sw.sstate_mirror | optional | Shared sstate/download mirror (see sstate-mirror/README.md). url: HTTPS base serving sstate-cache/ and downloads/; written to local.conf as SSTATE_MIRRORS, SOURCE_MIRROR_URL, INHERIT += "own-mirrors" between # >>> / # <<< buildhive sstate mirror markers on every run. hashserv: optional BB_HASHSERVE address (wss://host through the mirror proxy or host:8687 plain), sets BB_SIGNATURE_HANDLER = "OEEquivHash". Credentials user:password (basic auth) from SSTATE_MIRROR_AUTH env or the secrets server (secrets field); written to ~/.netrc inside the container, never to local.conf. Unavailable: warning, marker block removed, build uses the local cache only. |
Constants
| Constant | Value | Use |
|---|---|---|
VERSION | src/constants.py:1 | Single source of the tool version. Tags Docker images, stamps project.json, update archives, the installer. |
CONTAINER_USER / UID | ntl / 1000 | In-container user. |
WORK_DIR | /work | Yocto work tree, backed by ntl_buildhive_sw_<id>. |
SSTATE_DIR | /mnt/ntl_sstate | Shared sstate/download cache, backed by ntl_buildhive_sstate. |
Hive-S internals
LiteX/VexRiscv RISC-V soft-SoC on Artix-7. Buildroot rootfs, UWM firmware, single SPI flash image.
Project layout
| Path | Content |
|---|---|
project.json | Config: repos, SoC args, flash mapping. |
container/ | Dockerfile (RISC-V toolchain, LiteX pinned to tag 2025.04, bun), entrypoint. |
hw/ | LiteX SoC generator: make.py, soc_linux.py, vhdl/, xci/ (AXI clock converter IP). |
buildroot/ | BR2_EXTERNAL tree: configs/ntl_defconfig, board/hive-s/ (kernel config, post-image), patches/ (LiteX/LiteEth/LiteSPI/VexRiscv kernel patches, vexriscv-aes ssl patches), rootfs_overlay/ (motd, S99uwm.sh, /opt/ntl env + CA + uwm-loader). |
images/ | Build outputs + bitstream.bin. Never touched by template updates. |
tools/ | Vivado/OpenOCD flash scripts (flash_device.tcl, FTDI/Arty configs, bscan-spi bitstream). |
ip/ | Generated IP cores (output of build hw). |
Docker volumes: ntl_buildhive_hw_<name>_<id> and ntl_buildhive_sw_<name>_<id>. Host mounts: images/, hw/ (ro, HW container), buildroot/ (ro, SW container).
build hw
- HW container: pulls LiteX + VexRiscv sources, runs
./make.py <hw.args>- generates gateware sources and the device tree (rv32.dtb). - Rewrites paths in the generated
aionyx_hive_s.tclfrom container to host paths. - Host:
vivado -mode batch -source aionyx_hive_s.tclbuilds the IP cores, copied toip/.
Bitstream generation itself is manual in Vivado; import the result with buildhive configure bitstream -f <file> (→ images/bitstream.bin).
build sw
SW container, two stages:
- UWM - clone
sw.uwmintosources/uwm,make out/hive-s_full_tls→images/uwm.xz+uwm-loader; loader also copied intobuildroot/rootfs_overlay/opt/ntl. - Buildroot - clone
sw.buildroot,make ntl_defconfig(BR2_EXTERNAL=/buildroot),make -j→images/:Image.lzma,rootfs.cpio.xz,u-boot.bin,u-boot.dtb,opensbi.bin,rv32.dtb.
Flash image
build flash runs on the host, no Docker:
- Each
flashmapping entry is wrapped as.fbi: 4-byte length + 4-byte CRC32 + data. mapping_table.binholds the sorted (src, dst) address pairs.images/flash.binlayout: bitstream at offset 0, each component atsrc − 0x01000000, mapping table at0x3F0000.
At boot the SoC firmware walks the mapping table and copies each blob from its flash src to its RAM dst. Entries without dst (e.g. uwm.xz) stay in flash. info flash prints the table and the assembled layout.
deploy flash
vivado -mode gui -source tools/flash_device.tcl - programs the SPI flash over JTAG. Prompts for device selection if several are connected.
Hive-M internals
Xilinx ZynqMP, Yocto build (aionyx-image-minimal), RAUC A/B rootfs, uwm updater.
Project layout
| Path | Content |
|---|---|
project.json | Config: yocto manifests, clone repos, buildsign, secure_boot. |
container/ | Dockerfile: Ubuntu 24.04 Yocto host, repo tool, user ntl (uid 1000). |
images/ | system.xsa input + build outputs (sd.wic, Image, update archives). |
sources/sdt/ | System Device Tree (zynqmp*.dtsi, dt-bindings/), input to gen-machine-conf. Bitstream from configure xsa/configure bitstream lands here. |
sources/<repo> | Clones from sw.clone (uwm, udm, drivers). |
sw/meta-ntl-hive/ | The NTL Yocto layer, copied into the build tree on every run. |
sdk/ | Dockerfile + examples for the SDK image (export sw --image). |
oot/<module>/ | Out-of-tree module build dirs (created by build module). |
keys/ | Secure-boot keys (after secure-boot keygen, gitignored). |
Docker volumes
| Volume | Mount | Scope |
|---|---|---|
ntl_buildhive_sw_<name>_<id> | /work (.repo, build/) | per project |
ntl_buildhive_sstate | /mnt/ntl_sstate (sstate-cache/, downloads/) | shared across all hive-m projects; removed only by clean --all |
kdevsrc volume | /kernel-devsrc | per project, filled by build kernel-devsrc |
Bind mounts: sw/ → /sw (ro), sources/sdt → /custom_sources/sdt, keys/ → /keys (ro, if present), each clone repo → /custom_sources/<name>.
Init script
Prepended to every containerized hive-m command:
- Exports
BUILDHIVE_VERSION(tool version) viaBB_ENV_PASSTHROUGH_ADDITIONS- default version for update/migration archives. - First run:
repo init -u <yocto_manifests.url> -b <branch>,repo sync. source edf-init-build-env.- Reconciles
conf/bblayers.confwith layers on disk (removes stale, adds missing). - First run only: sets
SSTATE_DIR/DL_DIRinlocal.conf, runsgen-machine-conf parse-sdt --hw-description /custom_sources/sdt --machine-name zynqmp-ntl-hive-m-sdt, creates+addsmeta-ntl-hive, setsMACHINE ??= ntl-hive-m. - Every run: re-copies
/sw/meta-ntl-hive/*into../sources/meta-ntl-hive/- layer edits in the project take effect on the next build. - Adds/removes the
secure-boot.confrequire line inlocal.confpersw.secure_boot. - Rewrites the
# >>> buildhive sstate mirrorblock inlocal.confpersw.sstate_mirror; credentials fromSSTATE_MIRROR_AUTHenv or the secrets server, written to~/.netrc. Mirror misses fall back to the localntl_buildhive_sstatecache and a normal build. - Rewrites the
# >>> buildhive rm_workblock:INHERIT += "rm_work"withRM_WORK_EXCLUDE += "linux-xlnx aionyx-image-minimal". Work dirs are deleted after packaging (tmp/shrinks several times), sodevshell/tmp/workinspection only works for the excluded recipes;kernel-devsrcandexport sdk(busybox from the imagerootfs/) keep working. Existing projects reclaim space on the next build.
Repos in sw.clone are pulled in parallel before the container starts; --no-update skips this.
meta-ntl-hive layer
conf/layer.conf: priority 7, compatible scarthgap, depends on aionyx-hive. IMAGE_INSTALL:append adds extra packages to the image (template: ptp4l-config); edit with buildhive packages add|remove or the GUI Configure page, see commands.md.
| Group | Content |
|---|---|
recipes-apps | uwm (Rust server + loader, systemd uwm-loader.service), udm, ptp4l + configs, phc2sys-sync (systemd service syncing system clock from PHC, device set via /etc/default/phc2sys), sw-conf. uwm/udm fetch from /custom_sources/* with SRCREV=${AUTOREV}. |
recipes-bsp | device-tree bbappend (FIT pubkey injection), u-boot bbappends + boot script. |
recipes-core | base-files, update bundles (updates.md), image bbappend, legacy migration, persistent-data, rauc config, systemd-conf. |
recipes-devtools | bun-native, mdbook-native (uwm frontend/docs), wic fix. |
recipes-kernel | linux-xlnx bbappend + bsp.cfg. |
recipes-security | hive-ca, hive-jwt-secret. |
wic/rauc-zynqmp.wks | Disk layout (below). |
conf/machine/ntl-hive-m.conf requires the SDT-generated machine and backs /etc, /opt/ntl, /usr/lib/firmware overlays with /data.
Serial console lock
recipes-bsp/u-boot/u-boot-xlnx/console.cfg + patch 0002-configs-zynqmp-restrict-stored-env-*. Contract consumed by uwm (docs/src/security/serial-console.md in the uwm repo).
| Layer | Mechanism |
|---|---|
| U-Boot prompt | CONFIG_AUTOBOOT_KEYED + CONFIG_AUTOBOOT_ENCRYPTION: autoboot stops only on the password whose salted SHA256 is CONFIG_AUTOBOOT_STOP_STR_SHA256 (salt:hash). Hash lives in the signed u-boot binary, not in the env. |
| Prompt on/off | bootdelay in uboot.env (/boot, fw_setenv): 2 = password prompt, -2 = boot straight through. preboot maps -1 to 2; -1 would otherwise drop to the shell without a password check. |
| Stored env | CONFIG_ENV_WRITEABLE_LIST: uboot.env may only override bootdelay, BOOT_ORDER, BOOT_A_LEFT, BOOT_B_LEFT (CFG_ENV_FLAGS_LIST_STATIC). bootcmd, preboot, bootstopkeysha256 always come from the default env. Add new persisted vars to the list or u-boot silently drops them. |
| Linux login | serial-getty@ttyPS0.service (from SERIAL_CONSOLES), enable symlink in the /etc overlay. systemctl disable --now / enable --now by uwm. |
FSBL, PMUFW, ATF and U-Boot still print on UART0. Output only, no input path. JTAG not covered.
New password:
SALT=$(openssl rand -hex 4); printf '%s%s' "$SALT" "$PASSWORD" | sha256sum | awk -v s="$SALT" '{print s":"$1}'
Put the result in CONFIG_AUTOBOOT_STOP_STR_SHA256, rebuild u-boot. At the prompt type the password (no echo) during the delay.
SDK image
export sw --image wraps the populate_sdk installer into a standalone container for application developers: no Yocto tree, no buildhive.
| Item | Value |
|---|---|
| Image | ghcr.io/nettimelogic/hive-m-sdk:<version> (+ latest), linux/amd64 only (Yocto SDKMACHINE default) |
| Base | ubuntu:24.04 + build-essential (kernel HOSTCC), git, kmod, pkg-config, python3. cmake/ninja/ctest/qemu come from the SDK. |
| SDK | installer bind-mounted at build (RUN --mount), -y -d /opt/ntl-sdk; nativesdk locale-archive, non-aarch64 qemu-system-*/qemu-* and their firmware removed |
| Env | /etc/profile.d/ntl-sdk.sh sources environment-setup-* and sets CMAKE_CROSSCOMPILING_EMULATOR; pulled in by the entrypoint, /etc/bash.bashrc and login shells (docker run, docker exec, GitHub Actions bash -l) |
| qemu | qemu-aarch64 user-mode from nativesdk-qemu-xilinx; ntl-run <binary> wrapper, ctest transparent, QEMU_LD_PREFIX set so direct execution works on hosts with aarch64 binfmt_misc (Docker Desktop). System emulation uses Ubuntu qemu-system-arm (has virtfs/9p, the Xilinx build does not); Xilinx qemu-system-* and firmware removed. |
| User | ntl (uid 1000, passwordless sudo), WORKDIR /src |
| Kernel | kernel-devsrc in $SDKTARGETSYSROOT/usr/src/kernel, scripts prepare modules_prepare run at image build |
| Examples | /opt/ntl-sdk/examples/ (hello: C, CMake, Makefile; hello-module: out-of-tree kernel module) |
| modtest | ntl-modtest [-d overlay.dtso] [-r regs.txt] [-k] [-v] <module.ko>...: boots /opt/ntl-sdk/Image (copied from images/Image) in qemu-system-aarch64 -M virt -m 512M, initramfs = modtest/init (C, PID 1) + ld-linux + libc from the sysroot. Exit 0 on MODTEST: PASS. |
| shell | ntl-shell [-d overlay] [-r regs] [-m mod.ko]... [dir]: interactive busybox shell on the same kernel and mock. Base initramfs (/opt/ntl-sdk/busybox, copied from the image rootfs by export sw since target-sdk-provides-dummy keeps shells out of the SDK sysroot; usr/lib/*.so*, linuxptp, ntl-reg) cached in ~/.cache/ntl-shell, session archive (init script, overlay, regs, modules, /src copy) appended, one gzip stream, rootfstype=ramfs (tmpfs rootfs hits ENOSPC while unpacking). /src over 9p and eth0 via user networking when .config has 9P_FS/VIRTIO_NET. |
| PHC mock | ntl-shell -p <base>: ntl-mockphc daemon in the guest serving the ntl_phc register protocol (modtest/ntl-mockphc.c, header comment has the map): done bit permanent, time regs ticking from CLOCK_MONOTONIC + offset, settime/adjtime/adjfine command bits consumed. Makes phc_ctl, phc2sys, ptp4l usable without hardware. |
| HW mock | Kernel booted with mem=448M: 0x5c000000..0x5fffffff is guest RAM outside the kernel map, so ioremap and /dev/mem work on it. init writes regs.txt values there, applies the overlay via /sys/kernel/config/device-tree/overlays (target &{/}), loads modules, reports bound devices from /sys/module/<m>/drivers, dumps registers, unloads. Verified with ntl_phc (two cores probe, PTP clocks register). Registers are inert memory: no counters, no side effects. |
Build context is a temp dir under images/ holding only sdk/ and the installer (hardlinked when possible), so the wic files never reach the docker daemon. aionyx-image-minimal.bbappend sets TOOLCHAIN_HOST_TASK (+nativesdk-cmake/ninja/pkgconfig, makes the env script export CMAKE_TOOLCHAIN_FILE), TOOLCHAIN_TARGET_TASK (+kernel-devsrc) and SDKIMAGE_FEATURES = "dev-pkgs" (no dbg-pkgs/src-pkgs, ~1 GB less).
Kernel module: make -C $SDKTARGETSYSROOT/usr/src/kernel M=$PWD modules, see /opt/ntl-sdk/examples/hello-module. Module vermagic matches the kernel of the build the SDK came from; keep SDK version and firmware version paired. kernel-devsrc has no git metadata, so the Dockerfile derives the -g<sha>[-dirty] suffix from usr/lib/modules/<release> and passes it as LOCALVERSION to make prepare (exported in the profile too), failing the build on mismatch.
Usage:
docker run --rm -it -v "$PWD":/src ghcr.io/nettimelogic/hive-m-sdk:0.5.1 # shell
docker run --rm -v "$PWD":/src ghcr.io/nettimelogic/hive-m-sdk:0.5.1 make # one-shot
docker run --rm -v "$PWD":/src ghcr.io/nettimelogic/hive-m-sdk:0.5.1 sh -c 'cmake -B build -G Ninja && cmake --build build'
GitHub Actions (container: jobs skip the entrypoint, use a login shell):
jobs:
build:
runs-on: ubuntu-latest
container: ghcr.io/nettimelogic/hive-m-sdk:0.5.1
defaults: { run: { shell: bash -leo pipefail {0} } }
steps:
- uses: actions/checkout@v4
- run: cmake -B build -G Ninja && cmake --build build && ctest --test-dir build
Publish flow: buildhive export sw --push (or --no-build --push on an existing installer) after docker login ghcr.io. Match --version to the firmware release the SDK was built from.
recipes-kernel/linux/linux-xlnx/qemu.cfg adds virtio-mmio/pci, virtio-net/blk/console, 9p and PCI_HOST_GENERIC to the Hive-M kernel. Inert on the board, lets ntl-shell share /src and network under -M virt; virtio-blk is the path to booting sd.wic in qemu later. export sw also copies rootfs/usr/bin/busybox to images/busybox for the guest shell.
Disk layout (rauc-zynqmp.wks)
| Partition | Size | Content |
|---|---|---|
p1 /boot | 512M FAT | BOOT.bin, kernel, dtb, boot.scr, uEnv.txt, uboot.env |
| p2 root_a | 2G | squashfs, RAUC slot A |
| p3 root_b | 2G | empty, RAUC slot B |
p4 /data | 512M ext4 | persistent overlay upper (/etc, /opt/ntl); never updated |
p5 /home | ext4 | growable (x-systemd.growfs) |
Kernel development
build kernel-devsrc -> /kernel-devsrc volume: kernel source + build tree + env.sh
build module <name> -> oot/<name>/*.ko (scaffolds Kbuild Makefile, builds against /kernel-devsrc)
build kernel-oot -> images/Image (make -j Image modules, no bitbake)
shell --module <name> -> bash with env.sh sourced, cwd oot/<name>
code -> VS Code attached to dev container (/kernel-devsrc, /oot, /drivers)
env.sh sets ARCH, CROSS_COMPILE and the toolchain PATH. Module sources live in sources/drivers/staging/ntl/<name>/ (from the drivers clone repo). Re-run build kernel-devsrc after kernel config changes.
Update system (Hive-M)
Two layers: a RAUC bundle for the rootfs, wrapped in a uwm update archive that also carries boot artifacts.
RAUC bundle
recipes-core/bundles/update-bundle.bb: verity-format bundle, single slot rootfs = aionyx-image-minimal squashfs, signed with the RAUC key/cert from the layer (ntl-20260629.*). Output: update-bundle-ntl-hive-m.raucb.
Update archive
recipes-core/bundles/update-archive.bb packages the components in UPDATE_COMPONENTS plus a manifest.json into a flat hive-m-update-<version>.tar.gz applied by uwm.
| Component | Archive name | Destination |
|---|---|---|
raucb | update.raucb | RAUC install into the inactive slot |
kernel | Image | /boot/Image |
dtb | system.dtb | /boot/system.dtb |
bootscr | boot.scr | /boot/boot.scr |
bootbin | BOOT.bin | /boot/BOOT.bin |
fitimage | fitImage | /boot/fitImage (secure boot only) |
bitstream | source basename | /data/firmware/import/<name> (uwm import) |
dtbo | source basename (.dtso becomes .dtbo) | /data/firmware/import/<name> (uwm import) |
Default set: raucb kernel dtb bootscr bootbin; with SECURE_BOOT=1: raucb fitimage bootscr bootbin. Overridable per build via build update --include/--select (passed through as UPDATE_COMPONENTS).
Custom bitstream / DT overlay
build update --bitstream <file.bit|.bin> [--dtbo <file.dtso|.dtbo>] (or select the bitstream/dtbo component via --include/--select without the flag — a file-picker dialog opens) stages the files into images/update-fw/ (mounted at /images/update-fw); recipes-core/bundles/update-firmware.bb deploys them, compiling a .dtso with dtc (no -@, see sources/overlay/fpga-overlay.dtso). Both components map into /data/firmware/import/, where uwm's import_staged_boot_bitstream() picks them up on next start: validates, inserts as the boot bitstream (previous boot flag cleared by DB trigger), programs the FPGA; files are deleted only after a successful import.
Constraints (uwm side): one bitstream + one overlay per archive; an overlay without a bitstream is skipped (buildhive rejects --dtbo alone); shutdownReason must not be bitstreamUpdate (default systemReboot is correct — bitstreamUpdate programs the FPGA at shutdown before the import ran and skips cold-boot programming).
manifest.json: version, buildDate, fileMappings, optional minRequiredVersion, shutdownReason.
Version: UPDATE_VERSION defaults to BUILDHIVE_VERSION, which the init script exports from the tool version (src/constants.py). Manual bitbake outside buildhive falls back to 0.0.0.
Signing
The uwm updater only installs signed archives. build update signs automatically unless --no-sign; --sign-only signs an existing archive.
Flow (src/signing.py):
- Requires
BUILDSIGN_TOKEN(env var, else secrets server, see configuration.md) and NTL VPN reachability to thebuildsign.urlserver. - SHA-256 of the archive is computed locally and POSTed to
/signing-requests- the artifact itself is never uploaded. Payload: hash, filename, version, size, comment, pinned public key (server refuses other key material). - Polls every 10 s until
signed/rejected(exit 2) / timeout (default 3600 s, exit 3). - Signature (Ed25519, 64 bytes) is verified locally against the pinned
sw.buildsign.public_keyover"UWM-UPDATE-v1" + sha256. No pinned key: verification skipped with a warning. - Output:
images/signed_hive-m-update-<version>.bin= 64-byte signature followed by the raw tar.gz.
Legacy migration (0.3.9 → RAUC layout)
build update --legacy-migration builds hive-m-migration-<version>.tar.gz via recipes-core/migration/legacy-migration-update.bb - an archive in the old uwm format (unsigned) containing: new Image, system.dtb, BOOT.bin, RAUC boot.scr, a migration boot script, the migration initramfs, the rootfs squashfs, a migration.sha256 manifest.
Sequence on device:
- Old uwm-loader writes the mapped files to
/boot, reboots. - Migration
boot.scrboots the migration initramfs (migration-image.bb: RAM-only cpio withmigration-init, sfdisk, mke2fs, dosfstools). - Initramfs repartitions the SD card into the RAUC layout (hive-m.md), installs slot A, reboots into the new system.
MIGRATION_VERSION defaults to BUILDHIVE_VERSION like UPDATE_VERSION.
Secure boot (Hive-M)
Full chain: eFUSE PPK hash → RSA-signed BOOT.bin (FSBL, PMUFW, ATF, u-boot, dtb via bootgen) → signed FIT (kernel + dtb) verified by u-boot → RAUC-signed rootfs → buildsign-signed update archives.
Detailed device-side documentation: sw/SECURE_BOOT.md in a hive-m project (template: src/templates/hive-m/sw/SECURE_BOOT.md).
Setup
buildhive secure-boot keygen # generate keys/ (once)
# set "secure_boot": true in project.json (sw section)
buildhive build sw
buildhive secure-boot ppk-hash # -> images/ppk0_hash.txt for eFUSE provisioning
Keys (keys/, gitignored)
| File | Type | Use |
|---|---|---|
psk.pem | RSA-4096 | Primary secret key; PPK hash burned into eFUSEs. |
ssk.pem | RSA-4096 | Secondary secret key; signs BOOT.bin partitions. |
fit.key / fit.crt | RSA-2048, 30-year self-signed cert | FIT image signing; pubkey injected into u-boot control dtb. |
keygen --force overwrites - devices fused to the old PSK become unbootable.
Effect of sw.secure_boot: true
- Build fails if
keys/psk.pemis missing. require .../meta-ntl-hive/conf/secure-boot.confadded tolocal.conf. That conf setsSECURE_BOOT=1, the bootgen BIF (bh_auth_enableso signed images still boot unfused silicon; per-partition RSA authentication; bitstream deliberately unauthenticated),kernel-fitimage,UBOOT_SIGN_ENABLE=1,FIT_SIGN_ALG=rsa2048.device-tree.bbappendinjects the FIT public key into u-boot's control dtb withrequired="conf"- u-boot rejects unsigned FITs.- Update archive component set switches to
raucb fitimage bootscr bootbin(signed FIT replaces raw Image/dtb).
sw.secure_boot: false strips the require line from local.conf on the next run.
eFUSE provisioning
secure-boot ppk-hash runs bootgen (-efuseppkbits) against the deployed FSBL and the PSK → images/ppk0_hash.txt. Burn PPK0 hash + RSA_EN per the device documentation. After RSA_EN is fused, the device only boots BOOT.bin images signed with the matching PSK/SSK.
Development
Repo layout
| Path | Content |
|---|---|
src/main.py | Entry point; signal handlers stop running containers on Ctrl-C. |
src/commands/__init__.py | Command tree engine: nested dict (_help, _handler, _args) → argparse subparsers; unique-prefix abbreviation; version-mismatch dispatch. |
src/commands/common/ | create, gui, login/logout, upgrade, info project/secrets, configure project. |
src/commands/hive_s/, src/commands/hive_m/ | Template command sets. |
src/commands/hive_m/common/__init__.py | Init script prepended to every hive-m container run. |
src/project.py | Project load/save/create, Docker image build, container/volume orchestration. |
src/state.py | Docker client plus the running container/process handles the Ctrl-C handler stops. |
src/common.py | Template copy, hash manifest, baseline, smart_update (3-way merge). |
src/models/ | Pydantic models for project.json. |
src/validate.py | Argument validation: project names, module names, file paths. |
src/log.py | Console output with the INFO:/WARNING:/ERROR:/OK: prefixes the GUI colors by. |
src/vault.py | Secrets server client: OIDC login, token cache, secret lookup. |
src/signing.py | Buildsign client. |
src/selfupdate.py | Release lookup on GitHub, installer download and start (upgrade, GUI update button). |
src/constants.py | VERSION, container constants, SECRETS_URL, SDK_IMAGE. |
src/templates/ | default (shared skeleton), hive-s, hive-m project templates. |
src/gui/ | PySide6 GUI, see gui.md. |
src/layerconf.py, src/recipe_index.py, src/scripts/recipe_indexer.py | IMAGE_INSTALL:append editing and the recipe index (packages commands). recipe_indexer.py is shipped as a data file and run with python3 inside the container. |
tests/ | pytest suite, no Docker needed: python -m pytest tests. |
tools/gui_screenshots.py | Renders docs/images/gui/*.png for the GUI docs. |
docs/ | mdBook sources (book.toml, SUMMARY.md), published to GitHub Pages. |
buildhive.spec | PyInstaller spec, one analysis, two exes (buildhive.exe, buildhive-gui.exe). |
setup.iss | Inno Setup installer script. |
dist/ | PyInstaller output + BuildHive_Setup.exe. |
Release process
The version lives in one place: src/constants.py (VERSION). Everything else derives from it:
setup.issparses it at compile time (ISPP reads the first line ofconstants.py).- Hive-M containers export it as
BUILDHIVE_VERSIONthroughBB_ENV_PASSTHROUGH_ADDITIONS;update-archive.bbandlegacy-migration-update.bbdefaultUPDATE_VERSION/MIGRATION_VERSIONfrom it (fallback0.0.0for manual bitbake). project.jsonbuildhive_versionis stamped on save; a mismatch with the running tool triggers the update flow.upgradeand the GUI compare it against the newest GitHub release tag.
Release:
- Bump
VERSIONinsrc/constants.py. make build(PyInstaller,dist/buildhive/), then compilesetup.iss→dist/BuildHive_Setup.exe.- GitHub release in NetTimeLogic/Tools: tag
buildhive_v<x.y.z>, asset uploaded asBuildHive_<x.y.z>_Setup.exe. Drafts and pre-releases are ignored by the update check; the asset name must matchBuildHive_*_Setup.exe.
The installer (setup.iss) installs per user into %APPDATA%\NTL\BuildHive\bin, no admin. Same AppId on every version: Setup replaces the uninstall entry itself and deletes the old bin/ before copying, so an upgrade needs no separate uninstall. The upgrade command starts the downloaded installer two seconds after the CLI exits so bin/ is not in use. Uninstall removes the PATH entry and the app directory.
Documentation
docs/ is an mdBook. README.md is the front page: the workflow copies it to docs/README.md with docs/ stripped from its links (docs/README.md and docs/book/ are gitignored). Build locally:
sed 's#](docs/#](#g' README.md > docs/README.md
mdbook build docs # or: mdbook serve docs
.github/workflows/buildhive-docs.yml builds on every push to master touching buildhive/docs/** or buildhive/README.md and deploys to GitHub Pages (repository setting Pages > Source: GitHub Actions). New pages go into docs/SUMMARY.md.
GUI screenshots: python tools/gui_screenshots.py on a desktop session, no Docker needed. Rerun after visible GUI changes.
Template update mechanism
State per project: .buildhive_manifest.json (SHA-256 of every template file at last sync) and .buildhive_baseline/ (pristine text-file copies).
On version mismatch where the project is older than the tool, the user is prompted to update. Per file, three hashes decide (old manifest, new template, current disk):
| Situation | Action |
|---|---|
| Template unchanged, user changed | keep user file |
| Template changed, user unchanged | overwrite (backup .bak) |
| Both changed | 3-way merge via git merge-file against the baseline; conflicts get git-style markers |
| Unmergeable (binary / no baseline) | prompt: keep yours / take template |
| File removed from template | kept on disk, reported |
| New template file | copied in |
images/ and project.json are never touched. Unresolved conflicts keep their old manifest hash, so they are re-prompted on the next update. buildhive_version is bumped only after all conflicts are resolved - an aborted update prompts again.
If the project is newer than the tool, no files are modified; the tool asks you to upgrade BuildHive.
Template edits during development: hive-m copies sw/meta-ntl-hive into the Yocto build tree on every container start, so layer changes in a project take effect on the next build without any sync step. Changes to src/templates/ reach existing projects only through the version-mismatch update flow.