Claude Code on Termux
Current Claude Code, patched to fit, pinned so it stays that way
stable / next channels · CLAUDE.md-aligned prompt patches · byte-preserving binary patcher
Claude Code ships as a bun-compiled glibc binary that hardcodes the
/lib/ld-linux-aarch64.so.1 interpreter — absent on Android's
bionic libc — and reads /etc/resolv.conf with a raw syscall that
Termux cannot satisfy. ccx fetches each release from Anthropic's
native channel, runs it through bun-on-termux's
userland exec, patches the binary without moving a byte, and keeps a
stable launcher that only changes when you promote.
# Two channels
next
~/.local/bin/claude-next- Runs
- The newest release from Anthropic's native channel, fetched and patched by `ccx update`.
- Patching
- Byte-preserving: same-length edits survive bun-vfs offsets; a backup is kept.
- Commands
ccx update [--channel latest|stable] [--pin X.Y.Z]
stable
~/.local/bin/claude- Runs
- Whatever you last promoted. It never changes underneath you: the launcher sets DISABLE_AUTOUPDATER=1.
- Patching
- `ccx promote` snapshots next → stable and archives the outgoing binary for `ccx rollback`.
- Commands
ccx promote · ccx rollback [--to X.Y.Z]
The same model runs Codex (cdx: isolated Bun install, DNS and
ripgrep fixes, no PRoot) and Google's Antigravity CLI (agx:
SHA-512 verified glibc binary launched through proot with its self-updater disabled). Every manager
keeps immutable per-version releases, previews prune, and
only schedules next updates when asked.
# What the patcher does
Applied by ccx update to every fetched binary. Each edit is opt-out via an env var (CCPATCH_KEEP_*); a .bak-prepatch copy is kept.
DNS resolver
Rewrite the literal /etc/resolv.conf → /sdcard/dns.conf (same length).
bun's bundled c-ares reads that absolute path with a raw syscall; Termux has no /etc/resolv.conf, so OAuth login and token refresh time out. Opt out: CCPATCH_KEEP_RESOLV=1.
Socket path
Launcher exports CLAUDE_CODE_TMPDIR=$PREFIX/tmp.
Termux /tmp is not writable; Claude Code's own tmpdir helper honours the variable, so no binary edit is needed.
Commit directive
Blank “NEVER commit changes unless the user explicitly asks.”
Conflicts with a conventional-commit-per-round workflow. Opt out: CCPATCH_KEEP_NO_COMMIT=1.
Comment directive
Blank “Default to writing no comments.”
Conflicts with a typed, explanatory-comment house style. Opt out: CCPATCH_KEEP_NO_COMMENTS=1.
# Patching a bun-compiled binary
# bun embeds the JS in a vfs blob keyed by internal byte offsets
length-changing edit ──▶ offsets shift ──▶ binary corrupts
(--version → bun 1.3.x)
length-preserving edit ──▶ offsets intact ──▶ binary still runs
(blank a string with equal-length spaces, or swap a path for one of the same length)
bun-vfs is not checksummed, so a same-length overwrite leaves every
downstream offset in place: the patched binary still reports its own version and runs full inference.
That's why ccx blanks the conflicting prompt
lines with spaces instead of deleting them, and why the resolver redirect swaps
/etc/resolv.conf for the exactly-as-long /sdcard/dns.conf.
# Quick start
# Requirements: Termux + bun-on-termux (~/.bun/bin/bun-termux) + pkg install glibc-runner nodejs
npm i -g termux-tools # ccx (Claude Code), cdx (Codex), agx (Antigravity)
export PATH="$HOME/.local/bin:$PATH"
ccx update # fetch + byte-patch the latest release into claude-next
claude-next --version # try it
ccx promote # make it the stable `claude` (archives the previous one)
ccx status # channels, upstream versions, PATH check
# Something broke upstream? Go back in one command:
ccx rollback
Updates come from downloads.claude.ai/claude-code-releases, not npm.
Each binary is ~250 MB; ccx prune reclaims old ones (keeps 2 by default).
ccx schedule --every 24 is opt-in and only refreshes next.