ztOS / tools

ztConflo

ztConflo builds Arch Linux ARM with the ztMETRO shell for a phone, on a kernel and boot setup that already works for it. It looks in the ztOS device registry first, and falls back to postmarketOS for phones nobody has ported ztOS to yet.

It reuses what already boots: postmarketOS's kernels, firmware, initramfs and boot settings. It never ports new hardware. Every build is pinned to exact commits and checksums, and every fallback build leaves a draft registry entry behind, so the registry grows from real boots.

Screenshots

The terminal UI, on a real pmaports checkout.

Getting started

From a fresh clone to a dry run.

ztConflo runs on an x86_64 Arch-based computer: Arch, CachyOS, EndeavourOS or Manjaro. Python 3.12 or later is all it needs to start.

git clone https://github.com/Zerone-Laboratories/ztconflo
cd ztconflo
python -m ztconflo            # opens the terminal UI

The first run

The first time it starts, ztConflo checks the computer for what a build needs and installs anything missing with a single sudo pacman -S --needed. It asks for your password once. Every tool comes from Arch's own repositories:

PackageWhat it is for
pmbootstrapBuilds the kernel side from postmarketOS
arch-install-scriptspacstrap and arch-chroot for the root filesystem
android-toolsmkbootimg, img2simg and fastboot
e2fsprogsmke2fs for rootfs.img
qemu-user-static-binfmtRuns aarch64 programs on the x86_64 host
heimdallFlashing Samsung phones
rsync, gitCopying sources and fetching pmaports
python-textualThe terminal UI

Run ztconflo setup to check or install again later. On a system that isn't Arch-based, it prints the package list instead of installing.

A first look

ztconflo candidates                         # phones you could build for
ztconflo lookup oneplus-enchilada           # where one comes from, and why
ztconflo build oneplus-enchilada --dry-run  # every command, nothing run

The terminal UI

What plain ztconflo opens.

The TUI is built with Textual and drawn in ztOS's own Metro look: black, one blue accent, lowercase headings. It runs the same code as the command line and only shows it.

Welcome
A greeting and a check of this computer: each tool found, missing, or only needed for some phones.
Pick a phone
A fuzzy-searchable table: type roughly what you remember, arrow keys to choose, enter to open. Registry phones first, then eligible postmarketOS phones with their chip and GPU driver.
A phone's page
Where it comes from, its chip, kernel and flash method, and dry run, build, flash and checklist buttons.
Build
The steps ticked off as they run, with every command's output streamed live below.
Flash
The partitions it will overwrite, and a box where you type the codename before anything is written.
Checklist
The hardware checks, with the manual questions asked as yes or no dialogs.

Keys: p pick a phone, i install what's missing, d dry run, b build, esc back, q quit. In the phone list, just type to search. The search is fuzzy: typos, missing letters, word order and abbreviations are fine (op6, pixl and xiomi mido all find their phone), and the best match comes first. ↑ and ↓ choose a phone and enter opens it. esc clears the search first, then goes back. A real build asks for sudo once, in the plain terminal, and keeps it alive until it finishes.

Commands

One program, eight subcommands.

CommandWhat it does
lookup <codename>Resolves the phone and prints where it came from, its status and eligibility. Builds nothing.
build <codename>Resolves, then runs the whole pipeline into out/<codename>/.
flash <codename>Shows the target partitions, asks for the codename, then runs flash.sh.
promote <codename>Runs the hardware checklist on a connected phone and opens a pull request with the result.
refresh <codename>Re-reads pmaports at a newer commit and shows a diff of [boot] and [source.pmos] before writing.
listLists registry entries, filtered with --status and --origin.
candidatesLists eligible postmarketOS phones that have no registry entry yet.
setupChecks this computer and installs what a build needs.

Every command takes --registry <path or git url>, --pmaports <path>, --jobs N and --dry-run, before or after the subcommand. A phone can be named by its short codename too: enchilada finds oneplus-enchilada when only one phone matches.

Finding a phone

The registry always wins.

