LuneOS
Architecture

How LuneOS is built.

LuneOS runs the webOS user experience on phones and tablets, using webOS OSE's core and the webOS Ports card shell. It supports two kinds of device: Halium targets reuse the Android vendor stack through libhybris and binder, and mainline targets run upstream Linux drivers on Qualcomm, Allwinner, Rockchip, Broadcom and NXP silicon. Everything above the hardware-adaptation layer is shared. HP webOS 3.0.5 on the TouchPad is shown alongside for comparison.

Figure 1

The layered stack

The bands follow webOS OSE’s architecture diagram, from Core Applications down to BSP and Kernel. The bottom two bands split by target family, and the toggle dims whichever family you aren’t looking at. Each dot shows where a component’s source comes from.

webOS OSE (unchanged) OSE, webOS-ports fork LuneOS / webOS-ports original Palm Open webOS legacy Upstream open source
Shared above, split below. The same packages are built for both families. The machine include meta-android-halium.inc adds the halium override, which swaps EGL/GLES to libhybris, pulls in the LIBHYBRIS_RDEPENDS set, and gives packages a separate -halium package arch. What each individual device gets on top is no longer written per machine: since the September packagegroup rework it comes from MACHINE_FEATURES (see below).
Heritage

How LuneOS relates to webOS OSE

OSE was designed for a TV or signage device with one full-screen app at a time. LuneOS keeps OSE’s core (LS2, SAM, WAM, LSM, DB8, Nyx, audiod), changes some parts, and adds what a phone needs: cards, telephony, sensors and Android hardware.

OSE blockIn LuneOSWhy
Home · Launchpad · Status Barluna-next-cardshell QML, loaded as LSM’s WebOSCompositor importwebOS card multitasking, gestures, dock mode and lock screen. OSE’s placeholder compositor import is removed at build time.
SAMSAM, plus luna-appmanager serving com.palm.applicationManagerLegacy Enyo 1, Mojo and PDK apps still call the Palm-era API names.
WAM + ChromiumWAM fork (clang build) on webos-ports/chromium151Replaces the older QtWebEngine-based WebAppMgr. qtwebengine is blacklisted in the distro. Touch input is enabled.
Settings (Enact)settings-qmlPhone-sized settings, with panels for telephony, NFC, fingerprint and so on.
Nyx HALnyx-modules with a per-machine .cmake, plus nyx-modules-hybrisBattery, haptics, LEDs and keys come from Android HALs on Halium and from sysfs on mainline.
ConnectivityOSE connman adapter and Bluetooth, plus webos-telephonyd on oFonoOSE has no cellular stack. Voice calls use Sailfish’s voicecall.
MediaOSE uMS and g-media-pipeline, plus gst-droid or libcamera, plus sensorfwCameras and sensors on phones and tablets.
BSP (Raspberry Pi 4)meta-smartphone, meta-pine64-luneos, meta-rpi-luneosTwo hardware-adaptation families and 30+ machines.
Not in OSEEnyo 1, Mojo, mojomail, PDK shim, Preware, novacomd, keymanagerCompatibility with Palm and HP webOS 1.x–3.x apps.
Heritage · Figure 2

Legacy webOS side by side

Rows are the same layers as Figure 1. The legacy column is HP webOS 3.0.5, read directly from the TouchPad AT&T rootfs (build 86, Dec 2011). Where the Pre 2 on webOS 2.2.4 differs, the table notes it. In legacy webOS, one binary, LunaSysMgr, was the compositor, card manager, app manager and much of the system service layer. LuneOS still serves many of LunaSysMgr’s Palm bus names, but from separate luna-* services.

