Skip to content

npu: hardware-gate NPU phase + closed, encrypted kernel delivery - #503

Merged
bong-water-water-bong merged 9 commits into
mainfrom
npu/ship-in-product
Oct 22, 2026
Merged

bong-water-water-bong merged 9 commits into
mainfrom
npu/ship-in-product

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

Make the NPU engine shippable with the runtime and kernels closed, compiled binaries only.

What this adds (public, neutral)

  • scripts/hw-gate.sh — a npu phase. Not in the default phases (--phases npu). Auto-skips when there is no NPU (/dev/accel/accel0) or no checker configured. Otherwise it runs a private checker ($HW_GATE_NPU_CHECK) inside the gate's GPU claim and under ~/npu.lock (NPU through the fair queue; not wrapped in thermal-run, whose freeze can abort an NPU dispatch), reads its neutral result lines — oracle (dense Qwen3-0.6B decode vs an fp64 reference), greedy64, prefill, reproducible — and applies a tok/s floor (HW_GATE_NPU_FLOOR). The checker and the kernels are private; this script carries no method.
  • config/npu-kernels.config — the private NPU-kernels pin the release fetches (ONEBIT_NPU_PRIVATE), same shape as config/gpu-kernels.config.
  • docs/npu.md, docs/releases.md — the NPU kernels ship as compiled, encrypted artifacts that 1bit verifies and decrypts in memory at load (fails cleanly on a wrong key, no network callback of any kind; a lifted kernel file is ciphertext and does not run). No source or method is public.

Proven on Strix Halo

The private delivery mechanism (sealed kernel container, in 1bit-MONSTER/npu-kernels npu/ship-sealed-kernels) was proven on device: the sealed dx kernel set decodes byte-identical logits to the plain set (Qwen3-0.6B, 24/24 vs fp64, logits md5 9a405cf6…), 64/64 greedy at ~90 tok/s, a set sealed with a foreign key is refused cleanly (signature verification failed, no NPU dispatch), a plain ELF is refused under REQUIRE_SEALED, with 0 amdxdna/IOMMU faults.

Remaining (needs a post-freeze Strix Halo build)

  • Run the gate npu phase end to end on the box.
  • Wire the kernel build + seal + ship into scripts/weekly.sh so a release package carries them.

Pin PR — please review; not auto-merging, do not merge without the hardware gate.

🤖 Generated with Claude Code

Make the NPU engine shippable with the runtime and kernels closed, as compiled
binaries only:

- scripts/hw-gate.sh: a neutral `npu` phase (not in the default phases; add with
  --phases npu) that auto-skips with no NPU (/dev/accel/accel0) or no checker, and
  otherwise runs a private checker ($HW_GATE_NPU_CHECK) inside the gate's GPU claim
  and under ~/npu.lock, reads its neutral result lines (oracle vs an fp64 reference,
  greedy64, prefill, reproducibility) and applies a tok/s floor (HW_GATE_NPU_FLOOR).
  The checker and kernels are private; the gate carries no method.
- config/npu-kernels.config: the private NPU-kernels pin the release fetches
  (ONEBIT_NPU_PRIVATE), same shape as config/gpu-kernels.config.
- docs/npu.md, docs/releases.md: the NPU kernels ship as compiled, encrypted
  artifacts that `1bit` verifies and decrypts in memory at load (fails clean on a
  wrong key, no network callback); no source or method is public.

Proven on Strix Halo (private npu-kernels npu/ship-sealed-kernels): the sealed dx
kernel set decodes BYTE-IDENTICAL logits to the plain set (Qwen3-0.6B, 24/24 vs
fp64, md5 9a405cf6...), 64/64 greedy at ~90 tok/s, a foreign-key set refused cleanly,
a plain ELF refused under REQUIRE_SEALED, 0 amdxdna/IOMMU faults. The on-device gate
`npu` phase run and wiring the kernels into the weekly package are the remaining
steps (strixhalo was under a maintenance freeze).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@bong-water-water-bong bong-water-water-bong added the hw-gate Run the HRX hardware gate on Strix Halo (docs/hw-gate.md) label Oct 22, 2026
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 73c18dc)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

NPU Phase Logic

