Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
c9014c7
fix(runtime): fail closed on ABI handshake and correct KPM argv contract
Zhanfg Aug 7, 2026
d2b1e42
fix(kpm): pass module args according to actual CLI contract
Zhanfg Aug 7, 2026
3f52d3c
docs(review): establish fail-closed flash-readiness gate
Zhanfg Aug 7, 2026
c41c4f0
safety(review): block installation until P0 flash gates close
Zhanfg Aug 7, 2026
be0ea69
safety(review): abort install when flash review marker is present
Zhanfg Aug 7, 2026
36f6839
fix(flash): use private unpatch workspace and target-bound recovery
Zhanfg Aug 7, 2026
58cc166
fix(flash): preserve real flash failure status
Zhanfg Aug 7, 2026
f84e5bf
fix(review): select verified backups without parsing ls
Zhanfg Aug 7, 2026
c86ec93
fix(flash): add fail-closed partition and readback safety overrides
Zhanfg Aug 7, 2026
107de2f
fix(flash): activate safety overrides in unpatch path
Zhanfg Aug 7, 2026
65dab4b
fix(flash): activate fail-closed boot partition resolution
Zhanfg Aug 7, 2026
6369d16
fix(review): keep verified backup selection POSIX-safe
Zhanfg Aug 7, 2026
b3c8778
fix(flash): make boot patch transaction fail-closed
Zhanfg Aug 8, 2026
4ac08fb
test(flash): lock transactional patch safety contract
Zhanfg Aug 8, 2026
1aaa359
ci(flash): enforce pre-flash safety invariants
Zhanfg Aug 8, 2026
668fa28
fix(flash): preserve verified write failure status
Zhanfg Aug 8, 2026
a23bfb9
feat(flash): add transactional Public1158 superkey lifecycle
Zhanfg Aug 8, 2026
09ac096
feat(flash): bind Public1158 superkey transaction to image write
Zhanfg Aug 8, 2026
00bb7e2
test(flash): verify transactional superkey contract
Zhanfg Aug 8, 2026
9f7fb3b
ci(flash): lint superkey safety lifecycle
Zhanfg Aug 8, 2026
b2258cf
build(module): pin reviewed Public1158 CLI source commit
Zhanfg Aug 8, 2026
f057f5b
build(module): compile pinned Public1158 CLI instead of mixing Next ABI
Zhanfg Aug 8, 2026
7425d80
build(module): eliminate mixed userspace/kernel ABI package
Zhanfg Aug 8, 2026
ee71653
test(flash): keep repo root discovery POSIX-clean
Zhanfg Aug 8, 2026
7269dd4
fix(runtime): gate lifecycle operations by explicit ABI profile
Zhanfg Aug 8, 2026
9a69bd3
build(module): pin event-capable Public1158 CLI head
Zhanfg Aug 8, 2026
e7eb227
build(module): add deterministic ZIP packager
Zhanfg Aug 8, 2026
bd92096
build(module): prove deterministic local package bytes
Zhanfg Aug 8, 2026
9a72261
ci(module): prove final ZIP byte reproducibility
Zhanfg Aug 8, 2026
6290be1
fix(package): store canonical module root paths
Zhanfg Aug 8, 2026
2dddddd
test(runtime): lock ABI capability boundaries
Zhanfg Aug 8, 2026
fc109ff
test(runtime): make capability guard ordering assertion exact
Zhanfg Aug 8, 2026
d6726fe
ci(runtime): enforce ABI capability boundaries
Zhanfg Aug 8, 2026
ea6a0df
fix(webui): preserve KPM argv values when spawning patcher
Zhanfg Aug 8, 2026
8806869
feat(recovery): bind rollback to device and verified flash transaction
Zhanfg Aug 8, 2026
8a3b5be
feat(recovery): load transaction identity helpers with flash safety
Zhanfg Aug 8, 2026
685768e
feat(recovery): commit rollback transaction before Public1158 key
Zhanfg Aug 8, 2026
87722c2
feat(recovery): bind rollback to exact written byte range
Zhanfg Aug 8, 2026
1705264
fix(recovery): restore only the exact bound flash transaction
Zhanfg Aug 8, 2026
507c373
test(recovery): enforce device-bound exact rollback transaction
Zhanfg Aug 8, 2026
3314ce8
ci(recovery): lint transaction-bound rollback helper
Zhanfg Aug 8, 2026
1a0fb5f
test(device): add auditable physical flash lifecycle harness
Zhanfg Aug 8, 2026
75e7a61
ci(device): lint physical validation evidence harness
Zhanfg Aug 8, 2026
1d7d457
fix(device): bind evidence checks to installed patch context
Zhanfg Aug 8, 2026
5e43d83
fix(recovery): confine synthetic device identity to tests
Zhanfg Aug 8, 2026
63d94f4
ci(recovery): scope synthetic device identity to test step
Zhanfg Aug 8, 2026
928f44c
docs(review): close static gates and isolate physical validation
Zhanfg Aug 8, 2026
a4be624
fix(auth): make superkey commit one-way atomic
Zhanfg Aug 8, 2026
4f5741e
test(auth): keep root ownership strict outside CI transaction mode
Zhanfg Aug 8, 2026
1f1d6dc
fix(recovery): make rollback binding atomic after key commit
Zhanfg Aug 8, 2026
410e191
fix(auth): add crash-recoverable pending superkey state
Zhanfg Aug 8, 2026
3156b00
fix(runtime): recover interrupted Public1158 key promotion safely
Zhanfg Aug 8, 2026
c31883b
test(auth): cover pending-key crash consistency
Zhanfg Aug 8, 2026
33d7f11
fix(flash): add durable pending transaction state
Zhanfg Aug 8, 2026
e80a1b5
fix(flash): add verified destructive write transaction
Zhanfg Aug 8, 2026
37f3387
fix(auth): stage pending key only at destructive commit
Zhanfg Aug 8, 2026
93b3bce
fix(flash): make boot patch destructive write transactional
Zhanfg Aug 8, 2026
82897db
fix(service): bind pending credential recovery to written transaction
Zhanfg Aug 8, 2026
144a6a8
fix(restore): make unpatch transaction-bound only
Zhanfg Aug 8, 2026
ca4d5eb
test(flash): inject destructive write and rollback failures
Zhanfg Aug 8, 2026
8d86e84
ci(flash): run destructive failure-injection contract
Zhanfg Aug 8, 2026
9df238e
test(device): close rollback negative and post-restore evidence loop
Zhanfg Aug 8, 2026
81e4cd9
fix(recovery): require written transaction and rebuild binding after …
Zhanfg Aug 8, 2026
e7c265d
fix(recovery): rebuild rollback binding after pending key recovery
Zhanfg Aug 8, 2026
43c8d0c
fix(restore): retry exact backup after possible partial write
Zhanfg Aug 8, 2026
865d5f9
test(flash): cover crash binding recovery and restore retry
Zhanfg Aug 8, 2026
0cb4d4b
feat(device): package physical validation harness
Zhanfg Aug 8, 2026
b2b7328
refactor(device): make packaged harness canonical
Zhanfg Aug 8, 2026
48633f8
fix(install): set executable permissions for flash and validation scr…
Zhanfg Aug 8, 2026
5300c30
test(package): enforce flash-safety files in final ZIP
Zhanfg Aug 8, 2026
a0b787a
ci(flash): validate packaged device harness and archive gate
Zhanfg Aug 8, 2026
32d5501
test(flash): align baseline contract with transactional writer
Zhanfg Aug 8, 2026
c75c6c7
fix(boot): make bootloop recovery actually restore bound backup
Zhanfg Aug 8, 2026
d1d226f
test(boot): execute automatic rollback trigger contract
Zhanfg Aug 8, 2026
7c2f9a7
ci(boot): execute bootloop automatic recovery contract
Zhanfg Aug 8, 2026
d5fcf3e
feat(device): add controlled auto-recovery validation trigger
Zhanfg Aug 8, 2026
2b86657
feat(device): verify automatic rollback after reboot
Zhanfg Aug 8, 2026
51f04ec
fix(install): install automatic recovery validation tools
Zhanfg Aug 8, 2026
e984ae9
test(package): require automatic recovery validation tools
Zhanfg Aug 8, 2026
7856632
ci(device): validate packaged automatic recovery tools
Zhanfg Aug 8, 2026
5f770cf
test(flash): source superkey helper in parent transaction scope
Zhanfg Aug 8, 2026
399b88a
fix(install): use manager-provided MODPATH without hardcoded module copy
Zhanfg Aug 8, 2026
d87866f
test(install): execute cross-manager MODPATH installer contract
Zhanfg Aug 8, 2026
7ac7206
ci(install): execute cross-manager installer contract
Zhanfg Aug 8, 2026
9a298d0
fix(restore): parse JSON booleans with extended regex
Zhanfg Aug 8, 2026
91e2b6a
fix(device): parse restore receipt booleans correctly
Zhanfg Aug 8, 2026
3a3fd50
fix(device): derive module directory from packaged harness path
Zhanfg Aug 8, 2026
386e3df
feat(recovery): export verified pre-reboot boot recovery image
Zhanfg Aug 8, 2026
4e55624
fix(install): require off-device recovery export tool
Zhanfg Aug 8, 2026
7926848
test(package): require off-device recovery export tool
Zhanfg Aug 8, 2026
c626f0a
test(recovery): execute verified boot recovery export
Zhanfg Aug 8, 2026
f1703c9
ci(recovery): execute off-device recovery export contract
Zhanfg Aug 8, 2026
3b88ce8
test(install): include recovery export in fake module
Zhanfg Aug 8, 2026
9154127
test(recovery): create Android temp root in CI harness
Zhanfg Aug 8, 2026
1ee5adc
validation(device): remove review install blocker in isolated FR-014 …
Zhanfg Aug 8, 2026
da2a217
validation(device): mark isolated FR-014 candidate
Zhanfg Aug 8, 2026
857e963
validation(device): package only explicit FR-014 candidate marker
Zhanfg Aug 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 73 additions & 16 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,22 @@ jobs:
exit 1
}

