Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config/npu-kernels.config
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
#
# The private NPU kernels the release builds into the NPU lane (docs/npu.md; docs/releases.md).
# The kernels are private: their source is not part of this repository and is never published with
# the release. They ship only as compiled, encrypted artifacts that `1bit` verifies and decrypts
# in memory at load. scripts/fetch-private.sh checks out exactly this commit of this repository
# the release. They ship as compiled artifacts with a signed manifest per set, which `1bit`
# verifies at load. scripts/fetch-private.sh checks out exactly this commit of this repository
# (with the release machine's GitHub credentials); the build passes the checkout as
# ONEBIT_NPU_PRIVATE, and the release stops if it cannot be fetched.
#
Expand Down
12 changes: 7 additions & 5 deletions docs/npu.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ package serves GGUF models on HRX, not NPU models ([releases.md](releases.md#the

The NPU runtime and the NPU kernels are proprietary and closed source
([releases.md](releases.md#private-components)). They ship only as compiled binaries: the runtime
is compiled into `1bit`, and the kernels ship as compiled, encrypted artifacts that `1bit` verifies
and decrypts in memory at load (a wrong key fails cleanly, and nothing phones home). Their source,
build and method are not in this repository or in any release. This page covers the public parts:
the model format, how `1bit serve` runs an NPU model, and how to build.
is compiled into `1bit`, and each kernel set ships with a signed manifest that lists every file it
contains. `1bit` holds only the release public key and verifies a kernel against its manifest at
load; a modified file, a file the manifest does not list, or a set signed by another key is refused
before anything reaches the device, and nothing phones home. Their source, build and method are not
in this repository or in any release. This page covers the public parts: the model format, how
`1bit serve` runs an NPU model, and how to build.

## A model directory

Expand Down Expand Up @@ -137,7 +139,7 @@ The model directory holds the weights only; the kernels ship inside the package.

### Dense Qwen3, checked on the NPU

All three dense Qwen3 models run on the NPU from a sealed kernel set, and each one passes the
All three dense Qwen3 models run on the NPU from a signed kernel set, and each one passes the
hardware gate's `npu` phase ([hw-gate.md](hw-gate.md)): greedy decoding token for token against an
independent fp64 reference over a short prompt and teacher-forced steps, the 64-position build,
the prompt prefill, and byte-identical output when the run is repeated, with the phase's speed
Expand Down
29 changes: 15 additions & 14 deletions docs/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Release sha and pins (the submodules this candidate is built from):
| hrx-system | `82804197bcbe` |
| Lemonade (fork) | `27abcc24da99` |
| private kernel add-on (HIP) | `744bd713d968` |
| private NPU kernel set | `17ed5be7a28a` |
| private NPU kernel set (signed) | `17ed5be7a28a` |
| private NPU runtime | `ce9fa59adf95` |
| Laya | source `6f0496f797dc`, model `7b928d828b7b` |
| amdxdna (out-of-tree NPU driver) | `526c608197` |
Expand Down Expand Up @@ -108,9 +108,9 @@ context (`#466`), allocation safety bounds the program cache and poisons a devic
(`#486`), and the ZAYA1-8B NaN re-check closed on **120 fresh loads, 0 NaN** at the current pins
(`#433`).

**NPU (sealed).** The gate gained an NPU phase and the packages carry **only the sealed kernel
sets** — a plaintext artifact fails the packaging check, and the launcher refuses an unsealed one
(`#503`, `#505`, `#509`, `#512`). The 0.6B gate phase measures **90.33 tok/s**; the OS image proves
**NPU (signed).** The gate gained an NPU phase and the packages carry **only signed kernel sets** —
every shipped file is listed in the set's signed manifest, a set that does not verify fails the
packaging check, and the engine refuses an unlisted kernel file (`#503`, `#505`, `#509`, `#512`). The 0.6B gate phase measures **90.33 tok/s**; the OS image proves
`amd_pmf` sensors and the NPU from the booted image (`#517`, `#523`). The runtime's public header and
prebuilt-library build are in the tree (`#467`, `#479`, `#469`).

Expand All @@ -121,7 +121,7 @@ the Ubuntu 26.04 PPA's `lemond` runs our HRX server unchanged (`#483`). Synced t
one request in role, `lemonade bench ... chat-short` — and 3 NPU entries (fork `#19`, `#20`, `#23`);
chat-short spans 43.4 (Qwen3-8B) to 330.7 tok/s (Qwen3-0.6B), 0 faults in 16 holds.

**1bit OS.** The image carries the sealed NPU sets and the new Lemonade (`#512`, `#523`), the UKI is
**1bit OS.** The image carries the signed NPU sets and the new Lemonade (`#512`, `#523`), the UKI is
signed with the enrolled MOK so a stick boots with Secure Boot on (`#513`), the USB4 v2 / Thunderbolt
stack is loaded (`#514`), and `amd_pmf` sensors come up from the booted image (`#517`). The install
step is not in this candidate (branch `1bit/os-install`).
Expand Down Expand Up @@ -163,17 +163,18 @@ The builds target Strix Halo: the llama.cpp backends are built with `GGML_NATIVE

The release is the HRX runtime with our HIP kernels and the NPU engine, all inside Lemonade. The
release carries the NPU runtime, compiled into `1bit`, and the `onebit` recipe's `npu` backend.
The NPU kernels are closed too: they ship as sealed artifacts that `1bit` verifies and opens in
memory at load, failing cleanly on a
wrong key with no network callback of any kind; a lifted kernel file is ciphertext and does not run
([npu.md](npu.md)). The release package carries them, and serves dense Qwen3 (0.6B, 1.7B, 4B)
The NPU kernels are closed too: they ship as compiled artifacts with a signed manifest per set
(`ONEBIT_NPU_PRIVATE`), and `1bit` verifies each kernel against that manifest at load, failing
cleanly on a modified or unlisted file with no network callback of any kind ([npu.md](npu.md)). The
release package carries them, and serves dense Qwen3 (0.6B, 1.7B, 4B)
on the NPU.

`scripts/weekly.sh` packs them by default (`WEEKLY_NPU_KERNELS=0` builds without): the sealed
kernel sets go beside `1bit` in `npu/kernels`, the build loads sealed kernels only
(`ONEBIT_NPU_RELEASE`), the `lemond-onebit` launcher sets `ONEBIT_KCRYPT_REQUIRE_SEALED=1`, and the package checks refuse a plaintext kernel,
an ELF for anything but the host, a symbol table in `1bit`, or a private source name in it. The
release machine holds the kernel key in `ONEBIT_NPU_KEY_DIR` (default `~/.config/1bit-npu-key`).
`scripts/weekly.sh` packs them by default (`WEEKLY_NPU_KERNELS=0` builds without): each signed
kernel set goes beside `1bit` in `npu/kernels`, the build loads signed sets only
(`ONEBIT_NPU_RELEASE`), the `lemond-onebit` launcher sets `ONEBIT_KMANIFEST_REQUIRED=1`, and the
package checks refuse a kernel the manifest does not list, a set without its manifest, an ELF for
anything but the host, a symbol table in `1bit`, or a private source name in it. The release machine
holds the signing key in `ONEBIT_NPU_KEY_DIR` (default `~/.config/1bit-npu-key`).

### Private components

Expand Down
7 changes: 4 additions & 3 deletions os/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,11 @@ install to disk. Every check prints its own PASS/FAIL/SKIP line, a transcript la

It checks the boot mode (LIVE or INSTALLED), the kernel and the Secure Boot state, the GPU and NPU
devices, that Lemonade answers with its API key and refuses without it, that `/api/v1/models` lists
the onebit entries the build shipped (22 on HRX, plus the NPU entries whose sealed kernel set is on
the onebit entries the build shipped (22 on HRX, plus the NPU entries whose signed kernel set is on
the data partition) and the stick's own GGUF, one chat per backend the machine can run, one
`lemonade bench` chat-short row on the default model, that every file under `/data/npu/kernels` is a
sealed artifact, that `LICENSE-BINARY`/`NOTICE-BINARY` are present, and the image's tag + sha256.
`lemonade bench` chat-short row on the default model, that every file under `/data/npu/kernels` is
listed in its set's signed manifest and matches its sha256, that `LICENSE-BINARY`/`NOTICE-BINARY`
are present, and the image's tag + sha256.
The rows that need the hardware - `gpu`, `npu`, `models`, `chat-hrx`, `chat-npu` - report
`SKIP(reason)` in a VM.

Expand Down
6 changes: 3 additions & 3 deletions os/mini/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ else
say "[engine] no /data/1bit-llama: '1bit serve' has no HRX llama-server on this boot"
fi

# The sealed NPU kernels live on the stick (they used to be inside the RAM image, which made it too big
# to load): link them to the path the engine looks for, beside itself.
# The NPU kernels (each with its signed manifest) live on the stick - inside the RAM image they made it too
# big to load - and are linked to the path the engine looks for, beside itself.
if [ -d /data/npu/kernels ] && [ -d /home ]; then
mkdir -p /home/bcloud/wt/npupkg-build/npu 2>/dev/null
ln -sfn /data/npu/kernels /home/bcloud/wt/npupkg-build/npu/kernels 2>/dev/null
say "[kernels] $(ls /data/npu/kernels | wc -l) sealed NPU sets linked beside the engine"
say "[kernels] $(ls /data/npu/kernels | wc -l) signed NPU sets linked beside the engine"
fi

n=0
Expand Down
37 changes: 26 additions & 11 deletions os/mini/mkimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ bin=$build/onebit; [ -x "$bin" ] || bin=$build/1bit
[ -x "$bin" ] || { echo "no engine binary in $build (build with -DONEBIT_NPU=ON -DONEBIT_HRX=ON)"; exit 1; }
copy_elf "$bin"
ln -s "$bin" "$root/bin/1bit"
# The sealed NPU kernel sets travel BESIDE the engine binary (docs/releases.md, "The NPU engine"):
# The NPU kernel sets travel BESIDE the engine binary (docs/releases.md, "The NPU engine"), each
# with the signed manifest the engine verifies it against at load:
# in a package that is <pkg>/npu/kernels next to <pkg>/1bit, and here the binary keeps its build
# path, so the sets go to the same place relative to it. Without them the image ships the NPU
# route and nothing it can load - the route then refuses rather than falling back.
Expand Down Expand Up @@ -562,33 +563,47 @@ for m in ${MODELS:-}; do
done
data_mb=${DATA_MB:-$(( $(du -sm "$out/data.dir" | cut -f1) + 1024 ))} # the contents plus 1 GiB
rm -f "$out/data.part"; if [ -d "$build/npu/kernels" ]; then
# The sealed sets go on the STICK'S DATA PARTITION, not inside the RAM image. They are ~124 MB and
# The signed sets go on the STICK'S DATA PARTITION, not inside the RAM image. They are ~124 MB and
# were making the initramfs 103 MB compressed, which is what grub was failing to load on real
# hardware (the machine's own 37 MB initrd loads fine). init.sh links them to the engine's
# expected path at boot, so the engine still finds npu/kernels beside itself.
# Only the artifact surface goes on the stick: <set>/layout.json and <set>/layer1_*/layer1.elf, the
# allowlist scripts/package-linux.sh packs. A set directory also holds layer1.work/ (plaintext
# Only the artifact surface goes on the stick: <set>/manifest, <set>/manifest.sig and
# <set>/<id>/layer1.elf - the allowlist scripts/package-linux.sh packs. A set directory also
# holds layer1.work/ (plaintext
# MLIR, object files, intermediate ELFs) because that is where it was built, so copying the tree
# would put the kernel method on the stick; the guard that caught that used `find ... | head -3`,
# which exits 141 (SIGPIPE) under `set -o pipefail` and aborted the whole image instead.
mkdir -p "$out/data.dir/npu/kernels"
for set in "$build"/npu/kernels/*/; do
setname=$(basename "$set")
[ -f "$set/layout.json" ] || continue
[ -f "$set/manifest" ] || continue
mkdir -p "$out/data.dir/npu/kernels/$setname"
cp -a "$set/layout.json" "$out/data.dir/npu/kernels/$setname/"
for layer in "$set"/layer1_*/; do
cp -a "$set/manifest" "$set/manifest.sig" "$out/data.dir/npu/kernels/$setname/"
for layer in "$set"/*/; do
[ -f "$layer/layer1.elf" ] || continue
mkdir -p "$out/data.dir/npu/kernels/$setname/$(basename "$layer")"
cp -a "$layer/layer1.elf" "$out/data.dir/npu/kernels/$setname/$(basename "$layer")/"
done
done
sealed=$(find "$out/data.dir/npu/kernels" -name layer1.elf -exec \
sh -c 'head -c 8 "$1" | grep -q 1BPKCRYP' _ {} \; -print | wc -l)
total=$(find "$out/data.dir/npu/kernels" -name layer1.elf | wc -l)
[ "$total" -gt 0 ] || { echo "npu/kernels in $build carries no layer1.elf"; exit 1; }
[ "$sealed" = "$total" ] || { echo "npu/kernels: $sealed of $total kernels are sealed (plaintext would leak the method)"; exit 1; }
echo " npu/kernels: $(ls "$out/data.dir/npu/kernels" | wc -l) sealed sets on the data partition ($sealed kernels)"
# Each set must carry the manifest it is verified against, and every kernel it ships must be
# listed in it with the same sha256: a set that does not verify never reaches a stick.
bad=0
for set in "$out/data.dir/npu/kernels"/*/; do
[ -f "$set/manifest" ] || continue
[ -s "$set/manifest" ] && [ "$(stat -c%s "$set/manifest.sig" 2>/dev/null || echo 0)" = 64 ] ||
{ echo "npu/kernels: $(basename "$set") has no signed manifest"; bad=1; continue; }
for elf in "$set"/*/layer1.elf; do
[ -f "$elf" ] || continue
rel=${elf#"$set"}
want=$(awk -v p="$rel" '$1=="file" && $4==p {print $2}' "$set/manifest")
[ -n "$want" ] && [ "$want" = "$(sha256sum "$elf" | cut -d' ' -f1)" ] ||
{ echo "npu/kernels: $(basename "$set")/$rel does not match its manifest"; bad=1; }
done
done
[ "$bad" = 0 ] || exit 1
echo " npu/kernels: $(ls "$out/data.dir/npu/kernels" | wc -l) signed sets on the data partition ($total kernels)"
fi
# The engine's own llama-server goes on the DATA PARTITION, not into the RAM image. This point in the
# script is AFTER the initramfs was packed (above), so what used to happen here - copying llama-server,
Expand Down
40 changes: 29 additions & 11 deletions os/mini/release-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
# Rows that cannot PASS without the hardware are marked SKIP in a VM (Lemonade hides its onebit
# entries on a machine with no AMD GPU or NPU, and there is no HRX or NPU device to run them on):
# gpu, npu, models, chat-hrx, chat-npu. Everything else - mode, kernel, Secure Boot, Lemonade up
# with its key, the stick's GGUF answering on vulkan, `lemonade bench`, the sealed NPU sets, the
# licences and the image identity - is checked in the VM as well.
# with its key, the stick's GGUF answering on vulkan, `lemonade bench`, the signed NPU kernel sets,
# the licences and the image identity - is checked in the VM as well.
set -u

LOG=/data/release-check.log
Expand Down Expand Up @@ -304,20 +304,38 @@ else
fi
fi

# ── sealed NPU sets: the method never ships in the clear ────────────────────────────────────────
SETS=$(find /data/npu/kernels -name layout.json 2>/dev/null | wc -l)
# ── NPU kernel sets: every file verifies against the set's signed manifest ─────────────────────
# The signature is checked by the engine at load (it holds only the release public key and refuses
# a file its set's manifest does not cover), so this row proves the bytes on the stick are the ones
# that were signed and that nothing unlisted rides along.
SETS=$(find /data/npu/kernels -name manifest 2>/dev/null | wc -l)
if [ "$SETS" = 0 ]; then
fail sealed "no NPU kernel set on the data partition (find /data/npu/kernels -name layout.json)"
fail npu-kernels "no NPU kernel set on the data partition (find /data/npu/kernels -name manifest)"
else
BAD=0
for f in $(find /data/npu/kernels -type f 2>/dev/null); do
[ "$(basename "$f")" = layout.json ] && continue
[ "$(head -c 8 "$f" 2>/dev/null)" = 1BPKCRYP ] && continue
BAD=$((BAD + 1)); say " not sealed: $f"
for set in $(find /data/npu/kernels -mindepth 1 -maxdepth 1 -type d 2>/dev/null); do
[ -f "$set/manifest" ] || continue
if [ ! -s "$set/manifest" ] || [ "$(stat -c%s "$set/manifest.sig" 2>/dev/null || echo 0)" != 64 ]; then
BAD=$((BAD + 1)); say " no signed manifest in $set"; continue
fi
for f in "$set"/*/layer1.elf; do
[ -f "$f" ] || continue
rel=${f#"$set"/}
want=$(awk -v p="$rel" '$1=="file" && $4==p {print $2}' "$set/manifest" 2>/dev/null)
if [ -z "$want" ]; then BAD=$((BAD + 1)); say " not in the manifest: $f"
elif [ "$want" != "$(sha256sum "$f" 2>/dev/null | cut -d' ' -f1)" ]; then
BAD=$((BAD + 1)); say " does not match the manifest: $f"; fi
done
for f in $(find "$set" -type f 2>/dev/null); do
rel=${f#"$set"/}
case "$rel" in manifest|manifest.sig) continue ;; esac
[ -n "$(awk -v p="$rel" '$1=="file" && $4==p {print $2}' "$set/manifest" 2>/dev/null)" ] ||
{ BAD=$((BAD + 1)); say " unlisted file: $f"; }
done
done
[ "$(find /data/npu/kernels -name '*.mlir' -o -name 'layer1.work' 2>/dev/null | wc -l)" = 0 ] || { BAD=$((BAD + 1)); say " plaintext build tree on the data partition"; }
if [ "$BAD" = 0 ]; then pass sealed "$SETS sealed sets, $PLAIN_TOTAL kernels, none in the clear"
else fail sealed "$BAD file(s) under /data/npu/kernels are not sealed artifacts"; fi
if [ "$BAD" = 0 ]; then pass npu-kernels "$SETS signed sets, $PLAIN_TOTAL kernels, every file matches its manifest"
else fail npu-kernels "$BAD problem(s) under /data/npu/kernels"; fi
fi

# ── licences and image identity ────────────────────────────────────────────────────────────────
Expand Down
6 changes: 3 additions & 3 deletions scripts/lemond-onebit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
set -euo pipefail
here=$(cd "$(dirname "$(readlink -f "$0")")" && pwd)
export LEMONADE_ONEBIT_BIN=${LEMONADE_ONEBIT_BIN:-$here/1bit/1bit}
# the NPU engine loads sealed kernels only, always: no opt-out (the release 1bit refuses plain
# kernel sets on its own too)
export ONEBIT_KCRYPT_REQUIRE_SEALED=1
# the NPU engine loads signed kernel sets only, always: no opt-out (the release 1bit refuses an
# unlisted kernel file on its own too)
export ONEBIT_KMANIFEST_REQUIRED=1
export PATH=$here/bin:$PATH
exec "$here/bin/lemond" "$@"
Loading
Loading