Files
salmanoff/distro/ubuntuCore/ubuntu-core-plan.md
T
hayodea 038d59f972 Add distro/ubuntuCore for UC26 snap and image builds.
Centralize salmanoff snapcraft, dangerous-model image scripts, and QEMU
workflow so UC26 can be reproduced from the SMO repo without ubuntu-core-practice.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 23:01:52 -04:00

12 KiB
Raw Blame History

Salmanoff on Ubuntu Core — Plan (derived from Yocto work)

Purpose of this document

This plan is not a translation of an original Yocto design doc. It reflects what we actually built and validated in the Yocto path, reframed as goals for Ubuntu Core. Use it as primary context when scaffolding snaps, gadget/model, and dev VM workflow.

When done, snap definitions and support files land in the SMO repo under:

smo/distro/ubuntuCore/

(parallel to the existing smo/distro/yocto/meta-salmanoff/).


What we proved in Yocto (facts, not aspirations)

We shipped a minimal headless image that runs the Salmanoff (SMO) cognitive robotics runtime on QEMU x86-64, bridged onto a lab LAN, talking to a Livox LiDAR on a fixed IP.

Lab network (canonical — carry forward unchanged)

Host IP Role
Laptop / gateway 10.42.0.1 Gateway + DNS
RPi5 (production target) 10.42.0.2 Future production node
Dev guest (QEMU / UC VM) 10.42.0.16 SMO dev runtime
Livox Avia 10.42.0.139 LiDAR sensor

Runtime configuration we validated

  • Body file: yocto-qemu-x86-headless.dapss — Livox-only, headless (no win0/camera), smo-ip=10.42.0.16, includes avia0.dapss.
  • SMO build profile (CMake):
    • RelWithDebInfo
    • ENABLE_LIB_lcameraDev=ON, ENABLE_STIMBUFFAPI_lcameraBuff=ON (libcamera + OpenCL YUV path compiled in; no camera passthrough needed in QEMU)
    • ENABLE_LIB_xcbXorg=OFF, COMPILE_PCL_TOOLS=OFF, ENABLE_TESTS=OFF, ENABLE_LCAMERADEV_TOOLS=OFF
    • Git submodules required (libspinscale, etc.)
  • Runtime command shape (approximate):
    salmanoff -d /usr/share/salmanoff/devices/bodies/body_yocto_qemu_x86_headless.daps -p /usr/lib -v
    
    (exact installed .daps filename follows DAPSS build output naming)

Platform services / packages we pulled into the image

Need What we used in Yocto
SMO runtime + libs salmanoff recipe (libspinscale, liblcameraDev, liblcameraBuff, liblivoxGen1, …)
Boost Pinned 1.86 (shared boost-system; must stay < 1.89)
liburing Runtime dep
libcamera + v4l-utils Present on image; camera optional at runtime
OpenCL Mesa Rusticl via opencl distro feature; needs RUSTICL_ENABLE=llvmpipe or platform shows 0 devices
OpenCL verify clinfo (also discovered 256M RAM OOMs OpenCL probe — use ≥ 2G for dev VM)
Network Static eth0: 10.42.0.16/24, gw 10.42.0.1
Dev access SSH (openssh on Yocto image)
Kernel probe rseqsliceprobe + kernel config append for rseq slice extension
QEMU dev Host bridge br-smo, runqemu with bridge=br-smo, nographic, -m 2048

Repo / packaging decisions already made in SMO

  • Yocto layer vendored at smo/distro/yocto/meta-salmanoff/ (committed on clast).
  • SMO source changes on clast: headless body, flex/bison repo-relative #line paths, etc.
  • Fetch uses private Gitea (hayodea/salmanoff, branch clast, submodules via libspinscale on separate SSH host alias).

Conceptual goals (portable to Ubuntu Core)

These are the intent items — independent of BitBake:

  1. Repeatable dev environment — headless guest on 10.42.0.16, reachable from laptop, Livox at 10.42.0.139.
  2. SMO as first-class payload — not “install deps manually”; runtime + .daps bodies + plugin .sos packaged and startable.
  3. Same CMake feature profile as Yocto (lcamera + OpenCL enabled; xcb/PCL tools off).
  4. OpenCL that actually works headless — Rusticl/llvmpipe with env var set before SMO starts.
  5. libcamera stack present — libraries available even when no camera device is passed through (QEMU/UC dev).
  6. Boost version constraint honored — do not silently pick Boost ≥ 1.89.
  7. Production path to RPi5 — same snap(s), different body/IP (10.42.0.2), gadget/kernel for Pi hardware.
  8. Distro metadata lives in SMOdistro/ubuntuCore/ committed back into SMO when stable (mirrors Yocto layout).

