Files
firmware/docs/notes
Oleksandr ShyshatskyiandClaude Opus 5 1c89a9cd80 Neither Alpine nor OpenWrt has a binary we can install. That picks opkg.
The evaluation asked for apk vs opkg. The comparison turns on one fact that
makes most of the usual argument irrelevant here: Alpine has no MIPS port at
all, and OpenWrt's mipsel_24kc is musl against this printer's glibc 2.29. So
no upstream repository is reachable either way, every package is cross-built
here regardless, and a package manager buys packaging and installing -- not
software.

That reduces the choice to which format is cheapest to PRODUCE on every build,
and .ipk wins by a distance: ar + two tarballs + a three-byte version file,
no keypair, no Alpine host, no abuild. apk would need the signed
three-concatenated-gzip layout implemented by hand or abuild imported into a
Debian build image, paid per recipe forever, for a format with an in-progress
v2->v3 transition and no users on our architecture. The full table, the two
honest points for apk, and the phased plan are in docs/notes/85-packaging.md.

The proof of concept is libsodium, end to end and measured rather than
described:

  make packages   ->  libsodium_1.0.20-1_mipsel_xburst2.ipk + a feed index

Verified against real opkg 0.6.3 built from source -- offline-root install,
both symlinks preserved as symlinks, list-installed, files, remove leaving
nothing behind. dpkg-deb reads it too; the container format is Debian's.
A full recompile from a cold cache produces a byte-identical package, so a
pin bump is reviewable as a hash diff.

libsodium is now compiled ONCE. patch.sh section 5d's build block moved to
pkg/libsodium/build.sh and patch.sh runs it, so the tarball's copy and the
packaged copy cannot become different libraries wearing one version number --
asserted, not trusted. MODDIR, PY_HOST, PY_TOOLCHAIN_DIR and mips_abi_gate
moved to common.sh for the same reason: a package is a shipping vehicle and
gets ABI-gated like one, on a machine that may never run patch.sh.

Nothing ships. make build is unchanged and still produces one anvil.tar.xz;
make packages needs no stock FlashForge package, which is the point -- a
packaging gate that only runs where the proprietary firmware is stops being a
gate.

Two findings worth more than the code. opkg bakes its state directory in at
COMPILE time, so a phase-2 opkg must be configured --prefix=/usr/data/anvil or
it comes up believing nothing is installed -- the s6 libexecdir trap one layer
up. And opkg 0.6.3 will not configure without libarchive, which is what phase 2
actually costs; the opkg binary itself is 33KB. So pkg/ipk-install walks the ar
headers with tail/head instead: this busybox is 1.31.1 built small, with no
timeout, nc or ionice applet, and betting an installer on an ar nobody has
checked for is how an update fails at the last step.

qa/static/test_ipk.py is 18 tests that build their own package from a synthetic
tree -- no toolchain, no firmware -- so the lane keeps the property
qa/conftest.py insists on and can never be skipped into looking green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 11:45:51 +03:00
..