Resolution: the ztOS registry first, then pmaports, then the eligibility filter codename in the registry? in pmaports? eligible? build from entry stop: not found build, write draft noyes yesnoyes
A failed eligibility check stops with the rule that rejected the phone (exit 2).

ztConflo reads pmaports as text. It never sources a deviceinfo or APKBUILD file, so a pmaports checkout is never run as code on the host. Boot settings come from deviceinfo, and the kernel command line from kernel-cmdline.conf. The kernel package comes from the device package's dependencies, or from its kernel-* subpackages on phones with more than one kernel.

Eligibility

A pmaports phone is eligible when every one of these holds. ztconflo lookup names the rule that failed, and whether the phone is a dead end.

RuleWhy
Category is main, community, testing or archiveddownstream ports use vendor kernels, so they are rejected. archived ports are no longer maintained, but many still use a mainline kernel: they are eligible, with a warning.
Architecture is aarch6432-bit armv7 phones are out of scope for version 1.
The kernel is mainlineA vendor kernel has no Mesa GPU driver, so ztMETRO can't draw on it.

Today's pmaports has 135 eligible phones and tablets that ztConflo can flash. 64 are maintained by postmarketOS, led by Snapdragon phones such as the OnePlus 6 and 6T, the Pixel 3a, the Poco F1 and the Fairphone 4. The other 71 are archived ports, such as the Redmi Note 4X, the Mi A1 and the OnePlus 3 and 5.

GPU acceleration

ztMETRO draws everything with OpenGL ES, so it needs a GPU that Mesa drives. ztConflo reads the system on a chip from the device tree name and looks it up in a small table: freedreno for Qualcomm Adreno, panfrost and panthor for Mali, lima for older Mali, etnaviv for Vivante.

A phone without a known GPU driver is still built and flashed, but ztConflo warns at every step: lookup, candidates, build, flash and in the TUI, which also asks before building.

The warning Unable to detect a Graphics Accelerator for the selected device, the Metro Shell will not start or work properly !

20 of the 135 eligible phones are in this group, the Pixel 6 among them.

Archived ports

postmarketOS archives a port when no one maintains or tests it any more. That says nothing about the kernel: 71 archived phones still use a mainline one, so ztConflo lists them last, builds them, and warns first, in the same places as the GPU warning:

The warning postmarketOS no longer maintains this phone's port: it is archived, so it may not build or boot, and it has had no recent testing.

Archived phones with a vendor kernel, such as the Galaxy S9 (Exynos), are still rejected. That is where the downstream kernel rule bites. The Snapdragon S9, samsung-starqltechn, is a maintained port and is eligible.

The device registry

Everything ztConflo knows about a phone.

The registry is its own repository, ztos-devices. Each phone is a folder, devices/<codename>/, holding device.toml and, if needed, an overlay/ of files copied into the root filesystem and a packages.txt of extra Arch packages. There is no per-phone code in ztConflo itself.

untestedBuilt, never booted. Every fallback build writes one.
bootsReaches ztMETRO, with display and touch working.
testedThe whole hardware checklist passes.

Status only moves forward, and only through ztconflo promote, which opens a pull request rather than pushing to the main branch.

[device]
codename = "oneplus-enchilada"
name = "OnePlus 6"
arch = "aarch64"
status = "untested"            # untested | boots | tested
origin = "pmos"                # pmos | native

[source.pmos]
pmaports_commit = "<sha>"
kernel_pkg = "linux-postmarketos-qcom-sdm845"
firmware_pkgs = ["firmware-oneplus-sdm845"]
category = "community"

[kernel]
mainline = true
artifacts_sha256 = "<one hash over kernel, modules and dtb>"
soc = "sdm845"
gpu_driver = "freedreno"

[boot]                         # frozen from deviceinfo
flash_method = "fastboot"
pagesize = 4096
dtb = "qcom/sdm845-oneplus-enchilada"
cmdline = "console=ttyMSM0,115200"
append_dtb = true
rootfs_target = "userdata"

