Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.12.4] - 2026-06-11
yoe skills installadds yoe’s AI skills to your project. The skills that power/new-unit,/diagnose,/audit-unit, and friends now ship inside theyoebinary; runyoe skills installto drop editable copies into your project’s.claude/skillsso Claude Code picks them up. They’re yours to edit, andyoe skills updaterefreshes them to the latest versions when you upgradeyoe. New projects fromyoe initget them installed automatically.- The skills are also available as a Claude Code plugin. If you’d rather
Claude Code manage updates for you, add the marketplace with
/plugin marketplace add yoebuild/yoeand installyoe@yoe— the same skills, delivered the standard plugin way. - New projects ignore the right files out of the box.
yoe initnow writes a.gitignorethat also covers the local apk repository (repo/) and your per-developerlocal.star, so a fresh project stays clean in Git without you curating ignores by hand.
[0.12.3] - 2026-06-09
- Ubuntu arm64 images boot under software emulation too. When QEMU has no
KVM acceleration (an x86 host, or an arm64 host without
/dev/kvm), Ubuntu’s EFI-only arm64 kernel now boots instead of aborting QEMU, so the boot test andyoe runwork the same with or without hardware acceleration.
[0.12.2] - 2026-06-09
- Ubuntu arm64 images now reach a login prompt in QEMU. Ubuntu’s arm64
kernel needs UEFI firmware to boot, which the QEMU launcher now supplies
automatically (from the host’s
qemu-efi-aarch64/edk2-aarch64package) instead of hanging with a blank console; if the firmware isn’t installed, the run now fails with a clear message naming the package to install.
[0.12.1] - 2026-06-08
- Ubuntu arm64 images now boot in QEMU. The launcher no longer trips over an unused initramfs reference Ubuntu leaves behind, so arm64 Ubuntu images start cleanly instead of failing with “could not load initrd.”
[0.12.0] - 2026-06-05
Ubuntu support is ready to use in this version.
- Building Ubuntu images no longer runs the machine out of memory. Large Ubuntu builds could grow to tens of gigabytes and get killed; they now stay flat at a few hundred megabytes and complete reliably.
- Fresh Debian and Ubuntu image builds are faster. The package index is now built once when it is needed instead of being rebuilt after every package, so build time no longer grows sharply with the number of packages.
- The attr library builds reliably across architectures. It no longer intermittently fails to build from a build-system timestamp race that showed up on arm64.
[0.11.7] - 2026-06-04
- Ubuntu images now bring up the wired network automatically. NetworkManager manages and DHCPs the ethernet port out of the box, so the image is reachable over SSH on first boot with no connection profile — matching Debian.
- Passwordless root SSH login now works on Ubuntu dev images. Root could log in on the serial console but SSH rejected the empty password; Ubuntu images now get the same permissive dev-login drop-in Debian already had.
- Ubuntu images build correctly in projects that also use Debian. The two distros’ build toolchains no longer share an identity, which previously could crash an Ubuntu image build; each distro now builds with its own toolchain.
[0.11.6] - 2026-06-04
- The unit list now shows cached status the moment yoe opens. Cached units no longer render as blank lines until the first build; their status is correct on startup.
- Ubuntu packages now reuse the cache instead of rebuilding every run.
Building an Ubuntu image no longer re-fetches and re-extracts every package on
each
yoe build; unchanged packages are reused from the cache, the same as Debian and Alpine. - ncurses now builds on Ubuntu. The terminal library no longer fails to compile under Ubuntu’s newer toolchain, so Ubuntu images that pull in ncurses build successfully.
- bash now builds on Ubuntu. The shell no longer fails to compile under Ubuntu’s newer toolchain, so Ubuntu images that pull in bash build successfully.
[0.11.5] - 2026-06-04
- Ubuntu is now a selectable distro alongside Alpine and Debian. Point a
project at
module-ubuntuand set the distro toubuntu(per image, or via the default-distro override) to build images from Ubuntu’s package archive. Ubuntu and Debian images can live side by side in one project without their packages colliding. (Still debugging, not ready for use). - The
debian_feed(...)builtin is nowapt_feed(...)and takes adistro. One builtin serves every apt-based distro; passdistro = "debian"ordistro = "ubuntu". It also accepts an optionalarch_urlsmap so a single feed can pull amd64 from one mirror and arm64 from another — needed for Ubuntu, whose ports architectures live on a separate host. - Build failures now name the unit and task that broke. When a build fails,
the output leads with a clear
❌ FAILED: <unit> task: <task>line before the log, so you can tell at a glance which unit failed even when several are building in parallel. - Build status lines now carry icons for quick scanning. Each unit reports with an at-a-glance marker — ⚡ cached, 🔨 building, ✅ done, ❌ failed — and freshly packaged artifacts are flagged with 📦.
- Fixed source units failing to build in projects that mix two distros. When
a project combined distros that share image names (such as Debian and Ubuntu,
which both ship
dev-image), a source unit’s build-time-devdependencies could be silently dropped for one of the distros, leaving it to build against an empty sysroot and fail (for example, “zlib support requested but not found”). Both distros’ build dependencies now resolve correctly. - Boot smoke test output now carries status emojis. A
--boot-testrun flags each stage at a glance — 🚀 launch, 🔑 reaching the login prompt and connecting over SSH, 🩺 health check, and a final ✅ pass or ❌ fail.
[0.11.4] - 2026-06-04
yoe runnow boots arm64 images correctly. On arm64 (and other direct-kernel-boot targets) the launcher looked for the kernel at the wrong path, so QEMU started with nothing to boot and sat at a blank console. It now takes the kernel — and, for Debian, the initramfs — straight from the image’s own/boot, so arm64 images boot the same kernel they ship. This also makesyoe run --boot-testpass for arm64 images.
[0.11.3] - 2026-06-04
yoe run --boot-testboots an image and verifies it came up. It boots headless, waits for the login prompt, SSHes in to run a health check, then powers off — a one-command pass/fail smoke test for CI or a quick local sanity check.--timeoutbounds it;--distropicks which distro’s image to run when the name exists in several.
[0.11.2] - 2026-06-04
- Build failures now show the unit’s log right where they happen. When a unit fails to build, yoe prints the tail of that unit’s build log inline instead of only pointing at a file on disk, so you can see the actual error without opening anything — and so failures are diagnosable from CI output where the log file is thrown away with the runner.
- Per-task build lines now name their unit. During parallel builds the task progress lines from different units interleave; each line now leads with the unit name so you can tell which build it belongs to.
- Units with patches build on machines that have no git identity configured.
Applying a unit’s patches no longer depends on a global git
user.name/user.email, so builds succeed out of the box on fresh machines and CI runners instead of failing with “empty ident name not allowed.”
[0.11.1] - 2026-06-04
- Source downloads no longer fail intermittently with “gzip: invalid
header.” Some download mirrors serve a
.tar.gzin a way that made yoe save an already-decompressed archive under a compressed name, so a build would break or succeed depending on which mirror you happened to reach. Downloads are now fetched verbatim, so a unit that builds once builds every time.
[0.11.0] - 2026-06-03
- Debian Trixie support. yoe can now build Debian images alongside Alpine.
Set
defaults.distro = "debian"to target a whole project at Debian, or tag an individualimage(...)withdistro = "debian", and yoe builds it through the Debian backend — glibc toolchain,.debpackaging, a signed apt repo, and a fully configured, bootable rootfs.base-imageanddev-imageare available for Debian out of the box. yoe deploy,run, andflashwork for Debian targets. Deploy wires the project’s dev feed into apt and installs withapt-get(Alpine still installs over apk);runandflashlocate Debian images.apt-get installandapt-get upgradework offline against your project repo, which apt verifies with the project’s own GPG key. The edit-deploy loop is identical on both distros. (not fully tested yet)yoe buildtakes a--distroflag. When the same image name exists in more than one distro (say abase-imagefrom both the Alpine and Debian modules), pick which one to build for a single command —yoe build --distro debian base-image— instead of editinglocal.star.- The TUI shows and selects the default distro. The status header displays
the active distro, and TUI Setup (press
s) has a Default Distro picker that persists tolocal.starand re-walks the build cache immediately so status reflects the chosen backend. yoe logandyoe diagnosework on feed-based projects again. They no longer fail with anundefined: alpine_feederror and now find the build log for images and other machine-specific units.- Repository and build layout are now split by distro. So Alpine and Debian
artifacts can coexist, APK repos moved to
repo/<project>/alpine/<arch>/, Debian repos live underrepo/<project>/debian/, and build output moved tobuild/<distro>/<unit>.<scope>/. Update any hardcoded repo URLs and/etc/apk/repositoriesentries; oldrepo/<project>/<arch>/andbuild/<unit>.<scope>/directories are stranded and can be removed.prefer_modulesis now keyed by distro:prefer_modules = {"alpine": {"xz": "alpine.main"}, "debian": {...}}— rewrap any existing flat pins under their distro key.
[0.10.15] - 2026-05-26
- TUI Setup gained a QEMU settings sub-screen. Press
sthen Enter on “QEMU settings” to adjust the guest’s RAM with ←/→, toggle the graphical display, and add or remove host:guest port forwards foryoe run. Choices persist tolocal.starand apply automatically the next time you launch the guest — no need to remember--memory,--display, or--portflags for routine work. - The QEMU settings screen shows the equivalent qemu command. A live preview
at the bottom of the sub-screen renders the exact
qemu-system-*invocationyoe runwould emit with the current Memory / Display / Ports values, so you can confirm what each tweak changes before launching — and copy-paste the line to drive QEMU directly. - New
qt-imageboots straight into a Qt 6 Quick demo on the framebuffer. Build withyoe build qt-imageand run withyoe run qt-image --display; QEMU opens a window showing the demo scene (a “Hello from yoe!” message rendered through the linuxfb platform plugin and the software scene graph). Useful as a quick end-to-end check that a yoe-built image’s graphical stack works on hardware that ships virtio-gpu, Bochs, or a plain VESA/EFI framebuffer. yoe run --displaynow actually opens a QEMU window. Previously the flag dropped-nographicbut didn’t tell QEMU what to display with; running an image showed only an empty terminal. The launcher now attaches a virtio-vga adapter and keeps the serial console muxed onto host stdio (-serial mon:stdio) so kernel logs stay visible alongside the framebuffer window. Headlessyoe run(no--display) is unchanged.- The kernel ships framebuffer and DRM drivers for QEMU and common PC GPUs out
of the box.
linuxnow merges agraphics.cfgfragment that enables virtio-gpu, Bochs, vesafb, efifb, and DRM fbdev emulation, so every yoe image exposes/dev/fb0on first boot without per-image configuration.
[0.10.14] - 2026-05-26
- Modules tab no longer scrolls the title off the top. When a project declared one or more feeds, the FEEDS section pushed the body past the terminal height and you’d lose the title, tab bar, and column header. The viewport now sizes itself to leave room for the FEEDS section.
- The edit shortcut is hidden for feed-supplied units. Units coming from a
feed (e.g.
alpine_feed()) have no.starfile to open, so the Units tab no longer advertisese editwhen the cursor is on one, and pressingeon such a unit is a silent no-op.
[0.10.13] - 2026-05-26
alpine_feed()declares a feed directly in a module’s MODULE.star. A module can now expose thousands of upstream Alpine packages as yoe units with a single declaration — pointalpine_feed()at a checked-in directory of APKINDEX files and the named packages become available to image artifacts with no per-package.starfile. Package units materialize lazily as the image’s runtime closure needs them, so working memory stays bounded by the closure size rather than the catalog size.- The Modules tab shows declared feeds. Each
alpine_feed()call appears in a FEEDS section under the regular module list with its parent module and the total package count. yoe update-feedsrefreshes feed APKINDEX files from upstream. Run inside a module repo, the new subcommand fetches everyalpine_feed()’s APKINDEX for every active arch, verifies the upstream RSA signature against the keys the module declared, and writes the new indices to disk for the maintainer to review withgit diffand commit. Signature verification is pure-Go and never consults the host’s/etc/apk/keys/— the trust list the feed declares is the one that’s actually enforced.
[0.10.12] - 2026-05-22
- CI builds
base-imagefrom source on every push tomain. A full end-to-end build — bootstrap toolchain, kernel, and image assembly — now runs in CI, so build regressions surface immediately instead of at the next release. yoe runworks inside a QEMU guest (qemu-in-qemu). When no/dev/kvmis available,yoe runnow falls back to TCG software emulation instead of failing with a KVM error, so you can launch a guest from within a guest. It prints a one-line note that emulation is in use.yoe run --portcan remap a machine’s default forwards. A--portentry whose guest port matches a machine forward now replaces it instead of adding a second, colliding one — so a nestedyoe runcan move its host-side ports off the ones the outer guest already holds.yoe runflags work after the image name.yoe run base-image --port …previously ignored every flag that followed the image name; flags and the image name may now appear in any order.yoe runexplains a port conflict instead of failing cryptically. When a QEMU guest is already running,yoe run(and the TUIrkey) now report which host port is taken and that an earlier run is probably still up, rather than an opaqueexit status 1. Other QEMU launch failures now include the reason QEMU printed.yoe runremembers the QEMU guest memory. Pass--memory 8Gonce and the value is saved tolocal.star, so later runs reuse it without the flag. Set it without a run viayoe config set qemu-memory 8G, or on the TUI Setup page with ←/→. Clear it with an empty value to fall back to the machine default.- QEMU can now be installed into an image. Adding QEMU pulled in filesystem
libraries that conflicted with the bundled
e2fsprogs, aborting the image build; that conflict is now resolved. - QEMU machines now default to 4 GB RAM. The old 1 GB default was too small
for memory-heavy unit builds run inside the guest — a self-hosted
yoe buildof the Linux kernel was OOM-killed at the link step. Bump thememoryfield in your machine file if you need more or less. - TUI clean (
candC) now works on image units. Previously failed with permission errors on the root-owned files left by image builds; now routes through the same container-sidermthatyoe cleanuses. selfhost-image. Bootable image that bundlesyoe, Go, Docker, git, and the dev-image tool set (tested on QEMU, soon native ARM systems).
[0.10.11] - 2026-05-20
beagleplaymachine. New target for the TI AM625 BeaglePlay. Build with--machine beagleplay.jukebox-image. Music server image running Navidrome (Subsonic API).- Image rootfs preserves per-file ownership from packages. Services like
Navidrome and Postgres that need a specific data-dir owner now start cleanly.
Side effect:
rm -rf build/fails because some files are root-owned — useyoe cleaninstead. - New “Security and Threat Model” doc. What the build container does and
doesn’t protect against. Short version: treat units like
curl | sh. - Files tab for image units shows rootfs contents, not the whole
destdir/. Now matches the unit’s SIZE column. - Filtering the Units tab to no matches no longer scrolls the tab bar off the screen.
module-rpirenamed tomodule-bsp. Common boards live in one module.- New “Boards” section in the docs. Dedicated pages for BeaglePlay, Raspberry Pi, and QEMU.
- SD-card images boot on BeaglePlay (and other K3 boards). Previously hung in ROM with no serial output.
[0.10.10] - 2026-05-15
- Building a prebuilt package no longer fails on a fresh checkout. Building
an Alpine prebuilt package (or any image that pulls one in) on a clean tree,
or after a toolchain version bump, failed with
Unable to find image 'yoe/toolchain-musl:...'/pull access deniedbecause nothing built the toolchain container first. yoe now builds the container automatically before the units that run inside it, the same way it already did for source-built packages.
[0.10.9] - 2026-05-15
- The TUI home header is easier to scan. Field names (Machine, Image, Query, Units, feed, Modules, Diagnostics) now render in plain white and their values in a bright accent color, so the data stands out from the labels at a glance. The search expression keeps its own coloring.
- The TUI tab bar now sits on the top line next to
[yoe]. The Machine/Image line and the build progress bar moved down a row, so the tabs are the first thing you see and the layout reads top-to-bottom. - Image builds now resolve virtual packages the same way every run. When
several packages claimed the same virtual name (for example the providers of
ifupdown-any), which one an image picked could change from one build to the next — so a package would drift in and out of the image, never staying cached and sometimes silently dropping out entirely. Resolution is now stable, so the same project always produces the same image and the cache holds. - The TUI Setup page can now adjust how many units build in parallel. A “Parallel builds” row sits below Machine and Image; press ←/→ (or h/l) to raise or lower the count. The choice is saved per project and used by the next build.
yoe buildnow builds independent units in parallel. Units whose dependencies are ready build concurrently instead of one at a time, so a full build finishes much faster. Up to 5 units build at once by default; change it withyoe build -j N,yoe config set parallel-builds N, or aparallel_buildsline inlocal.star. The value is remembered per project, andyoe config showreports the one in effect.uon the unit list toggles a unit’s source between pin and dev mode. The same dev-mode prompt the detail view offers is now one keypress away from the list, acting on the unit under the cursor.g/Gjump to the top / bottom of the unit list. The keys were documented and worked on the Modules and Diagnostics tabs but were a no-op on the Units list itself; they now move the cursor there too.- Press
?on any TUI page for a keyboard cheat sheet. A centered help box lists every shortcut for the page you’re on — navigation, build, inspect, filter, and the page-specific actions — with plain-language descriptions. When the list is taller than your terminal it scrolls (↑/↓, PgUp/PgDn, g/G) with the title and footer pinned; any other key dismisses it. - The TUI tab bar now uses zellij-style ribbon tabs. Each tab is a rounded colored banner sitting on a dark bar, with the active one highlighted in amber so the selected tab is obvious at a glance. Needs a powerline-patched terminal font to render the rounded tab edges.
yoe deploynow actually installs your dev-mode rebuilds. Iterating in dev mode and deploying used to silently drop your edits when the version number hadn’t changed; deploy now reliably installs the rebuilt package. Restart the service from$to pick up the new binary.- Dev mode can track an upstream branch automatically. A unit that declares
both a tag and a branch flips to tracking that upstream branch when toggled
into dev mode, so
git pull,git push, andgit log @{u}..just work. The SRC column showsdev-modwhen your checkout is past the pin, and the detail page shows how many commits ahead you are. Ppins the current HEAD with no picker. PressingPrecords the checked-out tag (or commit SHA) as the unit’s pin — no popup. Available indevanddev-mod; a dirty tree prompts you to commit or stash first.- The SRC column shows
pinfor yoe-managed checkouts instead of leaving the cell blank, so you can tell at a glance which units are pinned. - Toggling
dev → pinno longer looks like data loss. It resets the existing checkout to the pin in place and keeps the clone’s full history instead of deleting the source tree and re-cloning on the next build.
[0.10.8] - 2026-05-13
-
Fix patch application when the cache path is relative.
applyPatchesbuilt the patch path relative to the project root (e.g.cache/modules/.../*.patch) but invokedgit amwithcmd.Dir = srcDir, so git looked for the file inside the source tree and failed withcould not open '...patch'. The path is now resolved to absolute before exec. The bug was masked in long-lived build dirs becausesrc/already had the patches committed and the prep step short-circuits via the “commits beyond upstream” check; fresh builds (or any project withYOE_CACHEunset and modules pulled from cache) hit it. -
Language runtimes move out of the toolchain container.
nodejs,npm,python3,py3-setuptools, andpy3-pipare no longer baked intotoolchain-musl’s Dockerfile.nodejs_appandpython_venvnow add the matching apks todeps, so the same Alpine prebuilt the device runs is also what builds the unit. Projects that don’t use Python or Node.js stop paying for them entirely; bumping a runtime version is now a unit edit rather than a Dockerfile change. Matches the bun setup and CLAUDE.md’s “no installing packages in the container” rule. Migration: any unit that invokespython3,node, ornpmin its build steps without using the corresponding class now needs the runtime in itsdeps(mesonandca-certificatesare updated in this release as examples). Thetoolchain-muslcontainer version bumps to 19 so the leaner image rebuilds on first use.
[0.10.7] - 2026-05-12
- Bun apps can ship as part of an image. A new
bun_appclass packages a Bun project plus itsnode_modulestree as a regular yoe unit, so apps that need a specific set of npm packages get them baked into the apk. Bun runs TypeScript natively, so the entry point can be a plain.tsfile with no compile step.bun-imageships abun-hellodemo — log in and runbun-hello "..."to see the bundledfigletdependency render an ASCII-art greeting. - New
bun-image. A ready-to-boot image with thebunruntime (andbunx) plus the dev-image diagnostic userland, sobun install <pkg>andbun runwork on first login without a separateapk add. - npm dependencies can ship as part of an image. A new
nodejs_appclass packages a Node.js app plus itsnode_modulestree as a regular yoe unit, so apps that need a specific set of npm packages get them baked into the apk instead of installed at first boot. Drop a normalpackage.json(and optionallypackage-lock.json) next to your unit and the class runsnpm install/npm ciagainst it at build time.nodejs-imageships anodejs-hellodemo — log in and runnodejs-hello "..."to see the bundledfigletdependency render an ASCII-art greeting. - New
nodejs-image. A ready-to-boot image withnode,npm, and the dev-image diagnostic userland, sonpm install <pkg>works on first login without a separateapk add. yoe initprojects now pinzstdto Alpine. Source-builtzstdshipslibzstd.so.1at a different soversion than Alpine’szstd-libs, so any image that mixes source consumers (curl, file) with Alpine prebuilts that link against Alpine’s libzstd (nodejs, bun) used to fail with an apk conflict over the shared.so. New projects get the pin out of the box; existing projects can copy the"zstd": "alpine"entry into theirprefer_modulesblock.- Patches live next to the unit, not under a separate
patches/tree. A unit’spatches = [...]paths are now relative to the unit’s own directory with nopatches/prefix — e.g.,patches = ["mdnsd/0001-….patch"]next tomdnsd.star.yoe dev extractwrites patches into<unit-dir>/<unit>/alongside the.starfile, so a module’s patches travel with the module that defines them. Migration: drop the leadingpatches/from existingpatches = [...]entries and move the files to match. yoe dev statusworks through project errors. It now walks the build directory directly instead of evaluating the project, so unrelated issues like a duplicate-provides collision in a module no longer block you from seeing which units have uncommitted local edits.
[0.10.6] - 2026-05-12
- TUI help bar stays pinned to the bottom of the screen on the Units tab, even when only a handful of units are visible. The unit list pads with blank rows so the keyboard shortcuts and the cursor-name strip don’t float up under a short query result.
yoe module syncworks even when modules have errors. The command now reads onlyPROJECT.starand re-syncs the declared modules, so a broken module that’s blocking the rest of the build can be re-fetched as soon as the upstream fix lands — no more chicken-and-egg.- One
ctxstruct in.starfiles instead of five separate globals. Unit and image definitions now referencectx.arch,ctx.machine,ctx.project_version,ctx.machine_config,ctx.provides, andctx.runtime_deps— what used to beARCH,MACHINE,PROJECT_VERSION,MACHINE_CONFIG,PROVIDES, andRUNTIME_DEPS. One named entry point is easier to discover and reason about than five floating predeclared names. External modules that referenced the old globals need a one-line rename. - Pip dependencies can ship as part of an image. A new
python_venvclass packages a Python virtualenv plus its pip dependencies as a regular yoe unit, so apps that need a specific set of PyPI packages get them baked into the apk instead of installed at first boot.python-imageships apython-hellodemo — log in and runpython-hello "..."to see the bundledpyfigletdependency render an ASCII-art greeting. - New
python-image. A ready-to-boot image withpython3,pip, and the dev-image diagnostic userland, sopip install <pkg>works on first login without a separateapk add. yoe deploy python3(and other openssl consumers) now installs onto a running device. Previously apk rejected the install with alibssl3>=3.3.0conflict against the source-built openssl. Source units that declare virtualprovidesnow publish them with this unit’s version, so>=constraints resolve the way they do on Alpine.<hostname>.localnow resolves over IPv4. On DHCP networks mdnsd was announcing only the IPv6 link-local address, sossh user@host.localfailed on plain IPv4 LANs. The host’s A record is now published as soon as the lease arrives.
[0.10.5] - 2026-05-09
- Build progress bar. While a build is in flight the feed banner at the top of the screen swaps out for a live progress bar showing the percentage done, how many units have finished, and how many are still to build. The bar disappears and the feed banner returns once the build settles.
[0.10.4] - 2026-05-08
- Search bar clears with
Ctrl+U. Readline’s kill-line shortcut wipes the query input back to a blank bar in one keystroke — faster than holding Backspace or pressing\to snap to the saved default. Live-applied like a backspace, so the unit list updates to “showing all” immediately. - Tab completions show up under the query bar. When the search input can’t be advanced further (multiple equally-good matches), the candidate list now renders as a vertical column directly under the query bar — closer to the cursor than the previous horizontal blob at the bottom of the screen, and easier to scan for the next character to type. Long lists truncate with a “(N more — type a letter to narrow)” hint.
- Fresh projects from
yoe initbuild out of the box. The generated PROJECT.star now pinsxzto the Alpine module, matching the canonical e2e-project template. Without this, kmod’s depmod failed at image-assembly time because module-core’s xz is static-only and doesn’t ship liblzma.so.5. - Switching a unit/module to dev mode transfers far less data. The
depth-limited fetch (
last 100 / 1000 commits,last year,last month) now narrows to the unit’s pinned ref instead of fanning out across every branch the upstream tracks, and adds--filter=blob:noneso file content comes down on demand instead of all at once. For a Linux-kernel-sized repo that’s the difference between a multi-gigabyte fetch and tens of megabytes;git logandgit blamestill work, and missing blobs are fetched lazily when needed. - TUI
/makes refining an existing query faster. When you press/and the active query is non-empty, the bar opens with a trailing space so you can immediately type the next term — no need to press End or space first. A blank query still opens empty. - Toggle any unit or module between pinned and dev mode from the TUI. A new
SRC column on the units and modules tabs surfaces whether each source dir is
yoe-managed (
pin), tracking upstream (dev), has commits beyond upstream (dev-mod), or has uncommitted edits (dev-dirty). Pressuon a unit’s detail page (or a module row) to switch between pin and dev — yoe asks whether to rewrite origin to SSH, then how much history to fetch (full / last 1000 commits / last 100 commits) so the Linux kernel’s full history doesn’t have to come down every time. A spinner runs while the fetch is in flight so you can see something is happening. Once you’re happy with adev-modHEAD,Pcaptures it back into the.starpin so other people building the project pick it up. Adev*unit is left untouched at build time, soyoe buildwon’t overwrite your working tree or undo in-flight changes. - TUI size column no longer overflows on big artifacts. Sizes like a 1003
KiB kernel image render as
1003Kinstead of1003.4K, keeping the column aligned. The decimal still shows for small values (e.g.9.9K,1.2M) where it carries useful precision. - Device hostname now matches the machine, not the image. A fleet of
raspberrypi4s flashed with
dev-imageno longer all answer toyoe-dev.local; each board comes up as<machine>.local(e.g.raspberrypi4.local,qemu-x86_64.local) so they’re distinguishable on the LAN out of the box. Sethostname = "..."on an image to override (e.g. a branded kiosk image). - TUI help bar reflects the active mode. While typing in the search bar, the
bottom help row swaps to the keys that actually work there (
type filter,tab complete,⌫ delete,enter apply,esc cancel) instead of pretendingb build,q quit, etc. still fire. Out of search-edit it shows the navigation shortcuts as before. - Tab in the search bar always shows progress. When Tab can’t advance the input (multiple candidates with no common prefix to extend — most visibly when you’ve just opened the bar, or typed a single ambiguous letter), the candidate list now flashes in place of the help bar instead of silently doing nothing. Single-candidate completions still splice in. Empty pool flashes “no completions”.
[0.10.3] - 2026-05-07
docker-imagestarts dockerd at boot. Pulls in Alpine’sdocker-openrcpackage (which ships/etc/init.d/dockerand the/etc/conf.d/dockerconfig template upstream maintains) and adds the default-runlevel symlink at packaging time, sodockerdis supervised on a fresh boot without manualrc-update add.prefer_modulesonproject()pins a unit to a specific module. Setprefer_modules = {"xz": "alpine"}inPROJECT.starand thexzunit registers only frommodule-alpine, regardless of which module wins the default last-module shadowing. Use it whenmodule-core’s source-built version of a package is broken or under-configured and the Alpine prebuilt is the right answer; the shadow appears on the Diagnostics tab the same way an ordinary cross-module shadow does.modprobeworks on the booted system. Image assembly now runsdepmodinside the rootfs afterapk add, so/lib/modules/<ver>/carries a realmodules.depindex instead of just bare.kofiles. The kernel build still skips depmod (the toolchain container has no copy of it); the rootfs’s ownkmodsupplies it via chroot.- Kernel ships container-runtime CONFIG by default. A
container.cfgfragment (overlayfs, bridge/veth, the full netfilter chain includingNFT_COMPATso iptables-nft works, IPv4 + IPv6 NAT, namespaces, seccomp, cgroup BPF, eBPF) is merged into the kernel’s defconfig during the build of the upstreamlinuxunit and the Raspberry Pilinux-rpi4/linux-rpi5units, sodockerdandcontainerdstart cleanly on every supported board without per-image kernel customisation. The cost on non-container images is a few hundred KB of kernel modules that nothing references. - TUI flash remembers the last device. Picking and confirming a flash target
writes
flash_device = "/dev/sdX"tolocal.star, and re-entering the flash view positions the cursor on that device when it shows up in the candidate list. Reflashing the same SD card or USB stick is nowf→ Enter →y. /etc/os-releasenow reports the project version.VERSION,VERSION_ID, andPRETTY_NAMEcome fromversion = "..."inPROJECT.star, so tools that read/etc/os-release(and humans on the device) can tell which build is running. Templates can reach the value as{{.project_version}}.- Image rows in the TUI show the project version. The
image()class defaults each image unit’sversiontoPROJECT_VERSION(fromPROJECT.star), so the VERSION column in the units table — which used to be blank for image rows — now shows the version the resulting.imgrepresents. - OpenRC replaces the old rcS startup script. Services now boot under
Alpine’s OpenRC service manager, so they get dependency ordering, supervised
start/stop, and proper status/restart commands (
rc-service sshd restart,rc-status) instead of the silent run-everything-in-/etc/init.d/S*pattern. Units declareservices = ["sshd"](plain names, noS40prefix) and the resulting apk drops the script in/etc/init.d/plus a runlevel symlink in/etc/runlevels/default/. - Source-built libraries auto-declare what they ship. Each
.apkyoe builds from a destdir now listsprovides = so:<soname>=<ver>-r<rel>for every shared library in the package, matching Alpine’s convention. Alpine prebuilt packages whose upstream PKGINFO declaresdepend = so:libcrypto.so.3or similar now resolve cleanly against yoe-source-builtopenssl,zlib, etc. — no manual SONAME bookkeeping in the.starfile. module-alpinepackages now ship with their upstream metadata intact. Prebuilt Alpine apks pass through yoe’s pipeline verbatim — only the signature is swapped for the project’s key — soreplaces,provides,triggers, and post-install hooks (busybox applet symlink creation, sshd privsep user adds, …) reach the on-target system the way Alpine intended. Image assembly drops--no-scriptsso those hooks actually run; this fixes the no-/sbin/initkernel panic that hit when relying on Alpine’s busybox.- Alpine packages no longer end up with doubled-
-rfilenames.alpine_pkgsplits upstream pkgver like1.2.5-r11into yoe’s separate version + release fields, so the published apk ismusl-1.2.5-r11.apkinstead ofmusl-1.2.5-r11-r0.apk. Apk’s solver finds the file at the URL it constructs from the index, fixing “package mentioned in index not found” on every module-alpine package. - noarch passthrough packages route correctly across the repo. apk-tools
constructs fetch URLs from PKGINFO’s
arch =field (always<base>/noarch/<file>for noarch packages), but its solver only reads one arch’s APKINDEX per repo. Three coordinated fixes:- Passthrough alpine_pkg units with
arch = noarchpublish under<repo>/noarch/(where apk fetches them from). - Each per-arch
APKINDEXnow scans the siblingnoarch/tree at generation time, so the solver sees noarch packages from any arch’s perspective. - A noarch publish refreshes every per-arch
APKINDEX(since each one references those noarch entries). - Cache validation also looks under
noarch/for the published apk, so noarch units don’t rebuild on every invocation.
- Passthrough alpine_pkg units with
- base-files ships an Alpine-style runlevel baseline. OpenRC services
cgroups,devfs,dmesg(sysinit),bootmisc,hostname,modules,sysctl(boot), andmount-ro,killprocs(shutdown) are now wired into the rootfs via/etc/runlevels/<level>/<svc>symlinks, so a fresh image boots with the hostname set, kernel modules loaded, the cgroup hierarchy mounted (so container runtimes don’t trip on “Devices cgroup isn’t mounted”), and shutdown that unmounts cleanly. - TUI SIZE column for images shows installed content, not partition size. An
image whose machine reserves a 600 MB rootfs partition now reports the ~50 MB
actually populated by
apk add, so you can see what your image contains rather than how big the partition was sized. - New
docker-image. Builds a dev-image-style rootfs that also ships Docker (engine, CLI, buildx, containerd, runc) so you can poke at the docker userspace on a yoe-built system. Kernel and init still need the container pieces beforedockerdcan actually launch a container — that’s the next step. - Files tab on the unit detail page. Tab into a sortable list of every file the unit installs and its on-disk size — easy to spot the biggest payloads or confirm a binary actually landed where you expected without leaving the TUI.
- Drop into a shell on the source. Press
$in the units tab or detail page to open a shell in the unit’s checked-out source directory, or in the Modules tab to open a shell in a module’s clone — handy forgit status, spot-edits, or running an out-of-tree command without leaving the TUI. VERSIONcolumn in the unit table. Each row now shows the unit’s declared version next to its module, sortable from theocycle, and the same version appears next to the unit name on the detail page — so spotting a stale pin or confirming what’s about to build is a glance, not a file open.- TUI auto-follow no longer yanks the cursor mid-navigation. The units list
scrolls to whatever is actively building only when you’re idle — pressing j/k
or typing a query keeps the cursor where you put it, while
bstill hands control back to the build so you can watch what’s compiling.
[0.10.2] - 2026-05-05
yoe initlists module-core last so it wins shadowing. New projects now order modules so module-core’s source-built units (busybox, openssl, …) take precedence over Alpine prebuilts and over module-rpi, avoiding image-assembly path collisions. Existing projects can movemodule-coreto the end of theirmodules = [...]list inPROJECT.starto get the same behavior.- Images with
network-configand busybox build again. A path collision on/usr/share/udhcpc/default.script(busybox ships an example script there; network-config installs the real one) was abortingapk addat image-assembly time.
[0.10.1] - 2026-05-05
- TUI flash offers
sudo chownon permission denied. Previously the flash view just showed “permission denied” and dead-ended — matching the CLI’s behavior, the TUI now prompts to runsudo chown $USER /dev/...and retries the write automatically. - TUI home screen has tabs. Press
tabto cycle between Units (the existing list), Modules (declared modules with git status), and Diagnostics (shadowed units and duplicateprovides). The diagnostics tab carries a count badge so issues are visible from any tab. --allow-duplicate-providesis on by default. No more passing the flag on every yoe invocation while thelinux-firmware-*fan-out keeps tripping the strict check.- Modules renamed:
units-*→module-*.units-core,units-rpi,units-alpine, andunits-jetsonare nowmodule-core,module-rpi,module-alpine, andmodule-jetson. Updatemodule(...)URLs and anypath = "modules/units-..."entries in yourPROJECT.star. helixactually runs on the device. Was previously bundled as a glibc-linked binary that failed silently withhx: not found; now uses Alpine’s musl build.- Images that include
apk-toolsorlibcurlbuild again. A collision between the source-builtca-certificatesand Alpine’sca-certificates-bundlewas abortingapk addat image-assembly time. SIZEcolumn in the TUI updates as each unit finishes. No more waiting for the whole image to complete before transitive deps show their size, and partial sizes survive a mid-build failure.- Modules show their declared name. The TUI’s
MODULEcolumn and any diagnostic that names a module now use the name set inMODULE.star’smodule_info(name = ...)instead of the path basename — so a module referenced viapath = "modules/units-core"displays ascoreif that’s what it calls itself. Falls back to the path basename when nomodule_infois declared. dev-imageshipshelixinstead ofvim. Drops the editor entry that was unintentionally resolving to Alpine’sgvim(and its X11/GTK runtime closure), keeping the image lean.- Unit detail shows what uses it and what it pulls in. The detail page now
opens with two new sections above the build log: USED BY traces back
through runtime_deps to show which packages you wrote in
image()pulled this unit in, e.g.dev-image → yazi → libpango → cairo, so you can answer “why is this on my device?” at a glance. PULLS IN shows the unit’s runtime-deps as a tree. Drilling into an image starts from exactly the packages you wrote inimage()(plus machine packages), then expands each one to show what it drags in transitively. - TUI layout overhaul. Title and banners stay put when the list is long, the
help bar is always the last line, status messages flash in its place, and
pressing
/turns theQuery:header itself into the search input. Long unit names get an ellipsis instead of breaking column alignment. - Sort columns from the keyboard. Press
oto cycle the unit table throughNAME → CLASS → MODULE → SIZE → DEPS → STATUS;Oflips direction. The active column shows↑or↓next to its label. - Help bar highlights shortcut keys. Each shortcut letter renders in amber
matching
[yoe], so you can scan keys without reading every word. - Cursor follows the work. The TUI opens with the cursor on the default image, jumps to whatever unit is currently building, and the cursor’s full unit name is always visible just above the help bar.
- Configure the default image per developer.
local.staracceptsimage = "..."to overridedefaults.image. Pick an image from the new Image entry in Setup (s) and the choice is saved — and the active search re-anchors toin:<image>. Flows throughyoe run,yoe config show, and the TUI. - More columns in the unit table. Each row now also shows the module that
owns the unit (after shadow resolution), its install size after build (
.imgsize for images), and how many units it pulls into a runtime closure — so bloat is easy to spot before flashing. yoe --helpworks and lists global options.--help,-h, andhelpall print usage, including--project,--show-shadows, and--allow-duplicate-provides.
[0.10.0] - 2026-05-05
Errata: due to an issue in the alpine module, you must currently run with:
yoe --allow-duplicate-provides.
- BREAKING CHANGE This project has been moved to a new Github org:
https://github.com/yoebuild.
yoe updatefrom previous versions will not work and you will need to download and manually install the 0.10.0 binary. - TUI search is now a query language; defaults to your image’s working set.
Press
/to filter bytype:,module:,status:, orin:(closure of any unit), in addition to plain substring search.Tabcompletes field names and values. The TUI starts filtered toin:<your-default-image>, so a project with thousands of units shows just what your image needs. PressSto save the current query tolocal.staras the new default; press\to snap back to it. The header showsQuery: … Units: N/Mso you always know how many of the project’s units the current filter is showing. - Use
apk-toolsfrom alpine layer for now. It is built with docs. yoe repo cleandrops stale.apkfiles. Removes any.apkin the project’s local repo whose name+version no longer matches a current unit (unit deleted, version bumped, release suffix changed) and re-signs the regenerated APKINDEX. Without this,apk addhappily picks the highest- versioned candidate even when that candidate is leftover from a since-deleted unit — which is how aLUA=no-builtapk-tools(“apk has been built without help”) could keep winning over Alpine’s prebuilt long after the source unit was removed.- Source-built
opensslno longer collides with Alpine’slibcrypto3/libssl3. Theopensslunit inunits-corenow declaresprovides = ["libcrypto3", "libssl3"], so any package whoseruntime_depsreachlibcrypto3orlibssl3(e.g.units-alpine’sapk-tools) routes back to the source-built openssl instead of pulling Alpine’s split libcrypto3 /libssl3 packages alongside. Without this, image-timeapk addaborted withtrying to overwrite usr/lib/libcrypto.so.3 owned by openssl-3.4.1-r0. units-alpinenow lives in its own repo.yoe initand the e2e project pullunits-alpineandunits-jetsonfromgithub.com/yoebuild/instead of carrying units-alpine inside this repo. Existing projects withpath = "modules/units-alpine"should switch to a remotemodule(...)ref.- Shadow notices are off by default. Cross-module unit shadowing and
providesoverrides no longer print a stderr notice on every load. Pass--show-shadowsto see them when you actually want to audit which module won. --allow-duplicate-provideslets multiple units share a virtual. When set, units in the same module may declare the sameprovides(apk-style “any of these satisfies”); the first one wins forPROVIDESlookup. Needed forunits-alpine’slinux-firmware-*fan-out, where ~100 packages all providelinux-firmware-any.patches=resolves relative to the unit’s own .star file directory. A unit can now ship its patches alongside its definition (e.g.units/bsp/foo/patches/0001-fix.patchnext tounits/bsp/foo.star), and the samepatches=["patches/foo/0001-fix.patch"]works whether the unit is loaded from a local module override or a fetched remote module. Previously patches were resolved against the project root, which meant module-shipped patches couldn’t be found unless every consumer copied them.
[0.9.1] - 2026-05-01
yoe deploy <unit>now installs the package’s runtime deps too. Previously it only built and published the named unit, so deploying a package withruntime_depsoutside what the device already had on disk failed with a crypticapk adderror likesqlite (no such package). Deploy now walks the full runtime closure (the same expansionimage()does at image-build time), so every transitive dep ends up in the feed beforeapk addruns.- Deploy refreshes the device’s apk index every time. The on-device
apk updatestep now usesapk --no-cache update, forcing a refetch of every repo’sAPKINDEXinstead of trusting whatever is in/var/cache/apk/. apk-tools 2.x can otherwise hold onto a stale index across a yoe-dev rebuild and silently miss packages you just published. - Added sqlite unit
[0.9.0] - 2026-05-01
- New design doc on libc and init choice.
docs/libc-and-init.mdlays out why yoe is musl + OpenRC + Alpine today, where that stack works (gateways, IoT, networking gear), where it doesn’t (Jetson, vendor BSPs, Adaptive AUTOSAR), and the planned rootfs-base abstraction that would let a single yoe codebase serve both Alpine and Ubuntu/L4T projects. Establishes the invariant that yoe stays apk-native on every target — Debian-derived bases get adeb_pkgconversion class, not dpkg/apt on the device. - Pull packages straight from Alpine. A new
units-alpinemodule wraps prebuilt Alpine.apkfiles as yoe units via thealpine_pkg()class — no source build, no patches, just fetch + verify + repack.muslandsqlite-libsship today; add more by pinning a version and sha256. muslnow comes from Alpine. The hand-rolled musl unit that copied the dynamic linker out of the build container is gone;muslis now an Alpine apk wrapped byalpine_pkg(). Output is byte-identical to the Alpine package other projects already ship..apkURLs work as a source type. Yoe’s source workspace now recognises.apkextensions and bare-copies them so the unit’s install task can extract the multi-stream gzip with GNU tar. Bare-copied sources also keep their URL filename, so install steps can reference the file by name instead of by cache hash.- Override an upstream unit by name. Define a unit with the same name in a
higher-priority module (or in the project itself) and it shadows the upstream
one — no
providesboilerplate needed. The project root beats every module, and later modules beat earlier ones. A notice on stderr tells you which one won. - Deploy from the TUI. Press
Don a non-image unit to deploy it to a running yoe device — host prompt is pre-filled from the last-used target, build + ssh + apk add output stream into the view, and the host is saved back tolocal.staron success. - Deploy actually updates the device’s apk index.
yoe deployandyoe device repo addpreviously wrote to/etc/apk/repositories.d/yoe-dev.list, which apk-tools 2.x ignores. They now append a marker block to/etc/apk/repositoriesso the nextapk updateactually fetches the dev feed andapk add <unit>finds the freshly built package. - TUI starts a feed automatically. When you launch
yoe, it brings up the project’s apk feed (or reuses one already running on the LAN), so devices configured withyoe device repo addcan pull packages without any extra setup. Status is shown in the header. - SSH target shorthand.
yoe deployandyoe device repo {add,remove,list}accept[user@]host[:port]— e.g.yoe device repo add localhost:2222for a QEMU vm oryoe deploy myapp pi@dev-pi.local:2200. The--ssh-portflag is gone. - APK live deployment tooling.
yoe deploy <unit> <host>builds and installs a unit on a running yoe device with full apk dependency resolution. Pair withyoe serveandyoe device repo addto keep a device pointed at your dev feed for ad-hocapk addfrom the device. See docs/feed-server.md.
[0.8.6] - 2026-04-30
- Container runtime build path documented.
docs/containers.md now walks through what it takes to
ship Docker, containerd, and runc on a musl yoe rootfs — why prebuilt “static”
binaries don’t work, the per-component build breakdown, and how cgo units like
runc plug into yoe’s existing Go toolchain and
toolchain-muslcontainer viadepsinstead of needing a new Go+GCC container image. - Rename
debugunits todev. - Expand roadmap. Reorganized as a pointer index into the design docs, with new sections for the app-developer build/deploy loop, hardware access, testing, self-hosting, and distribution variants.
- New testing design doc at docs/testing.md covers the
planned
yoe testdriver, build-time package QA, on-device upstream tests (Yoctoptestanalog), image smoke tests, and CI integration. - Kernel modules now ship in images — the
linux,linux-rpi4, andlinux-rpi5units previously built only the in-tree kernel image, so drivers compiled as loadable modules (Wi-Fi, USB, sound, many filesystems) were silently dropped. Modules are now built and installed to/lib/modules/<kver>/in the rootfs, somodprobefinds them at runtime. - Fix rPI4 builds package arch did not match what apk was expecting.
[0.8.5] - 2026-04-30
- `Yazi, Zellij, and Go units added.
- Clear error when an image’s rootfs won’t fit the partition. Yoe points at
the partition size to bump instead of failing mid-
mkfs.ext4with a cryptic ext2 error. - SSH works out of the box on
dev-image.sshdstarts on boot with per-device host keys;ssh -p 2222 user@localhost(passwordpassword) just works, and passwordless root SSH matches the serial console. - Image rebuilds recover from prior failed builds. A previous failure no longer wedges the next run on “Permission denied” — yoe reports the real error and cleans up automatically.
- New
binaryclass for prebuilt binaries. Units can ship upstream release binaries with SHA256 verification, no rebuild from source. Used bygo,helix, andyazi. apk addworks against the signed repo. Image-time and on-targetapkcommands no longer fail with “BAD signature” or need--allow-untrusted/--keys-dir.apk addandapk upgradework on yoe-built devices.dev-imageshipsapk-toolsand the project’s signing key, so OTA-style updates use stockapkcommands. Seedocs/on-device-apk.md.- Signed apks and APKINDEX. Every artifact is RSA-signed at build time and
verified by stock
apkon the target.yoe key generate/yoe key infomanage the project key; seedocs/signing.md. - Rootfs builds with APK. Much faster.
providesis now a list. Useprovides = ["a", "b"]; the string formprovides = "x"no longer parses.replacesis documented. New “Shadow files” section indocs/naming-and-resolution.mdcovers when to use it and how to read apk’s “trying to overwrite” errors.- “One .apk per unit” principle, documented. Image-to-image variation
belongs at runtime, not in build-flag forks. See
docs/naming-and-resolution.md. - SSH configured to autostart and work with blank passwords for dev builds.
[0.8.4] - 2026-04-29
- Networking picks the better DHCP client when available. The default
S10networkrunsdhcpcdif it’s onPATH(IPv6 SLAAC, DHCPv6, IPv4LL fallback) and falls back to busyboxudhcpcotherwise — so an image that shipsdhcpcdgets the modern client without changing the init script. - File conflicts in image builds now fail loudly. Units can declare
replaces = ["pkg", ...]to opt into shadowing another package’s files (e.g.util-linuxover busybox’s/bin/dmesg); apk honors that at install time and rejects any conflict that wasn’t declared. Image assembly no longer passes--force-overwrite, so a new shadow becomes a real error instead of a buried warning. - Unit edits no longer get masked by stale cache hits. Editing a unit’s
description, license, runtime deps, replaces, conffiles, build environment,
scope, image partitions, image excludes, or install-step files now invalidates
the cache as it should — previously these silently kept the old apk. A new
test in
internal/resolvefails if a future Unit field is added without being incorporated into the cache key. ipworks again ondev-image. iproute2 no longer pulls in libelf at link time, so/sbin/ipruns without “Error relocating /sbin/ip: elf_getdata: symbol not found” on images that don’t ship elfutils.- Boot no longer hangs when DHCP fails. The default network init script
waits briefly for the link to come up before starting udhcpc, runs udhcpc in
the background, and limits its retries — so
dev-imagereaches a login shell even when no DHCP server is reachable, instead of looping on “Network is down”. - Image rootfs is assembled by upstream
apk add. yoe no longer loopstar xzfover each apk; image builds runapk addagainst the project’s local repo, getting real dependency resolution, file-conflict detection, and an installed-package database in/lib/apk/dbfor free. On-target you can nowapk info,apk verify, and (once apk-tools ships as a unit)apk addandapk upgradeagainst the same repo. - Service symlinks ship inside the apk. A unit’s
services = [...]declaration is materialized as real/etc/init.d/SXX<name>symlinks inside the package’s data tar at build time. On-targetapk add <pkg>produces the same rootfs as image-time assembly — yoe never patches the rootfs after install. - Repo layout switched to Alpine-native —
repo/<project>/<arch>/<pkg>-<ver>-r<N>.apkplus a per-archAPKINDEX.tar.gz..apkfilenames no longer carry a scope suffix. Existingrepo/directories are obsolete; the next build repopulates the new layout. - Yoe-built apks install with upstream Alpine apk-tools.
.apkfiles andAPKINDEXproduced by yoe now round-trip through stockapk add --allow-untrusted: no checksum errors, no format warnings, and package metadata (name, version, arch, deps, origin, commit, install size) matches whatapk indexitself would emit. - Nine new units in
dev-image—e2fsprogs(mkfs.ext4 / fsck.ext4 / tune2fs on the target),eudev(full udev for dynamic /dev),iproute2(fullip/tc),dhcpcd(a DHCP client beyond busybox udhcpc),bash,less,file,procps-ng(realps/top/free/vmstat), andhtopare now built and included indev-imageso they’re available out of the box on a booted dev system.gperfis also added as a build-time dependency for eudev. - Updated units roadmap —
util-linux,kmod, andca-certificatesare marked done;dropbearis dropped (the project standardizes onopenssh); remaining work is nownftables(blocked on libmnl/libnftnl/gmp deps) anddbus. - Documented when NOT to use
provides—docs/naming-and-resolution.mdnow spells out thatprovidesis for leaf artifacts only (kernel, base-files, init, bootloader). Using it for build-time libraries or runtime alternatives forks every transitive consumer into a per-machine apk. Runtime alternatives likemdevvseudevshould ship side-by-side and be selected at boot from init scripts. - Image rootfs assembly now warns on path collisions — when two packages
install to the same path (e.g., busybox’s
/sbin/ipsymlink vs iproute2’s full binary), the later package silently overwrote the earlier one with no trace. Image assembly now emits awarning:line per collision naming the surviving package and the shadowed ones, plus a total count. The warnings appear in the image’sbuild.log(and on terminal whenyoe build -vis used). Existing dev-image builds surface 27 expected shadows of busybox applets by full alternatives — no behavior change, just visibility.
[0.8.3] - 2026-04-28
- mDNS via new
mdnsdunit — the dev-image now answers<hostname>.localon the LAN, sossh user@yoe-dev.localworks without knowing the device’s IP. Uses troglobit/mdnsd (a small dbus-free mDNS responder) and ships a default_ssh._tcpservice record so the host A record is advertised and SSH discovery works for Bonjour-aware tools. - NTP at boot via new
ntp-clientunit — boards without a battery- backed RTC (e.g., Raspberry Pi) booted at 1970, which broke TLS with “certificate is not yet valid”.ntp-clientdoes a blocking initial sync at S20 (retried a few times to cover DNS settling right after udhcpc) so subsequent services start with real time, then leaves a busyboxntpddaemon running to discipline drift over uptime. Added todev-imageby default.base-filesalso gets/var/runso daemons that write a pidfile have a place to put it. - Fix
simpleiotfailing to start at boot — the unit installed the binary as/usr/bin/simpleiotbut its init script invoked/usr/bin/siot, so booting the dev image showedsiot: not foundand the service never ran. The binary now installs assiotto match upstream.go_binarygains abinarykwarg for cases where the installed command name should differ from the apk package name. - Per-developer machine override via
local.star— when you switch machines from the TUI’s setup view, yoe now writeslocal.starat the project root with your selection. Subsequentyoecommands use that machine without you re-passing--machineevery time. The file is gitignored so each developer can pin their own target.--machineon the command line still wins. yoe flash listand TUI device picker —yoe flash listenumerates removable USB sticks and SD cards (filtered against the disk hosting the running system). In the TUI, pressingfon an image unit opens a device picker with a live progress bar during the write.yoenever invokessudoitself; if the device isn’t writable, it prompts once for consent and runssudo chown <you> /dev/....- Honest flash progress —
yoe flashnow opens the target device withO_DIRECTso writes bypass the kernel page cache and the progress bar tracks actual device throughput. Previously the bar could hit 100% with hundreds of MB still buffered in RAM, freezing the UI for tens of seconds during the final flush. WithO_DIRECTthe wait is paid out across the write itself, and “Flash complete” appears when the data is really on the card. - Fix
yoe flashrejecting non-system disks —flashpreviously refused to write to/dev/sda,/dev/nvme0n1, and/dev/vdaregardless of the actual layout. It now detects which disk hosts the running system (/,/boot,/boot/efi,/usr) and refuses only that disk, so flashing to a USB or external SATA drive named/dev/sdaworks on machines whose root is on NVMe. - Fix images silently shipping without packages — if an artifact’s apk was
missing from the local repo (e.g., its build was cancelled), the image used to
build anyway with a
warning: package X not found, skippingand produce a kernel-panicking rootfs. Image assembly now hard-fails with a clear message naming the missing package. The build cache now also treats a unit as out-of-date when its apk has gone missing, and rebuilding any unit invalidates its dependents — so reruns auto-recover instead of reusing stale outputs.
[0.8.2] - 2026-04-24
- Fix extlinux install under Docker 29 —
--privilegedcontainers no longer auto-populate/dev/loop*, solosetup --findfailed during image assembly. Pre-create/dev/loop0..31withmknodbefore callinglosetup.
[0.8.1] - 2026-04-24
- Fix rootfs ownership on booted systems — files under
/,/bin,/etc,/usr, etc. are now owned byroot:rooton the booted system instead of showing up as whatever user built the project. - Compare rootfs ownership handling across projects —
docs/comparisons.mdnow has a section explaining how Alpine, Debian, Buildroot, Yocto, and NixOS handle root ownership during image builds, and where[yoe]fits.
[0.8.0] - 2026-04-24
- Class task merge semantics — units passing
tasks=[...]to a class (autotools,cmake,go_binary) no longer fully replace the class’s default task list. Instead, overrides are merged by name: a same-named task replaces in place (preserving position and using the override’sstepsfully), a new-named task is appended, andtask("name", remove=True)drops a base task. This lets units add a new task (e.g.,init-script) without restating the class-generatedbuildtask. The merge is implemented in a newclasses/tasks.starhelper (merge_tasks(base, overrides)) shared by the three classes. Thesimpleiotunit dropped its duplicatedbuildtask as a result; existing units that overridebuildare unaffected (replace-in-place yields the same result as the previous full-replacement semantics). - Fix install_template/install_file path resolution for helper functions —
template paths now resolve relative to the
.starfile containing theinstall_template()/install_file()call, not to the file that ultimately callsunit(). Previously, a helper likebase_files(name = "base-files-dev")inunits/base/base-files.starinvoked fromimages/dev-image.starlooked for templates underimages/base-files-dev/instead ofunits/base/base-files/, breaking thedev-imagebuild. The base directory is now captured at install-step construction time from the Starlark caller frame; existing units that define and use install steps in the same.starfile are unaffected. - File templates — units can declare external template files (
.tmpl) and static files in a directory alongside the.starfile and install them via newinstall_template()andinstall_file()step-value constructors placed directly intask(..., steps=[...])alongside shell strings. Templates render through Gotext/templatewith a unifiedmap[string]anycontext auto-populated withname/version/release/arch/machine/console/projectand any extra kwargs passed tounit(). The context map and the contents of the unit’s files directory are hashed so template edits and extra-kwarg changes invalidate the cache. Install steps run on the host (not inside the sandbox), so$DESTDIR/$SRCDIR/$SYSROOTin install paths expand to host paths rather than the container bind-mount paths.base-files,network-config, andsimpleiotmigrated off inline heredocs. Seedocs/file-templates.md. - CLI flag parsing with flag.NewFlagSet — refactored all subcommands
(
build,run,flash,init,clean,log,refs,graph) from manual switch-based parsing to Go’sflag.NewFlagSet. Adds free--helpfor every subcommand, consistent-flag/--flagsupport, and repeatable flags (e.g.,--port). Net reduction of ~70 lines. - Go module cache — Go units now persist module and build caches across
builds via
cache_dirs = {"/go/cache": "go"}. The executor mountscache/go/from the project directory into the container, andGOMODCACHEandGOCACHEpoint to it. Subsequent builds skip module downloads. - Fix service enablement for S-prefixed init scripts — services declared
with an
S<NN>prefix (likeS10network) no longer get a symlink created on top of the actual script, which was causing a symlink loop and breaking networking at boot. - Unit environment field — units can declare
environment = {"KEY": "VAL"}which the executor merges into the build environment for all tasks. The Go class uses this forGOMODCACHE/GOCACHEso custom tasks (like simpleiot) get the cache env vars automatically. - QEMU port forwarding in machine config —
qemu_config()now accepts aportsfield (e.g.,ports = ["2222:22", "8118:8118"]) for default port forwarding. CLI--portflags extend these. Fixed a bug where multiple ports created duplicate QEMU netdevs. Fixed hostfwd syntax to use QEMU’shost-:guestformat. QEMU machines default to SSH (2222:22), HTTP (8080:80), and SimpleIoT (8118:8118). - Service enablement moved to units — units now declare
services = ["sshd"]to indicate which init scripts they provide. The image assembly auto-enables services by readingservicemetadata from installed APKs and creatingS50<name>symlinks (or custom priority likeS10network). Theservicesparameter onimage()is removed. - Design specs — added
docs/starlark-packaging-images.md(move packaging and image assembly to composable Starlark tasks) anddocs/file-templates.md(external template files using Gotext/template, replacing inline heredocs in units). - Go class uses golang container —
go_binary()now defaults to thegolang:1.24external container image instead oftoolchain-musl. Cross-compilation is handled viaGOARCH/GOOSenvironment variables withCGO_ENABLED=0for static binaries, so the container always runs at host architecture (no QEMU overhead). - Per-unit sandbox and shell selection — units now have
sandbox(bool, default false) andshell(string, default “sh”) fields. The autotools, cmake, and image classes setsandbox=True, shell="bash"for bwrap isolation. External containers (likegolang:1.24) use the defaults — no bwrap, POSIX sh — since they don’t ship bwrap or bash. - simpleiot unit — new
go_binaryunit for SimpleIoT v0.18.5, an IoT application for sensor data, telemetry, and device management. - ca-certificates unit — Mozilla CA bundle for TLS verification. Added to dev-image alongside simpleiot.
- Per-task container resolution — tasks can override the unit-level
container via
task(container = "..."). The executor resolves the container per-task, falling back to the unit default. - TUI: amber
[yoe]title — the top-left title in the TUI now renders[yoe]in amber on black, matching the project logo. - Fix module URLs in
initgenerated project file.
[0.7.1] - 2026-04-06
- Unit
releasefield — units can now specifyrelease = Nfor packaging revisions (apk-rNsuffix). Defaults to 0. Bump when the unit definition changes but the upstream version doesn’t. - Build metadata — each unit’s build directory now contains a
build.jsonwith status, start/finish times, duration, build disk usage, installed size (destdir/apk), and input hash. The TUI detail view shows build time and sizes alongside the unit name. - Persistent build output — executor output (
executor.log) is now written for both CLI and TUI builds, so the TUI detail view shows build output regardless of how the build was triggered.
[0.7.0] - 2026-04-06
- Container units — build containers are now Starlark units
(
toolchain-musl) instead of an embedded Dockerfile. Containers participate in the DAG, caching, and versioning. Classes setcontainerandcontainer_archexplicitly.run(host = True)enables host-side execution for container builds. The embedded Dockerfile andEnsureImage()are removed. Container images are tagged with arch for explicitness (yoe-ng/toolchain-musl:15-x86_64). Cross-arch containers usedocker buildxautomatically. - Container image prefix renamed — Docker image prefix changed from
yoe-ng/toyoe/(e.g.,yoe/toolchain-musl:15-x86_64). Arch is always included in the tag for explicitness. Cross-arch containers usedocker buildxautomatically. - TUI: detail view log search — press
/in the unit detail view to search build output and logs. Matching lines are highlighted in yellow;n/Njump to next/previous match. Firstescclears the search, second returns to the unit list. - TUI: color-coded unit types — unselected units are now subtly colored by
class: blue for regular units, magenta for images, cyan for containers.
Selected unit uses a brighter green for visibility. Search (
/) also matches unit class, so typing “image” or “container” filters to units of that type. - E2E build test scripts — added
yoe_e2e,yoe_e2e_x86_64, andyoe_e2e_arm64shell functions inenvsetup.shthat buildbase-imagefrom the e2e test project for x86_64 and arm64 (cross-build via QEMU user-mode).
[0.6.0] - 2026-04-03
- TUI: ctrl+f/ctrl+b page scrolling — added vim-style page-forward and page-back keybindings in both the unit list and detail views, alongside the existing PgUp/PgDn keys.
- Heavy development notice — GitHub releases and
yoe updatenow remind users to clean their build directory and re-create projects with each new release. - Updated plan/spec indexes — all specs and plans marked with current implementation status; added plans INDEX.
- Remove
repository()builtin — therepository(path = "...")config inPROJECT.staris removed. APK repos are now always atrepo/<project-name>/, derived from the project name. This eliminates a confusing override that defeated per-project repo scoping. - TUI: show all units — removed the filter that only showed units reachable from image definitions. The TUI now lists all units in the project.
- README: “Is Yoe-NG Right for You?” — new section clarifying when to use Yocto vs Yoe-NG. Added container workloads on the target device to the roadmap in Design Priorities.
- Fix
yoe updatedownload URL — binary name now matches goreleaser’s naming convention (yoe-Linux-x86_64) instead of incorrectly including the version (yoe-v0.1.0-Linux-x86_64), which caused 404 errors. - Unit name collision detection — duplicate unit names now error at evaluation time with a clear message showing which module first defined the unit.
- PROVIDES collision detection — two units providing the same virtual name in the same module now error. Units from higher-priority modules (later in the module list) override lower-priority ones with a notice.
--projectflag —yoe --project projects/customer-a.star buildselects an alternate project file. Available on all subcommands.- Per-project APK repo — package repositories are now scoped per project
name (
repo/<project>/) to prevent stale packages across project switches. - README: Principles section — added six core design principles covering leveraging existing infrastructure, aggressive caching, custom containers per unit, no intermediate formats, one tool for all levels, and tracking upstream closely.
- README: Build dependencies and caching — new section explaining the three kinds of build dependencies (host tools via containers, library deps via sysroot/apk, language-native deps via their own package managers), symmetric caching at the unit level, and how native builds unlock existing package ecosystems (e.g., PyPI wheels on ARM).
- README: Cross-compilation is optional — updated from “no cross compilation” to “cross compilation is optional,” acknowledging that Go and some C/C++ packages cross-compile easily while fussy packages can avoid it.
- Raspberry Pi in yoe init — rpi machine added to the project initialization template.
- Fix false “old build layout” warning —
warnOldLayoutwas written for the oldbuild/<arch>/<unit>/directory structure but the current layout isbuild/<unit>.<scope>/, causing every build directory to trigger a spurious warning.
[0.5.1] - 2026-04-02
- Remove version from release binary name to fix stable download URL.
[0.5.0] - 2026-04-02
BASE-IMAGE boots on RPI4
- Tasks replace build steps —
build = [...]replaced bytasks = [...]with named build phases. Each task hasrun(shell string),fn(Starlark function), orsteps(mixed list). Classes (autotools, cmake, go) are now pure Starlark. run()builtin — Starlark functions can execute shell commands directly during builds. Errors show.starfile and line number, not generated shell.run(cmd, check=False)returns exit code/stdout/stderr for conditional logic.run(cmd, privileged=True)runs directly in the container as root for operations like losetup/mount that bwrap can’t do.- Unit scope — units declare
scope = "machine","noarch", or"arch"(default). Machine-scoped units (kernels, images) build per-machine. Build directories are flat:build/<name>.<scope>/. Repo is flat with scope in filenames:repo/<name>-<ver>-r0.<scope>.apk. - Machine-portable images — images no longer hard-code machine-specific
packages or partitions.
MACHINE_CONFIGandPROVIDESinject machine hardware specifics automatically.base-imageworks across QEMU x86, QEMU arm64, and Raspberry Pi without changes. PROVIDESvirtual packages — units and kernels declareprovidesto fulfill virtual names.provides = "linux"onlinux-rpi4means images that list"linux"get the RPi kernel when building forraspberrypi4.- Image assembly in Starlark — disk image creation moved from Go to
classes/image.starusingrun(). Fully readable, customizable, forkable. - Raspberry Pi BSP module (
units-rpi) — machine definitions, kernel fork units, GPU firmware, and boot config for Raspberry Pi 4 and 5. - Runtime dependency resolution — image assembly now resolves transitive
runtime dependencies automatically.
RUNTIME_DEPSpredeclared variable available after unit evaluation. Three-phase loader: machines → units → images. - Layers renamed to modules —
layer()→module(),LAYER.star→MODULE.star,yoe layer→yoe module,layers/→modules/. Aligns terminology with Go modules model used for dependency resolution.
[0.4.0] - 2026-03-31
ARM BUILDS ON X86 NOW WORK
- TUI global notifications — the TUI now shows a yellow banner for background operations like container image rebuilds. Previously these events were only visible in build log files.
- cmake added to build container — cmake is now available as a bootstrap tool in the container (version bump to 14), enabling units that use the cmake build system.
- xz switched to cmake — the xz unit now uses the cmake class instead of autotools with gettext workarounds, simplifying the build definition.
- TUI reloads .star files before each build — editing unit definitions or classes no longer requires restarting the TUI. The project is re-evaluated from Starlark on each build, picking up any changes to build steps, deps, or configuration.
- Fix xz autoreconf failure — xz’s
configure.acusesAM_GNU_GETTEXTmacros which require gettext’s m4 files. The xz unit now provides stub m4 macros and skipsautopoint, allowingautoreconfto succeed without gettext installed in the container. - Cross-architecture builds — build arm64 and riscv64 images on x86_64 hosts
using QEMU user-mode emulation. Target arch is resolved from the machine
definition. Run
yoe container binfmtfor one-time setup, thenyoe build base-image --machine qemu-arm64works transparently. - Arch-aware build directories — build output is now stored under
build/<arch>/<unit>/and APK repos underbuild/repo/<arch>/, supporting multi-arch builds in the same project. Note: existing build caches underbuild/<unit>/will need to be rebuilt (yoe clean --all). yoe container binfmt— new command to register QEMU user-mode emulation for cross-architecture container builds. Shows what it will do and prompts for confirmation.- Multi-arch QEMU —
yoe runnow auto-detects cross-architecture execution and uses software emulation (-cpu max) instead of KVM. Container includesqemu-system-aarch64andqemu-system-riscv64. - TUI setup menu — press
sto open a setup view for selecting the target machine. Shows available machines with their architecture and highlights the current selection. Designed to accommodate future setup options.
[0.3.4] - 2026-03-30
- Build lock files — a PID-based
.lockfile is written during builds so otheryoeinstances can detect in-progress work instead of marking active builds as failed. Builds are skipped if another process is already building the same unit. yoe clean --locks— removes stale lock files left behind by crashed or killed builds.- TUI edit for cached layers — pressing
eon a unit now also searches the layer cache, so editing works for units from layers cloned viayoe layer sync.
[0.3.3] - 2026-03-30
- HTTPS layer URLs —
yoe initnow uses HTTPS URLs for the units-core layer instead of SSH, removing the need for SSH key setup to get started.
[0.3.2] - 2026-03-30
- TUI scrolling — both the unit list and detail log views are now
scrollable. The unit list shows
↑/↓overflow indicators when there are more units than fit on screen. The detail view supportsj/k,PgUp/PgDn,g/Gnavigation through the full build output and log, with auto-follow during active builds. - Auto-sync layers —
yoe buildand other commands that load the project now automatically clone missing layers on first use, matching the lazy container-build pattern. Existing cached layers are not fetched/updated, so there is no added latency on subsequent runs. Explicityoe layer syncis still available to update layers. - TUI confirmation prompts — quitting (
q/ctrl+c) and cancelling a build (x) now prompt for confirmation when builds are active, preventing accidental loss of in-progress builds. Declining a prompt clears the message cleanly. - Fix build cancellation not stopping containers — cancelling a build (via
TUI quit or
ctrl+con the CLI) now explicitly stops the Docker container (docker stop) instead of only killing the CLI client, which left containers running in the background. - Fix stale cache after cancelled builds — the cache marker is now removed before building so a cancelled or failed rebuild no longer appears cached from a previous successful build.
[0.3.1] - 2026-03-30
ALL UNITS ARE NOW BUILDING
- Per-unit sysroots — each unit’s build sysroot is assembled from only its
transitive
deps, not every previously built unit. Fixes busybox symlinks shadowing container tools (e.g., musl-linkedexprbreaking autoconf). - Run from TUI — press
ron an image unit to launch it in QEMU. - Log writer plumbing — container stdout/stderr in image assembly and source fetch/prepare output now route through the build log writer instead of os.Stdout. Fixes TUI alt-screen corruption during background builds.
- Autotools maintainer-mode override —
makeinvocations passACLOCAL=true AUTOCONF=true AUTOMAKE=true AUTOHEADER=true MAKEINFO=trueto prevent re-running versioned autotools (e.g.,aclocal-1.16) that aren’t in the container. Fixes gawk and similar packages. - rcS init script —
base-filesnow includes/etc/init.d/rcSwhich runs all/etc/init.d/S*scripts at boot. - network-config unit — new unit that configures a network interface via an init script.
- Build failure context — when a unit fails, the output now lists all downstream units blocked by the failure. The TUI shows cached units in blue and displays the full build queue (waiting/cached) before work begins.
- dev-image — added
kmodandutil-linuxto the development image. - Image rootfs dep fix — image assembly now follows only
runtime_depswhen resolving packages, not build-timedeps. Fixes build-only packages (e.g., gettext via xz) being installed into the rootfs and overflowing the partition.
[0.3.0] - 2026-03-30
THIS RELEASE DOES NOT WORK - this release is only to capture rename and TUI updates. Wait for a future one to do any work.
BREAKING CHANGE - due to rename, recommend deleting any external projects and starting over.
- Terminology rename — “recipe” is now “unit” and “package” is now
“artifact” throughout the codebase. The Starlark
package()function is nowunit(), the image fieldpackagesis nowartifacts, and therecipes/directory in layers is nowunits/. Therecipes-corelayer is nowunits-core. The Gointernal/packagingpackage is nowinternal/artifact. yoe log— view build logs from the command line. Shows the most recent build log by default, or a specific unit’s log withyoe log <unit>. Use-eto open the log in$EDITOR.yoe diagnose— launch Claude Code with the/diagnoseskill to analyze a build failure. Uses the most recent build log by default, or a specific unit’s log withyoe diagnose <unit>.- TUI rewrite —
yoewith no args launches an interactive unit list with inline build status (cached/waiting/building/failed). Builds run in-process viabuild.BuildUnits()with real-time status events — dependencies show as yellow “waiting”, then flash green as they build. Features: background builds (b/B), edit unit in$EDITOR(e), view build log (l), diagnose with Claude (d), add unit with Claude (a), clean with confirmation (c/C), search/filter (/), and a split detail view showing executor output and build log tail. Theyoe tuisubcommand has been removed. - Build events —
build.Options.OnEventcallback notifies callers (e.g., the TUI) as each unit transitions through cached/building/done/failed states.
[0.2.10] - 2026-03-30
yoe container shell— interactive bash shell inside the build container with bwrap sandbox, sysroot mounts, and the same environment variables recipes see during builds. Useful for debugging build failures and sandbox issues.
[0.2.9] - 2026-03-30
- Bash for build commands — switched build shell from busybox sh to bash.
Avoids autoconf compatibility issues (e.g.,
AS_LINENO_PREPAREinfinite loop) and matches what upstream build scripts expect. Removed per-recipe bash workaround from util-linux. - User account API — new
classes/users.starprovidesuser()andusers_commands()functions for defining user accounts in Starlark.base-filesis now a callablebase_files()function that accepts ausersparameter — image recipes can override it to add users (e.g., dev-image adds auseraccount with passwordpassword).
[0.2.8] - 2026-03-30
- meson build system support — added samurai (ninja-compatible build tool),
meson, and kmod recipes. Container updated to v11 with python3 and
py3-setuptools for meson. Build environment now sets
PYTHONPATHto the sysroot so Python packages installed by recipes are discoverable. - Container versioning note — CLAUDE.md now documents that both
Dockerfile.buildandinternal/container.gomust be bumped together. - gettext recipe — builds GNU gettext from source as a recipe instead of
relying on the container. Provides
autopointneeded by packages like xz that use gettext macros in their autotools build. - Sysroot binaries on PATH —
/build/sysroot/usr/binis now prepended toPATHduring builds, so executables from dependency recipes are discoverable. - Autotools class respects explicit
buildsteps — no longer prepends default autoreconf/configure when a recipe provides its own build commands. - Claude Code plugin — added
.claude/plugin with AI skills for recipe development:diagnose(iterative build failure analysis),new-recipe(generate recipes from URLs/descriptions),update-recipe(version bumps),audit-recipe(review against best practices and other distros). --cleanbuild flag — deletes source and destdir before rebuilding.--forcenow only skips the cache check without cleaning.--force/--cleanscoped to requested recipes — dependency recipes still use the cache, only explicitly named recipes are force-rebuilt.- Fixed
YOE_CACHEhelp text — was~/.cache/yoe-ng, actually defaults tocache/in the project directory.
[0.2.7] - 2026-03-27
- Per-recipe build logs — build output written to
build/<recipe>/build.log. Console is quiet by default; on error the log path is printed. Use--verbose/-vto stream build output to the console. - Fixed QEMU machine templates — removed UEFI firmware (
ovmf/aavmf/opensbi) incompatible with MBR+syslinux boot, fixed root devicevda2→vda1.
[0.2.6] - 2026-03-27
- base-files recipe — provides filesystem skeleton:
/etc/passwd(root with blank password),/etc/inittab(busybox init + getty),/boot/extlinux/(boot config), and essential mount point dirs (/proc,/sys,/dev, etc.). Moved from hardcoded Go to a recipe so users can customize via overlays. - Serial console uses
gettyfor proper login prompt.
[0.2.5] - 2026-03-27
Added
- musl libc recipe — copies the musl dynamic linker from the build container into the image so dynamically linked packages work at runtime.
- Automatic package dep resolution — image assembly now resolves transitive build and runtime deps from recipe metadata. e.g., openssh automatically pulls in openssl and zlib without listing them in the image recipe.
- Recipes without source — recipes with no
sourcefield (e.g., musl) skip source preparation instead of erroring.
Fixed
- Disable ext4 features (
64bit,metadata_csum,extent) incompatible with syslinux 6.03 so bootloader can load kernel from any partition size. - Image package dep resolution walks both
depsandruntime_depsso shared libraries are included. - OpenSSL recipe uses
--libdir=libso libraries install to/usr/libinstead of/usr/lib64— fixes “Error loading shared library libcrypto.so.3”. - Inittab no longer tries to mount
/dev(already mounted by kernel viadevtmpfs.mount=1). - Skip
TestBuildRecipes_WithDepsin CI — GitHub Actions runners don’t support user namespaces inside Docker. - Most stuff in
dev-imagenow works.
[0.2.4] - 2026-03-27
- update BL config
[0.2.3] - 2026-03-27
Changed
- Container as build worker —
yoeCLI always runs on the host. The container is now a stateless build worker invoked only for commands that need container tools (gcc, bwrap, mkfs, etc.). Eliminates container startup overhead for read-only commands (config,desc,refs,graph,clean). - File ownership — build output uses
--user uid:gidso files created by the container are owned by the host user, not root. - QEMU host-first —
yoe runtries hostqemu-system-*first, falls back to the container if not found. --forcescoped to requested recipes —--forceand--cleanonly force-rebuild the explicitly requested recipes; dependencies still use the cache for incremental builds.- Busybox init — images use busybox
/sbin/initwith a minimal/etc/inittabinstead ofinit=/bin/sh. Shell respawns on exit, clean shutdown viapoweroff.
Fixed
- Shell quoting in bwrap sandbox commands — semicolons in env exports no longer split the command at the outer shell level.
- Package installation in image assembly — always extracts
.apkfiles viatarinstead of gating onapkbinary availability. - Rootfs mount points (
/proc,/sys,/dev,/tmp,/run) now included in disk images via.keepplaceholder files. devtmpfs.mount=1added to kernel cmdline so/devis populated before init.
Removed
YOE_IN_CONTAINERenvironment variable — no longer needed.ExecInContainer/InContainer/HasBwrapAPIs — replaced byRunInContainer.- Container re-exec pattern — the yoe binary is no longer bind-mounted into the container.
[0.2.2] - 2026-03-27
Added
- Layer
pathfield — layers can live in a subdirectory of a repo viapath = "layers/recipes-core". Layer name derived from path’s last component. - Project-local cache — source and layer caches default to
cache/in the project directory instead of~/.cache/yoe-ng/ .gitignoreinyoe init— new projects get a.gitignorewith/buildand/cache- Autotools
autoreconf— autotools class auto-runsautoreconf -fiwhen./configureis missing (common with git sources) - SSH URL support for source fetching (
git@host:user/repo.git) - Design: per-recipe tasks and containers — planned support for named
task()build steps with optional per-task Docker container images. Container resolves: task → package → bwrap. Seedocs/superpowers/plans/per-recipe-containers.md.
Changed
- Default layer in
yoe inituses SSH URL (git@github.com:YoeDistro/yoe-ng.git) withpath = "layers/recipes-core" - Container no longer mounts a separate cache volume — cache/ is accessible through the project mount
- Container runs with
--privileged(needed for losetup/mount during disk image creation and /dev/kvm for QEMU)
[0.2.1] - 2026-03-27
Added
- Dev-image with 10+ packages — new
dev-imagebuilds end-to-end with sysroot, including essential libraries (openssl, ncurses, readline, libffi, expat, xz), networking (curl, openssh), and debug tools (strace, vim) - Remote layer fetching —
yoe layer syncclones/fetches layers from Git - Sysroot + image deps in DAG — build sysroot and image dependencies resolved as part of the dependency graph
yoe_sloc— source lines of code counter usingscc
Fixed
- Correct partition size for
losetup, ensure sysroot dir exists - Recipe fixes for end-to-end dev-image builds
Changed
- Moved design docs into
docs/directory - Expanded build-environment and comparisons documentation
[0.2.0] - 2026-03-26
Added
- Bootable QEMU x86_64 image — end-to-end flow from recipes to a partitioned disk image that boots to a Linux kernel with busybox
- Starlark
load()support — class imports and@layer//pathlabel-based references across layers,//resolves to layer root when inside a layer - Recursive recipe discovery —
recipes/**/*.stardirectory traversal recipes-corelayer — autotools/cmake/go/image classes, busybox/zlib/ syslinux/linux recipes, base-image, qemu-x86_64 machine- APKINDEX generation —
APKINDEX.tar.gzfor apk dependency resolution - Bootstrap framework —
yoe bootstrap stage0/stage1/status - Container auto-enter — host
yoebinary bind-mounted into container, Dockerfile embedded in binary, versioned image tags
Fixed
- Build busybox as static binary (no shared lib dependency on rootfs)
- APKINDEX uses SHA1 base64 as required by apk
- Handle git sources in workspace (tag upstream without re-init)
- bwrap sandbox inside Docker with
--security-opt seccomp=unconfined - Mount git root for layer resolution
Changed
- Prefer git sources with shallow clone over tarballs
- Move build commands to
envsetup.sh(yoe_build,yoe_test)
[0.1.0] - 2026-03-26
Initial release of yoe-ng — a next-generation embedded Linux distribution builder.
Added
- CLI foundation —
yoe init,yoe config show,yoe clean,yoe layercommands with stdlib switch/case dispatch (no framework) - Starlark evaluation engine — recipe and configuration evaluation using
go.starlark.net with built-in functions (
project(),machine(),package(),image(),layer_info(), etc.) - Dependency resolution — DAG construction, Kahn’s algorithm topological
sort with cycle detection,
yoe desc,yoe refs,yoe graph - Content-addressed hashing — SHA256 cache keys from recipe + source + patches + dep hashes + architecture
- Source management —
yoe source fetch/list/verify/cleanwith content-addressed cache and patch application - Build execution —
yoe buildwith bubblewrap per-recipe sandboxing, automatic container isolation via Docker/Podman - Package creation — APK package creation,
yoe repocommands, local repository management - Image assembly — rootfs construction, overlay application, disk image generation with syslinux MBR + extlinux
- Device interaction —
yoe flashwith safety checks,yoe runfor QEMU with KVM - Interactive TUI — Bubble Tea interface for browsing recipes and machines
- Developer workflow —
yoe dev extract/diff/statusfor source modification - Custom commands — extensible CLI via
commands/*.star - Patch support — per-recipe patch files applied as git commits