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 remain for
compatibility and enabled-tool setup validation. A successful resolution does
not itself authorize execution. PixeneOS supplies the helper's
--tool-runner-prefix-json with an exact argument array containing the absolute
Python interpreter, flags disabling Python environment overrides and user
site packages, absolute bootstrap script path, absolute work directory, and
run subcommand. The helper appends the allowlisted
tool ID, --, and tool arguments without shell parsing. Its tool invocations
therefore pass through the same post-seal-verified descriptor execution as
direct PixeneOS calls, even when the helper changes its working directory or
an executable with the same name exists on PATH. Compatibility directories
on PATH do not authorize helper execution.
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
and helper execution only through its post-seal-verified anonymous file
descriptor. Run acquisition through check_and_download_dependencies; do not call the legacy downloader
for these three executable tools. Real OTA integration remains subject to
the other documented host-code supply-chain gates.
The earlier helper PATH execution gap is closed in the goal integration: direct ModOS and helper calls use the exact authenticated runner prefix and post-seal-verified descriptor execution described above. Ordinary build-only jobs enforce read-only repository permissions and use disposable signing material; the separately selected repository-signer device candidate forwards only its signing inputs, with publishing still disabled.
The strict care-map source engine at
6107d7e92c35b81cac0d28aec3806b45c8cb1f59 and helper passed actual October 9
source verification and disposable-key rootless/Magisk output pairs with microG
off and on. The genuine complete enabled-module profile has also passed
rootless/Magisk postflight, with module archives verified through the pinned SSH signer.
The review receipt records exact complete-profile
outputs and selected modules. Independent inspection passed for genuine module
payloads, compatible policy labels, both regular boot variants, updater removal
and shared images/root distinctions. Retained inert updater caches are recorded
without adding another gate. The real read-only source-build/repository-signing workflow and independent
verification of its exact retrieved encrypted candidate have now also passed.
Acceptance and the receipt's
repository_candidate section record the exact run, archive and output identities.
The public PKMD is authenticated by configured-key workflow provenance; physical
enrolled-key matching, boot/persistence and production lock promotion remain
separate and unobserved.
The manual LineageOS workflow may select development-profile=care-map-pdx235
for build-only. This profile requires LineageOS/pdx235 and
local-unpublished output scope. It uses the common acquisition, module,
root-mode, signing, and output-verification pipeline, with one checked-in
candidate helper contract at
tools/compat/avbroot_setup_care_map_pdx235.json. Missing or modified candidate
metadata rejects activation. Production helper and executable pins remain
unchanged; ordinary build-only signing remains disposable.
The prepared candidate helper's tools/care-map/build_and_run.py reads tracked,
clean source inputs from the captured helper commit, verifies its fixed patch
and Cargo.lock hashes, obtains a fresh exact upstream checkout and builds with
cargo build --release --locked. Signing secrets and GitHub credentials are
excluded from compiler processes. The parent retains the newly compiled binary
digest/size in memory and supplies its own sealed runner to the helper with
--runtime-care-map; a saved receipt never authorizes execution. ModOS's
existing open_sealed_executable binds each execution to those measured bytes.
This development path assumes a trusted compiler/host/dependency cache and is
repository-reviewed source authorization, not a chenxiaolong-signed executable
release. Historical measured binary digests are evidence, not mutable replacement
trust pins. afsr and Custota continue through the official executable lock.
Supported LineageOS 24.0 pdx235 publication has a separate source and output
verification policy. A separate, default-off export-device-candidate request
permits device-acceptance artifacts only with this profile,
signing-mode=repository, release-type=build-only, and no publication.
With an empty device-candidate-recipient-base64, the device-candidate
artifact contains normal signed OTA ZIPs, their selection metadata, the primary
microG report when enabled, and the designated public AVB key. GitHub wraps
these files in an artifact ZIP: extract that outer ZIP once, then pass the
selected OTA ZIP to adb sideload without extracting the OTA itself.
These artifacts follow the repository's normal artifact visibility.
Supplying a public recipient certificate selects encrypted-device-candidate
instead. The certificate is validated before signing; this artifact contains
one CMS AES-256-GCM envelope per OTA and one nonsecret transport manifest.
Both modes use the same final-output allowlist. Private signing material and
raw working directories are never exported. Candidate artifact delivery does
not publish a GitHub release or update Custota OTA metadata.
Separate envelopes keep the paired output out of OpenSSL's combined-object ASN.1 size limit; the public AVB key appears identically in both, and the microG report belongs to the primary OTA envelope.
The recipient decrypts each envelope to a temporary archive and extracts only after OpenSSL reports success. For example:
openssl cms -decrypt -binary -inform DER -in candidate-1.cms.der \
-recip recipient.crt -inkey recipient.key -out candidate.tar.tmp &&
mv candidate.tar.tmp candidate.tarKeep the recipient private key outside the repository and GitHub workflow; supply only the base64-encoded public certificate. GitHub artifact delivery is private device-acceptance transport, not release or updater publication.
The reusable ROM publisher inherits its calling job's permissions. Publishing
callers explicitly grant contents: write; the shared workflow does not request
write itself, because GitHub validates skipped reusable jobs against a read-only
caller's ceiling before evaluating their conditions. The executed build-only
job explicitly requests contents: read, including under a write-capable caller.
The same Lineage workflow can retransport an existing encrypted candidate by
supplying source-artifact-id and expected-source-zip-sha256. This mode skips
all build, signing, and publication jobs. Read-only jobs verify GitHub's artifact
digest and successful same-repository source run, then fetch exact 256 MiB byte
ranges with a nonsecret part manifest. GitHub authorization is withheld from
the signed artifact-storage request. Reassemble the parts in index order and
verify the original ZIP digest before normal envelope decryption and OTA
verification. Splitting addresses the client's artifact-download limit; it
does not change the encrypted payload or establish OTA acceptance. The source
run and commit are preserved in each manifest, independent of the transport run.
Custota module 6.6, MSD module 2.5 and custota-tool 6.6 were downloaded from
their exact official release URLs and verified against the unchanged checked-in
SSH signer (identity chenxiaolong, namespace file). Exact archive/signature
and member sizes/digests are recorded in
the module qualification receipt.
The Custota host tool's archive is 2,389,309 bytes, SHA-256
c556e5e72b7671914d7c772728abb0d2f602ff53d8873a1e6efdf202df654d73.
Its single custota-tool member retains executable mode 0755. The canonical
lock and validator policy advance together; no signer change is accepted.
The new module archives retain the helper's APK/XML/tool paths and separate platform app selectors. Their source changes do not alter SELinux patcher code or context rules. The image adapter uses signed APK/XML/native payloads; it does not execute module installer or post-fs-data shell scripts. Existing vendor and ODM app-context files remain unchanged, preserving the bootloop repair. These are source/build compatibility checks, not new physical-device acceptance. At the time of this October 13 note, publication remained quarantined. Current policy permits verified official LineageOS 24.0 pdx235 nightlies.
The maintained helper now builds the guarded development engine from avbroot
3.35.0, at upstream commit 4d678ffe8955de49403e593960626dfa48bd0cce.
The existing explicit-key rejection and expected-care-map logic are unchanged.
The frozen source build and full source tests passed, followed by successful
verification of the previously accepted October 9 OTA. Wrong keys, unsigned
images with an explicit expected key, tampering, and a wrong expected-map
fingerprint were rejected. See qualification evidence.
On October 13, the official prebuilt lock was deliberately retained at 3.34.1 after demonstrating that upstream 3.35.0 accepts an unsigned AVB header even with an explicitly expected key. Subsequent source comparison established this behavior is identical in 3.34.1 and 3.35.0, not a 3.35 regression. The guarded source engine adds a fail-closed signed-header check; that guard must remain active on any path requiring actual authenticated AVB headers.
The official 3.35.0 x86_64-unknown-linux-gnu release archive and detached
signature were downloaded in nonpublishing ModOS CI
run 38743045133.
The exact archive SHA-256 and 4,250,535-byte size match the published release
metadata. Both the archive and signature SHA-256 were verified before
ssh-keygen -Y verify accepted the signature with the existing allowed
signer chenxiaolong, namespace file, Ed25519 fingerprint
SHA256:Ct0HoRyrFLrnF9W+A/BKEiJmwx7yWkgaW/JvghKrboA.
| Artifact | SHA-256 |
|---|---|
| Official 3.35.0 ZIP | bbc7f8b4df8d9e6d2162bf354ac9f06643b2f64247a6f345d71e963f645f58c5 |
| Detached .sig | 7ab8b19e7c47bbaf7f846df5c601b3a0888d605f5a3176144b7bedf64600176b |
Extracted LICENSE (35,149 bytes, 0644) |
3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986 |
Extracted README.md (33,214 bytes, 0644) |
398f4be471f467b40e33a042b13ef3a31ac47c969672521ed86cf29da335ffe6 |
Extracted avbroot (10,916,976 bytes, 0755) |
2539724e0add2b8f6d855b8311897d01a4c28e851d387551f130325f006fa614 |
The checked-in executable lock, offline validator and independent lock regression fixture advance together; no executable signer, signing key, or release permission changes. The official binary does not replace the source-built guarded expected-care-map verifier in the pdx235 development path. At the time of this October 15 qualification, publication review remained outstanding. Current policy permits verified official LineageOS 24.0 pdx235 nightlies; physical Android 17 acceptance remains a separate task.