The new 'npu' phase in hw-gate.sh introduces logic to run a private checker ($HW_GATE_NPU_CHECK) for verifying NPU correctness and performance. However, the script assumes that the checker will produce output lines in a specific format (e.g., oracle PASS, tok_s 42.5). If the checker does not conform to this expected format, the parsing logic in lines 568-569 could fail or misinterpret results, leading to incorrect pass/fail decisions. This is a potential correctness issue if the private checker's output format changes or is not strictly adhered to.

get() { sed -n "s/^$1 //p" "$log" | tail -n 1; }
oracle=$(get oracle); greedy=$(get greedy64); prefill=$(get prefill); repro=$(get reproducible); tps=$(get tok_s)
st=PASS
for v in "$oracle" "$greedy" "$prefill" "$repro"; do [ "$v" = PASS ] || st=FAIL; done
[ "$rc" = 75 ] && { st=FAIL; infra=1; }
slow=""
# no number is a failure, not a skipped floor (the first on-device run printed an empty tok_s)
if ! [[ "$tps" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then st=FAIL; slow=" (no tok/s reported: the speed floor cannot be checked)"
elif awk -v t="$tps" -v f="$floor" 'BEGIN{exit !(t+0 < f+0)}'; then st=FAIL; slow=" (below the ${floor} tok/s floor)"; fi
result npu $st $(( $(date +%s) - t0 )) "oracle=${oracle:-?} greedy64=${greedy:-?} prefill=${prefill:-?} reproducible=${repro:-?}${tps:+ tok/s=$tps}$slow"
NPU Kernel Sealing Verification

The package_checks function in scripts/weekly.sh includes checks to ensure that NPU kernel files are sealed (identified by the magic header 1BPKCRYP). However, the check only verifies the presence of the header and does not validate the integrity or authenticity of the sealed kernel artifacts. If a malicious or corrupted kernel file is mistakenly included in the package, it could pass this check and be loaded by the NPU engine, potentially leading to incorrect behavior or security vulnerabilities.

    [ "$(basename "$f")" = layout.json ] && continue
    [ "$(head -c 8 "$f")" = 1BPKCRYP ] || { echo "FAIL $f is not a sealed kernel artifact"; nk=$((nk + 1)); }
done < <(find "$t" -path '*/npu/kernels/*' -type f -print0)
[ "$nk" = 0 ] || bad=1
Symbol Table and Private Source Detection

The package_checks function in scripts/weekly.sh attempts to verify that the packed 1bit binary does not contain a symbol table or references to private source files. However, the method of detecting private source names using strings -a "$f" | grep -c -F -f <(printf '%s\n' "$privsrc") is not robust. It may produce false positives if strings from the binary match substrings of private source names, or it may miss some references if they are not directly present in the strings output. This could lead to a false sense of security if private information is still present in the binary.

privsrc=$(for pin in npu-runtime npu-kernels; do
              [ -d "$W/$pin" ] || continue
              git -C "$W/$pin" ls-files -- "$(sed -n 's/^path=//p' "$SRC/config/$pin.config")" 2>/dev/null
          done | grep -E '\.(c|cc|cpp|cxx|h|hpp|py)$' | xargs -r -n1 basename | LC_ALL=C sort -u)
n=0; [ -z "$privsrc" ] || n=$(strings -a "$f" | grep -c -F -f <(printf '%s\n' "$privsrc") || true)
[ "$n" = 0 ] || { echo "FAIL the packed 1bit names a private source file ($n strings)"; b1=1; }

Moves the NPU-kernels pin to 1bit-MONSTER/npu-kernels npu/ship-sealed-kernels HEAD
(neutral sealed metadata). The pin moves to the merge commit when PR #13 lands.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 4e03ad6

bong-water-water-bong and others added 2 commits October 22, 2026 01:22
…e npu gate passes)

The release side of shipping the NPU kernels closed, compiled and encrypted only:

- scripts/weekly.sh build: with WEEKLY_NPU_KERNELS=1, fetch config/npu-kernels.config and
  call the private checkout's one entry point (addons/release-kernels.sh <out> <key dir>),
  which installs the kernel key for the runtime build and writes the sealed sets to
  $BUILD/npu/kernels; build with ONEBIT_NPU_PRIVATE and ONEBIT_NPU_RELEASE=ON, so the
  packed 1bit loads sealed kernels only. The policy lives in the binary: the package has no
  launcher wrapper (Lemonade execs 1bit directly), so an environment variable could be unset.
  Default off until the hardware gate's npu phase passes on Strix Halo.
- scripts/package-linux.sh: packs $BUILD/npu/kernels beside 1bit (npu/kernels), where the
  route looks for it.
- package_checks (npu): every file under npu/kernels but layout.json is sealed, never a
  plaintext ELF; every ELF in the tarball is for the host (x86-64); the packed 1bit has no
  symbol table and its strings name no private source file and no build-machine home.
  Private-source file names now include the NPU-kernels checkout.
- config/npu-kernels.config: pin the private branch head that has the entry point; neutral
  require= list. The pin moves to the merge commit when npu-kernels #13 lands.
- docs/releases.md: the switch, the checks, the key directory (ONEBIT_NPU_KEY_DIR).

Checked on ryzen: bash -n; package_checks on a synthetic good package passes all four new
checks, and on a bad one fails each (plaintext kernel ELF, an EM 264 ELF, an unstripped 1bit
with 25 symbols, a private source name, a /home/ path). Not run on Strix Halo yet
(maintenance freeze): no weekly build with the switch on has been made.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… gate needs no llama.cpp

- Pin 1bit-MONSTER/npu-kernels at dfe511236d03, the npu-dx merge of #13 (sealed kernel
  loading, release entry point, sealed-only release switch). fetch-private.sh fetches it and
  finds every require= file.
