Repository navigation
os/v3/puppy: the appliance image (our kernel, the release package embedded, the console menu) - #828
Conversation
…ections The first real build of kernel-puppy.fragment (off-box, v7.3-rc4 + the two PerfOpt backports) reduced 3 of its 24 symbols back to =m: DRM_SIMPLEDRM, HID_GENERIC and USB_HID are children of DRM and HID, both =m in our config, so the same well-known trap the medium chain already documents hit the console chain too. Only the fragment's per-symbol verification loop reported it. - the fragment gains CONFIG_DRM=y and CONFIG_HID=y (26 symbols), with the note that DRM_KMS_HELPER/DRM_GEM_SHMEM_HELPER are select-only and cannot be set here; every one of the 26 lands =y on v7.3-rc4 - docs/os-v3-live-puppy-eval.md: Blocker 1 records that second half - docs/kernel.md: the backport patch files are plain diff --git with a prose lead-in, so the runbook's 'git am --3way' fails with 'Patch format detection failed' - strip to the first diff header and git apply, then commit so the kver does not gain a stray '+'. Also: the unversioned llvm-objcopy lives in /usr/lib/llvm-<n>/bin on a host that ships only the versioned tools.
G3 ('uname -r is our kernel, amdgpu/amdxdna/ec_su_axb35 present and signed')
had no probe at all, so an image whose kernel bump left the out-of-tree
modules behind passed every existing check: mk-huge-kernel.sh asserts the
modules it is GIVEN and an empty updates/ passes it silently.
The probe reads the ISO and the zdrv inside it and asserts the release
string, /lib/modules/<kver> in the zdrv, both OOT modules with vermagic ==
that release string, firmware dirs, and the module count.
Its first run found a real defect in the ISO built before it existed:
amdxdna.ko is signed by 'Build time autogenerated kernel key' (the module
build's ephemeral key), not an enrolled MOK. Measured on the box: Secure Boot
is enabled, lockdown is [none], so it loads today - which is exactly why
--require-mok exists, for the release gate (U4 wants Secure Boot ON).
Measured on a built resolute64 rootfs: sudo, apt, mokutil and openssh-server are simply absent (only openssh-client has a row in woof-CE's package table), which is the 'sh: sudo: not found' class of gap from c20 that TPUPPY-RELEASE U1/U4/U7 exist to close. packages-extra carries the four rows with the measurements that justify each; spec-insert-rows.sh puts them inside PKGS_SPECS_TABLE (before its closing quote, and before the LAST one: after a build the file holds both the original and the resolved table) and skips a generic name that is already there, so it is idempotent. build.sh mounts os/v3/puppy at /recipe, applies the rows after merge2out, and after 3builddistro refuses to publish an image whose rootfs lacks sudo/apt/sshd/mokutil - an image missing them is not a 1bit OS and nothing else in the build would say so.
…e style list tools/copyright.py knows a '#'-comment style by file name (lemonade-hrx, Dockerfile) and for extensions; packages-extra is a bare name it had never seen, so --check refused it. Both halves: the NOTICE at the top of the file, and the name in LINE_STYLES beside lemonade-hrx. build.sh filters the rows with '^yes|' before inserting, so the header never reaches PKGS_SPECS_TABLE.
…boot U3/U4 want Lemonade and the engine inside the image, not installed beside it. The release package is relocatable by construction (lemond-onebit resolves bin/ and 1bit/ beside itself), so the embed is a copy to /opt/1bit-package, two symlinks, and one boot service - no installer, no dpkg. The boot contract is Puppy's and it was measured in a built resolute64 rootfs: /etc/rc.d/rc.services runs every EXECUTABLE /etc/init.d script with 'start' (directories with start/stop links are not involved), while the 295 files the Ubuntu packages leave under /lib/systemd/system are read by nothing because PID 1 is not systemd. Hence /etc/init.d/10lemonade and its 0755 mode. The service sources /etc/1bit/libhsa.env if the release build pins one (the same file v2's lemond unit took as EnvironmentFile), mints LEMONADE_API_KEY on first boot when the image carries none, and logs to /var/log. The build refuses an image missing the engine, the launcher, /usr/local/bin/1bit or the service - and marks the service executable, since rc.services silently skips a non-executable one.
…rect my first reading The kernel ships its own amdxdna in drivers/accel AND the pinned one lives in updates/, so a zdrv holds two files with the same name and depmod resolves updates/ first. The probe's 'find | head -1' took whichever came first in find order - the in-tree copy - and reported it as the module's signing state. That made my first finding WRONG: I reported the shipped amdxdna as signed by 'Build time autogenerated kernel key'. Re-measured with the order the kernel uses: updates/amdxdna.ko is signed by 'strixhalo Secure Boot Kernel', the in-tree copy is the one with the ephemeral signature, and the in-tree copy is never loaded. G3 is green on this artifact for the right reason, not because a gate was read loosely. The fix sorts /updates/ first and reports the shadowed copy instead of ignoring it, so a future kernel that DROPS the pinned module still fails loudly.
…e EC driver load at boot
Two defects, both found by reading the running build rather than by trusting it:
1. The embed step never ran. PAYLOAD is set on the host, but docker only forwards
what -e names, so inside the container it was empty and the whole block was
skipped silently. The mount was there; the variable was not. Now -e
PAYLOAD=/payload, and the build prints '== embed the release package' only
when it is about to do it - the log is the evidence either way.
2. ec_su_axb35 has no PCI modalias, so nothing auto-loads it: measured on the
box, lsmod has no entry and the fans ride the firmware curve. U5 needs it at
boot for fans, EC temperature and power caps. Puppy reads ADDLIST in
/etc/rc.d/MODULESCONFIG (rc.sysinit), so the build rewrites that one variable
in woof-CE's file instead of shipping a replacement, and refuses the image if
it cannot - an appliance without fan control is not shippable, not 'a detail'.
Also: an apostrophe inside the single-quoted docker block breaks the outer
quoting, and it cost two edits ('board's', 'Puppy's'). Both reworded.
…d image 3builddistro starts by deleting sandbox3/rootfs-complete and rebuilding it from rootfs-skeleton, which I measured in woof-code/3builddistro:62-78 after cycle 1 came back with an ISO that had neither the ADDLIST edit nor the embedded package. Both were written into the sandbox copy, so both were thrown away, and the build was the only thing that would have said so - it refused instead, because the same tree was gone by the time it looked. So the hook is rootfs-skeleton for ADDLIST and for the PAYLOAD embed, and the verification reads what 3builddistro actually packed: one listing of every SFS in sandbox3/build (each line sfs-name/path) plus an unsquashfs -cat of etc/rc.d/MODULESCONFIG, which is the file that silently kept its empty ADDLIST. The package check unions the SFS set because sudo is not in the puppy SFS - it is in bdrv, the debootstrap base - so a check that only looked at the main SFS would have failed a correct image. Measured on cycle 1's image (1,315,962,880 B, sha256 867dba3b9d3a0da0...): sudo in bdrv_upup64r_26.04.sfs, apt/sshd/mokutil in puppy_upup64r_26.04.sfs, and no /opt/1bit-package and no /etc/init.d/10lemonade, which is this defect.
…e layer)
Cycle 1's image booted to Puppy's quicksetup dialog on our kernel and the
probe still answered "neither a layer nor the not-found error", rc=2, because
its green marker was PUPMODE= - printed only when there is a save layer, and
a fresh ISO has none. G2 asks whether the init finds and layers the medium,
so the marker is the layer itself: the "Loading the <sfs> main file" line for
each SFS, plus rc.sysinit taking over ("Loading kernel modules", "Setting up
services") and the first-run dialog behind it.
Measured on the cycle-1 image (1,315,962,880 B, sha256 867dba3b9d3a0da0...),
three boots, all now green with that evidence printed.
U7 wants the stick to authorise the owner's ryzen key, and U3/U4 want to be checkable from another machine, but a built resolute64 image has no /etc/ssh/ssh_host_* at all: Puppy ships /etc/init.d/ssh and rc.services runs it, host keys are normally dpkg's job, and sshd refuses without them. The same image gives the files 644/755 from git, which StrictModes rejects for authorized_keys. 20onboard-ssh generates the host keys, fixes those two modes, and owns sshd (it tells Puppy's own script to stand down through the sshd_not_to_be_run marker that script already honours). With no key shipped it provisions no listener, which is the right default. 21serial-console answers the other half of headless: the boot transcript goes out on ttyS0 but nothing reads input there, so an appliance in a rack with no monitor is unadministrable and the automated VM run cannot type into it. It is opt-in on 1bit.serial=1 because that shell needs no password. build.sh chmods the three services (rc.services only runs executable ones) and the artifact check now refuses an image without the key or the services.
vm-check.sh answers the TPUPPY-RELEASE rows that do not need the board inside a QEMU VM: it boots the image with 1bit.serial=1, drives the serial root shell and prints PASS/FAIL/INFO per row - bash and sudo (U1), apt install neofetch (U1), lemond running with its minted key and /api/v1/models answering both inside the guest and over the forwarded port (U3), 1bit on PATH, /opt/1bit-package and the EC driver in Puppy's ADDLIST (U4) - then prints the hardware rows as SKIP with the reason. The guest 13305 forwards to host 21305 because another lane's lemond already holds 127.0.0.1:13305 here. tools/copyright.py: an ssh authorized_keys file is one key per line and a comment in it would be read as an option string, so it belongs with the other data exemptions rather than under a comment style.
…, and vm-check.sh
U4 is HRX chat on the 8060S, and none of what that needs was in the image:
* the pinned libhsa. The board reports XNACK enabled: NO and Coherent Host Access:
FALSE, so the no-SVM trio is not a preference (engine#715: without it HRX
page-faults on its first graph replay, 58 fault lines, HTTP 500 in <10 ms),
and ONEBIT_HRX_LIBHSA has to name the release-gated runtime rather than the
distro ROCm one that XRT drags in. build.sh takes LIBHSA=<libhsa-runtime64.so.1>,
installs it at /opt/1bit/libhsa and refuses an image that lost it; without
LIBHSA it drops etc/1bit/libhsa.env and says HRX has no runtime, because an
env file pointing at a missing library is worse than an honest gap.
* memlock unlimited: the NPU driver's own README asks for soft and hard
unlimited ("Lock limit exceeded", "failed to allocate BO" otherwise) and there
is no systemd here to carry a LimitMEMLOCK=. 10lemonade sets it before exec,
and /etc/profile.d/90onboard-limits does it for every shell - Puppy's
/etc/profile sources /etc/profile.d/* (not only *.sh) and already sets
ulimit -c 0 there, so that is the right hook.
tools/copyright.py learns the new names (*.env, 20onboard-ssh, 21serial-console,
90onboard-limits) so the licence gate keeps covering them.
…ts (copyright.py --check)
PR Reviewer Guide 🔍(Review updated until commit 5d245d0)Here are some key observations to aid the review process:
|
|
Persistent review updated to latest commit 495dc6e |
…k scripts work
Owner decision (12:2x): the appliance ships as a GPT disk image like os/mini's, not as an ISO, and it
takes os/mini's data-partition contract. Two prerequisites for U2/U3/U4/U7 that were simply missing.
mkimage.sh <puppy-out-dir> <out-dir> [data-mb] builds 1bit-os-puppy.img: an ESP with the shim -> grub
chain (Microsoft-signed shim, Canonical-signed grub, mmx64, grub.cfg with three entries) and a
1BIT-DATA ext4 partition carrying the frugal install at /1bit plus the contract directories. That is
os/mini/mkimage.sh's shape on purpose, so prepare-stick.sh (grow 1BIT-DATA with sfdisk size=+, mint the
API key into lemonade/lemond.conf, write ssh/authorized_keys at 600 root:root, copy models/) runs on it
unchanged - it asserts those exact paths. The ISO stays a side artifact: it is what boots in QEMU
without a disk and it is not what ships.
/etc/init.d/05onboard-data mounts 1BIT-DATA at /data before 10lemonade and 20onboard-ssh (rc.services
runs /etc/init.d/* in name order) and wires the contract:
* /etc/1bit/lemond.conf becomes a symlink to /data/lemonade/lemond.conf, so the key minted on first
boot is on the partition instead of in the save layer;
* ssh keys come from /data/ssh/authorized_keys, and the host key is kept there and linked into
/etc/ssh, so a client's learned fingerprint survives a reboot;
* Lemonade's state moves to /data/lemonade/{cache,config} (lemond's positionals are cache_dir
config_dir) with GGML_HRX_JIT_CACHE_DIR=/data/cache/hrx-jit, so the catalogue, the downloaded
backends and the HRX JIT cache persist;
* /data/models is registered in Lemonade's own user_models.json - a .gguf as onebit, a directory with
model.q4nx as onebit_backend npu - which is how os/mini makes /api/v1/models list what is on the
partition (U3's catalogue row). python3 does the JSON, because awk on JSON is how registries rot;
* the boot line may name the partition (1bit.data=...), because #711 measured that LABEL=1BIT-DATA is
ambiguous the moment a machine is installed; the label stays the default.
Puppy's save folder lands in the frugal dir, which is /1bit on that same partition, so U2's persistence
and /data are one filesystem, as asked.
grub.cfg gets a serial terminal (menu and input on ttyS0 as well as the screen): the appliance is
headless, and vm-check.sh selects the serial-shell entry over the wire the way a person would. vm-check.sh
now takes an .iso or an .img - the .img boots through OVMF as a USB disk (shim, grub, kernel and initrd
off the data partition) and adds rows for the mount, the key on the partition, the host key path and the
registry. build.sh refuses an image without /etc/init.d/05onboard-data.
…e and the boot services
|
Persistent review updated to latest commit 6f1e4b4 |
…k to its caller
Three things the first real boots of the disk image paid for:
* psubdir=1bit. Read out of Puppy's own initrd: PSUBDIR is both the directory the SFS are searched in
and SAVE_DIR (init:848, init:681), so without it a frugal install under /1bit is not found and the
save file goes to an unexpected place. It is what puts the save layer on the same partition as /data.
* -device qemu-xhci. q35 has no USB bus, so "-device usb-storage,drive=d0" fails with "No usb-bus bus
found" before firmware runs - in img-boot.sh and in vm-check.sh's .img path.
* chown to SUDO_UID. mkimage needs root for mkfs/sfdisk, and the 644 root-owned image it left behind
was then unreadable to qemu and dd as the invoking user ("Could not open ... Permission denied").
Measured on the result: 1bit-os-puppy.img (cycle 1 payload, data 2048 MB) boots under OVMF as a USB
disk - shim, grub (menu on the serial line, the serial-shell entry selected over it), then
"/1bit/vmlinuz pmedia=usbhd psubdir=1bit pfix=copy", all four SFS copied and layered, rc.sysinit up.
…e Puppy writes its save layer
|
Persistent review updated to latest commit d541700 |
Cycle 2 built every package and every petbuild and then died in the ISO wrapper: "losetup: /dev/loop20: failed to set up loop device: No such file or directory". The container's /dev carries loop0-7; this box keeps about twenty loop devices busy (snaps, and another lane's stick selftest), so the number loop-control hands out has no node behind it. attempt 7 and 9 hit the same class from the outside, which is why the host-finished ISO was a thing. So the container mknods loop0-63 before 3builddistro (privileged, so it may), and loop-control is always passed instead of being behind LOOP_DEVICE. Cycle 2's payload is otherwise complete: SFS, our kernel and initrd are in its sandbox3/build, which is what mkimage.sh needs.
Cycle 2 proved the ISO wrapper is the fragile part of the pipeline: every package, every petbuild and all four SFS were done when losetup killed it. The artifact that ships is the disk image, and it needs exactly three things from the build - vmlinuz, initrd.gz and the four SFS - so mkimage.sh takes either build.sh's out dir (and pulls the two files out of the ISO) or a build tree's sandbox3/build directly. An empty initrd is refused by name, because that is the failure that looks like a green build (attempt 7: a 27-byte initrd from a missing cpio). Measured: mkimage.sh on ~/wt/1bit-puppy-build9/…/sandbox3/build produced 1bit-os-puppy.img (4,833,935,360 B, ESP 512 MB + 1BIT-DATA 4096 MB), cycle 2's payload: /etc/init.d/10lemonade, /opt/1bit-package/1bit/1bit, /usr/local/bin/1bit and ADDLIST=ec_su_axb35 present - and no 05onboard-data, because it was written while that build was past its embed step.
|
Persistent review updated to latest commit f749b71 |
… this time was the interface not the listener
|
Persistent review updated to latest commit b39e495 |
|
Persistent review updated to latest commit 2e54f71 |
A symlink is always 777 and chmod cannot change it, so the five stock links in the skeleton (/etc/localtime, /etc/ppp/resolv.conf, /etc/X11/rgb.txt, /etc/xdg/menus/puppy-calculate.menu, /etc/wvdial_options/APN-VirginBroadband) were reported as group-writable files and refused a good tree. Regular files and directories only; what matters is the mode of the file the link names.
|
Persistent review updated to latest commit 4124384 |
…, and the fallback is loopback Measured on a dd-only copy of the cycle-13 image (pi-rlk's report, reproduced here from the scripts): a stick that never went through prepare-stick --mint booted Lemonade on 0.0.0.0:13305 with NO API key, on every boot - 200 without an Authorization header, including /internal/*, with the firewall opening 13305 to the LAN by design. Cause: 05onboard-data pre-created an EMPTY /data/lemonade/lemond.conf, and 10lemonade minted on "the file does not exist". So the guard was false, `. "$CONF"` exported nothing, and lemond started unauthenticated. - 05onboard-data: no longer writes the file. It creates the directory and the symlink - what the mint needs - and fixes the mode if a key is already there. - 10lemonade: mints when there is no LEMONADE_API_KEY line (missing file, empty file, or a file with no key), keeps a shipped file's other variables working, and if it cannot write the key it starts Lemonade on 127.0.0.1 instead of 0.0.0.0 - with no key the only thing between the LAN and /internal/* is the bind address. - vm-check.sh: "U3 key file" (a 48-hex key, mode, and where it lives) and "U3 auth" (401 without a key, 200 with it) - both must pass on a bare dd copy, which is the case that was broken.
|
Persistent review updated to latest commit d3cdb8f |
…arrive after the skeleton chown The packed-SFS check from the previous commit did its job on its first real run: cycle 16d was refused with three entries owned by 6975/2030 - /usr/local/frugalpup/doc/en/*.html, which 3builddistro unpacks from the PET packages into rootfs-complete AFTER the skeleton is normalised, and mksquashfs keeps whatever ownership it finds there. -all-root is not available for those packs (SFSCOMP is woof-CE's own), so container-build.sh patches woof-code/3builddistro to `chown -R 0:0` rootfs-complete and sandbox3/devx immediately before each mksquashfs, and verifies the patch landed twice and still parses - a silently unapplied patch would silently regress a security fix.
|
Persistent review updated to latest commit 51471fe |
|
The artifact scan I owed you, on the cycle-16 build. The blocker is gone: 0 host-owned entries. Compare cycle 15f, where all five were What the scan found anyway, and it is the gap I mentioned rather than a new defect: 119 group-writable entries remain
Also on the artifact, unchanged by this head: Nothing changes about my READY at |
The cycle-16 prepared-copy run came back 19 PASS / 19 FAIL with every row holding the previous row's answer. Cause
was the retry added in cycle 15: when the first line HAD run but its answer was slow, the resend queued a second
identical command, the row matched the first answer, and the second answer became the next row's body. Three fixes,
each measured:
- the sentinel now carries the attempt number (`__DONE_<attempt>_$(date +%s%N)__`, expanded only when the command
runs, because a literal token matched its own echo and every row came back EMPTY), so a stale attempt cannot match
this one;
- after a resend, whatever a duplicate leaves behind is dropped instead of being fed to the next row;
- any sentinel text that still reaches a body is stripped - one made `awk '{print $NF}'` pick a sentinel instead of
the API key and turned U3 lan into a 401.
|
Persistent review updated to latest commit 0a34063 |
|
pi-prs — both blockers from your verdict at 1. uid-1000 ownership (security blocker). Reproduced exactly what you measured —
That check earned its keep on its first real run: it refused three Result on the shipped SFS set: 2. CI "Copyright notice on every file". Also in this head (orchestrator's item 4): engine#867 — a bare /review |
… review note) The review guide asked why an embedded appliance can be built without LIBHSA_DIR. It cannot: an image with the release engine and no libhsa is precisely the cycle-13 appliance that booted, served CPU and had HRX register no device on the board (the `hrx-device` / `chat-hrx` rows failed on it). The runtime is part of the appliance, so the warning is now a refusal - for a PAYLOAD image only, because a plain Puppy base has no engine that could use one. Both branches simulated locally (no LIBHSA_DIR -> rc 1 with the reason; with it -> rc 0).
|
Three answers to the review guide's focus areas at 1. "Incorrect ownership normalization" — the premise is not what the code does. Line 318 onward normalises the whole skeleton, not just the directories the recipe writes: echo "== ownership: the whole skeleton -> root:root, nothing group/other-writable in /etc"
chown -R 0:0 "$skeleton" 2> /dev/null || true
chmod -R go-w "$skeleton/etc" "$skeleton/usr/local/petget" 2> /dev/null || trueand that block sits outside the 2. "Inconsistent handling of initrd guard". 3. "Missing HRX runtime validation" — now a refusal ( The cycle-16 artifact and its gates are unchanged by this commit — head is now |
|
Preparing review... |
|
/review |
|
Persistent review updated to latest commit e1e7e27 |
|
Persistent review updated to latest commit 5d245d0 |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY
At 5d245d03a (merged onto 23d6972d9). My READY was at 2e54f7171, four files changed since, so I re-checked the one
this PR was blocked on and re-measured the artifact — and the update goes beyond what I asked.
The ownership fix is intact at this head: container-build.sh:262 has "$skeleton/etc" in the per-directory chown,
:327 chown -R 0:0 "$skeleton", :328 chmod -R go-w, :334 the REFUSED arm.
The post-pack gap I reported is closed, and I verified the mechanism on the real script. The build now patches
woof-code/3builddistro to chown -R 0:0 each tree immediately before it is packed, asserts it landed exactly twice,
and bash -ns the result. I applied the same two sed expressions to this box's woof-CE checkout
(~/wt/1bit-puppy-build/woof-CE/woof-code/3builddistro) and got exactly that:
markers: 2 bash -n: ok
1116 chown -R 0:0 rootfs-complete … # 1bit: ownership before pack
1117 mksquashfs rootfs-complete build/${PUPPYSFS} …
1218 chown -R 0:0 sandbox3/devx … # 1bit: ownership before pack
1219 mksquashfs sandbox3/devx ./sandbox3/${DEVXSFS} …
One hole left in that patch, and it is the interesting one: 3builddistro has a third mksquashfs, at
:1133, for SYS_SFS in bdrv adrv fdrv ydrv … mksquashfs $SYS_SFS build/${TYPE_SYS_SFS}. Your two sed expressions cover
rootfs-complete and sandbox3/devx only, so a petbuild-supplied bdrv/adrv/fdrv/ydrv still packs with whatever
ownership it arrived with — the same class, one loop away. Adding a third expression (or, cleaner, wrapping mksquashfs
itself with a chown inside the function) would close it; the count assertion would then be 3.
Artifact re-measured across every SFS the appliance unions, not just the puppy one — this is a stronger statement than
my earlier scan, and it is where the zdrv shows up:
cycle 16 (this head's fix, built 00:20): bdrv 0 devx 0 docx 0 fdrv 0 nlsx 0 puppy 0 zdrv 0
cycle 15f (before): bdrv 0 devx 0 docx 0 fdrv 0 nlsx 0 puppy 8 zdrv 5332
zdrv going 5332 → 0 is the module tree: a .ko tree owned by uid 1000, which is the worst shape of this bug and was
outside the file list I had given you. And puppy at 8 (not the 5 I reported) confirms your frugalpup note — three of those
eight were owned by 6975/2030, not 1000, which is why a uid 1000-only check would have missed them.
Also good, and both from my earlier notes: the scan is now -type f -o -type d because symlinks are always 777 (the first
version refused a good tree — that is the right reason to narrow it), and a PAYLOAD image without LIBHSA_DIR is a
refusal rather than a warning, which is exactly the cycle-13 board failure.
CI at 5d245d03a: build success (07:55Z).
…ick does get a key now Three claims went stale since this branch opened, all measured against origin/main today rather than against the cycle-13 artifact: - the installed-disk blockquote said "until both are in an image". #890 (#865's init fix) merged 2026-11-03T12:45Z on top of #889 (#866), and its own probe boots a disk installed by `1bit-install` as the only disk in QEMU; an image built from main boots the installed disk as written. - the measurement table's #865 row still said "fix open as #890". - the dd-only stick paragraph said a stick with no key serves every endpoint unauthenticated and that #867 is an open defect of this image. On main it is not: 05onboard-data no longer pre-creates an empty lemond.conf and 10lemonade mints on "is there a key" rather than "is there a file" (#828). The cycle-13 measured behaviour is kept as history, with the fix pointer and how to read the minted key (console 1 -> Show API key).
…'s item 1 is the toggle #934 made blank the shipped mode: 10lemonade starts with LEMONADE_API_KEY unset and binds 0.0.0.0:13305 anyway, so every request works with no header until a person turns a key on from console item 1 (which mints 48 hex into /etc/1bit/lemond.conf, 0600, on /data, restarts the service and prints it), or prepare-stick --mint writes one offline. install.md and clients.md still described the #867/#828 behaviour - 'the appliance mints one on its first boot', 'read the key with show API key', 'every request needs the API key', '401 without the key', 'Lemonade Mobile: on 1bit OS it is required'. All of those are now false of the shipped default and wrong in the unsafe direction (they imply the box is closed when it is open). Rewritten against the sources, with file:line: 10lemonade:43-62,78-81; 1bit-console-menu:238,242,300-370; console-tui/src/ui.rs:203-215, 280-292; prepare-stick.sh:346-410; vm-check.sh:710-730; 1bit-webapp-proxy:37-44. The cycle-13 measured rows stay as history, marked as that image's behaviour, and the os/mini media difference (it still mints) is stated per docs/os-v3.md.
…rget (#859) * docs: 1bit OS is the Puppy Linux appliance, and os/mini is a CI/VM target 1bit OS is Puppy Linux (ResolutePup64, woof-CE on the Ubuntu 26.04 resolute base) with Lemonade Server and the engine embedded, our MOK-signed 7.3 perfopt kernel, headless by default with a 1bit OS (desktop) boot entry, bash/sudo/apt, data and settings on 1BIT-DATA, Secure Boot on. The BusyBox os/mini image is the CI/VM target and is no longer called 1bit OS anywhere. No release is claimed: the next release carries the appliance. Files: README.md (get-started steps + a milestone row), os/README.md (the 1bit OS page is the appliance; os/mini, os/v2 and os/v3/puppy laid out), os/v2/README.md (title and 'ships today' line), os/LICENSES.md (which image carries what), docs/releases.md (top note, artifact row, known issues, os/mini build note), docs/os-v3.md (Puppy is v3, with the board result; the Ubuntu plan is the fallback), docs/os-v3-live-puppy-eval.md (dated update: the four blockers are closed and it booted on the board), docs/kernel.md (the kernel reaches 1bit OS as woof-CE's kernel package), docs/PORTING.md (same, one clause). * docs: install 1bit OS (stick, disk, USB) and connect a client to Lemonade docs/install.md covers the options the owner asked for, each with who it is for, the steps and what is erased: run from the stick, install to an internal disk or another USB drive with 1bit-install (the seven steps, the refusals, the firmware entry), keep the existing OS (no dual-boot wizard in this release), the engine on an existing Linux (advanced, no PPA route yet), Secure Boot and the MOK, and the Bosgame front-port note (#823). docs/clients.md covers the address and the key, curl, the web app, Lemonade Mobile on iPhone and Android, the OpenAI SDKs and any OpenAI-compatible app, the lemonade CLI from another machine, and the security/rotation rules. Every example was run against the cycle-13 appliance image booted in QEMU with Secure Boot on (~/wt/rlk-vm/verify.sh, results.txt): the key is enforced (401/401/200, /api/v1 and /v1), the web app is served at :13305, the curl and OpenAI-SDK chat examples returned 200 with content, the CLI flags and the LAN beacon work, and the firewall opens only 13305 and 22. The install ran end to end on a bare disk. Three defects it found are filed as #865 (an installed disk does not boot: init resolves pupsfs=<label> but not pupsfs=UUID=...), #866 (step 7's BootNext lands on the BootManagerMenuApp) and #867 (a dd-written stick never gets an API key minted, so Lemonade serves the LAN unauthenticated); the pages state each where it matters, with the measured label A/B that isolates #865. README: step 4 of Get started points at both pages. * docs/install: no unmeasured install duration * docs: the 1bit OS image ships from Hugging Face, not a GitHub release asset The Puppy image is 9,128,902,656 B written / 2,212,389,580 B as 1bit-os-puppy.img.xz, and a GitHub release asset is capped at 2 GiB, so the image lives at https://huggingface.co/1bit-MONSTER/1bit-OS with its SHA256SUMS. README, docs/install.md and docs/releases.md now say so and give the same two write lines everywhere (sha256sum -c SHA256SUMS --ignore-missing, then xz -dc ... | dd; balenaEtcher and Rufus DD mode take the .img.xz directly). The GitHub releases page keeps the lemonade-onebit package, the release notes and the release SHA256SUMS, and links to the image. History rows are untouched: the c18-c20 candidates really did ship the BusyBox os/mini image as 1bit-os-<tag>.img.zst, and those sizes and shas stay as recorded. * docs: the Hugging Face image repository is private until v2026.44 is published Every place a reader acts on the download (README quickstart, the install page's image row and write block, the release table's image row) now says the repository is private until v2026.44 is published, so the link is the download from publication day and an earlier release carries only the BusyBox os/mini image, the CI/VM target. Addresses the CHANGES on #859: the URLs are correct and the upload landed, but they answer 401 while the repository is private. * docs: the appliance boots as 1bit-os, and installer #866 is fixed Two claims in this branch were true of the cycle-13 image and are not true of the image that ships next: - docs/clients.md said the hostname is Puppy's per-machine puppypc<MAC>; the image sets /etc/hostname to 1bit-os (os/v3/puppy/embed/etc/hostname, and the c16 board boot read back 1bit-os). Keep the cycle-13 observation as history and tell the reader the name, not a guessed .local. - docs/install.md said installer step 7 and the pupsfs=UUID boot are both open. #866 is fixed on main by #889 (merged 2026-11-03); #865's fix is #890, open. * docs/clients: say puppypc<digits>, not <MAC digits> - the MAC derivation is unverified * docs/install: both installed-disk fixes are in main, and a dd-only stick does get a key now Three claims went stale since this branch opened, all measured against origin/main today rather than against the cycle-13 artifact: - the installed-disk blockquote said "until both are in an image". #890 (#865's init fix) merged 2026-11-03T12:45Z on top of #889 (#866), and its own probe boots a disk installed by `1bit-install` as the only disk in QEMU; an image built from main boots the installed disk as written. - the measurement table's #865 row still said "fix open as #890". - the dd-only stick paragraph said a stick with no key serves every endpoint unauthenticated and that #867 is an open defect of this image. On main it is not: 05onboard-data no longer pre-creates an empty lemond.conf and 10lemonade mints on "is there a key" rather than "is there a file" (#828). The cycle-13 measured behaviour is kept as history, with the fix pointer and how to read the minted key (console 1 -> Show API key). * docs: both MOKs are needed, the real menu order, the save layer, #860 not in tree, the shipped artifact name Six Copilot review findings, each checked against the sources rather than the prose: - README and install.md §6 said Secure Boot "can stay on" with "shim, grub, the kernel and its modules all signed with our MOK". The shim is Microsoft-signed (os/v3/puppy/mkimage.sh:75-80 pulls shimx64.efi.signed), and on a machine that has never trusted our key the firmware refuses the kernel and the certificates are not on the stick — so a fresh machine has to enroll first or turn Secure Boot off. Both places now say that and link to the enrollment steps. - install.md step 1 named the third menu entry as the serial shell; the recipe orders headless, desktop, install-to-a-disk, serial shell (os/v3/puppy/mkimage.sh:152-166), so the documented ordinal opened the installer. All four are now named with their flags. - the RAM-only paragraph contradicted the PUPMODE=12 save layer above it. It now separates the two measured cases: no save folder + `poweroff -f` (marker gone, the cycle-13 observation) versus a save folder + normal shutdown, where G7 measured an /etc marker surviving (os/v3/puppy/g7-probe.sh:28-33,93-99), with 05onboard-data's shutdown sync/unmount named. - install.md advertised `scripts/mk-plugin-package.sh` as an existing route; it is on PR #860, not in this tree. Marked as such. - the enrollment row imported only `MOK-Kernel.der`; kernel.md's table has a second key, `MOK.der`, for every module we build, so a machine that enrolled only the kernel key still refuses the NPU and `ec` modules. The row now imports both, and §6 states the split. - releases.md's artifact row listed `1bit-os-<tag>.efi` as the appliance file; the Puppy recipe builds a disk image (`1bit-os-puppy.img.xz` + SHA256SUMS) and there is no standalone `.efi` for it. The `.efi` and `.img.zst` names stay with the earlier BusyBox os/mini candidates, which the same page's gated-candidate table already lists. The Hugging Face privacy clause is unchanged. copyright.py --check rc 0; site builds 53 pages, one <h1> per touched page. * docs: the console is a menu that prints how to connect, not a login prompt The cycle-18 image puts a CONNECT block (address, web UI, API, ssh) above the console menu, and 1bit-console replaces plogin on a headless boot, so README's "console login" and the bare "a console with no X" no longer describe what comes up. Also names the desktop entry by its grub.cfg label, `1bit OS (desktop, JWM)` (os/v3/puppy/mkimage.sh:156). * docs: the appliance ships with no API key (cycle 20), and the console's item 1 is the toggle #934 made blank the shipped mode: 10lemonade starts with LEMONADE_API_KEY unset and binds 0.0.0.0:13305 anyway, so every request works with no header until a person turns a key on from console item 1 (which mints 48 hex into /etc/1bit/lemond.conf, 0600, on /data, restarts the service and prints it), or prepare-stick --mint writes one offline. install.md and clients.md still described the #867/#828 behaviour - 'the appliance mints one on its first boot', 'read the key with show API key', 'every request needs the API key', '401 without the key', 'Lemonade Mobile: on 1bit OS it is required'. All of those are now false of the shipped default and wrong in the unsafe direction (they imply the box is closed when it is open). Rewritten against the sources, with file:line: 10lemonade:43-62,78-81; 1bit-console-menu:238,242,300-370; console-tui/src/ui.rs:203-215, 280-292; prepare-stick.sh:346-410; vm-check.sh:710-730; 1bit-webapp-proxy:37-44. The cycle-13 measured rows stay as history, marked as that image's behaviour, and the os/mini media difference (it still mints) is stated per docs/os-v3.md. * docs/clients: a non-1bit-os hostname is a person's rename, not a fault (cycle-16 u2-hostname) The cycle-16 board booted as `strix` while the frozen image bytes still read `1bit-os`: a person renamed it at the console, and the release lane's `u2-hostname` red was that reading, not an image defect. Section 1 told a reader `hostname` prints `1bit-os` with no way to tell a rename from the `puppypc<digits>` regression it is contrasted with, so a reader on a renamed box would go looking for a fault. Name the console path (3 Network -> 3 Set the hostname, `1bit-console-menu:437,463-466,632`) and say to check the address instead of the name. * docs/os-v3: where v3 stands, as of cycle 20 — 33-row board runs and a blank key by default The standing paragraph was written on 2026-11-02 and had gone wrong in two ways in the same breath. It quoted '20 of puppy-check's 25 rows PASS' as the appliance's current state (the checker has 33 rows now; cycle 19 came back 33 rows, 0 failed, 1 skipped, and cycle 20's two reds were the acceptance script's own defect, engine#945, fix open as #951), and it said 'Lemonade answers on :13305 with its key' — false and unsafe since engine#934 made blank mode the shipped default (10lemonade:43-62 binds 0.0.0.0:13305 with LEMONADE_API_KEY unset; console item 1 turns a key on). Restate it against the c19/c20 board verdicts and the #934 sources, and add one sentence to the eval page's dated 2026-11-02 update so the 'with its key' reading there is marked as that cycle's behaviour rather than today's. * docs/os-v3: #945's fix landed as #951, so the sentence stops calling it open * docs/clients: the appliance announces 1bit-os.local from cycle 21 on The page told a reader 'mDNS is not claimed' and to read the address off the box. Cycle 21 item 10 (#953, merged b8a07df) ships the responder, asserts it at build time and prints it in the console's CONNECT block, so that sentence is now false in the direction that costs a reader a step. Said what the sources say, with the two limits kept: the announced name is $(hostname).local (so it follows a rename), the CONNECT row says when avahi is not running, and a peer resolving it over multicast is still not claimed on this page. * docs/clients: two citations point at the lines that carry the claim container-build.sh gained 12 lines in #976, so the mdns pack-checks it cites moved from 496/505 to 508/517; and the webapp proxy's foreign-Origin refusal is at 45-49 (the rule and ALLOWED_ORIGINS) and 74-77 (the code), not 37-44 - which is the config block and the sentence that introduces the key attachment. Both numbers read back out of the merged tree this pass, not carried over. * docs/clients: the 8080 chat page is not served on the c19/c20 boards Both boards log `OSError: [Errno 98] Address already in use` for 127.0.0.1:8080 (c19 finding, unchanged on c20, and #959's own body says so), so the proxy's chat page is not reachable there while Lemonade's own page on :13305 is. A reader sent to :8080 chases a dead port; the sentence names it and points at :13305, with the init script's own two lines cited. * docs: re-point two citations at the lines that now hold them (CMakeLists 322, serve.cpp 202-213, ui.rs 218-222) * docs/os-v3-live-puppy-eval: a bare `</dev/null` code span truncated the rendered page at that table row python-markdown reads the angle-bracket close tag as raw HTML and drops everything after it: 46,922 B of markdown rendered to 21,897 B, cutting the page mid-table in front of the whole cycle-13/19/20 acceptance evidence. Reworded to plain text (same meaning); the page now renders 61,087 B with the cycle-13 rows, and the site build is 56 pages, rc 0. * docs/clients: re-point the two vm-check.sh citations after #1001/#1007 moved them (616-621, 740-754) * docs/clients: the c22 board announced the name and nothing answered it - rc.firewall drops inbound mDNS Cycle 23 (#1016) opened udp/5353 in zz-onboard-ports for the LAN ranges and added the `U3 mdns rule` gate row. Section 1 said only the responder and the LAN could leave you reading an address, which reads as "works on a c22 stick" when the measured c22 board shows the opposite. Stated what was measured, with the fix's citations, and kept the one hop nobody has proved: the packets the rule accepts were avahi's own loopback announcements, not a peer's query. Also re-points the blank-mode U3 citation (:740-754 -> :749-762) after #1016. * docs: the image's kernel is 7.4.0-rc1-1bit since cycle 21, and the cycle-23 board is green os-v3.md said the appliance ships `7.3.0-rc4-1bit-perfopt`. Measured in the frozen images rather than taken from a note: the c20 image carries `7.3.0-rc4-1bit-perfopt+`, and the c21, c22 and c23 images carry `7.4.0-rc1-1bit` (string present in each), so the page now says which cycle moved it. The same paragraph's newest board was cycle 20; cycle 23 (4 Nov) is in, with its row counts and the two facts the c23 verdict records - the single red is a bug in the check's own u7-mdns row, not the image. clients.md: three citations re-pointed at the lines that now carry them after #1020 moved vm-check.sh and container-build.sh by 13 and 1 lines. * docs/clients: re-point the two vm-check.sh citations #989 moved again #989 added the U4 kernel rows above them, so the U3 mdns rule row is at 711-714 (was 689-692) and the blank/keyed U3 network block at 784-797 (was 762-775). The citations now also name the row, because this file has moved under them three times tonight. * docs/os-v3-live-puppy-eval: CMakeLists.txt:322 -> 331-332, moved by #1021 #1021 added nine lines to CMakeLists.txt above the HRX block; 331-332 is still target_compile_definitions(... ONEBIT_HRX_LIBHSA=...). * docs/clients: the mDNS fix now states the outbound leg too, and two citations #1025 moved #1025 (c24 prep) adds the outbound 5353 rule to open_mdns - intent, not a fix: the packed c23 image accepts all output, and the c23 board's "out: NONE" was the row demanding a rule the image correctly lacks. The page now says which half each cycle opened and why the second one exists, and the gate has a row per half. Citations: open_mdns 55-66 -> 56-74, the blank/keyed U3 block 784-797 -> 789-802. * docs/clients: re-point the three vm-check.sh citations after the file grew ~190 lines The gate script gained rows since these numbers were written, so the three citations in clients.md pointed at unrelated rows (616-621 landed in the service-up/U3-key rows, 711-714 in the driver-signature row, 789-802 in the engine-sha row). Re-read them against the merged tree: U3 mdns rule 849-852, U3 mdns out 884-886, U3 auth (blank vs keyed) 670-676, getent hosts 722-723. * docs/clients: the CONNECT name rows are ui.rs:236-238, not 213-216 Caught by re-reading every citation in the page with sed instead of trusting the string: 213-216 is the 'web UI: waiting for network' line. The name/mDNS rows the paragraph quotes are at 236-238 on main (#1041/#1059 moved them). * docs: the 1bit OS kernel is 7.4.0-rc1-1bit, and the boot entry label says so The appliance images from cycle 22 on carry 7.4.0-rc1-1bit (c22/c24/c26 hand-offs, and the c24 release verdict), but four current-state lines in this PR still named 7.3.0-rc4-1bit-perfopt: README's 1bit OS paragraph, os/README.md's kernel row, and install.md's boot step plus its Secure Boot chain. install.md's boot step is the sharpest: mkimage.sh builds the menu title from the vmlinuz version (`menuentry "1bit OS (headless console, ${kver})"`), so the label a reader sees is the kernel's own string. The dated cycle-13 measurement rows keep 7.3.0-rc4-1bit-perfopt+ - they say what that image carried, and install.md:277 and clients.md:254 say so. Also os/README.md names the second boot entry as mkimage.sh words it, `1bit OS (desktop, JWM)`. * docs/clients, docs/install: four citations point at the rows the prose names Re-read every file:line in both pages out of origin/main (fadcc1e) instead of trusting the earlier pass - the pattern that has failed in this lane before. Three had rotted under the cycle-25 console/logging work: - clients.md:78-81 -> 104-108 for "blank mode is LAN-reachable on purpose"; 78-81 is now the cycle-25 log_level comment, the LAN comment moved down. - clients.md:632 -> 645 for the console's top-level Network row; 632 is the closing brace of the CLI dispatcher. - install.md:218-222 -> 240-243 for the TUI's api row; 218-222 is the "web UI: waiting for network" block, the api/keyed rows are 240-244. And one was under-cited: clients.md:88 cited 10lemonade:43-62 for the 0.0.0.0:13305 bind, which is line 107-108. Now 43-62,107-108. Checked mechanically: /tmp citcheck over both pages, every ref printed with its first line, all twelve in clients.md and eight in install.md name what the sentence says. * docs/os-v3-live-puppy-eval: the update box's numbers are cycle 12's, and one citation went stale Two claims on this page had drifted, both found by checking them against today's tree rather than re-reading the prose: 1. The update box led with '20 of `puppy-check`'s 25 rows PASS' as if it were the appliance's acceptance state. That was cycle 12, the first hardware boot; cycle 19 came back 33 rows / 0 failed and cycle 23 41 rows / 40 PASS / 1 FAIL (the FAIL being the checker's own `u7-mdns` row). Within this same PR, docs/os-v3.md already carries those. The numbers stay, labelled as cycle 12's - they are what decided the plan - and the page now points at os-v3.md as the page of record instead of growing a second, diverging copy. 2. `ONEBIT_HRX_LIBHSA` is a CMake compile definition, cited at `CMakeLists.txt:331-332`; those lines are the npu_sources block now. It is at `CMakeLists.txt:356` (verified against origin/main). The other nine file:line citations in this branch were checked the same way and are correct (ui.rs:236-238 and 240-243, container-build.sh:508 and 518-522, vm-check.sh:670-676 / 722-723 / 849-852, prepare-stick.sh:346-410, g7-probe.sh:28-33, app/serve.cpp:202-213). * docs: the .img.xz size is approximate, because the exact bytes change with every cut pi-prs flagged 2,212,389,580 B as the one figure on the download pages a reader can falsify with `ls -l`: the only .xz on this box is ~/wt/hf-1bitos/1bit-os-puppy.img.xz at 2,212,388,768 B (3 Nov, an earlier cut), and the Hugging Face repository is private, so the published file cannot be read to check. Every re-cut changes it (c26 and c27 are both 9,128,902,656 B written but their images differ), so an exact compressed byte count is a number this page cannot keep true. Dropped it in favour of "about 2.2 GB compressed" (README, install.md, releases.md x2) and kept the figure that is stable across cuts, the 9,128,902,656 B written, which install.md:41 now pairs with the rule that matters: the size to check is the one `ls -l` prints after the download. No sha or exact size now depends on a cut this page cannot see. --------- Co-authored-by: pi-rlk <pi-rlk@1bit.gg> Co-authored-by: agent <agent@local> Co-authored-by: pi-rlk <rlk@1bit.local> Co-authored-by: bong-water-water-bong <bong-water-water-bong@users.noreply.github.com> Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg> Co-authored-by: pi-os3 <pi-os3@1bit.local>
os/v3/puppy: the Puppy appliance — our kernel, the release package, the console menu, and a demo that records itself
The recipe, the artifact and the gates for 1bit OS v3 as a Puppy appliance. Everything below was measured on this
box or in QEMU; everything that needs the Max+ 395 is listed as the board's at the bottom, not claimed.
Cycle 16 — the two blockers pi-prs found, and the demo work
1. Nothing in the image is owned by uid 1000 any more (security, blocker)
Measured on the cycle-15 artifact:
/etc/modprobe.d(dir and1bit-quiet.conf),/etc/hostname,/etc/keymap,/usr/local/petget/find_catand the whole zdrv module tree (5332 entries) wereuid/gid 1000—and inside the image uid 1000 is
ftp. Several were group-writable: ainstall <module> /bin/shline in/etc/modprobe.dis root code for anyone in that group, and a module tree owned by 1000 is a loadable.koplanted by that group.
Fixed at three levels, and refused at build time so it cannot come back:
container-build.sh:"$skeleton/etc"joins the chown list, then the whole skeleton ischown -R 0:0'dbefore
3builddistro(the per-directory list only covered what this recipe writes; the PET packages left filesbehind),
chmod -R go-won/etcand/usr/local/petget, asserted on the tree (symlink-aware: a link is always777 and chmod cannot change it, so links are not counted as files).
container-build.sh: after3builddistrothe packed SFS is scanned and the build refuses any entry whoseuid or gid resolves to >= 1000, printing them. Owners are resolved with
id -u/getent groupbecauseunsquashfsprints a name when the id exists in the build container and a number when it does not; anunresolvable owner counts as unknown and fails.
mk-huge-kernel.sh: the zdrv is built withmksquashfs -all-root(the script runs as an ordinary user, which isexactly why every module inherited uid 1000), the SFS is verified to have no non-
root/rootentry, and the tar isstamped
--owner=0 --group=0. New kernel package:huge-7.3.0-rc4-1bit-perfopt+.tar.zst2065370d….Also: the fresh
woof-CEcopy inside the container is nowchown -R 0:0'd —cp -acarries the host uid, andmerge2out hit
detected dubious ownership in repository at /work/woof-CE.2. CI "Copyright notice on every file" — green
*.desktopjoinsLINE_STYLESwith#comments (an XDG entry is ini-shaped), the autostart entry carries thenotice, and
/opt/1bit/webapp/index.htmlcarries the full Apache header as an HTML comment.python3 tools/copyright.py --check→ rc 0 on this head.3. The demo work (owner: "open a browser and feed it a prompt and then we record it")
Measured in the image's own Pale Moon 33.0.1: Lemonade's own web UI renders (chat page, model picker,
composer), but a prompt sent from it produced no request at the server, and a capability probe in that browser
prints
MISSING: TextDecoderStream, AbortSignal.timeout, CSS backdrop-filter, CSS :has()— while the same browserstreams fine from
fetch+body.getReader()+TextDecoder(198 SSE chunks, 0.9 s, 972 chars, twice). So theimage now ships a chat screen that uses only what the browser has, and a recorder:
/usr/local/bin/1bit-recordstart= ffmpeg x11grab of$DISPLAYat 30 fps, libx264 crf 23,+faststart, into/data/recordings/1bit-demo-<date>.mp4on1BIT-DATA;stopfinalises and prints file + size + duration;status;demo= start, record the chat, stop on one click. Proven under Xvfb:h264 1280x800 yuv420p 30/1 fps, 13.07 s/opt/1bit/webapp/index.htmldone: N chunks in T s (first token after …), picks only a downloaded model, key from?key=→localStorage, and a link to Lemonade's own UI/etc/init.d/24onboard-webapp127.0.0.1:8080(loopback only, read-only,python3 -m http.server)/usr/local/bin/1bit-lemonade-webapp+/etc/xdg/autostart/1bit-lemonade-webapp.desktop:13305, reads the key from/etc/1bit/lemond.conf, openshttp://localhost:8080/?key=…in/opt/palemoon/palemoon, once per session (_root_.jwmrc→xdg_autostart.sh→/etc/xdg/autostart/*.desktop, verified in the image)container-build.shffmpeg/ffprobe/python3/opt/palemoon/palemoonfirefox-esr/Chromium could not be staged because they cannot be:
firefox-esris not in Ubuntu 26.04, andfirefox/chromium-browserare snap transition stubs (1:1snap1).4. engine#867 — a
dd-written stick used to come up with NO API key05onboard-datapre-created an empty/data/lemonade/lemond.confand10lemonademinted on "the file does notexist", so on any stick that never went through
prepare-stick --mintthe guard was false, nothing was exported,and Lemonade served
0.0.0.0:13305unauthenticated (200 without a header, every boot,/internal/*included, withzz-onboard-portsopening the port to the LAN by design). Now:05onboard-dataonly creates the directory and thesymlink,
10lemonademints when there is noLEMONADE_API_KEY=line (missing, empty, or keyless file) and —if it cannot write a key — binds
127.0.0.1instead of0.0.0.0.vm-check.shgainsU3 key fileandU3 auth(401 without a key, 200 with it), which must pass on a bareddcopy: that is the case that wasbroken, so they are a gate.
The artifact (cycle 16)
1bit-os-puppy.img(ESP 512 MB +1BIT-DATA8192 MB)89779cb6fc427ee72f49fc07bacad92641d37477a4eb51e5fbd97f2966fe90e2upup64r-26.04.iso(side artifact)3cefb2b21df0f1614720b34a62b36e614719e92fc24207e2febb399e651fda57lemonade-onebit-rc-2026-11-027177f60a…, libhsaf6ef689d…7.3.0-rc4-1bit-perfopt+, MOK-signed, 3780 modules, zdrv 100% root/root2065370d…Gates (all run here)
build.shrc=0, its own checks green — including the two ownership ones:ok: 3builddistro chowns rootfs-complete and devx before packing them,ok: every packed entry is owned by a system account (uid and gid < 1000). Ownership of the shipped SFS set:puppy,zdrv,devx,fdrv,nlsx= 0 entries not owned byroot/root;bdrvhas 6 anddocx1, all system accounts (root/shadow,root/staff,root/mail,syslog/root,man/man) — ids < 1000, which is what the check enforces.vm-check.sh, bareddcopy (noprepare-stick) — 37 PASS / 0 FAIL:U3 key file /data/lemonade/lemond.conf 66 bytes mode 600 | key-minted-48-hex,U3 auth no-key=401 with-key=200 | lemonade-auth-enforced,U3 lan http=200. That is engine#867 closed on the artifact.vm-check.sh, prepared copy, Secure Boot ON — 38 PASS / 0 FAIL, includingU2 tty1,U2 menu,U2 menu status,U2 menu actions(--selftestrc=0),boot console,U8 boot progress(on tty0 and ttyS0),U1 hostname,boot quiet,U7 ssh(+ssh-bash),U3 key file,U3 auth.--cert --require-mokrc=0 (kernel verifies against the enrolled MOK, 3780modules,
amdxdna/ec_su_axb35vermagic + signer, firmware present);prepare-stick.shten assertions on acopy (API key
3209b0…).Cycle 15 (kept for the record): the console menu,
1bit-os, the console order, a quiet bootOwner on the c14 board boot: "no menu system", and of the ~90 s of black screen: "it hangs hard here".
tty1 is now a dialog menu — 7 lines of live status (release, hostname, LAN address, Lemonade +
http://<ip>:13305,HRX device with free VRAM, amdxdna/firmware, Secure Boot, fans + temps,
/datafree, models) over nine items(API key behind a confirm, chat test per backend with tok/s, network, models, guarded installer, desktop for this
session, bash, logs, reboot/power off); redraws every 15 s, never exits to a dead console,
--status/--run N/--selftestfor the rows.hostname 1bit-os;console=ttyS0,115200 console=tty0on all four grub entries (thelast
console=wins, so the screen is/dev/console) with the init's progress and rescue lines written to/dev/consoleand/dev/ttyS0; the serial shell isbash --noediting -li(readline's bracketed-paste togglewas flushing pasted lines mid-way);
snd_pcspblacklisted.Still the board's, not claimed
The menu and the chat screen on the JetKVM,
1bit-recordon a real GPU console (x11grab of:0with amdgpu isunmeasured — everything here ran under Xvfb),
chat-hrx/chat-npuand the device bindings (green on c14), U6'ssecond-disk half, U8 at USB speed, the fan curve being written.