LayerHP webOS 3.0.5 · TouchPadwebOS OSELuneOS
Shell & compositorLunaSysMgr, a single Qt process handling cards, launcher, notifications, dock, keys and display. No Wayland.LSM with QML System UI (Home, Launchpad, Status Bar)luna-surfacemanager + luna-next-cardshell: the webOS card UI rebuilt as a Wayland compositor
UI toolkit & GPUQt 4.8.0 (4.6.1 on 2.2.4) rendering straight to vendor EGL/GLES (Adreno on TouchPad, PowerVR SGX on Pre 2)Qt 6, QtWayland, MesaQt 6.12, QtWayland; libhybris over vendor EGL, or Mesa
App lifecycleBuilt into LunaSysMgr: com.palm.applicationManager, com.palm.appinstallerSAM, appinstalld2SAM, plus luna-appmanager serving the same Palm names
System bus namesLunaSysMgr also owns com.palm.display, com.palm.systemmanager, com.palm.vibrate, com.palm.keyscom.webos.* namesSplit out: luna-displaymanager, luna-authmanager, luna-haptics
Web runtimeWebAppMgr, a forked child of LunaSysMgr running all web apps in one WebKit (libWebKitLuna) processWAM + Chromium, one renderer per appWAM + Chromium 151, one renderer per app
BrowserBrowserServer, a separate WebKit server processEnact BrowserAtlas on WAM
App frameworksMojo, Enyo 1.0, foundations librariesEnactEnyo 1 and Mojo (compatibility), Enyo 2, Enact, QML
JS servicesNode.js v0.4.12 (v0.2.3 on 2.2.4) with palmbus.node, fork_server.jsNode.js, webos-serviceNode.js 22, mojoservicelauncher, nodejs-module-webos-*
Native appsPDK: SDL 1.2, libpdl, GLES, started by LunaSysMgr’s IpcServer, with “direct rendering” for full-screen appsNative Qt/QML appsQt 6 apps; PDK apps through pdk-luneos under qemu-user (armel)
Busls-hubd_private + ls-hubd_public (two hubs, palm://)LS2 (luna-service2)luna-service2, one ls-hubd
Datamojodb-luna and tempdb on an encrypted LVM volume (cryptofs), filecacheDB8 on LevelDBDB8 on LevelDB, still named com.palm.db
Hardware abstractionlibhid* plugins (touch panel, keypad, accelerometer, compass, light, proximity), hidd, libhalNyxnyx-modules, plus nyx-modules-hybris on Halium
Telephony & networkTelephonyInterfaceLayer, PmWanDaemon, qmuxd, PmNetConfigManager, PmWiFiServiceconnman adapter; no cellularwebos-telephonyd on oFono; connman adapter
BluetoothProprietary PmBtStack / PmBtEngineBlueZBlueZ 5 (with bluebinder on Halium)
Mediamediaserver, GStreamer 0.10, PulseAudio 0.9.22, audioduMediaServer, GStreamer 1.x, PulseAudio, audioduMediaServer, GStreamer 1.28, PulseAudio 17, audiod
Initupstart 0.3.8 jobs in /etc/event.dsystemd, bootdsystemd 257, bootd
Kernel & storage2.6.35-palm-tenderloin (2.6.32 on Pre 2). Read-only ext3 root plus LVM store volumes (var, cryptodb, filecache, media)Raspberry Pi 4 kernelAndroid GKI or vendor kernel, or mainline 6.9–7.2. Rootfs in userdata or on a wic partition
Updates & developer accessUpdateDaemon (OMA DM), novacomd, ipkgSW Updater, devmodeorg.webosports.service.update, devmode, novacomd, opkg
HP webOS 3.0.5 one UI process forks one web process LunaSysMgrQt 4.8 · UI process cards · launcher · notifications · dock · keys applicationManager · display · systemmanager draws straight to vendor EGL / framebuffer fork() PIpc · shm buffers WebAppMgrsame binary · libWebKitLuna app A app B app C every Mojo / Enyo app shares this process one crash takes all cards down (webKitDied) PDK appSDL 1.2 · direct render launchNativeProcess BrowserServerbrowser’s WebKit ls-hubd private + public · mojodb-luna · Node 0.4 services upstart 0.3.8 · kernel 2.6.35 · libhid LuneOS compositor, launcher and renderers are separate processes luna-surfacemanagerQt 6.12 · Wayland compositor luna-next-cardshell QML: cards · launcher · notifications Palm bus names moved to luna-appmanager, -displaymanager … EGL via libhybris (Halium) or Mesa KMS/GBM (mainline) SAMlaunch over LS2 WAMdaemon · Chromium 151 renderer A renderer B renderer C a crashing web app loses only its own card Qt / PDK appnative Wayland client wl_surfacewl_surface one ls-hubd · DB8 (LevelDB) · Node 22 services systemd 257 · Android or mainline kernel · nyx-modules
From one process to many. On the TouchPad, LunaSysMgr forked a WebAppMgr child from the same binary. That child hosted every web app in one WebKit instance and passed rendered frames back over libLunaSysMgrIpc shared memory. LunaSysMgr watched for webKitDied because one crash took down every card. LuneOS uses the OSE model instead. Every app is a Wayland client, each web app gets its own Chromium renderer, and lifecycle has moved from the shell into SAM.
Per-device composition

What a device gets, and who decides

Two separate mechanisms decide what lands in an image. The halium machine override decides whether the Android vendor stack is built at all. MACHINE_FEATURES decide which hardware stacks ride along, so a machine config declares what the device has rather than listing packages. This replaced the per-machine RDEPENDS:append:<machine> lines in packagegroup-luneos-extended.bb.

Declared on the machinePulls inWho sets it
halium overrideLIBHYBRIS_RDEPENDS: libhybris, android-system, lxc, android-property-service, android-tools, qbootctl, hwcomposer QPA, pulseaudio-modules-droid(-hidl), gst-droid, bluebinder, nyx-modules-hybris, wlan-dynamic-start, wlan-suspend-modeNot a feature — set by meta-android-halium.inc for every Halium machine
camera-rear · camera-front · camera-swivelCamera app, v4l-utils, and on mainline libcamera + libcamera-gst. A front or swivel camera also pulls luneos-faced30 of 32 phones declare their camera topology; it replaced blanket camera dependencies
fingerprintbiomd + webos-fingerprint-adapter19 machines, mostly Halium arm64 phones
nfcnfcd, nfcd-tools, nfcd-binder-plugin, webos-nfc-adapter19 machines. Deliberately absent where the hardware lacks it, or where there is no Android container to reach the binder NFC HAL
esimlpac, luneos-esim-adapter, a zbar GStreamer plugin for QR activationThe Pixels and halium-arm64
keyboard-qwerty · keyboard-t9 · keyboard-touch · trackpadHardware-keyboard support: maliit switches to Maliit::Hardware, the on-screen keyboard stays down, and kbdscroll turns key-surface or trackpad gestures into scrollingKEY2, MP01, Q25, the three Titans, MindPhone (T9)
killswitchorg.webosports.service.killswitchFuriPhone FLX1s, PinePhone, PinePhone Pro
wifiwireless-regdb-static, cfg80211-regdb-reloadEvery phone
phoneoFono, webos-telephonyd, voicecall and the phone appEvery cellular device

E Ink is the exception that proves the rule: there is no eink feature, and the MP01 pulls org.webosports.service.eink through MACHINE_EXTRA_RRECOMMENDS instead. Waydroid, the Qualcomm modem helpers and a few QEMU bits are also still per-machine lists.

Figure 3

Hardware adaptation, subsystem by subsystem

Each subsystem reaches one shared LuneOS consumer in the middle column. The Halium path goes through Android HALs and the mainline path through kernel drivers. Services above this layer don’t know which path is in use.

SubsystemHalium pathShared consumerMainline path
GraphicsVendor EGL/GLES loaded by libhybris · qt6-qpa-hwcomposer-plugin · hybris EGL server buffer in qtwayland (dmabuf off) · LUNEOS_ANDROID_EGL names the driver where the vendor does notluna-surfacemanager
Wayland clients
Mesa (freedreno, panfrost, lima, vc4) · Qt eglfs with kms gbm · dmabuf · softpipe fallback where the board has no GPU driver
Audiopulseaudio-modules-droid (+hidl) → audio HAL · droid-audio-config-genPulseAudio + palm-policy
audiod
ALSA UCM profiles (msm8953, PinePhone Pro, RK817) · alsa-state
Telephonyofono-binder-plugin + libgbinder-radio → radio HAL · rilmodem/qmimodem disabledoFono 2.19
webos-telephonyd
QMI/MBIM via libqrtr-glib · rmtfs qrtr rpmsgexport · eg25-manager on PinePhone
Cameragst-droid + droidmedia → camera HALGStreamer
camera service
libcamera 0.7.2 + libcamera-gst · V4L2
Sensorssensorfw built with autohybris binder → sensors HALsensorfw
qtsensors plugin
sensorfw iiosensorsadaptor · IIO drivers
Bluetoothbluebinder bridges the BT HAL to a virtual HCIBlueZ 5
bluetooth2
Kernel HCI drivers · bes2600, brcmfmac firmware
Platform HALnyx-modules-hybris (libhybris, libsuspend, libgbinder) overrides individual modules; the LED module probes the Android lights HALnyx-lib
sleepd · displaymanager
The same nyx modules. Device paths come from /etc/nyx.conf, written at boot by generator 30-nyx-conf
Biometrics · NFCbiomd → fingerprint HAL · nfcd-binder-pluginfingerprint adapter
webos-nfc-adapter
No fingerprint path · neard (NFC)
Android appsWaydroid (halium-arm/arm64, *-halium)Waydroid containerWaydroid (Pine64, Raspberry Pi) with binderfs
Figure 4

Inside a Halium target

A Halium device runs two userspaces on one Android kernel. LuneOS is the glibc host. Android’s /system and the device’s /vendor run in an LXC container started by android-system. Host code reaches vendor code in two ways: libhybris loads the vendor libraries into the host process, and libgbinder calls the HAL services over binder.

LUNEOS HOST · GLIBC USERSPACE consumerHalium adapterbridge library surfacemanager · WAM hwcomposer QPA · EGL PulseAudio modules-droid GStreamer camera gst-droid · droidmedia oFono ofono-binder-plugin sensorfw · nyx hybris adaptor · nyx-hybris BlueZ · fingerprint · NFC bluebinder · biomd · nfcd libhybrisbionic linkerin-process libgbinderbinder client ANDROID LXC CONTAINER /vendordevice blobs: EGL/GLES, gralloc, HWC, codecs /system · Halium 16 GSIinit · property service · (hw)servicemanager Vendor HAL servicescomposer · audio · radio · camerasensors · bluetooth · biometrics · nfcpower · lights · vibrator mount-android.sh · mount-apexes.pystart-android-hals.sh dlopen binder IPC via vendor libs Android kernel GKI android14-6.1 (Pixel 6a/7/Tablet, MP01, Q25) or device vendor kernel 3.4–4.19 · binder · vendor drivers & modules /dev/*binder, ioctl evdev · sysfs · wlan module · binder
Two bridges. Graphics, audio and camera load vendor .so files into LuneOS processes through libhybris. Radio, sensors, Bluetooth, biometrics and NFC talk to HAL services over binder through libgbinder. luna-surfacemanager starts only after Android is up (surface-manager-daemon-after-android.conf). Current targets use the generic Halium 16 GSI as /system on top of the device’s own /vendor. Halium’s charger UI is stopped during start-up because it holds DRM master, which the compositor needs.
Figure 5

Boot flow

The two families boot differently until the initramfs runs switch_root into systemd. After that they share one unit graph. On Halium, android-system.service is required by basic.target, and the graphics stack waits until Android reports its HALs are running. There is no android-boot-complete target; readiness is checked by polling getprop.

Halium

  1. Vendor bootloader (aboot / ABL)

    Unlocked (verifiedbootstate=orange). Loads boot.img and passes androidboot.slot_suffix on A/B devices.

  2. boot.img: kernel + initramfs-android-image

    Packed by kernel_android.bbclass (GKI devices also get init_boot). The Halium rootfs itself has no kernel, so one halium-arm64 rootfs serves every device.

  3. Initramfs /init

    Lights the panel backlight, starts mdev, then synthesises by-partlabel links the kernel does not provide, so Halium’s own mountroot can stay unpatched. Loads vendor and GKI modules, waits for userdata, and holds boot to charge if the battery is flat.

  4. Find and mount the rootfs

    Mounts userdata and looks for rootfs.img (loop) or /data/luneos (bind). The rootfs is read-only unless .writable_image exists, which dev images create.

  5. Assemble Android

    Loop-mounts the GSI system.img at /var/lib/lxc/android/rootfs → /android. Mounts the device’s own vendor$slot. Binds /var, /home and /media/internal from userdata/luneos-data.

  6. Stage the modules, then switch_root /rfs /sbin/init

    LuneOS’s own build of the vendor modules is copied out before the initramfs is freed. systemd then starts ls-hubd, configd and surface-manager early (from local-fs.target).

  7. android-system.service

    mount-android.sh (dynamic partitions, vendor_dlkm, APEX, binderfs), then lxc-start -n android -- /init. Android init brings up servicemanager and the HALs. start-android-hals.sh and wait-for-android.sh gate on getprop, for up to 120 s.

  8. Units that wait for Android

    luneos-device-config runs twelve generators that write nyx.conf, the oFono binder slots, surface-manager.env, the battery and PulseAudio settings, the hardware-key map and luna-platform.conf. After it come surface-manager, oFono (which first waits for the RIL’s binder slots), PulseAudio, sensorfwd, bluebinder, biomd and nfcd.

When it goes wrong. With initrd_log_fail on the kernel command line, the initramfs dumps dmesg every three seconds into a spare partition — init_boot_a for the failure dump and init_boot_b for progress, or one partition named by initrd_log_part on devices without them. A failed boot now parks forever instead of panicking, so the dump and the USB gadget survive.

Mainline

  1. Board bootloader

    PinePhone: EFI firmware → systemd-boot. PinePhone Pro and PineTab2: megi U-Boot → extlinux → fitImage. Raspberry Pi: GPU firmware → U-Boot → boot.scr. msm8953: aboot, optionally with lk2nd prepended.

  2. Kernel + DTB + initramfs

    initramfs-simple-image (PinePhone, PineTab2 inside the FIT). PinePhone Pro’s FIT carries initramfs-uboot-image, but the kernel discards it and mounts root=PARTLABEL=rootfsA itself. msm8953 phones use initramfs-scripts-android inside an Android boot.img.

  3. Initramfs /init

    Mounts pseudo-filesystems and starts mdev. Checks for recovery (bootmode=recovery or a key held down → luneos_recovery_ui). Sets up USB networking.

  4. Find and mount the rootfs

    Looks up the partition named luneos-root* (using LoaderDevicePartUUID on EFI), grows it to fill the card, and mounts it read-write. There is no /var or /home split.

  5. switch_root → systemd

    Same unit graph as Halium, without the Android units. luneos-device-config still runs early.

  6. Modem & board helpers

    eg25-manager (before oFono), luneos-usb-gadget (before connman) and luneos-diag. PineTab2 also runs wifi-module-load and hciattach. Qualcomm phones load firmware with msm-firmware-loader.

Shared LuneOS bring-up

  1. ls-hubd

    The Luna bus is up (Type=notify). PmLogDaemon has no unit; the bus starts it on demand.

  2. surface-manager + card shell

    surface-manager -platform eglfs loads WebOSCompositor = luna-next-cardshell. BootLoader.qml shows the logo, then runs initctl emit lsm-ready.

  3. default-webos.target

    Starts sam (after the bus and the compositor), then bootd. webapp-mgr is gated on /tmp/lsm-ready.

  4. bootd → core-boot-done

    webos-cbd.target: audiod, sleepd, power, Bluetooth, camera. Then init-boot-done → connman and PulseAudio.

  5. datastore-init-start

    webos-dis.target: DB8 (main, media, temp), configurator-db8, luna-sysservice, settings, pdm.

  6. minimal-boot-done

    webos-mbd.target: maliit, notificationmgr, uMediaServer, mediaindexer.

  7. rest-boot-done → boot-done

    activitymanager, appinstalld, configurator-activity and the download manager. Then /tmp/boot-done triggers lsm-ready.service, which restarts WebAppMgr and maliit.

  8. Hardware services alongside

    wlan-dynamic-start arms qcacld-3.0 before wpa_supplicant and connman, kbdscroll follows the compositor on keyboard devices, and the MP01 runs einkd for E Ink refresh modes.

  9. luna-appmanager decides the UI

    Serves org.webosports.bootmgr. If ran-firstuse is missing it launches First Use; otherwise it launches com.palm.launcher, com.palm.systemui and the boot-time apps. The card shell reads this state.

Legacy HP webOS 3.0.5 (TouchPad), for comparison

  1. Bootie (boot.bin)

    Palm’s bootloader. Loads /boot/uImage (2.6.35-palm-tenderloin) from the ext3 boot partition.

  2. Read-only ext3 root + LVM store

    /var and /var/log mount from LVM. cryptodb and cryptofilecache are encrypted and mounted later. Media is FAT.

  3. upstart 0.3.8: rcS → mountall → bootmisc

    Jobs in /etc/event.d start in order of start on stopped … events.

  4. ls-hubd_private → ls-hubd_public

    The public hub starts on ls-hubd_private-ready. finish runs on ls-hubd_public-ready.

  5. After finish

    Starts together: powerd, mojodb, tempdb, filecache, audiod, mediaserver, TelephonyInterfaceLayer, PmNetConfigManager, bluetooth, sensord, hidd, keymanager and configurator.

  6. LunaSysMgr

    Starts on stopped configurator. Without ran-first-use it runs with -a com.palm.app.firstuse. It forks WebAppMgr and emits LunaSysMgr-ready.

  7. LunaReady, activitymanager

    Jobs follow LunaSysMgr’s ready event. activitymanager waits for datastore-initialized, and PmWanDaemon for configurator.

Every device now brings up its own USB network: each board has its own /24 (PinePhone 172.16.42, PinePhone Pro .43, PineTab2 .44, Halium .45, FuriPhone .46), the device takes .2, and a one-lease udhcpd hands the host .1, so a single host-side profile works everywhere. The gadget also carries a per-device product string and a serial taken from the device tree.

The webos-*.target files have no ordering of their own. bootd calls initctl emit, which touches /tmp/<event> and starts the matching target. The only LuneOS-specific targets are lsm-ready.target and nyx.target.

Figure 6

What happens when you tap an app

Launching an app is the same on every target. The shell asks SAM over the Luna bus, SAM picks a runtime from the app type in appinfo.json, and the app process draws a Wayland surface that the compositor shows as a card.

luna-surfacemanagerQt 6 Wayland compositor luna-next-cardshellQML: launcher, cards,Just Type, notificationsWebOSCompositor composes surfaces as cards LS2 ls-hubdluna-service2 SAMreads appinfo.json applicationmanager/launch Legacy callers: com.palm.applicationManagerserved by luna-appmanager (Palm-derived) webWAM → Chromium 151 rendererEnyo 1/2, Mojo, Enact — one process per app qmlqml-runner / boosterdQML apps using luneos-components nativeQt 6 binarylifecycle via libwebos-application pdkpdk-luneos shim · SDLlegacy armel games under qemu-user each app process draws its own Wayland surface (webOS shell extension) → shown as a card
One entry point, four runtimes. WAM runs as a daemon from boot, so a web app launch starts a new Chromium renderer, not a new browser. SAM is the lifecycle authority. The dashed legacy path keeps Palm-era callers working. Which launch path SAM uses for QML apps (qml-runner or a native binary) depends on the app. The research left this open.
Figure 7

Yocto layer structure

This is the bblayers.conf of the wrynose tree. Arrows are LAYERDEPENDS, and the amber number is BBFILE_PRIORITY, where the higher number wins when two layers provide the same recipe. Hardware layers are on the left, distro layers on the right, and both sit on OpenEmbedded.

DEVICE LAYERS HARDWARE SUPPORT DISTRO · meta-webos-ports 12 vendor layers7 blackberry furilabs google greentouch huawei lg minimal motorola oneplus unihertz xiaomi zinwa unihertz is in bblayers but not yet committed meta-hp7TouchPad — Halium and mainline meta-samsung7Galaxy A3 2015 (mainline) · Tab A7 Lite (Halium) meta-pine64-luneos7PinePhone, PinePhone Pro, PineTab2 meta-mecha-luneos7Mecha Comet — NXP i.MX8M Plus, i.MX95 meta-rpi-luneos20Raspberry Pi 3 / 4 · also requires meta-luneos meta-android7Halium, hybris, GKI bootimg, lk2nd meta-mainline7initramfs, mainline kernel classes meta-qualcomm-modems7qrtr, rmtfs, qmic meta-rockchip1rk3399, rk3566 meta-raspberrypi9bcm27xx BSP meta-arm5+ arm-toolchain · i.MX, Rockchip firmware backports-6.133libcamera 0.7.2 from the next release holdbacks-5.334systemd 257.8 — 258 dropped cgroup v1 meta-luneos20distro conf · images · packagegroupsOSE / owo / LuneOS recipes · PDK meta-luneui15luna-service2, nyx-lib, Qt / Wayland / Mesa meta-qt65Qt 6.12 branch meta-clang7WAM, webruntime meta-openembedded5meta-oe · meta-python · meta-networking · meta-multimedia · meta-filesystems openembedded-core · meta5Yocto 6.0 wrynose bitbakebuild engine depends includes meta-luneos requires android-layer and qualcomm-modems-layer, even for mainline builds qt6 · oe · networking · multimedia
Reading the priorities. The distro layers (20 and 15) override the BSP and OpenEmbedded layers (7 and 5). The backport and holdback layers (33 and 34) are deliberately the highest: each pins one recipe version and holds no LuneOS configuration, so it can be deleted once wrynose ships that version. Four vendor layers arrived in the last two weeks — furilabs, samsung, unihertz and zinwa — plus meta-mecha-luneos as a third board layer beside Pine64 and Raspberry Pi. Branches here: meta-webos-ports herrie/latest-20260930, meta-smartphone herrie/fajita-and-q25, meta-pine64-luneos herrie/kernel-7.2, meta-mecha-luneos main, meta-qt6 6.12, everything else wrynose.
Build output

Images, packaging & install

Every target builds luneos-dev-image, which is webos_image plus the packagegroup-webos-extended, packagegroup-luneos-extended and packagegroup-luneos-development package groups. What differs per target is how the image is packaged and installed.

Halium · GSI eraHalium · legacyMainline QualcommPine64 · Raspberry Pi
Boot artefactluneos-bootimg-gki → boot + init_boot / vendor_boot (header v3–v4), for both true Tier A devices and Tier B devices that only borrow the packaging.kernel_android.bbclass, header v0 to v2 (the OnePlus 6T is the first v1, which keeps its device tree appended to the kernel)Image.fastboot, optionally with lk2nd prepended at a configurable offset: 1 MiB for the msm8953 fork, 512 KiB for upstream lk2nd on msm8916Kernel and DTB inside the wic image; U-Boot or systemd-boot
Rootfsrootfs.img inside userdata.img, next to the GSItar.gz in a recovery zipRecovery zip.wic.gz + .wic.bmap
Installflash.sh: vbmeta, boot_a/b, userdata over fastbootTWRP sideload · webos_deploy.shTWRP / fastbootdd or bmaptool to SD or eMMC
Recipeluneos-fastboot-package.incluneos-package.inckernel_android.bbclass*-luneos-bsp-image.wks

In the working tree, kernel_android.bbclass is also gaining an unsigned AVB hash footer for vendors that chain boot from vbmeta. No machine config switches it on yet.

The UBports Installer fork has validated configs for mako and hammerhead. Configs for mido, rosy and tissot are still to do, and Pine64 needs a new block-device plugin.

Machines

Target matrix

Every MACHINE in the tree: 44 of them, ten added in the last two weeks. Active means a current image in tmp/deploy/images or confirmed working by the maintainer. Builds means build history but no current image. Halium devices are split into two tiers: tier A runs the Android Common Kernel (GKI) with the stock vendor modules, tier B builds a device-specific vendor kernel in-tree. Three tier B devices (Q25, MP01, Pixel 4a 5G) borrow GKI packaging without a GKI kernel.

MachineDeviceSoCFamilyKernelLayerStatus

SoC names for the older Tier B devices are from general knowledge, not the machine configs. Three of these are not committed yet: the meta-unihertz layer with its three Titans, sm-t220, and mako-halium. rosy is a mainline machine but still carries a leftover android-system-image-rosy recipe.