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.
What
The release package carried the NPU kernel build tree, and the packaging run could not finish. Both come from one line:
package-linux.shdidcp -a "$build/npu/kernels".A sealed set directory still holds its
layer1.work/beside the sealed kernels. Measured on strixhalo 2026-10-22, per set: ~340 object files, ~250 intermediate AIE ELFs, 10design.mlir.prjdirs, ~20 plaintext.mlir.seal-kernels.shonly rewriteslayer1_*/layer1.elf; everything inlayer1.work/stays plaintext, so the MLIR and object files went into a public artifact.pack_localizetreats every staged ELF as dynamically linked and runspatchelf --set-rpathon it. An AIE object is static, so:engine-pkg/empty, no PR. The weekly NPU-kernels PR stacked on npu: hardware-gate NPU phase + closed, encrypted kernel delivery #503 is blocked on this, not on "next".Changes
scripts/package-linux.sh— stage the artifact surface explicitly:<set>/layout.jsonand<set>/layer1_*/layer1.elf, nothing else. (Loop variable issetname, notname:nameis the package nametaruses further down.)scripts/pack-elf.sh— newpack_is_dynelf;pack_localizeonly rewrites ELFs that actually have a.dynamicsection, so a static ELF is skipped instead of aborting the package.scripts/weekly.sh—package_checksasserts the exact allowlist undernpu/kernelsand names a leaked path;.mlir/.mlirbcnow count as source files in the source scan.Verified on strixhalo (main
cfa197b1scripts, existing e2e build tree)package-linux.sh1bit-e2e-linux-x86_64.tar.zst16 MBnpu/kernelsin the packagelayout.json+ 30layer1.elf1BPKCRYPmagic).mlir,.o,layer1.work,design.mlir.prjpackage_checksok NPU kernels: 33 files under npu/kernels, all sealedpack_is_dynelf1bit; no on an AIE object; no on a sealed kernelTwo other
package_checksfailures on that build are properties of the build, not of this change: its1bitcarries four/home/build-machine paths baked in at compile time, and the tree has noonnx/because it was configured without-DONEBIT_ONNX=ON.Follow-up (separate repo, stacked on npu-kernels #15)
addons/release-kernels.shstill doescp -a "$cache/$name" "$out/$name", so a freshly built set is born dirty. Fixing it at the producer means the sealed tree on disk never holds plaintext MLIR either. This PR makes the consumer correct regardless, which is what unblocks the weekly run today.