locks/executable-tools-v1.json records the immutable release archives and
authenticated layouts for the three executable tools currently pinned by
PixeneOS on x86_64-unknown-linux-gnu. The lock is repository data only. It is
validated offline by src/validate_executable_tool_lock.py.
The sole allowed signer is the Ed25519 key published in chenxiaolong's SSH signature verification guide:
identity: chenxiaolong
namespace: file
key type: ssh-ed25519
fingerprint: SHA256:Ct0HoRyrFLrnF9W+A/BKEiJmwx7yWkgaW/JvghKrboA
The exact allowed-signers binding is checked in at
trust/chenxiaolong.allowed_signers. The validator parses the OpenSSH key blob,
requires an Ed25519 key with exactly 32 public-key bytes, recomputes the OpenSSH
SHA-256 fingerprint with Python's standard library, and requires every lock
entry to name that fingerprint, identity, and namespace.
The following archive sizes and SHA-256 digests were independently recomputed
from downloads made on 2026-07-20 for afsr v1.0.4, on 2026-09-18 for avbroot
v3.34.1 (published 2026-09-06), and on 2026-08-08 for Custota v6.4. They matched
the sizes and SHA-256 asset digests exposed by GitHub's release API. Each
detached signature was exactly 294 bytes and was accepted by
ssh-keygen -Y verify using identity chenxiaolong and namespace file only
after the archive digest and size had been checked.
| Tool | Release archive | Bytes | SHA-256 |
|---|---|---|---|
| afsr 1.0.4 | afsr-1.0.4-x86_64-unknown-linux-gnu.zip |
1,548,868 | 8fdbc9aa6c31b4e6530388ffc5adc42652ec6bbd753aef0815d27d8c3a4b9687 |
| avbroot 3.34.1 | avbroot-3.34.1-x86_64-unknown-linux-gnu.zip |
4,254,252 | b1740ebf92d503cf2e81ca443afa4b615fb97ec365e170b71791ed72d9e559f8 |
| custota-tool 6.4 | custota-tool-6.4-x86_64-unknown-linux-gnu.zip |
2,357,231 | 7d45c0200839f4527b9cdae45ee53bc6579944a85eafb5016fc10e252e120911 |
Detached-signature SHA-256 digests observed during that verification were:
| Signature | SHA-256 |
|---|---|
afsr-1.0.4-x86_64-unknown-linux-gnu.zip.sig |
354bd28d0c1cf20a9ca76dfb958451ad17fa1f34f125fec1ddae58fffc315616 |
avbroot-3.34.1-x86_64-unknown-linux-gnu.zip.sig |
b94ed3c697509aa98b0fec55999694ef18a33f1b83a0d82d9d9321632072da01 |
custota-tool-6.4-x86_64-unknown-linux-gnu.zip.sig |
ecf07da6094f8d9c407a48641badcab88b3f813b1944092469c315a3b34db632 |
GitHub does not publish separate publisher-signed checksum files for these releases. GitHub's asset digest is discovery metadata, not a replacement for the publisher's detached SSH signature. Builds must consume the reviewed digest in the checked-in lock and must not obtain mutable digest policy from the API.
Official releases:
The release archives were downloaded to a private temporary directory. Before any extraction, each archive was checked for its exact byte size and SHA-256, verified with the detached OpenSSH signature, and inspected through Python's ZIP central-directory parser. Inspection rejected duplicate names, absolute paths, backslashes, empty or dot components, non-normalized paths, encrypted members, and non-file/non-directory Unix entry types. The reviewed archives contained:
| Tool | Member | Type | Mode | Bytes | SHA-256 |
|---|---|---|---|---|---|
| afsr | afsr |
file | 0755 |
3,469,744 | 923fa7caaac8b5e3b15b3f0f2e9a08ca34b226cbbcee3f80f40ee5afc735c6d7 |
| avbroot | LICENSE |
file | 0644 |
35,149 | 3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986 |
| avbroot | README.md |
file | 0644 |
33,128 | 36f29f6c9142be36a1895ff1f1b8675b9f29f1d2949d9680da8100ed47555d29 |
| avbroot | avbroot |
file | 0755 |
10,931,888 | de0ed1b439175c8c358e2b2de289adc1a8947c655e3cce1598de374ab71d316b |
| custota-tool | custota-tool |
file | 0755 |
5,552,592 | eb3fc5a31a955d74222ec00c7349e9148719a3ccd6b25ede12e24e34030767ee |
Only after those checks passed were the archives extracted into separate private temporary directories. The extracted member types, modes, sizes, and SHA-256 digests matched the central-directory inspection and the committed lock.
The verification environment was:
- curl 8.18.0 with OpenSSL 3.5.5
- OpenSSH 10.2p1 with OpenSSL 3.5.5
- Python 3.14.5 standard-library
zipfile - GNU coreutils
sha256sum9.10 - Info-ZIP
unzip6.00, used only after authentication and inspection
Run:
python3 src/validate_executable_tool_lock.pyFor fixtures or independent copies, use --lock PATH --trust PATH. Validation
does not access the network. It fails closed on duplicate or unknown JSON fields,
duplicate tools or members, noncanonical JSON, uppercase or malformed hashes,
unbounded sizes, unsafe or unsorted layout paths, unknown members or modes,
unreviewed tool IDs, versions, architectures, URLs, signature parameters, trust
keys, and fingerprints. Both input paths must resolve directly to bounded regular
files; symlinks are not followed.
The current schema deliberately recognizes only these three releases on
x86_64-unknown-linux-gnu. Adding another version, platform, tool, member, or
mode requires a reviewed validator-policy update and regenerated canonical lock.
Never fetch or replace signing keys automatically. A signer rotation requires an authoritative publisher statement binding the replacement key, independent review of that provenance, and a single-purpose change that updates the trust file, validator binding, and affected lock entries together. Rotation tests must demonstrate acceptance of the new key and rejection of the retired key, wrong identities, and wrong namespaces. Re-verify all retained release artifacts after rotation; do not infer that a new key authenticates old assets without valid new signatures or an authoritative cross-binding.
src/bootstrap_executable_tools.py consumes this lock directly. The shell
runtime does not reconstruct executable release versions, asset URLs, or
layouts. It selects enabled tool IDs and submits the entire set in one batch.
Every selected archive must pass its locked byte size, SHA-256, detached
OpenSSH signature, exact hostile-archive inspection, and full member digest
check before extraction of any selected archive begins.
Downloads use bounded streaming into exclusive, no-follow files in a private
mode-0700 transaction directory. Verified bytes are fsynced and atomically
published under bootstrap-cache/objects; a canonical receipt binds the
archive digest to its verified signature digest. Cache hits repeat size,
digest, signature, and archive inspection when both referenced objects exist.
If either cached object is missing, the receipt is treated as a cache miss and
the normal download and validation flow is used. A corrupt, linked, or
noncanonical cache entry fails closed.
Members are extracted without unzip into private transaction directories.
The extractor accepts only the exact locked top-level regular-file layout and
rejects traversal, absolute paths, backslashes, aliases, duplicates, links,
special files, directories, extras, encryption, unsupported ZIP flags or
compression, overlapping compressed ranges, and excessive expansion. It
rechecks extracted types, modes, sizes, and digests before atomically publishing
the directory at tools/by-sha256/<archive-sha256>.
Existing digest-addressed installations are fully revalidated. Legacy
tools/<id> directories are never trusted as an installation bypass. Direct
PixeneOS invocations use the runtime run command, which opens the exact
digest-addressed installation through a held directory descriptor, revalidates
the complete locked layout, copies the verified executable bytes into an
anonymous file descriptor, sets the locked mode, seals the snapshot against
content changes, and rechecks its digest and mode after sealing. It executes
that sealed descriptor without resolving the executable pathname again.
The resolve command and digest-addressed executable paths exist only for
compatibility with the pinned helper. A successful resolution does not itself
authorize execution. The helper still resolves avbroot, afsr, and
custota-tool through PATH; PixeneOS places only enabled digest-addressed
directories at the front of that path, but the helper's later pathname lookup
remains an open execution boundary. Close it with the planned trusted-prefix
ToolRunner integration before treating helper execution as inode-bound.
The canonical JSON report contains stable tool identity, version, architecture, archive and member sizes/digests, and signer verification results. It omits URLs, timestamps, cache-hit state, temporary names, and all cache/report paths. The report is written atomically only after the complete selected transaction succeeds.
The offline lock validator by itself still does not authorize extraction,
permission changes, or execution. A successful runtime transaction authorizes
authenticated installation, and the run command authorizes direct PixeneOS
execution only through its post-seal-verified anonymous file descriptor. It
does not authorize the helper's compatibility PATH execution. Run acquisition
through check_and_download_dependencies; do not call the legacy downloader
for these three executable tools. Real OTA integration remains blocked until
the helper uses the trusted runner and the other documented host-code
supply-chain gates are closed.