Skip to content

About

Official OpenAI Codex CLI on the Yumi Smart Pi One (Allwinner H3, armv7l) — turnkey installer, 64-on-32 qemu emulation, OTA updates

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

41 Commits

Folders and files

Repository files navigation

Codex CLI for Yumi Smart Pi One (32-bit ARM)

The official OpenAI Codex CLI on Allwinner H3 / armv7l (Smart Pi One, Yumi SmartPad) — hardware the official installer rejects, because OpenAI ships codex for x86_64 and aarch64 only.

Two ways to get it running on the board, and the installer picks for you:

  • native — this project cross-compiles the Apache-2.0 source for armv7 and publishes the binary. No emulator. codex --version in 0.16 s on the pad.
  • emulated — the official aarch64 binary under 64-on-32 qemu, available the day upstream ships. Same command, 2.40 s.

Sign in with a ChatGPT Plus/Pro/Business account (device code, no browser on the board) or an API key. One command installs everything, and the same command is the updater.

Install

curl -fsSL https://raw.githubusercontent.com/Yumi-Lab/codex-cli-smartpi/main/install.sh | bash

Then sign in — the board never needs a browser:

codex login --device-auth        # open the URL on any machine, type the code
printenv OPENAI_API_KEY | codex login --with-api-key    # or use an API key

CODEX_VERSION=x.y.z pins a version, CODEX_FORCE=1 reinstalls anyway.

Usage

Command Purpose
codex Interactive TUI
codex exec "task" One-shot, non-interactive (scripts, the gateway, cron)
codex exec --dangerously-bypass-approvals-and-sandbox "task" Unattended: no prompt, no sandbox. The only flavour that runs without a human here — do not use --full-auto, it asks for a sandbox this kernel cannot give (see below). Run it in a directory you control.
codex-check-update OTA probe: {"cli","installed","engine","latest","update_available"}
codex-slot status [--json] How many agents are running, how many are queued, how much memory is left
codex-bin … The wrapper without the dispatcher's co-tenancy warning

Environment knobs, no reinstall needed:

Variable Effect
CODEX_ENGINE=native|emulated|auto Which payload to install (default auto), and which one to run when both are present
CODEX_CPUS=0,1 Thermal throttle — default is all 4 cores, both engines
CODEX_QEMU=7.2 Fall back to the vendored qemu 7.2 engine (emulated only)
CODEX_TB_SIZE=256 Translation-cache size in MiB (default 128, fork engine only)
CODEX_KEEP_EMULATION=1 Install the emulated payload next to the native one
CODEX_NATIVE_TARBALL=<path|url> Install a native build from a snapshot release, or one you built yourself
CODEX_MAX_PARALLEL=10 Hard ceiling on concurrent agents — extra ones queue (0 disables the limiter)
CODEX_MIN_FREE_MB=180 Memory floor: no new agent starts below it, whatever the ceiling says
CODEX_SLOT_TIMEOUT=0 Give up after n seconds of queuing instead of waiting forever (exit 75)

⚠️ Never run codex update or the upstream installer on this board — both would drop a 64-bit binary outside the wrapper. Re-run install.sh.

Which engine you get

install.sh asks the releases of this repo whether a native armv7 build exists for the version it resolved. If it does, it installs that and downloads no emulator at all; if there is none, it falls back to emulation; if GitHub does not answer, it stops instead of guessing. codex-check-update reports which engine is in use.

Since 0.147.0 the native build follows upstream: once a day a job builds the newest codex release for armv7 and publishes it here. Emulation covers the hours between an upstream release and its build, and any version that cannot be built for armv7 — up to 0.146.0 the CLI linked V8, which has no armv7 build. The whole story: docs/NATIVE-BUILD.md.

A build of upstream main can also be published as a snapshot prerelease. It reports version 0.0.0 (upstream stamps versions only when it tags) and install.sh never picks one up on its own — install it explicitly, the checksum beside it is verified:

curl -fsSL https://raw.githubusercontent.com/Yumi-Lab/codex-cli-smartpi/main/install.sh | \
  CODEX_NATIVE_TARBALL=<url of the snapshot .tar.gz> bash

What the emulated path does

  1. The official binary is static Rust (musl, aarch64), 269 MB — the shape that emulates well in user mode.
  2. QEMU dropped "64-bit guest on a 32-bit host" in version 10, so two engines are installed: the Yumi qemu fork 9.2.4 (default — restores correct single-copy-atomic 64-bit accesses on Cortex-A7, which a tokio-based Rust app needs, plus a sizeable translation cache) and qemu-aarch64-static 7.2 from Debian bookworm (vendored in vendor/, reachable with CODEX_QEMU=7.2).
  3. A wrapper runs it at low priority on all 4 cores. Watch thermals on long agentic runs: this SoC has reached ~102 °C under a sustained 4-core emulated load and frozen — CODEX_CPUS=0,1 throttles it. earlyoom is installed as the memory safety net, and the rule on 1 GB is one heavy CLI at a time.
  4. Optional, root only: an aarch64 binfmt_misc handler pointing at the fork engine, so any aarch64 helper codex re-execs is loaded instead of failing with ENOEXEC (echo -1 > /proc/sys/fs/binfmt_misc/qemu-aarch64-yumi removes it).

Bounded parallelism

A batch — the gateway, a cron, a loop — could otherwise start ten agents at once, and on 1 GB with SD-card swap memory exhaustion freezes the board before the OOM killer reacts. Every launch therefore goes through codex-slot: a hard ceiling of 10 concurrent agents (extra ones wait, they do not fail) plus a memory floor — no new agent starts while MemAvailable is under 180 MB. The count alone would be theatre; the floor is what makes it honest on a board that may only fit a handful of real turns.