The [boot] table is a frozen copy: later pmaports changes never alter it. Only ztconflo refresh changes it, after showing a diff. Fields that pmaports left to mkbootimg's defaults are listed under defaulted, so a person can check them.

The build pipeline

Eight steps into out/<codename>/.

The build runs on an x86_64 Arch host and cross-builds through QEMU user emulation. With --dry-run it prints every command and changes nothing.

  1. Kernel sidepmaports checked out at the entry's commit; pmbootstrap builds the kernel and firmware, and its install step makes the initramfs. The .apk files are unpacked with tar.
  2. VerifyOne sha256 over the kernel, modules and dtb. A mismatch stops a tested phone; an untested one has the hash written in.
  3. Root filesystempacstrap bootstraps Arch Linux ARM with its own keyring, then the kernel's modules and firmware are copied in.
  4. ztMETRO from sourceztMETRO, the SDK and the system apps are compiled from a ztOSMobile checkout inside the new root, under QEMU.
  5. ConfigureHostname ztos-<codename>, the default user, the phone's daemons, /etc/ztmetro/config, and ztMETRO as the greetd session.
  6. InitramfspostmarketOS's, reused unchanged. It is busybox and shell, so musl or glibc doesn't matter.
  7. Root imageAn ext4 image labelled pmOS_root, so that initramfs finds it, made sparse for fastboot.
  8. Boot imagemkbootimg with every [boot] field, the dtb appended when the phone needs it. Then flash.sh and build.json, which records every input hash and commit.

Flashing

Never automatic.

flash.sh is generated from the phone's flash method. ztconflo flash runs it only after showing every partition it will overwrite and having you type the codename, because it erases userdata.

MethodWhat it writes
fastbootboot.img to boot, rootfs.img to userdata, then reboots.
heimdall-bootimgboot.img and rootfs.img to the Samsung partitions named in [boot].
heimdall-isorecKernel, initramfs and root image as three separate partitions.

Hardware checklist

How a phone earns its status.

ztconflo promote connects to the phone over SSH on USB networking and runs ten checks. Automatic ones run on their own; for the rest, the person holding the phone says yes or no.

CheckHowNeeded for
Boots to ztMETROAutomatic: the shell is running and has shown Startboots
DisplayAutomatic: a connected output with a modeboots
TouchManual: three targets tapped on screenboots
GPU rendererAutomatic: the renderer isn't llvmpipetested
Wi-FiAutomatic: a scan finds networkstested
VibratorAutomatic and manual: a force-feedback device, and a buzz felttested
AudioManual: a test tone heardtested
BatteryAutomatic: a capacity reportedtested
Suspend and resumeManual: the screen and touch come backtested
ModemAutomatic: ModemManager sees one. Recorded, never blockingtested

Exit codes

For scripts that call ztConflo.

CodeMeaning
0Success
1Not found in the registry or pmaports
2Found but not eligible: a downstream (vendor) kernel, a downstream port or a 32-bit phone
3Build failed
4Hash mismatch on a tested entry
5Checklist failed during promote

Status and limits

Where ztConflo is today.

Early days The resolver, eligibility filter, registry and dry runs work against live pmaports, with 47 tests passing. No full build has been flashed to a phone yet, so no registry entry has reached boots.
  • The initramfs is postmarketOS's. That is why the root image must be labelled pmOS_root; mkinitcpio hooks replace it in a later version.
  • ztMETRO is compiled from source during each build, until a ztOS pacman repository exists. Phones with origin = "native" wait on that repository too.
  • Only fastboot and heimdall phones can be flashed. mtkclient, rkdeveloptool and the rest are not supported yet.
  • Building the root filesystem needs sudo, for pacstrap and arch-chroot.

What's next

  1. A first fastboot phone built and booted end to end, with its entry reaching boots.
  2. Heimdall phones, so older Samsungs work.
  3. mkinitcpio in place of the postmarketOS initramfs.
  4. Native kernel packages in a ztOS pacman repository for phones that reach tested.

See also