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.

PlatformSoCBuild systemRootfs
Hive-SLiteX/VexRiscv RISC-V soft-SoC (Artix-7)Buildrootinitramfs (cpio)
Hive-MXilinx ZynqMPYocto (bitbake)squashfs, RAUC A/B

Documentation

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 code CLI on PATH - only for buildhive code
  • Company SSO account - build secrets (BUILDSIGN_TOKEN, SSTATE_MIRROR_AUTH) come from the secrets server (secrets in project.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

Commandhive-shive-mDockerOther 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 linuxinteractive terminal
configure buildroot-interactive terminal
configure uboot-interactive terminal
configure bitstream-.bit file
configure xsa--.xsa file
configure dts--.dtsi file
build swgit 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

FlagRequiredDescription
-n, --nameyesProject name. Letters, digits, -, _; max 128 chars.
-t, --templateyeshive-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

FlagDescription
--checkOnly report installed vs. latest version.
--silentInstaller 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

FlagDescription
-n, --nameRename the project.

Hive-S commands

buildhive build hw

FlagDescription
-d, --debugAppend hw.debug_args (RISC-V JTAG debug).
-v, --verboseShow 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

FlagDescription
--rebuildWipe work volumes, build from scratch.
-v, --verboseShow container output.

Clones and builds UWM (out/hive-s_full_tlsimages/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

FlagDescription
-v, --verboseAlso 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

FlagRequiredDescription
-f, --fileyesBitstream 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

FlagDescription
--rebuildWipe SW volume + bitbake linux-xlnx -c cleansstate first.
--no-updateSkip git pull of cloned repos.
-v, --verboseShow container output.

bitbake aionyx-image-minimalimages/sd.wic.

buildhive build kernel

FlagDescription
--rebuildbitbake linux-xlnx -c cleansstate first.
-v, --verboseShow container output.

bitbake linux-xlnximages/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

FlagDescription
--cleanmake clean instead of build.

Rebuilds the kernel Image directly from /kernel-devsrc (make -j Image modules), no bitbake → images/Image.

buildhive build module <name>

FlagDescription
name (positional)Driver under sources/drivers/staging/ntl/<name>/. C identifier, max 64 chars.
--cleanClean before build.

Scaffolds oot/<name>/Makefile if absent, cross-compiles the module against /kernel-devsrcoot/<name>/*.ko.

buildhive build update

FlagDefaultDescription
--legacy-migrationoffBuild hive-m-migration-*.tar.gz (0.3.9 upgrade path) instead. Unsigned.
--include <csv>allComponents: raucb,kernel,dtb,bootscr,bootbin,fitimage,bitstream,dtbo.
--selectoffInteractive 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-signoffSkip remote signing (archive not installable by uwm).
--sign-onlyoffSign the newest existing images/hive-m-update-*.tar.gz, no build.
--sign-timeout <s>3600Poll timeout for the signing server.
--no-updateoffSkip repo pulls.
-v, --verboseoffShow 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-archiveimages/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/.

FlagDefaultDescription
--imageoffBuild the SDK Docker image from the installer (sdk/Dockerfile). Tags <tag>:<version> and <tag>:latest.
--pushoffdocker push both tags. Implies --image. Needs docker login ghcr.io.
--tagghcr.io/nettimelogic/hive-m-sdkImage repository.
--versiontool versionImage version tag, also written to the org.opencontainers.image.version label.
--no-buildoffSkip populate_sdk, use the newest *.sh in images/.

See hive-m.md for the image layout and usage.

buildhive configure xsa

FlagRequiredDescription
-f, --fileyes.xsa file. Extracts the .bit, validates the header, copies to sources/sdt/.

buildhive configure bitstream

FlagRequiredDescription
-f, --fileyes.bit file. Validates the header, copies to sources/sdt/.

buildhive configure dts

FlagRequiredDescription
-f, --fileyesDTS 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

FlagDescription
-v, --verboseShow 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]

FlagDescription
-n, --limitMaximum results (default 30).
-g, --packagegroupsOnly packagegroup-* recipes. Without a term lists them all.
-l, --layerOnly 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

FlagDescription
--forceOverwrite 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

FlagDescription
--module <name>Source /kernel-devsrc/env.sh, cd oot/<name> (must exist).
--no-updateSkip repo pulls.
-v, --verboseShow container output.

Interactive bash in the SW container with the Yocto environment sourced.

buildhive code

FlagDescription
--stopStop 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

FlagDescription
-a, --allAlso 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

HowNotes
Start menu BuildHiveRuns buildhive-gui.exe, no console window. Installed by BuildHive_Setup.exe.
buildhive guiFrom any terminal.
buildhive.exe without argumentsSame as buildhive gui, keeps the console window.
python src/main.pyDevelopment 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

Project page

AreaContent
SidebarPages: Project, Configure, Build & Deploy, Settings. Tool version under the name. Update to x.y.z appears when a newer release exists, see BuildHive updates.
HeaderProject 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.
PageCards per topic. Each card names what it does under its title.
OutputLog 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.

ElementBehavior
Header textCommand and elapsed time while running, then Done (m:ss) or Failed, exit N.
ClearEmpties the log.
StopTerminates 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.

Running command

Project

ItemAction
Open folderDirectory picker. The directory needs a project.json. Ctrl+O works on every page.
RecentLast 10 opened projects, newest first. Double-click opens, right-click removes the entry. Missing directories are dropped.
New projectbuildhive 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.

Update dialog

SectionMeaning
UpdatedTemplate file changed, yours did not. Overwritten, backup as .bak.
AddedNew template file.
MergedBoth changed, 3-way merged against the stored baseline. Backup as .bak.
Merged with conflictsBoth changed the same lines. The file contains <<<<<<< markers.
Conflicts - skippedBoth changed, not mergeable (binary or no merge base). Per-file choice in the dialog: keep my version or take the template version.
Kept your versionYou changed it, the template did not.
RemovedDropped from the template, you had not changed it.
Removed from templateDropped 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

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.

RowCommandEffect
XSAconfigure xsa -f <file>Extracts the .bit from the Vivado archive into sources/sdt/, header validated.
Bitstreamconfigure bitstream -f <file>Copies a .bit into sources/sdt/. Same target as XSA, use one of the two.
Device treeconfigure 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.

ElementBehavior
Installed listCurrent entries. Select one or more, Remove selected.
Add fieldPackage name, Add or Enter. Allowed: lowercase letters, digits, + . _ -. Several names separated by spaces.
FiltersAll recipes / Packagegroups only, and All layers / one layer. With filters set and an empty field the list shows everything matching.
SuggestionsFiltered 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 indexRuns 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

Configure, 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 & Deploy, hive-m

Build

ButtonCommandOptions
Build SW imagebuild sw -vRebuild adds --rebuild (clean the build volumes first). Skip repo pulls adds --no-update (no git pull of the cloned repositories).
Build kernelbuild kernel -vKernel Image only.
Export SDKexport sw -vCross 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.

ElementCommand / 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 updatebuild update -v with the flags above.
Skip signingAdds --no-sign.
Sign existingbuild update --sign-only -v, signs the archive already in images/.

Signing needs the buildsign token from the secrets server or from Settings.

hive-s

Build & Deploy, hive-s

ButtonCommandOptions
Build HWbuild hw -vVivado on PATH. RISC-V JTAG debug adds --debug.
Build SWbuild sw -vBuildroot.
Build flash imagebuild flash -vNeeds the bitstream from Configure and built software.
Deploy flashdeploy flash -vVivado 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).

Clean confirmation

Settings

Settings

CardContent
Secrets serverLogin 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 updatesCheck for updates queries the GitHub releases now, logging in to the secrets server first if needed. Result goes to the output panel.
CredentialsOverrides 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).

StepBehavior
StartSilent check with the cached secrets login. No login, offline or API error: nothing happens.
Newer releaseINFO: BuildHive x.y.z is available in the output, Update to x.y.z in the sidebar.
Update buttonConfirmation, 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.
ErrorsERROR: 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

SymptomCause / fix
Docker not running in the headerStart Docker Desktop. The header updates within 10 s.
Command fails at once with Docker is not running or accessibleSame, the command checked Docker itself.
Failed, exit NRead 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 entryThe 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 firstThe project predates the meta-ntl-hive layer. Reopen it to get the update dialog.
Suggestions stay emptyNo index yet, click Build index. Needs Docker and the first build environment setup (repo sync), a few minutes.
Project update shows conflicts every timeMerge markers still in a file. Resolve them, Re-check, Finish update.

Implementation

FileContent
src/gui/app.pyQApplication, Fusion style, dark palette, stylesheet.
src/gui/main_window.pySidebar, header, page stack, output panel, run_cli(), Docker check, project open and update flow.
src/gui/pages.pyThe four pages. card() = titled section, ActionRow = button, description, options, FileRow = path field with Browse.
src/gui/runner.pyCommandRunner 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.pyProject update dialog, calls Project.update() and finish_update() in-process.
src/gui/app_update.pyUpdateCheck and UpdateInstall threads around src/selfupdate.py, see BuildHive updates.
src/gui/settings.pygui.json load/save.
src/gui/style.pyQt stylesheet, accent #ffaa28.
tools/gui_screenshots.pyRenders 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

FilePurpose
project.jsonProject configuration (schema below). Never touched by template updates.
.buildhive_manifest.jsonSHA-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)

FieldTypeDefaultMeaning
idstringgeneratedUUID hex, set on first load. Names the per-project Docker volumes/containers.
namestring-Project name. Letters, digits, -, _; max 128 chars.
templatestring-hive-s or hive-m. Selects model and command set.
buildhive_versionstringtool versionVersion 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..." }
    }
}
FieldMeaning
sw.buildrootBuildroot repo, cloned in the SW container, built with ntl_defconfig.
sw.uwmUWM repo (private, SSH). Built before Buildroot; loader copied into the rootfs overlay.
hw.argsArguments for the LiteX SoC generator (make.py).
hw.debug_argsAppended to args with build hw --debug.
flashMap 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"
        }
    }
}
FieldType/DefaultMeaning
secretsoptionalOpenBao 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_manifestsGitrepo init -u <url> -b <branch> on first container run.
sw.clonedict of Git, optionalExtra 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.buildsignoptionalSigning 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_bootbool, falsetrue: 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_mirroroptionalShared 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

ConstantValueUse
VERSIONsrc/constants.py:1Single source of the tool version. Tags Docker images, stamps project.json, update archives, the installer.
CONTAINER_USER / UIDntl / 1000In-container user.
WORK_DIR/workYocto work tree, backed by ntl_buildhive_sw_<id>.
SSTATE_DIR/mnt/ntl_sstateShared 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

PathContent
project.jsonConfig: 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

  1. HW container: pulls LiteX + VexRiscv sources, runs ./make.py <hw.args> - generates gateware sources and the device tree (rv32.dtb).
  2. Rewrites paths in the generated aionyx_hive_s.tcl from container to host paths.
  3. Host: vivado -mode batch -source aionyx_hive_s.tcl builds the IP cores, copied to ip/.

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:

  1. UWM - clone sw.uwm into sources/uwm, make out/hive-s_full_tlsimages/uwm.xz + uwm-loader; loader also copied into buildroot/rootfs_overlay/opt/ntl.
  2. Buildroot - clone sw.buildroot, make ntl_defconfig (BR2_EXTERNAL=/buildroot), make -jimages/: 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 flash mapping entry is wrapped as .fbi: 4-byte length + 4-byte CRC32 + data.
  • mapping_table.bin holds the sorted (src, dst) address pairs.
  • images/flash.bin layout: bitstream at offset 0, each component at src − 0x01000000, mapping table at 0x3F0000.

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

PathContent
project.jsonConfig: 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

VolumeMountScope
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-devsrcper 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:

  1. Exports BUILDHIVE_VERSION (tool version) via BB_ENV_PASSTHROUGH_ADDITIONS - default version for update/migration archives.
  2. First run: repo init -u <yocto_manifests.url> -b <branch>, repo sync.
  3. source edf-init-build-env.
  4. Reconciles conf/bblayers.conf with layers on disk (removes stale, adds missing).
  5. First run only: sets SSTATE_DIR/DL_DIR in local.conf, runs gen-machine-conf parse-sdt --hw-description /custom_sources/sdt --machine-name zynqmp-ntl-hive-m-sdt, creates+adds meta-ntl-hive, sets MACHINE ??= ntl-hive-m.
  6. Every run: re-copies /sw/meta-ntl-hive/* into ../sources/meta-ntl-hive/ - layer edits in the project take effect on the next build.
  7. Adds/removes the secure-boot.conf require line in local.conf per sw.secure_boot.
  8. Rewrites the # >>> buildhive sstate mirror block in local.conf per sw.sstate_mirror; credentials from SSTATE_MIRROR_AUTH env or the secrets server, written to ~/.netrc. Mirror misses fall back to the local ntl_buildhive_sstate cache and a normal build.
  9. Rewrites the # >>> buildhive rm_work block: INHERIT += "rm_work" with RM_WORK_EXCLUDE += "linux-xlnx aionyx-image-minimal". Work dirs are deleted after packaging (tmp/ shrinks several times), so devshell/tmp/work inspection only works for the excluded recipes; kernel-devsrc and export sdk (busybox from the image rootfs/) 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.

GroupContent
recipes-appsuwm (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-bspdevice-tree bbappend (FIT pubkey injection), u-boot bbappends + boot script.
recipes-corebase-files, update bundles (updates.md), image bbappend, legacy migration, persistent-data, rauc config, systemd-conf.
recipes-devtoolsbun-native, mdbook-native (uwm frontend/docs), wic fix.
recipes-kernellinux-xlnx bbappend + bsp.cfg.
recipes-securityhive-ca, hive-jwt-secret.
wic/rauc-zynqmp.wksDisk 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).

LayerMechanism
U-Boot promptCONFIG_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/offbootdelay 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 envCONFIG_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 loginserial-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.

ItemValue
Imageghcr.io/nettimelogic/hive-m-sdk:<version> (+ latest), linux/amd64 only (Yocto SDKMACHINE default)
Baseubuntu:24.04 + build-essential (kernel HOSTCC), git, kmod, pkg-config, python3. cmake/ninja/ctest/qemu come from the SDK.
SDKinstaller 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)
qemuqemu-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.
Userntl (uid 1000, passwordless sudo), WORKDIR /src
Kernelkernel-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)
modtestntl-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.
shellntl-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 mockntl-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 mockKernel 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)

PartitionSizeContent
p1 /boot512M FATBOOT.bin, kernel, dtb, boot.scr, uEnv.txt, uboot.env
p2 root_a2Gsquashfs, RAUC slot A
p3 root_b2Gempty, RAUC slot B
p4 /data512M ext4persistent overlay upper (/etc, /opt/ntl); never updated
p5 /homeext4growable (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.

ComponentArchive nameDestination
raucbupdate.raucbRAUC install into the inactive slot
kernelImage/boot/Image
dtbsystem.dtb/boot/system.dtb
bootscrboot.scr/boot/boot.scr
bootbinBOOT.bin/boot/BOOT.bin
fitimagefitImage/boot/fitImage (secure boot only)
bitstreamsource basename/data/firmware/import/<name> (uwm import)
dtbosource 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):

  1. Requires BUILDSIGN_TOKEN (env var, else secrets server, see configuration.md) and NTL VPN reachability to the buildsign.url server.
  2. 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).
  3. Polls every 10 s until signed / rejected (exit 2) / timeout (default 3600 s, exit 3).
  4. Signature (Ed25519, 64 bytes) is verified locally against the pinned sw.buildsign.public_key over "UWM-UPDATE-v1" + sha256. No pinned key: verification skipped with a warning.
  5. 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:

  1. Old uwm-loader writes the mapped files to /boot, reboots.
  2. Migration boot.scr boots the migration initramfs (migration-image.bb: RAM-only cpio with migration-init, sfdisk, mke2fs, dosfstools).
  3. 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)

FileTypeUse
psk.pemRSA-4096Primary secret key; PPK hash burned into eFUSEs.
ssk.pemRSA-4096Secondary secret key; signs BOOT.bin partitions.
fit.key / fit.crtRSA-2048, 30-year self-signed certFIT 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.pem is missing.
  • require .../meta-ntl-hive/conf/secure-boot.conf added to local.conf. That conf sets SECURE_BOOT=1, the bootgen BIF (bh_auth_enable so 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.bbappend injects the FIT public key into u-boot's control dtb with required="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

PathContent
src/main.pyEntry point; signal handlers stop running containers on Ctrl-C.
src/commands/__init__.pyCommand 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__.pyInit script prepended to every hive-m container run.
src/project.pyProject load/save/create, Docker image build, container/volume orchestration.
src/state.pyDocker client plus the running container/process handles the Ctrl-C handler stops.
src/common.pyTemplate copy, hash manifest, baseline, smart_update (3-way merge).
src/models/Pydantic models for project.json.
src/validate.pyArgument validation: project names, module names, file paths.
src/log.pyConsole output with the INFO:/WARNING:/ERROR:/OK: prefixes the GUI colors by.
src/vault.pySecrets server client: OIDC login, token cache, secret lookup.
src/signing.pyBuildsign client.
src/selfupdate.pyRelease lookup on GitHub, installer download and start (upgrade, GUI update button).
src/constants.pyVERSION, 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.pyIMAGE_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.pyRenders docs/images/gui/*.png for the GUI docs.
docs/mdBook sources (book.toml, SUMMARY.md), published to GitHub Pages.
buildhive.specPyInstaller spec, one analysis, two exes (buildhive.exe, buildhive-gui.exe).
setup.issInno 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.iss parses it at compile time (ISPP reads the first line of constants.py).
  • Hive-M containers export it as BUILDHIVE_VERSION through BB_ENV_PASSTHROUGH_ADDITIONS; update-archive.bb and legacy-migration-update.bb default UPDATE_VERSION/MIGRATION_VERSION from it (fallback 0.0.0 for manual bitbake).
  • project.json buildhive_version is stamped on save; a mismatch with the running tool triggers the update flow.
  • upgrade and the GUI compare it against the newest GitHub release tag.

Release:

  1. Bump VERSION in src/constants.py.
  2. make build (PyInstaller, dist/buildhive/), then compile setup.issdist/BuildHive_Setup.exe.
  3. GitHub release in NetTimeLogic/Tools: tag buildhive_v<x.y.z>, asset uploaded as BuildHive_<x.y.z>_Setup.exe. Drafts and pre-releases are ignored by the update check; the asset name must match BuildHive_*_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):

SituationAction
Template unchanged, user changedkeep user file
Template changed, user unchangedoverwrite (backup .bak)
Both changed3-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 templatekept on disk, reported
New template filecopied 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.