for key in patchnest kernelpatch magiskboot; do
for key in patchnest kernelpatch magiskboot patchnest_public1158_commit; do
grep -Fq "${key}=" version.properties || {
echo "::error::Missing version key: $key"
exit 1
}
done

source_commit=$(grep -F 'patchnest_public1158_commit=' version.properties | head -n 1 | cut -d= -f2- | tr -d '"')
printf '%s' "$source_commit" | grep -Eq '^[0-9a-f]{40}$' || {
echo "::error::patchnest_public1158_commit must be an exact 40-hex commit"
exit 1
}

for key in \
kpimg_linux_0.13.3 \
kptools_android_0.13.3 \
kpatch_android_0.13.5-2 \
magisk_apk_v30.7; do
value=$(grep -F "${key}=" version.properties | head -n 1 | cut -d= -f2-)
printf '%s' "$value" | grep -Eq '^[0-9a-f]{64}$' || {
Expand All @@ -85,23 +91,30 @@ jobs:
}
done

if grep -q '^kpatch_android_' version.properties; then
echo "::error::Historical Next2026 kpatch release pin must not be used by the Public1158 module"
exit 1
fi

- name: Check shell syntax
run: |
set -euo pipefail
for file in build.sh module/*.sh module/patch/*.sh; do
for file in build.sh scripts/package_module.sh module/*.sh module/patch/*.sh; do
bash -n "$file"
done

- name: ShellCheck
run: |
shellcheck -s bash -S error build.sh
shellcheck -s sh -S warning scripts/package_module.sh
shellcheck -s sh -S warning \
--exclude=SC3043,SC2034,SC2115,SC2046,SC2319,SC2155 \
module/service.sh module/customize.sh module/post-fs-data.sh \
module/action.sh module/status.sh module/uninstall.sh \
module/install_kpm.sh module/compile_kpm.sh \
module/patch/boot_patch.sh module/patch/boot_extract.sh \
module/patch/boot_unpatch.sh
module/patch/boot_unpatch.sh module/patch/flash_safety.sh \
module/patch/superkey_safety.sh
shellcheck -s sh -S error module/patch/util_functions.sh || true

- name: Verify locale completeness
Expand Down Expand Up @@ -166,12 +179,12 @@ jobs:
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y jq zip unzip file
sudo apt-get install -y jq zip unzip file cmake ninja-build

- name: Load dependency versions
run: |
set -euo pipefail
for key in patchnest kernelpatch magiskboot; do
for key in patchnest kernelpatch magiskboot patchnest_public1158_commit; do
value=$(grep -F "${key}=" version.properties | head -n 1 | cut -d= -f2- | tr -d '"')
[ -n "$value" ] || { echo "::error::Missing $key"; exit 1; }
echo "$key=$value" >> "$GITHUB_ENV"
Expand All @@ -186,9 +199,6 @@ jobs:
gh release download "$kernelpatch" \
-R Zhanfg/KernelPatch-Public \
-p kpimg-linux -p kptools-android -D module/bin
gh release download "$patchnest" \
-R Zhanfg/PatchNest \
-p kpatch-android -D module/bin
gh release download "$magiskboot" \
-R topjohnwu/Magisk \
-p 'Magisk*.apk' -O magisk.apk
Expand All @@ -213,20 +223,56 @@ jobs:
}
verify "kpimg_linux_${kernelpatch}" module/bin/kpimg-linux
verify "kptools_android_${kernelpatch}" module/bin/kptools-android
verify "kpatch_android_${patchnest}" module/bin/kpatch-android
verify "magisk_apk_${magiskboot}" magisk.apk
exit "$fail"

- name: Prepare module binaries
- name: Prepare core module binaries
run: |
set -euo pipefail
mv module/bin/kpimg-linux module/bin/kpimg
mv module/bin/kptools-android module/bin/kptools
mv module/bin/kpatch-android module/bin/kpatch
unzip -p magisk.apk 'lib/arm64-v8a/libmagiskboot.so' > module/bin/magiskboot
test -s module/bin/magiskboot
rm magisk.apk
chmod 0755 module/bin/kpatch module/bin/kptools module/bin/magiskboot
chmod 0755 module/bin/kptools module/bin/magiskboot

- name: Build pinned Public1158 userspace CLI
run: |
set -euo pipefail
printf '%s' "$patchnest_public1158_commit" | grep -Eq '^[0-9a-f]{40}$'
rm -rf /tmp/patchnest-public1158 build/public1158
git clone --filter=blob:none --no-checkout https://github.com/Zhanfg/PatchNest.git /tmp/patchnest-public1158
git -C /tmp/patchnest-public1158 fetch --depth=1 origin "$patchnest_public1158_commit"
git -C /tmp/patchnest-public1158 checkout --detach "$patchnest_public1158_commit"
test "$(git -C /tmp/patchnest-public1158 rev-parse HEAD)" = "$patchnest_public1158_commit"

cmake -S /tmp/patchnest-public1158 -B build/public1158 \
-G Ninja \
-DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \
-DCMAKE_BUILD_TYPE=Release \
-DANDROID_PLATFORM=android-33 \
-DANDROID_ABI=arm64-v8a
cmake --build build/public1158 --target kpatch-public1158 --parallel
cp build/public1158/kpatch-public1158 module/bin/kpatch
chmod 0755 module/bin/kpatch

strings module/bin/kpatch | grep -Fxq 'public1158'
strings module/bin/kpatch | grep -Fxq 'hello1158'
if strings module/bin/kpatch | grep -Fxq 'hello2026'; then
echo "::error::Packaged kpatch still contains Next2026 hello identity"
exit 1
fi

mkdir -p module/provenance
cli_sha=$(sha256sum module/bin/kpatch | awk '{print $1}')
cat > module/provenance/kpatch-public1158.json <<EOF
{
"profile": "public1158",
"sourceRepository": "Zhanfg/PatchNest",
"sourceCommit": "$patchnest_public1158_commit",
"binarySha256": "$cli_sha"
}
EOF

- name: Build kp-safemode
run: |
Expand All @@ -237,7 +283,7 @@ jobs:
-o module/bin/kp-safemode module/tools/kp-safemode.c
chmod 0755 module/bin/kp-safemode

- name: Verify ARM64 binaries
- name: Verify ARM64 binaries and ABI profile
run: |
set -euo pipefail
for binary in module/bin/kpatch module/bin/kptools module/bin/kp-safemode; do
Expand All @@ -250,6 +296,9 @@ jobs:
done
test -s module/bin/kpimg
test -s module/bin/magiskboot
jq -e '.profile == "public1158"' module/provenance/kpatch-public1158.json >/dev/null
test "$(jq -r '.sourceCommit' module/provenance/kpatch-public1158.json)" = "$patchnest_public1158_commit"
test "$(jq -r '.binarySha256' module/provenance/kpatch-public1158.json)" = "$(sha256sum module/bin/kpatch | awk '{print $1}')"

- name: Build WebUI
run: |
Expand All @@ -259,15 +308,23 @@ jobs:
- name: Validate complete module
run: node tests/validate_module.js

- name: Package module
- name: Package module reproducibly
run: |
set -euo pipefail
mkdir -p out
(cd module && zip -qr ../out/PatchNest-Module.zip .)
sh scripts/package_module.sh module out/PatchNest-Module.zip
sh scripts/package_module.sh module out/PatchNest-Module.repeat.zip
cmp -s out/PatchNest-Module.zip out/PatchNest-Module.repeat.zip || {
echo "::error::Same assembled module tree produced different ZIP bytes"
sha256sum out/PatchNest-Module.zip out/PatchNest-Module.repeat.zip
exit 1
}
rm -f out/PatchNest-Module.repeat.zip
test -s out/PatchNest-Module.zip
sha256sum out/PatchNest-Module.zip > out/PatchNest-Module.zip.sha256
unzip -l out/PatchNest-Module.zip | grep -q 'module.prop'
unzip -l out/PatchNest-Module.zip | grep -q 'webroot/index.html'
unzip -l out/PatchNest-Module.zip | grep -q 'provenance/kpatch-public1158.json'

- name: Determine release tag
id: release_meta
Expand Down
127 changes: 127 additions & 0 deletions .github/workflows/flash-safety.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: Flash safety

permissions:
contents: read

on:
pull_request:
branches: [main]
paths:
- 'module/patch/**'
- 'module/service.sh'
- 'module/post-fs-data.sh'
- 'module/customize.sh'
- 'module/device_validation.sh'
- 'module/arm_auto_recovery.sh'
- 'module/verify_auto_recovery.sh'
- 'module/export_recovery_boot.sh'
- 'scripts/device_validation.sh'
- 'scripts/package_module.sh'
- 'version.properties'
- 'tests/flash_safety_contract.sh'
- 'tests/destructive_transaction_contract.sh'
- 'tests/bootloop_recovery_contract.sh'
- 'tests/installer_contract.sh'
- 'tests/recovery_export_contract.sh'
- 'tests/runtime_abi_contract.sh'
- '.github/workflows/flash-safety.yml'
push:
branches:
- 'review/flash-readiness-hardening'
- 'review/flash-readiness-final'
paths:
- 'module/patch/**'
- 'module/service.sh'
- 'module/post-fs-data.sh'
- 'module/customize.sh'
- 'module/device_validation.sh'
- 'module/arm_auto_recovery.sh'
- 'module/verify_auto_recovery.sh'
- 'module/export_recovery_boot.sh'
- 'scripts/device_validation.sh'
- 'scripts/package_module.sh'
- 'version.properties'
- 'tests/flash_safety_contract.sh'
- 'tests/destructive_transaction_contract.sh'
- 'tests/bootloop_recovery_contract.sh'
- 'tests/installer_contract.sh'
- 'tests/recovery_export_contract.sh'
- 'tests/runtime_abi_contract.sh'
- '.github/workflows/flash-safety.yml'
workflow_dispatch:

jobs:
contract:
name: Transactional flash and ABI contract
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install shell validation tools
run: |
sudo apt-get update
sudo apt-get install -y shellcheck zip unzip

- name: Shell syntax
run: |
set -euo pipefail
for file in module/patch/*.sh module/service.sh module/post-fs-data.sh module/customize.sh module/device_validation.sh module/arm_auto_recovery.sh module/verify_auto_recovery.sh module/export_recovery_boot.sh scripts/device_validation.sh scripts/package_module.sh tests/flash_safety_contract.sh tests/destructive_transaction_contract.sh tests/bootloop_recovery_contract.sh tests/installer_contract.sh tests/recovery_export_contract.sh tests/runtime_abi_contract.sh; do
sh -n "$file"
done

- name: ShellCheck reviewed flash/runtime path
run: |
shellcheck -s sh -S warning \
--exclude=SC3043,SC2034,SC2115,SC2046,SC2319,SC2155 \
module/patch/boot_patch.sh \
module/patch/boot_extract.sh \
module/patch/boot_unpatch.sh \
module/patch/flash_safety.sh \
module/patch/transaction_safety.sh \
module/patch/transactional_flash.sh \
module/patch/superkey_safety.sh \
module/service.sh \
module/post-fs-data.sh \
module/customize.sh \
module/device_validation.sh \
module/arm_auto_recovery.sh \
module/verify_auto_recovery.sh \
module/export_recovery_boot.sh \
scripts/device_validation.sh \
scripts/package_module.sh \
tests/flash_safety_contract.sh \
tests/destructive_transaction_contract.sh \
tests/bootloop_recovery_contract.sh \
tests/installer_contract.sh \
tests/recovery_export_contract.sh \
tests/runtime_abi_contract.sh

- name: Run baseline flash contract
env:
PATCHNEST_TRANSACTION_TEST: '1'
run: sh tests/flash_safety_contract.sh

- name: Run destructive failure-injection contract
env:
PATCHNEST_TRANSACTION_TEST: '1'
run: sh tests/destructive_transaction_contract.sh

- name: Run bootloop automatic recovery contract
run: sh tests/bootloop_recovery_contract.sh

- name: Run cross-manager installer contract
run: sh tests/installer_contract.sh

- name: Run off-device recovery export contract
run: sudo sh tests/recovery_export_contract.sh

- name: Run runtime ABI contract
run: sh tests/runtime_abi_contract.sh

- name: Prove flash-safety files enter deterministic ZIP
run: |
set -euo pipefail
mkdir -p /tmp/patchnest-package-test
sh scripts/package_module.sh module /tmp/patchnest-package-test/module.zip
test -s /tmp/patchnest-package-test/module.zip
Loading
Loading