Yocto → Ubuntu Core mapping

Yocto (what we did) Ubuntu Core (target)
meta-salmanoff/ layer distro/ubuntuCore/ — snaps, gadget, model, helper scripts
salmanoff-image.bb Model assertion + core + gadget snap + app snaps on seeded image
salmanoff.bb recipe salmanoff snap (snapcraft.yaml: cmake build, submodules, organize libs + daps)
IMAGE_INSTALL Snap stage-packages / parts / content snaps (mesa, libcamera deps)
salmanoff-rusticl-env recipe Snap environment: block or wrapper script: RUSTICL_ENABLE=llvmpipe
init-ifupdown static IP Netplan via gadget default config, network-setup snap, or system-connections on UC
runqemu-salmanoff-bridge Dev VM script: UC image in QEMU/LXD/multipass on br-smo, static 10.42.0.16, 2G RAM
DISTRO_FEATURES += opencl Ensure Mesa/OpenCL ICD available to snap (layout + plugs or bundled libs)
DEPENDS / RDEPENDS Snapcraft build-packages, stage-packages, slots/plugs, layout: for /usr/lib ICD paths
gitsm:// + SRCREV Snapcraft source + source-submodules or override-pull git submodule update
yocto-qemu-x86-headless.dapss Installed by salmanoff snap; UC-specific body can be ubuntu-core-x86-headless.dapss later
rseqsliceprobe Validate UC kernel/base; custom kernel snap only if probe fails on stock UC kernel
SSH on image UC: serial console, ssh-keys in model, or dedicated snap — dont assume openssh like Yocto
Boost 1.86 pin Pin in snapcraft (stage-packages version) or build Boost part from source
RelWithDebInfo + debug maps Snapcraft override-build cmake flags; consider debug snap or stripped separate artifact

Proposed Ubuntu Core architecture

Host (10.42.0.1)
  └── bridge br-smo (10.42.0.0/24)
        └── UC dev VM / RPi5 (10.42.0.16 or .2)
              ├── snap: core (or core24)
              ├── snap: gadget-<platform>   (boot, partitions, default netplan)
              ├── snap: salmanoff           (daemon — main payload)
              └── optional: mesa-support / libcamera-support content snaps if not bundled

salmanoff snap (primary deliverable)

Type: likely daemon (simple) or daemon (notify) once startup semantics are known.