- scripts/hw-gate.sh: the llama.cpp binary check applies to the HRX phases only, so
  --phases npu runs on a box with no HRX build. Dry run on ryzen (no NPU): npu SKIP, exit 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit c2a5592

…arries the kernels

Neutral: "the 1bit NPU engine runs the model on the XDNA 2 NPU", the serve command for a Qwen3
NPU model directory (0.6B, 1.7B, 4B), Lemonade's onebit npu backend, and that the kernels ship
inside the package. README.txt gains the NPU serve line only when the package has npu/kernels.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 949029b

@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 2c418be

bong-water-water-bong and others added 2 commits October 22, 2026 03:35
The first on-device run of the npu phase passed with an empty tok_s (a checker bug,
npu-kernels #14): the floor check only ran when a number was present. Require a number;
unit-checked on three checker outputs (90.50 PASS, empty FAIL, 41.2 below a 60 floor FAIL).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 7d0738a

npu-kernels #14 fixes the awk printf redirection that left tok_s empty, so the gate npu phase speed floor now has a number to check (11.05 ms/step -> 90.50 tok/s on both gawk and mawk).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 73c18dc

@bong-water-water-bong
bong-water-water-bong merged commit cfa197b into main Oct 22, 2026
8 checks passed
@bong-water-water-bong
bong-water-water-bong deleted the npu/ship-in-product branch October 22, 2026 07:26
bong-water-water-bong pushed a commit that referenced this pull request Oct 22, 2026
…e paths

package_checks refuses a package whose 1bit names a build-machine home, and that
rule is what the NPU delivery tripped: of the four /home/ strings the e2e
package carried, two came from the NPU defines themselves
(ONEBIT_NPU_REPACK_SCRIPT / ONEBIT_NPU_LANE_PACK_SCRIPT = ${CMAKE_SOURCE_DIR}/...)
and the other two from ONEBIT_BUILD_DIR = ${CMAKE_BINARY_DIR}. So the package
gate failed on #503's own NPU path.

Every backend path is now embedded relative to the build dir via
file(RELATIVE_PATH ...) (a new onebit_build_relative() helper) and
app/built_path.h resolves it from the executable's own directory
(/proc/self/exe). That is the same file a 1bit in the build tree has always
found, because the binary lives in the build dir; the package keeps that layout
beside the binary (scripts/package-linux.sh, pack_localize). Nothing
machine-specific is compiled in any more.

Sites: ONEBIT_HRX_SERVER, ONEBIT_DS4_SERVER, ONEBIT_ONNX_SERVER,
ONEBIT_LAYA_GGML_BIN, the two NPU python tools. ONEBIT_BUILD_DIR is removed and
has no remaining references. Absolute paths still work if a caller passes one
(an older build), and the env overrides (ONEBIT_LLAMA_SERVER etc.) are
unchanged and still win.
bong-water-water-bong added a commit that referenced this pull request Oct 22, 2026
…patchelf abort) (#505)

* package: ship only the sealed NPU artifact surface

The release package carried the NPU kernel build tree. package-linux.sh did
`cp -a "$build/npu/kernels"`, and a sealed set directory still holds its
layer1.work/ next to the sealed kernels: ~340 object files, ~250 intermediate
AIE ELFs, 10 design.mlir.prj dirs and ~20 plaintext .mlir per set (measured on
strixhalo 2026-10-22, all three sets). Two consequences:

1. The kernel method travels in the clear. Sealing only rewrites
   layer1_*/layer1.elf; everything in layer1.work/ stays plaintext, so the
   MLIR and objects would have shipped in a public artifact.
2. Packaging could not finish. pack_localize treats every staged ELF as
   dynamically linked and runs `patchelf --set-rpath` on it; an AIE object is
   static, so the run died with
   "patchelf: cannot find section '.dynamic'. The input file is most likely
   statically linked" - the 06:04 e2e run, engine-pkg/ empty, no PR.

Staging now copies the artifact surface explicitly: <set>/layout.json and
<set>/layer1_*/layer1.elf, nothing else. package_checks asserts that allowlist
on the tarball and names a leaked path, and .mlir/.mlirbc count as source files
in the source scan. pack_localize only rewrites ELFs that actually have a
.dynamic section (new pack_is_dynelf), so a static ELF is skipped instead of
aborting the package.

Verified on strixhalo against the e2e build (main cfa197b scripts, existing
build tree): package-linux.sh rc=0, 1bit-e2e-linux-x86_64.tar.zst 16 MB, and
the package now holds exactly 33 files under npu/kernels - 3 layout.json and 30
layer1.elf, every one sealed (1BPKCRYP magic), zero .mlir, .o, layer1.work or
design.mlir.prj. package_checks: "ok NPU kernels: 33 files under npu/kernels,
all sealed". pack_is_dynelf returns yes on the real 1bit, no on an AIE object
and no on a sealed kernel.

The two remaining package_checks failures on that build are properties of the
build, not of this change: its 1bit binary carries four /home/ build-machine
paths baked in at compile time, and the tree has no onnx/ because it was
configured without -DONEBIT_ONNX=ON.

* build: embed backend paths relative to the build, not as build-machine paths

package_checks refuses a package whose 1bit names a build-machine home, and that
rule is what the NPU delivery tripped: of the four /home/ strings the e2e
package carried, two came from the NPU defines themselves
(ONEBIT_NPU_REPACK_SCRIPT / ONEBIT_NPU_LANE_PACK_SCRIPT = ${CMAKE_SOURCE_DIR}/...)
and the other two from ONEBIT_BUILD_DIR = ${CMAKE_BINARY_DIR}. So the package
gate failed on #503's own NPU path.

Every backend path is now embedded relative to the build dir via
file(RELATIVE_PATH ...) (a new onebit_build_relative() helper) and
app/built_path.h resolves it from the executable's own directory
(/proc/self/exe). That is the same file a 1bit in the build tree has always
found, because the binary lives in the build dir; the package keeps that layout
beside the binary (scripts/package-linux.sh, pack_localize). Nothing
machine-specific is compiled in any more.

Sites: ONEBIT_HRX_SERVER, ONEBIT_DS4_SERVER, ONEBIT_ONNX_SERVER,
ONEBIT_LAYA_GGML_BIN, the two NPU python tools. ONEBIT_BUILD_DIR is removed and
has no remaining references. Absolute paths still work if a caller passes one
(an older build), and the env overrides (ONEBIT_LLAMA_SERVER etc.) are
unchanged and still win.

---------

Co-authored-by: agent <agent@local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hw-gate Run the HRX hardware gate on Strix Halo (docs/hw-gate.md) Review effort 4/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant