Skip to content
Merged
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ Root changes the device security model and can introduce compatibility breakage

ModOS defaults to the official `topjohnwu/Magisk` repository. The source remains configurable through `MAGISK[REPOSITORY]` in `env.toml`, but alternate Magisk forks are opt-in rather than part of the GrapheneOS build contract. If you use Zygisk Next for GrapheneOS compatibility, install and manage it separately from the OTA build. Magisk/Zygisk behavior can change across releases, so rooted builds should be revalidated after ROM, Magisk, or Zygisk changes.

ModOS currently pins **official Magisk v30.7** as its avbroot compatibility baseline. This mirrors the current rooted-GrapheneOS reference pairing with avbroot 3.34.1. avbroot 3.34.0 and newer explicitly recognize Magisk 31000, but parser/patch-format support is not the same as target-device runtime validation, so ModOS does not automatically advance to the highest Magisk tag. Override `MAGISK_VERSION` only for an intentional compatibility test.

With avbroot, Magisk is not updated using the app's normal **Direct install** path. Magisk version changes must be made by repatching/re-signing the OTA and installing that OTA. The manager's **Additional setup / Environment fix** is a separate runtime-environment step and does not replace OTA repatching.

A Magisk-patched OTA is only the boot-image half of a working Magisk installation. On a clean install, after a data wipe, or whenever Magisk reports that additional setup is required, install/open the **matching Magisk manager APK**, accept **Additional setup / Environment fix**, and allow the device to reboot. That step provisions the runtime binaries (including `su`) under `/data/adb/magisk`; those files are device state and cannot be embedded in or verified from an OTA image.

After that reboot, verify runtime root on the actual device:
Expand Down
3 changes: 3 additions & 0 deletions env.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ ROM_FAMILY = "grapheneos"
# 'MAGISK[REPOSITORY]' = "topjohnwu/Magisk"