Build (mirror Yocto recipe intent):

  • Source: SMO repo clast (submodules!)
  • CMake options: same as Yocto EXTRA_OECMAKE block
  • Install: salmanoff binary, lib*.so*, /usr/share/salmanoff/devices/**
  • Apps: main daemon + optional salmanoff.probe for debugging

Runtime environment:

environment:
  RUSTICL_ENABLE: llvmpipe

Interfaces to evaluate early (exact set TBD by confinement testing):

  • network / network-bind — Livox UDP/TCP to 10.42.0.139
  • hardware-observe — may be needed for camera discovery on Pi
  • camera — for libcamera on production Pi
  • opengl — if Mesa GL stack needed beyond OpenCL ICD
  • raw-usb / serial-port — only if Livox path requires it on some platforms
  • home — usually avoid; use $SNAP_DATA for state

Layouts (likely needed):

  • OpenCL ICD loader expects /etc/OpenCL/vendors or known LD_LIBRARY_PATH
  • May need layout: bind mounts for Mesa libRusticlOpenCL.so / gallium stack

Platform snaps

Platform Gadget Notes
QEMU x86 dev pc or custom gadget Static IP 10.42.0.16, 2G RAM, bridged NIC
RPi5 production pi gadget (22+ arm64) Static IP 10.42.0.2, libcamera, Livox

Bodies / config

  • Dev (UC VM): start from Yocto body or add ubuntu-core-x86-headless.dapss with same Livox-only intent and smo-ip=10.42.0.16.
  • Production (RPi5): reuse/adapt existing rpi5-persys-headless.dapss pattern with smo-ip=10.42.0.2.

Body files stay in SMO devices/bodies/; snap selects default via config or snap config hook.


Phase 0 — Repo scaffold

  • New repo (or distro/ubuntuCore/ in SMO) with snapcraft.yaml skeleton
  • Document lab IP table and bridge prerequisites (port runqemu-salmanoff-bridge concepts)
  • Link to SMO clast branch + submodule SSH host requirements

Phase 1 — Build snap on host (no hardware)

  • snapcraft / craft builds SMO with same CMake profile as Yocto
  • Confirm submodule pull (libspinscale) in clean CI/local environment
  • Resolve Boost < 1.89 (stage-package pin or source part)
  • Package .daps + shared libs; verify salmanoff -v / dry-run in snap run environment

Phase 2 — OpenCL + libcamera in confinement

  • Replicate Yocto OpenCL stack inside snap (bundled vs system Mesa)
  • Confirm RUSTICL_ENABLE=llvmpipeclinfo shows ≥1 device inside snap
  • Confirm liblcameraDev / liblcameraBuff load; no crash without camera device
  • Document RAM requirement (≥ 2G for dev VM)

Phase 3 — UC dev VM on lab LAN

  • Boot Ubuntu Core x86 VM bridged to br-smo
  • Static IP 10.42.0.16 (netplan/gadget)
  • Install seeded salmanoff snap (devmode first, then strict)
  • Ping gateway; reach Livox at 10.42.0.139
  • Run headless body; validate Livox traffic / SMO logs

Phase 4 — Production path (RPi5)

  • Model assertion for Pi5 + arm64 core
  • Gadget with 10.42.0.2 netplan
  • libcamera plug + Pi body file
  • OTA refresh via snap channels

Phase 5 — Commit back to SMO

  • Move stable distro/ubuntuCore/ into SMO repo
  • Commit on clast (or dedicated branch); do not block on unfinished WIP snaps

Known constraints & pitfalls (from Yocto — expect repeats)

  1. OpenCL: platform visible but 0 devices until RUSTICL_ENABLE=llvmpipe.
  2. Memory: 256M insufficient for Rusticl/clinfo; use 2G for dev VM.
  3. Boost: must stay < 1.89 (shared boost-system linkage).
  4. Submodules: libspinscale is mandatory; fetch must be recursive; separate SSH host alias on Gitea.
  5. lcamera without hardware: compile and ship libs; no QEMU camera passthrough required for basic bring-up.
  6. PCL / xcb: keep off unless explicitly needed (MPI/cmake pain in Yocto).
  7. Private git: snapcraft build needs SSH keys / credentials for Gitea fetch (or vendored source tarball part).
  8. Strict confinement: biggest unknown vs Yocto — plan devmode → strict iteration; interfaces will take multiple passes.
  9. rseq kernel feature: validate on UC base; custom kernel snap is expensive — only if probe fails.

Success criteria (match Yocto milestone)

Minimum “were at parity” for UC dev:

  • UC VM at 10.42.0.16 on 10.42.0.0/24 via host bridge
  • salmanoff snap installed and daemon running (or manual snap run equivalent)
  • Headless Livox body loaded; SMO stable with Livox on 10.42.0.139
  • OpenCL initialized (llvmpipe); lcamera libs present
  • Snap definitions committed under smo/distro/ubuntuCore/

Stretch (production):

  • Same snap on RPi5 at 10.42.0.2 with Pi-appropriate body

Explicit non-goals (for now)

  • Re-implement Yocto layer / BitBake in UC repo
  • Camera passthrough in QEMU/UC x86 dev
  • PCL tools / xcb window stack
  • lcameraDev probe tools (need test support libs — off in Yocto too)
  • Full OTA/signing production pipeline (Phase 4+)

Reference: Yocto artifacts in SMO repo

When unsure, read the working Yocto implementation:

smo/distro/yocto/meta-salmanoff/
  conf/layer.conf
  conf/salmanoff-local.inc          # opencl distro feature
  recipes-salmanoff/salmanoff/salmanoff.bb
  recipes-core/images/salmanoff-image.bb
  recipes-core/init-ifupdown/...      # 10.42.0.16 static IP
  recipes-support/salmanoff-rusticl-env/
  scripts/runqemu-salmanoff-bridge

smo/devices/bodies/yocto-qemu-x86-headless.dapss

SMO branch: clast on hayodea/salmanoff (Gitea).


Instructions for the LLM in the Ubuntu Core repo

  1. Treat Yocto work as validated reference, not something to re-derive from scratch.
  2. Preserve lab IP plan and CMake profile unless user says otherwise.
  3. Start with snapcraft build on host, then UC VM, then strict confinement — same order we used (build → package QA → runtime).
  4. Plan for distro/ubuntuCore/ in SMO as the final home of snaps/gadget/model/scripts.
  5. Ask before committing snaps into SMO; user will commit when done.
  6. Do not assume UC has openssh or classic Ubuntu package management — everything is snaps + interfaces.