Skip to main content

Build verified Morphe-patched Android APKs and modules

Project description

Morphe Builder logo

Morphe Builder

Verified Morphe-patched Android builds from stock Google Play splits, configured in one TOML file.

Daily verified update Python Java uv


Morphe Builder downloads stock APK splits from Google Play, verifies their integrity and signing certificate, merges them, patches them with the Morphe CLI, signs the result, and packages non-root APKs plus deterministic Magisk/KernelSU modules.

Every app, architecture, patch source, and tool version is declared in a single TOML file, so a fork can build its own release matrix in CI without touching Python code. Nothing untrusted is published: downloads stay in a temporary directory until manifest coverage, hashes, package identity, version, ABI, and signer all pass, and each published set carries deterministic provenance.

Google Play delivery uses pinned goopdl releases from PyPI, with verified direct, archive, APKMirror, and Uptodown fallbacks.

Requirements

Requirement Version Notes
Python 3.11–3.13 3.11 minimum for standard-library tomllib
Java 21+ Required by Morphe CLI, APKEditor, and uber-apk-signer
uv latest Only for development installs from source

Windows and Linux are supported. See docs/security.md for platform limits.

Features

  • Verified downloads: Google manifest coverage, safe paths, sizes, SHA-256 hashes, package identity, version, split requirements, ABI, and signing certificate are all checked before publication.
  • Signer pinning: YouTube and YouTube Music require known Google signers; any app can pin its own expected-signer SHA-256.
  • Provider fallback: fixed order google-play → direct → archive → apkmirror → uptodown; integrity failures stay terminal instead of falling through to a later provider.
  • Config-driven matrix: apps, architectures, densities, patch sets, and module output live in TOML; no hardcoded app registry.
  • Per-app toolchains: each app can override Morphe CLI version, patch source, and patch version, resolved and cached per owner/repo@tag.
  • Pinned, hashed tooling: Morphe CLI, patches, APKEditor, and uber-apk-signer are resolved to exact tags with recorded asset hashes in toolchain-provenance.json.
  • Non-root APKs and modules: deterministic Magisk/KernelSU modules that install matching stock, survive reboots, and receive updates from the Magisk app.
  • Reproducible provenance: provenance.json, build-summary.json, release-manifest.json, and CHANGELOG.md record every source, version, and hash.
  • Atomic publication: existing output is never overwritten; a failed job removes its temporary set and publishes nothing.
  • Verified CI releases: daily update checks resolve upstream versions, skip unchanged builds, verify every artifact, and attach GitHub artifact attestations.
  • Secret-safe by design: no committed credentials, no cached tokens or APK payloads, redacted error output.

Installation

Morphe Builder depends on reviewed, exactly pinned goopdl releases from PyPI.

Global tool install:

uv tool install morphe-builder

Into an existing environment:

python -m pip install morphe-builder

Development install from source:

git clone https://github.com/Villoh/morphe-builder
cd morphe-builder
uv sync
uv run morphe-builder --help

Quick start

morphe-builder auth --arch arm64
morphe-builder tools
MORPHE_KEYSTORE_PASSWORD=secret morphe-builder build morphe.toml \
  --keystore /path/outside/repository/builder.p12 \
  --output build

auth stores Google Play credentials outside the repository, tools fetches and hashes the pinned Java toolchain, and build produces the full configured matrix from morphe.toml.

Google Play access

Set these two together to download directly from Google's servers through the goopdl fork. This is the recommended setup: it is the only reliable path from CI runners, and it avoids the shared anonymous dispenser entirely.

Variable Value
GOOPDL_ACCOUNT_EMAIL Google account email used for Play authentication.
GOOPDL_AAS_TOKEN Persistent AAS token for that account. Must start with aas_et/.

A value starting with g.a is a temporary bearer token, not an AAS token, and is rejected. Both variables are required together; a partial configuration fails instead of silently falling back. Treat the token as a password: use a dedicated account, keep it only in environment variables or GitHub Environment secrets, and never place it in TOML, argv, or committed files. Changing the account password invalidates it.

How to generate an AAS token with goopdl

goopdl aastoken prints a Google account AAS token. Credentials and tokens are used in memory only; no credentials.txt or token.txt file is created.

OAuth mode (recommended)

goopdl aastoken --oauth

Get the required one-time oauth_token:

  1. Open Google EmbeddedSetup.
  2. Sign in and select I agree.
  3. The page may stay frozen or loading forever. This is expected; the cookie is normally already available.
  4. Open browser developer tools (F12).
  5. In Chrome or Edge, open Application › Cookies › https://accounts.google.com.
  6. Copy the value of the oauth_token cookie. It starts with oauth2_4/.
  7. Run goopdl aastoken --oauth, enter the same email, then paste the token. Both inputs are handled interactively and the token is hidden.

The oauth_token is short-lived and single-use. The resulting AAS token is printed to the console.

Password or app-password mode

# Interactive: asks for email and hides password input
goopdl aastoken

# Arguments: convenient, but password remains in shell history
goopdl aastoken user@example.com password

Google commonly rejects both account passwords and 16-character app passwords with BadAuthentication. Keep 2-Step Verification enabled and use OAuth mode instead.

Without these variables, goopdl falls back to the public anonymous dispenser, which is frequently blocked from datacenter IP ranges. In that case the builder continues through the verified APKMirror and Uptodown fallbacks declared in your configuration.

Other environment variables

Variable Used by Purpose
MORPHE_KEYSTORE_PASSWORD build Signing keystore password. Required; never accepted in argv.
MORPHE_DISPENSER_URL auth, download Custom anonymous dispenser, used only when no Google account is configured.
MORPHE_MODULE_VERSION_CODE build Monotonic module versionCode. CI passes the workflow run number.
GITHUB_TOKEN tools, build, list-patches Authenticates GitHub API calls. Never sent to asset downloads.
HTTP_PROXY, HTTPS_PROXY build Proxy for provider downloads.
XDG_CACHE_HOME, LOCALAPPDATA all cached commands Override the default cache root.

Configuration

Everything a fork needs to change lives in morphe.toml. No Python code has to be touched to build a different set of apps.

The file has one [toolchain] table with the default Morphe CLI and patch sources, then one ordered [[apps]] entry per app:

[toolchain]
morphe-source = "MorpheApp/morphe-desktop"
morphe-version = "latest"
patches-source = "MorpheApp/morphe-patches"
patches-version = "latest"

[[apps]]
package = "com.google.android.youtube"
name = "YouTube"
slug = "youtube"
patched-package = "app.morphe.android.youtube"
version = "auto"
build-mode = "apk"
arch = "arm64-v8a"
density = "xxhdpi"
include-patches = ["GmsCore support", "SponsorBlock"]
exclude-patches = []

[apps.patch-options."SponsorBlock"]
toast-on-connection-error = true

[apps.google-play]
profile = "D2"
country = "US"

[apps.fallbacks]
apkmirror = ["https://www.apkmirror.com/apk/google-inc/youtube/"]

Only package and name are required. Everything else has a documented default:

Setting Required Default when omitted
package yes Validated Android package name.
name yes Display name used in release notes and modules.
slug no package with dots replaced by hyphens. Controls output, cache, and module names; must be unique.
patched-package no No package change is expected after patching. Set it when patches rename the package.
version no auto: highest version supported by every selected patch. Also accepts latest or an exact version name.
version-code no Resolved from the version name. Requires an explicit version; auto and latest are rejected with it.
build-mode no apk: non-root APK only. Use module or both for Magisk/KernelSU modules.
arch no arm64-v8a. Also arm-v7a, all, or both (two independent jobs).
density no The profile's only available density split is selected automatically.
include-patches no Empty: the patch bundle's default selection is used.
exclude-patches no Empty: nothing is removed from the selection.
exclusive-patches no Unset. When set, only these patches apply; cannot be combined with include/exclude.
patch-options no Patch defaults. Scalar values only, never shell-interpolated. Every option must belong to a selected patch.
expected-signer no Unpinned: a valid provider-served signer is accepted and logged as unpinned signer accepted. YouTube and YouTube Music always enforce built-in Google signer pins.
google-play.profile no goopdl picks a profile. Leave unset with arch = "both", since one profile cannot cover both ABIs.
google-play.country no goopdl account region.
google-play.proxy no Direct connection.
google-play.dispenser no MORPHE_DISPENSER_URL, or the built-in public dispenser.
fallbacks.* no Empty: Google Play is the only source, so a Play failure fails the job.
patches-source, patches-version, cli-source, morphe-version no The [toolchain] values.