[build]
# Magisk is pinned deliberately for avbroot compatibility. Bump only after
# reviewing avbroot support and validating the target device/runtime path.
# MAGISK_VERSION = "v30.7"
# Legacy single-output switch: false = rootless, true = Magisk.
# ROOT = true
# Optional override: "rootless", "magisk", or "both".
Expand Down
9 changes: 8 additions & 1 deletion src/ci/selection_variant.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function selection_variant_manifest() {
SELECTION_ROOT
SELECTION_MAGISK_PREINIT
SELECTION_MAGISK_REPOSITORY
SELECTION_MAGISK_VERSION
SELECTION_DEBUG
SELECTION_COMPATIBLE_SEPOLICY
SELECTION_CLEAR_VBMETA_FLAGS
Expand Down Expand Up @@ -103,7 +104,13 @@ function selection_variant_manifest() {
echo "Error: rooted selection identity has an invalid Magisk repository." >&2
return 1
fi
printf '%s\n' "magisk_repository=${SELECTION_MAGISK_REPOSITORY}"
if [[ ! "${SELECTION_MAGISK_VERSION}" =~ ^v[0-9]+([.][0-9A-Za-z_-]+)*$ ]]; then
echo "Error: rooted selection identity has an invalid Magisk version." >&2
return 1
fi
printf '%s\n' \
"magisk_repository=${SELECTION_MAGISK_REPOSITORY}" \
"magisk_version=${SELECTION_MAGISK_VERSION}"
fi

if [[ "${SELECTION_BOOT_ANIMATION}" == 'true' ]]; then
Expand Down
2 changes: 2 additions & 0 deletions src/config_schema.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ _config_schema_define update_channel 'GRAPHENEOS[UPDATE_CHANNEL]' device string
GRAPHENEOS array UPDATE_CHANNEL GRAPHENEOS_UPDATE_CHANNEL '' true existing-or-default
_config_schema_define magisk_repository 'MAGISK[REPOSITORY]' device string \
MAGISK array REPOSITORY '' topjohnwu/Magisk false caller-or-default
_config_schema_define magisk_version MAGISK_VERSION build string \
VERSION array MAGISK MAGISK_VERSION v30.7 true caller-or-existing-or-default
_config_schema_define afsr 'ADDITIONALS[AFSR]' build boolean \
ADDITIONALS array AFSR ADDITIONALS_AFSR true true caller-or-existing-or-default
_config_schema_define alterinstaller 'ADDITIONALS[ALTERINSTALLER]' build boolean \
Expand Down
36 changes: 22 additions & 14 deletions src/fetcher.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ source src/declarations.sh
source src/rom_profiles.sh
source src/ota_providers.sh

# Fetch the latest version of GrapheneOS and Magisk and sets up the OTA URL
# Resolve the ROM version plus the configured Magisk compatibility version
function get_latest_version() {
local latest_magisk_version=$(
git ls-remote --tags "${DOMAIN}/${MAGISK[REPOSITORY]}.git" |
awk -F'\t' '{print $2}' |
grep -E 'refs/tags/' |
grep -v '\^{}$' |
sed 's/refs\/tags\///' |
sort -V |
tail -n1
)
local selected_magisk_version="${VERSION[MAGISK]:-}"

if [[ -z "${selected_magisk_version}" ]]; then
selected_magisk_version=$(
git ls-remote --tags "${DOMAIN}/${MAGISK[REPOSITORY]}.git" |
awk -F'\t' '{print $2}' |
grep -E 'refs/tags/' |
grep -v '\^{}$' |
sed 's/refs\/tags\///' |
sort -V |
tail -n1
)
fi

resolve_rom_profile || return 1

Expand All @@ -30,12 +34,16 @@ function get_latest_version() {
fetch_rom_ota_metadata || return 1
echo -e "${ROM_FAMILY} OTA target: \`${GRAPHENEOS[OTA_TARGET]}\`\nOTA URL: ${GRAPHENEOS[OTA_URL]}\n"

if [[ -z "${latest_magisk_version}" ]]; then
echo -e "Failed to get the latest Magisk version."
if [[ -z "${selected_magisk_version}" ]]; then
echo -e "Failed to resolve the Magisk version."
exit 1
else
VERSION[MAGISK]="${latest_magisk_version}"
fi
if [[ ! "${selected_magisk_version}" =~ ^v[0-9]+([.][0-9A-Za-z_-]+)*$ ]]; then
echo -e "Invalid Magisk version tag: ${selected_magisk_version}"
exit 1
fi

VERSION[MAGISK]="${selected_magisk_version}"
}

# Getter function to download the magisk, modules, signatures and tools
Expand Down
7 changes: 7 additions & 0 deletions src/rom_profiles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ function module_selection_fingerprint() {
local profile_digest="disabled"
local magisk_preinit="disabled"
local magisk_repository="disabled"
local magisk_version="disabled"
local boot_animation_digest="disabled"
local entry
local -a module_entries=(
Expand Down Expand Up @@ -186,6 +187,7 @@ function module_selection_fingerprint() {
if [[ "${ADDITIONALS[ROOT]}" == 'true' ]]; then
magisk_preinit="${MAGISK[PREINIT]}"
magisk_repository="${MAGISK[REPOSITORY]}"
magisk_version="${VERSION[MAGISK]}"
if [[ ! "${magisk_preinit}" =~ ^[A-Za-z0-9._-]+$ ]]; then
echo "Error: rooted profiles require a canonical Magisk preinit device." >&2
return 1
Expand All @@ -194,6 +196,10 @@ function module_selection_fingerprint() {
echo "Error: rooted profiles require a canonical Magisk repository." >&2
return 1
fi
if [[ ! "${magisk_version}" =~ ^v[0-9]+([.][0-9A-Za-z_-]+)*$ ]]; then
echo "Error: rooted profiles require a canonical Magisk version tag." >&2
return 1
fi
fi

if [[ "${ADDITIONALS[FDROID_PRIVILEGED_EXTENSION]}" == 'true' ]]; then
Expand All @@ -214,6 +220,7 @@ function module_selection_fingerprint() {
SELECTION_ROOT="${ADDITIONALS[ROOT]}"
SELECTION_MAGISK_PREINIT="${magisk_preinit}"
SELECTION_MAGISK_REPOSITORY="${magisk_repository}"
SELECTION_MAGISK_VERSION="${magisk_version}"
SELECTION_DEBUG="${ADDITIONALS[DEBUG]}"
SELECTION_COMPATIBLE_SEPOLICY="${ADDITIONALS[MAS_COMPATIBLE_SEPOLICY]}"
SELECTION_CLEAR_VBMETA_FLAGS="${ROM_PROFILE[CLEAR_VBMETA_FLAGS]}"
Expand Down
19 changes: 19 additions & 0 deletions tests/provider_acquisition_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ reset_acquisition_fixture() {
ADDITIONALS_MAS_COMPATIBLE_SEPOLICY=""
}

test_pinned_magisk_version_skips_tag_discovery() (
reset_acquisition_fixture
ROM_FAMILY="grapheneos"
DEVICE_NAME="shiba"
VERSION[MAGISK]="v30.7"

git() { fail "pinned Magisk version unexpectedly queried git tags"; }
curl() {
local url="${!#}"
[[ "${url}" == "https://releases.grapheneos.org/shiba-stable" ]] ||
fail "unexpected GrapheneOS metadata URL: ${url}"
printf '%s\n' '2026071700 1784260800'
}

get_latest_version >/dev/null
assert_equals "v30.7" "${VERSION[MAGISK]}" "pinned Magisk version"
)

test_grapheneos_text_metadata() (
reset_acquisition_fixture
ROM_FAMILY="grapheneos"
Expand Down Expand Up @@ -200,6 +218,7 @@ test_unsafe_lineageos_metadata_fails_closed() (
fi
)

test_pinned_magisk_version_skips_tag_discovery
test_grapheneos_text_metadata
test_lineageos_v2_metadata
test_lineageos_digest_verification
Expand Down
14 changes: 11 additions & 3 deletions tests/rom_contract_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ test_profiles_are_stable() {
https://download.lineageos.org/api/v2
}

test_magisk_repository_default() (
test_magisk_compatibility_defaults() (
load_contract
assert_equals "topjohnwu/Magisk" "${MAGISK[REPOSITORY]}" \
"default Magisk repository"
assert_equals "v30.7" "${VERSION[MAGISK]}" \
"default Magisk compatibility version"
)

test_unknown_rom_fails_closed() (
Expand Down Expand Up @@ -132,7 +134,7 @@ fingerprint() {
test_selection_fingerprint() (
local baseline repeated afsr_on afsr_off root_changed rom_changed module_changed
local boot_changed second_boot_changed fingerprint_input
local rootless_repo_changed rooted_repo_changed
local rootless_repo_changed rooted_repo_changed rooted_version_changed

load_contract
set_selection_fixture
Expand Down Expand Up @@ -180,6 +182,12 @@ test_selection_fingerprint() (
fail "Magisk repository selection did not change the rooted fingerprint"
MAGISK[REPOSITORY]="topjohnwu/Magisk"

VERSION[MAGISK]="v30.6"
rooted_version_changed="$(fingerprint)"
[[ "${rooted_version_changed}" != "${root_changed}" ]] ||
fail "Magisk version selection did not change the rooted fingerprint"
VERSION[MAGISK]="v30.7"

MAGISK[PREINIT]="sda47"
local preinit_changed
preinit_changed="$(fingerprint)"
Expand Down Expand Up @@ -282,7 +290,7 @@ test_output_policy() (
)

test_profiles_are_stable
test_magisk_repository_default
test_magisk_compatibility_defaults
test_unknown_rom_fails_closed
test_invalid_compatible_sepolicy_fails_closed
test_selection_fingerprint
Expand Down
1 change: 1 addition & 0 deletions tests/upstream_integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ grep -Fq 'VERSION[CUSTOTA]="${VERSION[CUSTOTA]:-6.5}"' src/declarations.sh || fa
grep -Fq 'VERSION[AVBROOT_SETUP]="634e6185cf70ea3ec9229ae957ddf2304f52e9e8"' src/declarations.sh || fail "maintained helper pin is unexpected"
grep -Fq '"version": "2.0.0"' locks/executable-tools-v1.json || fail "AFSR 2.0 lock entry is missing"
grep -Fq "topjohnwu/Magisk" src/config_schema.sh || fail "official Magisk default is missing"
grep -Fq "VERSION array MAGISK MAGISK_VERSION v30.7" src/config_schema.sh || fail "Magisk v30.7 compatibility pin is missing"
if grep -Fq "pixincreate/Magisk" src/config_schema.sh; then
fail "pixincreate Magisk must remain opt-in rather than the default"
fi
Expand Down
Loading