CLI reference
morphe-builder is a single entry point with eight subcommands. aastoken, auth, and download handle Google Play access; tools prepares external tooling; build, list-patches, and verify read TOML configuration or existing artifacts; clean maintains the cache.
Every example below uses the installed entry point. From a source checkout, prefix each command with uv run.
Command overview
| Command | Reads config | Network | Purpose |
|---|---|---|---|
aastoken | no | Google Accounts | Generate a persistent AAS token through pinned goopdl |
auth | no | Google Play | Create a reusable Google Play session |
download | no | Google Play | Download and verify one package's splits |
tools | no | GitHub releases | Resolve, download, and hash the Java toolchain |
list-patches | yes | GitHub releases | Show patch-compatible versions per app |
build | yes | all providers | Full matrix: download, merge, patch, sign, package |
verify | no | none | Re-verify an existing split set on disk |
clean | no | none | Remove the disposable work cache |
Global conventions
Architectures
--arch accepts arm64 (arm64-v8a) or armv7 (armeabi-v7a). auth and download additionally accept both, which runs two fully independent jobs and, for download, writes them into arm64/ and armv7/ subdirectories of the output. verify requires one concrete architecture.
Output formats
tools, build, verify, list-patches, and clean accept --json. Progress events always go to stderr; the final machine-readable result goes to stdout, so morphe-builder build ... --json > result.json keeps the JSON clean.
Human-readable progress looks like:
[tools] preparing toolchains
[resolve] resolving compatible version package=com.google.android.youtube arch=arm64
[complete] build completed jobs=2 output=buildWith --json, each event becomes one JSON object per line on stderr.
Secrets and redaction
Passwords are never accepted as arguments. build reads the keystore password from MORPHE_KEYSTORE_PASSWORD only. Error messages and progress events are redacted: URL query strings and fragments are stripped, and authorization, cookie, token, password, and dispenser values are replaced with <redacted>.
Cache layout
--cache defaults to %LOCALAPPDATA%/morphe-builder on Windows, $XDG_CACHE_HOME/morphe-builder when that variable is set, otherwise ~/.cache/morphe-builder.
| Subdirectory | Contents | Removed by clean |
|---|---|---|
tools/ | Pinned Java tools and their provenance | no |
trusted/ | Verified stock split sets, re-verified before reuse | no |
work/ | Disposable intermediate build state | yes |
Environment variables
| Variable | Used by | Purpose |
|---|---|---|
GOOPDL_ACCOUNT_EMAIL | auth, download, build | Google account email; requires GOOPDL_AAS_TOKEN. |
GOOPDL_AAS_TOKEN | auth, download, build | Persistent account token starting with aas_et/; requires account email. |
GOOPDL_BROWSER | aastoken --browser | Browser executable when automatic detection fails. |
MORPHE_KEYSTORE_PASSWORD | build | Signing keystore password. Required. |
MORPHE_DISPENSER_URL | auth, download | Default anonymous dispenser when --dispenser is absent. |
MORPHE_MODULE_VERSION_CODE | build | Monotonic module versionCode; CI passes the 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. |
LOCALAPPDATA, XDG_CACHE_HOME | all cached commands | Resolve the default cache root. |
MORPHE_LIVE_TEST_PACKAGE | test suite | Opt in to the live Google Play test. |
aastoken
Generates a persistent Google account AAS token through pinned goopdl. Morphe Builder inherits interactive input and output without capturing the token.
morphe-builder aastoken --browser| Option | Meaning |
|---|---|
--browser | Open an isolated browser profile and capture the signed-in email plus one-time OAuth token. |
--oauth | Prompt for an OAuth token captured manually from Google EmbeddedSetup. |
Exactly one mode is required. Browser mode is recommended. See Google Play authentication for setup and credential-handling rules.
auth
Creates the Google Play session that later downloads reuse, and stores it outside the repository.
morphe-builder auth --arch arm64| Option | Default | Meaning |
|---|---|---|
--arch {arm64,armv7,both} | arm64 | Device profile architecture to authenticate for. |
--profile PROFILE | — | goopdl device profile name. |
--region CC | — | Two-letter region, uppercased before use. |
--dispenser URL | MORPHE_DISPENSER_URL | Anonymous token dispenser. Must be HTTPS with no credentials, query, or fragment. |
With GOOPDL_ACCOUNT_EMAIL and GOOPDL_AAS_TOKEN set, goopdl authenticates directly against the account and no dispenser is contacted. See Google Play authentication for token generation, local setup, CI secrets, and handling rules.
download
Downloads one package's split set, verifies it independently, and publishes it atomically with provenance.
morphe-builder download com.example.app --arch arm64 --output downloads
morphe-builder download com.example.app --arch both --version-name 21.04.223| Argument / option | Default | Meaning |
|---|---|---|
package | required | Android package name; rejected unless it matches the package-name grammar. |
--output PATH | downloads | Publication directory. Must not already exist. |
--arch {arm64,armv7,both} | arm64 | both writes arm64/ and armv7/ subdirectories. |
--version-name NAME | latest | Request an exact version name. |
--version-code CODE | latest | Request an exact version code. Mutually exclusive with --version-name. |
--profile, --region, --dispenser | — | Same as auth. |
The download itself lands in a temporary sibling directory of --output. Before anything is published, the builder verifies the Google integrity manifest against the files on disk, then independently re-derives package name, version, split requirements, ABI, density, and signing certificate from the APKs themselves. Only then is the staged set moved into place with provenance.json. A failure removes the temporary set and leaves no partial output; an existing --output is refused rather than overwritten.
tools
Resolves the external Java toolchain to exact release tags, downloads the assets, hashes them, and records provenance.
morphe-builder tools
morphe-builder tools \
--morphe-version v1.12.0 \
--patches-version v1.37.0 \
--apkeditor-version V1.4.9 \
--signer-version v1.3.0| Option | Default | Meaning |
|---|---|---|
--cache PATH | platform cache root | Toolchain cache location. |
--morphe-version TAG | latest | Morphe CLI release tag. |
--patches-version TAG | latest | Patch bundle release tag. |
--apkeditor-version TAG | pinned default | APKEditor release tag. |
--signer-version TAG | pinned default | uber-apk-signer release tag. |
--json | off | Emit the provenance path as JSON. |
Prints the path to toolchain-provenance.json, which records each resolved tag, pinned asset URL, size, and SHA-256. Requires Java 21+. Cache, checksum, and update policy: toolchain.md.
list-patches
Resolves each configured app's patch bundle and reports which app versions the selected patches support. Use it before build to see what a configuration change will actually produce.
morphe-builder list-patches morphe.toml
morphe-builder list-patches morphe.toml --json| Argument / option | Default | Meaning |
|---|---|---|
config | required | Path to the TOML configuration. |
--cache PATH | platform cache root | Toolchain cache used to resolve patches. |
--json | off | Full compatibility result per app instead of the summary line. |
Human output is one line per app:
com.google.android.youtube: 21.04.223 (147 patches)Include, exclude, and exclusive patch selections from the configuration are applied, so the reported version is the one build would choose. Version resolution rules: configuration.md.
build
Runs the complete configured matrix. This is the command CI uses.
MORPHE_KEYSTORE_PASSWORD=secret morphe-builder build morphe.toml \
--keystore /path/outside/repository/builder.p12 \
--output build| Argument / option | Default | Meaning |
|---|---|---|
config | required | Path to the TOML configuration. |
--keystore PATH | required | Signing keystore. Must be a regular file, not a symlink. |
--keystore-alias ALIAS | morphe-builder | Key alias inside the keystore. |
--output PATH | build | Publication directory. Must not already exist. |
--cache PATH | platform cache root | Tool and trusted-stock cache. |
--json | off | Machine-readable build summary on stdout. |
Per configured job, build:
- Prepares the app's pinned toolchain (one preparation per distinct source, reused across apps).
- Resolves a patch-compatible app version, intersecting selected patch versions.
- Downloads or reuses a verified stock split set, walking the provider fallback order.
- Selects the required splits and merges them with APKEditor.
- Signs the merged stock, then verifies package, version, architecture, ZIP structure, and certificate before patching.
- Patches the signed stock with the Morphe CLI, signs the result, and verifies it again.
- Optionally builds Magisk/KernelSU modules.
Everything is staged in a temporary directory next to --output and moved into place only after every job succeeds, so a partial matrix is never published. Successful output includes build-summary.json and CHANGELOG.md. Trusted original splits are never modified, and cached trusted sets are re-verified before reuse.
verify
Re-verifies a split set that already exists on disk, without any network access. Useful for auditing a cached or archived download.
morphe-builder verify trusted/splits com.google.android.youtube \
--arch arm64 --version-name 21.04.223| Argument / option | Default | Meaning |
|---|---|---|
directory | required | Directory containing the split APKs. |
package | required | Expected Android package name. |
--arch {arm64,armv7} | required | Expected architecture. |
--version-name NAME | — | Expected version name. |
--version-code CODE | — | Expected version code. |
--json | off | Machine-readable verification result. |
Reports package, version name and code, architecture, and file count on success. Any identity, version, architecture, split-requirement, or signature mismatch fails with exit code 5.
clean
Removes only the disposable work cache.
morphe-builder clean
morphe-builder clean --cache /custom/cache --json| Option | Default | Meaning |
|---|---|---|
--cache PATH | platform cache root | Cache root to clean. |
--json | off | List removed and preserved paths as JSON. |
Deletes <cache>/work and nothing else. <cache>/trusted, <cache>/tools, build outputs, keystores, and provenance files are preserved, and the resolved path is validated to be exactly <cache>/work before removal so a crafted --cache cannot delete an unrelated directory.
Exit codes
Exit codes are stable per failure category, so CI can branch on them.
| Code | Category | Typical cause |
|---|---|---|
| 0 | success | — |
| 2 | configuration | Invalid or unreadable TOML, rejected field. |
| 3 | compatibility / availability | No patch-compatible version, provider or requested version unavailable. |
| 4 | external toolchain | Missing Java, unresolvable release, checksum mismatch. |
| 5 | integrity / identity / signature | Manifest, hash, package, version, ABI, or signer mismatch. |
| 6 | merge | Split selection or APKEditor merge failure. |
| 7 | APK build / signing | Patching or signing failure, missing keystore or password. |
| 8 | module build | Module packaging failure. |
| 9 | overwrite refusal | --output already exists. |
Any exception outside these categories propagates as an unhandled traceback rather than being silently mapped to a success-adjacent code.
Provider availability versus integrity
The pinned goopdl fork exits with code 3 only when anonymous Google Play authentication is unavailable, for example an unreachable or rate-limited dispenser. The builder treats that as provider unavailability and advances to the configured fallbacks in providers.md.
Every other Google Play failure is terminal. The builder never assumes that an integrity failure is safe to bypass, and a later provider can never satisfy a request that already failed verification.