The [toolchain] table itself is optional; omitting it uses MorpheApp/morphe-desktop and MorpheApp/morphe-patches at latest.

Parsing is strict: unknown keys, duplicate packages, duplicate slugs, unsupported values, and conflicting patch selections are all rejected before any download starts. Run morphe-builder list-patches morphe.toml to see which version each app will resolve to before committing a change.

For CI-built releases, declare at least one fallbacks entry per app. Anonymous Google Play access is unreliable from hosted runners, and a verified APKMirror or Uptodown source keeps the build working without credentials.

Full field reference, version-resolution order, and fallback URL rules: docs/configuration.md.

Usage

Command Purpose
auth Authenticate against Google Play and cache the session outside the repository.
download Download and verify one package's splits into an output directory.
tools Resolve, download, and hash the pinned Java toolchain.
list-patches Show patch-compatible versions for the configured apps.
build Run the full configured matrix: download, merge, patch, sign, verify, package.
verify Re-verify an existing split set against a package, architecture, and version.
clean Remove the disposable work cache only; trusted sets and tools are preserved.
morphe-builder download com.example.app --arch arm64 --output downloads
morphe-builder list-patches morphe.toml
morphe-builder verify trusted/splits com.google.android.youtube \
  --arch arm64 --version-name 21.04.223

All commands accept --json (progress on stderr, machine-readable result on stdout). --arch both runs independent ARM64 and ARMv7 jobs. Keystore passwords come from MORPHE_KEYSTORE_PASSWORD, never argv. Exit codes are stable per failure category.

Full reference, exit-code table, and cache semantics: docs/cli.md.

Documentation

Topic Document
CLI reference and exit codes docs/cli.md
TOML configuration and version resolution docs/configuration.md
Toolchain cache, pins, and checksums docs/toolchain.md
Google Play delivery and integrity checks docs/google-play-integrity.md
Provider fallback order docs/providers.md
Split selection and merge policy docs/merge.md
Signed APK build flow docs/build.md
Magisk/KernelSU module format docs/modules.md
Threat model and platform limits docs/security.md
CI, release verification, and publication docs/releases.md
Third-party licenses docs/THIRD_PARTY.md
Repository rules for contributors and agents docs/rules/README.md
Active roadmap docs/roadmap/ROADMAP.md
Completed milestones and evidence docs/roadmap/COMPLETED.md

License

Morphe Builder is released under the MIT License. Downloaded Morphe tooling remains separately licensed under GPL-3.0 and is not bundled in the Python distribution. See docs/THIRD_PARTY.md.

Credits

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

morphe_builder-0.2.0.tar.gz (85.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

morphe_builder-0.2.0-py3-none-any.whl (62.6 kB view details)

Uploaded Python 3

File details

Details for the file morphe_builder-0.2.0.tar.gz.

File metadata

  • Download URL: morphe_builder-0.2.0.tar.gz
  • Upload date:
  • Size: 85.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for morphe_builder-0.2.0.tar.gz
Algorithm Hash digest
SHA256 99cff41995dd807e287092b38e9e823d5419072dc9ca46f450ee6c666a46aa83
MD5 918d630313ce7dcf4df41080a3374b2d
BLAKE2b-256 f5ab009f88e8aec0bbbe0ea3a9f1489ce185acb471c27f700698771d3f0cf204

See more details on using hashes here.

Provenance

The following attestation bundles were made for morphe_builder-0.2.0.tar.gz:

Publisher: publish-pypi.yml on Villoh/morphe-builder

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file morphe_builder-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: morphe_builder-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 62.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for morphe_builder-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 beb3011fbdfe5f4a80cbc57fd83edc3622db782629f322f9f6512cc7f9b89dec
MD5 42727b4beb703c68f611dd520e5c2815
BLAKE2b-256 561105571c3a4e5c84b722b5631a81ddba3e2d629791d0e2953aca8cd0437efa

See more details on using hashes here.

Provenance

The following attestation bundles were made for morphe_builder-0.2.0-py3-none-any.whl:

Publisher: publish-pypi.yml on Villoh/morphe-builder

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page