There is no daemon: slots are flock locks held by the codex process itself. Nothing to start, nothing to crash, nothing to leak — a lock dies with its process, and a limiter that cannot write its slot directory steps aside rather than breaking the CLI.

Measured on the board, 12 agents launched simultaneously: peak exactly 10 running with 2 queued, MemAvailable never below 662 MB, zero swap, 44 °C, queue drained by itself. With the floor raised above what is free, jobs queue and then exit 75 (try again later) instead of taking the board down.

Sandboxing is off — read this once

~/.codex/config.toml is created, only if you have none, with:

sandbox_mode = "danger-full-access"

This is a real trade-off, not a shortcut. Codex's Linux sandbox cannot work on this board: the seccomp filter it builds exists for x86_64 and aarch64 only (upstream's seccomp.rs ends in unimplemented!() for anything else), and bwrap and the re-exec'd codex-linux-sandbox are 64-bit binaries. A native armv7 build hits the same wall.

What still asks before running a command. Since codex 0.149 there is no global "ask every time" setting any more (approval_policy = "untrusted", which this installer used to write, now stops codex from starting — re-running install.sh removes that line). With the sandbox off, codex runs commands without asking except those it flags as dangerous. To be asked before every command, mark the folder untrusted in the config (codex's own trust prompt only offers "trust" or "quit"):

[projects."/home/pi/work"]
trust_level = "untrusted"

On a board exposed to the network, run codex only in folders marked that way.

Updating (OTA)

  • Check: codex-check-update prints one JSON line — the probe the Yumi AI Gateway console polls for its update badge. --latest / --installed / --engine print a single value.
  • Update: re-run install.sh — that IS the updater. It exits fast when already newest, verifies the published sha256, and keeps the previous binary until the new one is in place. A network failure changes nothing: no fallback version, no unverified binary, no silent switch to the emulator — it stops and says so (GitHub is filtered in bursts from mainland China).
  • Automatic upstream tracking: a daily GitHub Actions cron compares upstream against the releases here and builds + publishes the armv7 binary when a new version lands. Nothing to bump anywhere; a version that cannot be built for armv7 is reported as a skip, not a failure.
  • Privileges: root/sudo for the first install only. Updates run as any user that owns /opt/codex, so the gateway service user updates without sudo.

Requirements

armv7l board, Debian/Armbian bookworm or newer, ≥ 1 GB RAM, curl and tar. Disk: ≈ 350 MB for the native payload (240 MB binary + 100 MB download), ≥ 700 MB for the emulated one (269 MB binary + 105 MB download + the previous version during an update) — the installer checks before touching anything. The native build is dynamically linked and needs libssl.so.3, libcrypto.so.3 and libgcc_s, present by default on Debian bookworm/trixie and DietPi.

Status

Validated on a real Smart Pi One (DietPi / Debian 13 trixie):

native armv7 — 0.162.1, 2026-10-12 emulated aarch64 — 0.146.0, 2026-08-04
Update from the published release 1346 s on a ~75 KB/s home line, checksum verified —
codex --version 0.16 s 2.40 s
codex exec, no credentials reaches wss://api.openai.com, expected 401 same
codex login --device-auth prints URL + one-time code same
On disk 240 MB 269 MB + 35 MB emulator
Temperature 36-37 °C 42-46 °C

Also green: the cross-build pipeline (~54 min on a runner, 100 MB tarball), the daily watcher, and CI on every push — unit tests, a staged install on x86_64, and a full install plus emulated smoke test inside a real armv7l userland.

Not yet exercised on the board: the TUI over a long session, an authenticated agent turn, thermals under sustained load. From mainland China the install does not complete today: measured from Hangzhou on 2026-10-12, github.com and raw.githubusercontent.com dropped requests for minutes at a time and the release CDN delivered 1.2 KB/s — the installer stops with an error instead of hanging. test/pad-smoke.sh user@pad collects the measurable part in one pass.

Full reasoning, measurements and dead ends: docs/METHODOLOGY.md.

Development / CI

No board needed — .github/workflows/ci.yml runs all of this on every push:

test/unit.sh                  # offline: metadata parsing, OTA probe, no hardcoded paths
test/install-armv7-docker.sh  # full install + emulated smoke in a real armv7l userland
CODEX_DRY_RUN=1 bash install.sh              # resolve everything, write nothing
CODEX_OPT=/tmp/opt CODEX_BINDIR=/tmp/bin …   # relocate the install, no root required
CODEX_ALLOW_ANY_ARCH=1 CODEX_SMOKE=0 …       # stage the payload on a non-armv7l host, skip the run

(The installer refuses a 64-bit host without CODEX_ALLOW_ANY_ARCH — that guard has its own CI job.)

The native binary is built by build/cross-armv7.sh, reproducible anywhere with Docker:

docker run --rm -v "$PWD:/repo" -w /repo rust:bookworm bash build/cross-armv7.sh

Sister projects (same board, other CLIs)

Licensing

  • Scripts in this repo: MIT (Yumi Lab)
  • vendor/qemu-aarch64-static: GPL-2.0, extracted as-is from the Debian bookworm package (vendor/README.md)
  • Codex itself is Apache-2.0 (openai/codex). The official binary is downloaded from OpenAI at install time, not redistributed here; the native binary is built from that same public source.

About

Official OpenAI Codex CLI on the Yumi Smart Pi One (Allwinner H3, armv7l) — turnkey installer, 64-on-32 qemu emulation, OTA updates

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages