Releases: ferrumclaudepilgrim/claude-code-android
Release list
v2.9.4
A reliability release for the installer and the launcher, with a documentation pass alongside it.
Three things could go wrong on a real phone. On a device where the current Claude Code cannot start at all, install.sh ran its launch probe as a bare command under a shell setting that exits on any failure, so it stopped right there, before writing the launcher, the settings, or the PATH entry. The user got a raw Killed and no working claude command instead of the explanation the script was written to give. Android 8 and 10 are where that fired, and a healthy install passes straight through it, so it sat unseen. Separately, the daily update check staged every download to one shared file with no lock, so two ordinary launches inside the same few-minute window could delete each other's download, report a false "checksum mismatch", and leave the device a version behind until the next day. And every launch printed error: Cannot read directory "/": EACCES, because loading the DNS helper by its absolute path made the runtime walk up the folders above it until it reached the top of the filesystem, which Android does not let an app read. That last one was cosmetic throughout. Thanks to @Tanbeer191, who found both its cause and its fix.
Verified across four devices. On the Moto G7 Power (Android 10), where the binary genuinely cannot start, a fresh install classifies the crash, writes the launcher, settings, and PATH entry, and finishes with the explanation and a working way forward. On the Galaxy S7 (Android 8), the migration aborts on that same crash and leaves the existing pinned install running. On the Pixel 6 and Pixel 10 Pro (Android 17), the startup error is gone, name resolution returns a real address, and two simultaneous launches leave no false "checksum mismatch" and no partial download behind. The Pixel 6 also ran a full install from scratch.
Added
- A single-updater lock and a per-process staging path in the launcher (
install.shandmigrate.sh), so two launches inside the download window no longer race on a shared file. - Enforcement that
install.shandmigrate.shstay byte-identical where they must. Both scripts mark the shared sections withSYNC:BEGINandSYNC:END, andscripts/check-sync.shfails when they drift. Thanks to @doomcrew328. - An offer to install the working pinned release. When the installer finds that the current Claude Code cannot run on this device, it now offers to fetch and run
install-pinned.shfor you rather than leaving you to do it by hand, and it names the upstream issue so you can see why. Declining, or a failure in that optional step, leaves the completed install alone. A non-interactive install prints the command instead of prompting. - A mutation harness (
tests/mutation-check.sh) that applies eleven known regressions to temporary copies and requires each to be caught, so a suite that has stopped protecting the code cannot pass unnoticed. - Regression suites for the launcher's update path and the install-time probe classification (
tests/wrapper-update-tests.sh,tests/installer-smoke-tests.sh). Both fail against the previous release. - CI now runs shell syntax, ShellCheck, the sync check, all three suites, and the mutation harness on every push and pull request.
- A troubleshooting entry for the startup EACCES line, which was reported but undocumented.
- GitHub issue forms for bug reports and device reports, scoped to the failure classes this repo actually sees, so a report arrives with the device, Android version, install path, and exact error instead of free text.
- A "Related projects" section pointing to other ways to run Claude Code on Android, so a reader can pick the one that fits their device.
- A returning-user recovery callout near the top of the README for the "it worked yesterday, now it crashes or hangs" case.
- Path C (Android Virtualization Framework) device support in the README, kept clearly separate from the Termux paths.
Changed
- CONTRIBUTING is now a full contribution guide, including the automated checks to run before opening a pull request and the byte-identical rule the two installers follow.
- The "question or success story" link on the issue chooser now goes to Discussions. It pointed at a documentation page, which left anyone with a question nowhere to go, since blank issues are disabled.
- The security policy is rewritten for a solo public repo, with the current nested hooks schema in the permission-guard docs.
- The README and install guides are shorter and less dense: a single maintainer note at the top, jargon glossed in place, and Path B standardized to "proot-distro Ubuntu". The device table drops a column that repeated a date already shown in the row, which also stops it overflowing on a phone.
- The Path A disk figure now says what a device actually uses. The stated base covered a fresh install only, while the launcher keeps the previous binary for rollback, so real use is roughly double that once the first update lands.
- install.md's description of what the launcher does on each launch matches what it does, including the update lock and the difference between a crash and a probe that could not finish.
VERSION: 2.9.3 -> 2.9.4.
Fixed
install.shandmigrate.shno longer exit before finishing when the launch probe reports a crash or a timeout. On a device that cannot run the current release, the install now runs to completion, writes the launcher and settings, and ends with a message saying plainly that this release will not run here, where to go instead, and not to start claude. It exits successfully, because nothing about the install failed. Previously the script stopped at the probe, left no workingclaudecommand at all, and then reported failure with advice that could not help on that device.- The startup
Cannot read directory "/": EACCESline is gone. It was cosmetic, and the DNS fix it came from keeps working either way. - Launching Claude twice inside the download window no longer strands the update or reports a false "checksum mismatch".
- A launch probe that times out no longer permanently rejects a good build. The self-heal separates a definite crash, which is recorded and rolled back, from an inconclusive result, which is retried and never recorded as bad, and falls back to the best non-crashing build rather than refusing to launch. It decides which happened by how long the probe ran rather than by an exit code, because the exit status for a killed process is not the same across the tools different Android builds ship.
- A download interrupted part-way no longer sits on the device. The launcher clears abandoned staging files on its next update check, and both installers now remove the partial file when a download fails outright, which matters most on the nearly full devices where downloads fail in the first place.
install.shremoves the staged binary when patching fails, matching whatmigrate.shalready did. Thanks to @doomcrew328.- The pinned-install search recovery names the ripgrep symlink as the primary fix, scoped to pinned installs, and points at the correct upstream issue (anthropics/claude-code#13021).
- Corrected the phantom-process comment and the install-type label in the environment check script, the source attributions in the verification harness, and the One UI 8.5 timeline and Tensor G5 core topology in the AVF guide.
v2.9.3
A DNS reliability release. It works around a Bun bug that makes Claude Code hang on "checking connectivity" and fail with ETIMEOUT on Termux, and it makes search work out of the box on the pinned install for older devices.
Why the DNS fix is needed: Claude Code is a Bun binary, and on Linux Bun's DNS resolver defaults to c-ares, which reads /etc/resolv.conf. Termux has no /etc/resolv.conf, so c-ares falls back to a resolver at 127.0.0.1:53 where nothing is listening, and lookups fail. Because the binary is the linux-arm64 build, Bun treats the environment as Linux and takes that c-ares path rather than Android's system resolver. glibc's own getaddrinfo reads the resolver config Termux provides and works, which is why the failure is intermittent and shows up under load. Upstream: the c-ares behavior is oven-sh/bun#24970 (open); the proposed fix to default to getaddrinfo on Linux, oven-sh/bun#29231, was closed unmerged after CI regressions; the slow-fail that turns it into a hang and an ETIMEOUT is oven-sh/bun#32165 (open).
Verified on 2026-07-01 across four devices. On the Pixel 10 Pro and Pixel 6 (Android 17, native 2.1.197 with the preload), name resolution no longer touches the dead 127.0.0.1:53 loopback and a real authenticated turn reaches the server; search works. On the Moto G7 Power (Android 10) and Galaxy S7 (Android 8), the pinned 2.1.112 install runs and search works through the ripgrep symlink. The self-healing launcher from 2.9.2 was re-confirmed on both native devices: a planted crashing version is smoke-tested, recorded as bad, and rolled back with no user action.
Fixed
- Claude Code no longer hangs on "checking connectivity" or fails with ETIMEOUT because of Bun's DNS resolver.
install.shandmigrate.shinstall a small preload that points Bun's resolver at a working nameserver (8.8.8.8 / 8.8.4.4) before Claude starts, so name resolution no longer falls back to the dead127.0.0.1:53loopback. It touches nothing in the binary. Thanks to @Tanbeer191 for the root cause and the fix (issue #7, PR #9). - Search (Grep and Glob) works out of the box on the pinned install.
install-pinned.shinstalls system ripgrep and symlinks it onto the vendored path Claude Code looks for (vendor/ripgrep/arm64-android/rg), which upstream does not ship. Previously the pinned install left search failing withspawn ENOENTuntil the user ranscripts/fix-ripgrep.shby hand.
Changed
- The troubleshooting guide's search recovery now points at the ripgrep symlink, which is what works on the pinned 2.1.112 install. The
CLAUDE_CODE_USE_NATIVE_FILE_SEARCHenv var does not redirect the bundled search on 2.1.112 (verified on device); the symlink onto the vendored path is the fix. VERSION: 2.9.2 -> 2.9.3.
Added
- A troubleshooting entry for the DNS hang and ETIMEOUT, with the upstream Bun references.
v2.9.2
A resilience release for the auto-updating wrapper. It makes the wrapper refuse to run, and refuse to promote, a Claude Code binary that crashes on this device, and it rolls a device back to the last working version automatically. The trigger was Claude Code 2.1.181, which bundles an unreleased Bun 1.4 that segfaults at launch on Android (issue #6). It is the same class of failure as the Android 10 problem (issue #5): a newer syscall the native binary uses, blocked by Android's per-app seccomp filter.
Why this is needed: the native binary runs under that seccomp filter, which blocks syscalls it does not allowlist. On Android 10 the binary issues statx and dies with SIGSYS. On newer devices Bun 1.4 issues epoll_pwait2 and dies with SIGSEGV. In both cases the binary passes claude --version but crashes on full launch, so the previous wrapper auto-updated to it and then could not start. Upstream: oven-sh/bun#32489 and #32490 (unmerged at the time of this release).
Verified on 2026-06-18. On a device carrying both a working 2.1.176 and a crashing 2.1.181, the wrapper rolled back to 2.1.176 on launch with no user action, recorded the bad version so it is not re-downloaded, and a second launch ran the verified-good binary with no re-test. The launch probe was confirmed to trip both failure modes: the epoll_pwait2 crash on a current device and the statx crash on Android 10. The selection loop was exercised against empty, all-broken, stale-pointer, and corrupt-file states.
Fixed
- The wrapper no longer leaves a device stuck on a crashing auto-update. On launch it smoke-tests the version it is about to run (a short
--init-onlyprobe under a timeout). If that binary dies by a fatal signal, hangs, fails to execute, or prints a runtime crash banner, the wrapper records it as bad and falls back to the next-highest installed version that launches. A device that auto-updated to a broken release recovers on the next launch with no user action. install-pinned.shnow recovers a device that already has a crashing native install. Previously the pinnednpm installaborted withEEXISTbecause the native launcher file was still at$PREFIX/bin/claude, leaving the crash in place; the script now clears that file first so the pinned package can take over theclaudecommand. This is the fallback the troubleshooting guide points to when no working binary is left.
Added
- A smoke test before a downloaded update is promoted. The wrapper runs the same launch probe on the freshly downloaded, patched binary before swapping it in. A binary that crashes is discarded, the working binary is kept, and the bad version is recorded so it is not downloaded again.
- An install-time smoke test in
install.sh. After installing, it runs the launch probe; if the latest release crashes on this device it says so and points toinstall-pinned.shor proot, instead of leaving a crash on first launch. - Re-running
install.shon a device that already has the v2.9 launcher now refreshes the launcher in place (skipping the package install and binary download) instead of exiting with nothing to do. This is how an existing install receives the self-healing launcher: the daily auto-update refreshes the binary, not the launcher, so re-running the installer is what delivers a launcher change to a device that is already set up.
Changed
VERSION: 2.9.1 -> 2.9.2.
v2.9.1
A bug-fix release for the v2.9.0 Path A architecture. No architecture change. It makes install.sh and migrate.sh work on a device that already had claude, brings back claude's bundled grep, rg, and ugrep, and stops the package step from printing a warning on every call. It also adds an opt-in pinned installer for people who want the smallest setup.
Verified on 2026-05-30, all on Android 17. A fresh install.sh on a Google Pixel 6 ran end to end: claude launches, and grep, rg, and ugrep all work. On a Google Pixel 10 Pro, migrate.sh converted an official native install and install.sh installed in place over a leftover config, both keeping login and settings. On the Pixel 6, install-pinned.sh then migrate.sh took a pinned 2.1.112 install up to the 2.9.x wrapper. Older devices (Galaxy S7, Moto G7 Power) are not re-verified.
Fixed
- claude's bundled grep, rg, and ugrep work again.
install.shandmigrate.shno longer writeenv.LD_PRELOADinto~/.claude/settings.json. That preload leaked into claude's subprocess environment, where claude's own grep/rg/ugrep re-exec the native binary directly. The glibc loader then rejected the bionic preload, and every search failed with "invalid ELF header". There is a trade-off: without the preload, claude's subprocesses also lose termux-exec, so a directly-run#!/usr/bin/env ...script cannot find its interpreter (Android has no/usr/bin/env). Search working was the higher priority. Running an interpreter explicitly (bash file,python file,node file) and tools called by name are unaffected. install.shno longer refuses on a device that previously ran claude. It classifies the current state and either installs fresh, installs in place while preserving~/.claude(sessions, login, settings), routes a pinned npm install tomigrate.sh, or exits if the current wrapper is already present. The earlier "this installer is fresh-only" failure fired whenever~/.claudeexisted, even after claude had been removed.migrate.shrecognizes and converts an official native claude install (a versioned binary with a~/.local/binlauncher and no wrapper) in place, instead of refusing it as an install it did not create.migrate.shno longer ends with a false failure when it migrates a claude that was installed but never launched. The final session-count step ranlson a~/.claude/projectsdirectory that does not exist yet and aborted underset -e, after the migration had already finished. It now reports zero sessions and completes normally.
Changed
- The scripted package steps use
apt-getinstead ofpkg, so the install no longer prints apt's "does not have a stable CLI interface" warning on every call. - A Termux mirror is selected automatically only when none is set, so a brand-new Termux does not stall the unattended install on a mirror prompt. It never overrides a mirror you already chose.
install.sh(andinstall-pinned.sh) warn before installing if launched inside a running claude session, where Android's low-memory killer can end the install.VERSION: 2.9.0 -> 2.9.1.
Added
install-pinned.sh: an opt-in installer that pins Claude Code2.1.112(the last upstream version with a JS entry point) and locks the pin. No binary patching and no auto-updating wrapper. It is for people who want the smallest, simplest install and are content to stay pinned.install.shis still the default for current claude.
v2.9.0
Path A architectural switch from the pinned 2.1.112 npm install to a patched native linux-arm64 binary with an auto-updating wrapper. Also rolls up the Path C (Android Virtualization Framework, AVF) refresh for Android 17 and a Path B walk-through clarification originally drafted under [Unreleased].
The Path A change is empirically motivated. The v2.x install pinned @anthropic-ai/claude-code@2.1.112 (the last upstream version to ship a JS entry point) and defended that pin with chmod -R a-w on the install directory plus DISABLE_AUTOUPDATER=1 in shell env and ~/.claude/settings.json. Tracking upstream at anthropics/claude-code#50270. The v2.9.0 install runs the same linux-arm64 native binary that PC users run, by patching the binary's ELF interpreter via Termux's glibc-runner and patchelf-glibc. The approach was originally described by gtbuchanan in a comment on the upstream issue; this repo's install.sh adapts it with empirical verification, an auto-updating wrapper, and the two interactive prompts.
Verified end-to-end on Pixel 10 Pro (Android 17) on 2026-05-28. v2.9.0 retests on Pixel 6, Moto G7 Power, and Galaxy S7 are pending. Path B and Path C are independent of the Path A change and unaffected.
Path A: new architecture
install.shrewritten. Two yes/no questions up front (Q1: fresh Termux, choice of--force-confnewvs--force-confoldfor the upgrade pass. Q2: install recommended packages, the 17-package kit). After the questions, install runs unattended. End state:- Patched linux-arm64 claude binary at
~/.local/share/claude/versions/<version> - Auto-updating wrapper at
$PREFIX/bin/claude ~/.claude/settings.jsonwithautoUpdates:falseandenv.LD_PRELOADfor subprocess shebang resolution~/.local/bin/claudelauncher pointing at the wrapper, and~/.local/binappended to PATH in~/.bashrc. This is the native-install layout Claude Code expects; providing it silences the binary's "Native installation ... not in your PATH" startup notices at the source, rather than matching their text.
- Patched linux-arm64 claude binary at
- Wrapper behavior on each launch: query npm registry once per 24h for the latest version (or immediately with
claude --update-now), if newer download + verify checksum + patchelf + atomic swap into versions/, keep the previous version for rollback and remove older ones (N-1 retention). Self-heal the binary's ELF interpreter if anything outside the wrapper replaced it. UnsetLD_PRELOADbefore exec so the glibc binary doesn't crash on libtermux-exec's unversionedlibc.sodependency. - All-failure-modes (network, checksum mismatch, patchelf error): wrapper prints a one-line warning to stderr and falls through to launch the cached binary. The user's session never breaks because of an update problem.
Migration from a pinned v2.x install
migrate.shadded: upgrades an existing pinned install (the npm@anthropic-ai/claude-codepackage) to the v2.9.0 architecture without data loss. It backs up~/.claude,~/.claude.json, and~/.bashrc(with a generatedrestore.sh) before any change. It downloads, checksum-verifies, and patches the new binary before removing the old pin, so a mid-run failure leaves the old install intact. It installs the wrapper and merges~/.claude/settings.jsonin place, preserving existing keys and following a symlinked settings file rather than replacing it. It refuses to run while aclaudesession is active.install.shis now existing-install aware: it detects a pinned v2.x install and routes the user tomigrate.shrather than overwriting it, and detects an already-installed v2.9.0 wrapper and exits without changes.
Removed (Path A architectural)
@anthropic-ai/claude-code@2.1.112npm pin: replaced by the native binary path described above.chmod -R a-wlock on the install directory: no longer needed. The wrapper is the only mutator of the binary directory and the binary path is under~/.local/share/rather than$PREFIX/lib/node_modules/.DISABLE_AUTOUPDATER=1in~/.bashrc: the wrapper handles updates instead.- The April 18 upstream-regression recovery procedure: no longer applicable (the v2.9.0 install runs the linux-arm64 binary directly; there is no pin to clobber).
- The Path A
pkg install ripgrep+CLAUDE_CODE_USE_NATIVE_FILE_SEARCH=1workaround: the linux-arm64 binary reportsprocess.platform === 'linux'and shipsvendor/ripgrep/arm64-linux/rg, so the Grep / Glob tools work out of the box.scripts/fix-ripgrep.shis vestigial under the new architecture and is no longer referenced from user-facing docs.
Changed
README.mdPath A section rewritten aroundinstall.sh+claude. Path A footprint figures stated honestly: the linux-arm64 binary download is ~233 MB and the recommended packages add ~200 MB, so the base install is ~280 MB and a full install with the recommended kit is ~480 MB. Version badge bumped to 2.9.0. Last Verified bumped to 2026-05-29. Device Compatibility table calls out which rows are v2.9.0-verified and which are pending v2.9.0 retest.docs/install.mdPath A walkthrough rewritten. Path B and Path C sections retained largely as-is; the Path B verification footer remains 2026-05-16.docs/troubleshooting.md: April 18 regression entries removed; OAuth entries retained; the Grep/Glob ENOENT entry updated to note that the v2.9.0 install does not exhibit the original bug.VERSION: 2.8.1 -> 2.9.0.
Rolled up from [Unreleased] (pre-v2.9.0 work)
README.mdPath B Ubuntu setup walkthrough fleshed out: theproot-distro login ubuntustep is now followed by a note that the download can take a few minutes, theroot@localhost.prompt that signals completion, and the inside-Ubuntu install steps laid out as a separate block.README.mdPath C now ships an inline Quick Install matching the shape of Path A and Path B: how to check for support, where to toggle Linux development environment, what to expect in the Terminal app, and the install line for Claude Code inside the VM.docs/avf-guide.mdsubstantially rewritten for Android 17 reality. Memory size, Display resolution, and Keep awake are now documented as Terminal app Settings (gear icon) under Advanced, rather than file edits. The Recovery section (Reset to initial version, Remove backup data) is documented for the first time. Graphics Acceleration is described as a Pixel 10 Pro toggle, not a universal default. AOSP architecture reference linked.docs/avf-guide.mdScreen-Off Stability section rewritten around Settings > Advanced > Keep awake (preset timer up to one day) instead of the older ADB whitelist commands.docs/avf-guide.mdSecurity Defaults table now reflects that SSH is installed but not started by default; the exim4 row is gone since exim4 is not running by default either.docs/avf-guide.mdKnown Issues, Comparison, and Technical Details sections updated to match the above.docs/troubleshooting.mdPath C section synchronized with the avf-guide rewrite.docs/install.mdPath C entry under "Devices verified" extended to cover Pixel 6 and Pixel 10 Pro on Android 17.README.md"Device Compatibility" footnote now records Path C re-verification on Pixel 6 and Pixel 10 Pro running Android 17 on 2026-05-26.- TMPDIR / proot-as-Claude-requirement obsolete instructions removed across
docs/install.md,docs/troubleshooting.md,scripts/check-termux-env.sh,.claude/skills/termux-safe/SKILL.md,docs/constitution-template.md,docs/skills.md,.github/ISSUE_TEMPLATE/bug_report.md, andtests/verify-claims.sh. Empirical verdicts on individual claims intests/results/*.txtare unchanged from the 2026-05-16 test run; only the claim index shifted. README.mdPrerequisites moved above Quick Install. "Before You Start" section names the three things a first-time reader has to do before any command can succeed: confirm aarch64 / Android 8+, hold a Claude account, install Termux from F-Droid. Delegates F-Droid and Termux install steps to upstream maintainers rather than reinventing them.README.mddevice compatibility table: a "Test artifact" column was added so each row's claim of "verified" can be checked against an on-disk file. The four devices withtests/results/*.txtartifacts link to their result file. Samsung Galaxy S26 Ultra and Galaxy S23+ are marked doc-only (no currenttests/results/file): the S23+ row dates to the 2026-03-19 cycle that pre-dates the currentverify-claims.shartifact regime, and the S26 Ultra's v2.9.0 verification was a manualmigrate.shrun on 2026-05-29, which produces noverify-claims.shtranscript.docs/troubleshooting.mdEMFILE entry: "measured 32,768 on Android 16 / kernel 6.12" reattributed to the actual measurements intests/results/(Pixel 10 Pro Android 17 and Pixel 6 Android 13).docs/install.mdPrerequisites and Environment Reference: the "Android 14/15 use 5.10-6.6, Android 16 uses 6.12" kernel mapping replaced with the empirical per-Android-version kernel observations from the four test-artifact devices in this repo.
Notes
- Tests directory
tests/results/*.txtartifacts are from the v2.x pinned install. v2.9.0 retest transcripts will be added per device as they are produced. The Pixel 10 Pro v2.9.0 install was verified on 2026-05-28 manually; a deterministicverify-claims.shtranscript for the v2.9.0 architecture is a follow-up item. scripts/fix-ripgrep.shis left in the repo for users on a v2.x install who still need it. It is no longer referenced from user-facing docs and is not exercised by the v2.9.0 install path.scripts/check-termux-env.shupdated to auto-detect v2.9.0 vs v2.x install layouts via filesystem signals (presence of~/.local/share/claude/versions/plus a non-symlink wrapper at$PREFIX/bin/claudeindicates v2.9.0; prese...
v2.8.1
Hotfix. The v2.8.0 install was empirically broken: the chmod -R a-w lock on the install dir is necessary but not sufficient. Within minutes of starting a real claude session on a fresh v2.8.0 install (verified on Pixel 6 / Android 13), the in-process auto-updater chmod'd the dir writable and clobbered the 2.1.112 pin with 2.1.143 -- the broken android-arm64 version. claude then exited with Error: claude native binary not installed.
The v2.7.0 install kept two additional layers that v2.8.0 incorrectly removed as "belt-and-braces": DISABLE_AUTOUPDATER=1 in ~/.bashrc (every shell that launches claude inherits the env) and merged into ~/.claude/settings.json (inside a running claude session this is what stops the in-process updater from firing). Empirically those layers were load-bearing. v2.8.1 restores them. The chmod still matters as a final defense, but every layer is needed.
Fixed
install.shwritesexport DISABLE_AUTOUPDATER=1to~/.bashrc(idempotent: only if not already present; creates the file if missing)install.shmerges"env": {"DISABLE_AUTOUPDATER": "1"}into~/.claude/settings.jsonusing Node (preserves all existing keys including hooks; no jq dependency needed since Node is already installed)- v2.8.0 CHANGELOG framing on which layers were load-bearing was empirically wrong. v2.8.0 entry left as-is per CHANGELOG-history convention; this entry corrects the framing.
Added
docs/install.md"Recommended Common Packages" section: one-linerpkg installfor the 17 packages Claude Code typically reaches for that vanilla Termux +install.shdoes not provide (git,gh,jq,python,openssh,tree,proot,termux-api,proot-distro,make,clang,file,xxd,htop,bat,fzf,wget). What's already in vanilla Termux is listed too (rg,curl,unzip,tar,gzip,less,nano) so the user knows which holes are real.- README callout after Path A pointing users to the common-packages section -- a "vanilla Claude Code in an environment it is not used to" warning that also recommends injecting the list into
CLAUDE.mdor an environment hook to prevent recurring tool failures. docs/faq.md"Which packages should I install afterinstall.sh?" entry pointing to the install.md section.docs/troubleshooting.md"Claude can't find a tool (jq / git / python / ...)" entry covering the symptom-shaped path to the install.md section.
Notes
- A backup tag
backup/pre-v2.8.1was created onmainbefore this release for rollback - The bare-minimum
pkg installprinciple still holds:install.shstill only installsnodejs. The added layers are file edits in$HOME, not new system packages.
v2.8.0
Audit-driven cleanup release. Documentation tightened to match empirical device behavior. Test suite rewritten to produce deterministic PASS/FAIL/SKIP verdicts; verified end to end on four lab devices spanning Android 8, 10, 13, and 17 Beta. FAQ added for decision-shaped questions; troubleshooting refined for symptom-shaped entries with cross-links between the two. The Path A pin to 2.1.112 against upstream regression #50270 remains in effect. Path B continues to install upstream-latest claude inside proot-Ubuntu cleanly.
This change is a major shift to install.sh and the user-facing instructions around it. The goal is to bring the install down to the bare minimum needed for Claude Code to run successfully without persistent error inside Termux. It does not install packages or dependencies a standard Claude Code user would use -- common ones like git, GitHub CLI (gh), curl, ripgrep, termux-api, and jq are NOT installed. Install whatever you need yourself with pkg install <name>. After running install.sh, you type claude -- the same command you would on a PC. If you have an error with this update, please open an issue immediately. The previous v2.7.0 install.sh is preserved at v2.7.0/install.sh for comparison.
Added
docs/faq.mdcovering install gotchas (Play Protect on Termux APK install, mirror selection, theY/I/N/O/D/Zdpkg prompt), path-choice decisions, and the Android-version OAuth auto-open cutoff (verified on Android 8 / 10 / 13 / 17 Beta)scripts/directory with three deterministic bash diagnostics:check-termux-env.sh(full environment probe, 13 checks),fix-ripgrep.sh(recovery for the missing arm64-android ripgrep binary),config-validator.sh(audit a.claude/directory).gitattributespinning*.md,*.txt,*.yml,*.yaml,*.json,LICENSEto LF; image assets explicitly marked binarytests/results/device files written by the newverify-claims.shon Pixel 10 Pro (Android 17 Beta), Pixel 6 (Android 13), Moto G7 Power (Android 10), Galaxy S7 (Android 8)release-check.shat repo root: mechanical pre-release sanity checks (VERSION / CHANGELOG / README / SECURITY consistency, backup-tag presence, em-dash absence, current-tag absence). Exit 0 on PASS, non-zero on FAIL. Run before every release push
Changed
tests/verify-claims.shrewritten: 13 deterministic claims, each returning PASS, FAIL, or SKIP only. Methodology corrected for/tmpwritability tests, proot bind-mount roundtrip, and doc-existence checks. Output format intended for end-user contributionREADME.mdrestructured for scannability: Quick Install moved to the top of the page; verbose sections relocated todocs/; April-18 recovery banner kept above the fold with a deep linkdocs/install.md-- Path C added as a third column in the path-comparison table; April-18 recovery procedure consolidated here; OAuth section captures the empirical Android-version auto-open cutoff; Step 1 distinguishes MVP-required packages from the recommended general kitdocs/skills.md-- reflects current set: one Android-specific skill (termux-safe) and two workflow skills (minimum-viable,scope-framing); deterministic checks now live underscripts/docs/troubleshooting.md-- Hooks-section anchor corrected in the table of contents; OAuth entry gains the Android-version auto-open cutoff and a cross-link to the FAQdocs/faq.mdPath B size and native-binary detail (no Node required) clarifieddocs/avf-guide.mdAndroid 17 Beta status note refresheddocs/adb-wireless.mdgains a decision table for "When to use ADB vs Termux:API"docs/fingerprint-gate.mdadds a callout on case-pattern bypass vectors and safer-shape alternatives for biometric gate scriptsinstall.shheader documents scope and package set.github/CONTRIBUTING.mdexample-skill reference updated to point at a current skillassets/logo.jpgreplaced withassets/logo.png(transparent PNG)
Fixed
tests/verify-claims.shno longer returnsCANNOT TESTfor claims that the test methodology was failing to exercise; the rewrite uses appropriate proot-wrapped subshells and repo-rooted doc-path checks- README maintenance row in the path-comparison table no longer references the obsolete ripgrep symlink re-fix step; the env var
CLAUDE_CODE_USE_NATIVE_FILE_SEARCH=1(which users can set in their own~/.bashrc) makes the symlink unnecessary anyway - README docs-table skill-count drift removed
Removed
install.shno longer installsgit,curl,proot,ripgrep,termux-api, orjq; onlynodejsis installed. Users who want those packages canpkg installthem directly. The full kit is recommended indocs/install.mdStep 1 for general use.install.shno longer writes aclaude-androidalias, no longer exportsTMPDIR/CLAUDE_CODE_USE_NATIVE_FILE_SEARCH/DISABLE_AUTOUPDATERto~/.bashrc, no longer creates the ripgrep symlink, and no longer merges~/.claude/settings.json. The load-bearing protection is thechmod -R a-won the install directory; the rest were belt-and-braces in earlier versions.- The
claude-androidalias is no longer the documented launch command. Users run bareclaude(empirically verified across Android 8 / 10 / 13 / 17 Beta on 2026-05-16). If you used the alias in a prior version, remove it from~/.bashrcto avoid drift.
Notes
- A backup tag
backup/pre-v2.8.0was created onmainbefore this release for rollback - Disk-usage numbers in path-comparison tables are approximate ranges, not freshly measured; a follow-up release may refresh them with on-device
du -shdata - Upstream Termux issues referenced in docs (proot-distro #567, termux-packages #29319) are not re-verified in this release
v2.7.0 - upstream regression workaround
[2.7.0] - 2026-04-18
Emergency release pinning Path A (native Termux) install to @anthropic-ai/claude-code@2.1.112 — the last upstream version that ships the bundled cli.js JavaScript entry point. Versions 2.1.113 and later switched to a platform-native binary distribution that excludes android-arm64; on native Termux those versions install but claude exits immediately with Error: claude native binary not installed. Tracked upstream at anthropics/claude-code#50270. The in-process auto-updater also re-fetches latest on a timer inside running sessions, so the pin must be defended with DISABLE_AUTOUPDATER=1 plus a load-bearing chmod -R a-w on the install directory. Path B (proot-distro Ubuntu) is unaffected — process.platform === 'linux' matches the published linux-arm64 native binary. Path C (AVF) is unaffected for the same reason.
If you installed using v2.6.0 or earlier and your claude is now broken, re-run the new install.sh or follow the recovery steps in the README and docs/troubleshooting.md.
Fixed
- Path A install no longer ships a working CLI without intervention — upstream
@anthropic-ai/claude-code2.1.113 (April 17–18) and 2.1.114 dropped the bundledcli.jsentry and switchedbin/claude.exeto a platform-native binary stub. The android-arm64 native binary is not in the published optional-dependencies list. Postinstall on native Termux finds no matching platform package, leaves the 500-byte error stub in place, and everyclaudeinvocation printsError: claude native binary not installed. v2.6.0 of this guide shippednpm install -g @anthropic-ai/claude-codewith no version pin, so anyone running it after April 17–18 hit the broken state. v2.7.0 pins to2.1.112. - Auto-updater clobbers manual downgrade — Claude Code's in-process updater re-fetches
lateston a timer inside running sessions. A user who manually downgrades to 2.1.112 sees the pin silently re-overwritten with 2.1.114 within minutes. Fix:DISABLE_AUTOUPDATER=1in shell env and~/.claude/settings.json, pluschmod -R a-won the install dir. Thechmodis the load-bearing one — the env reduces but does not stop the attempt. cli-wrapper.cjsreferences in install.sh, README, anddocs/install.mdare now incorrect for 2.1.113+ and were used by v2.6.0 — the wrapper in 2.1.113+ is a strict platform dispatcher withprocess.exit(1)on unsupported platforms (including android), not a JS fallback. v2.7.0 removes those references and uses the bundledcli.jsentry exposed by theclaudesymlink in 2.1.112.
Changed
install.shrewritten — pinsCC_PIN="2.1.112", installs withDISABLE_AUTOUPDATER=1in env, applieschmod -R a-won$PREFIX/lib/node_modules/@anthropic-ai/claude-code/after install, addsDISABLE_AUTOUPDATER=1to~/.bashrc, mergesenv.DISABLE_AUTOUPDATERinto~/.claude/settings.jsonvia jq (preserving any existing config). Detects existing installs and recovers idempotently: chmod +w → reinstall pin → chmod -R a-w. Re-running the script is the supported recovery path.docs/install.md— Step 3 documents the version pin, the auto-updater env, the chmod, and links to upstream#50270. Step 4 launches viaproot -b $PREFIX/tmp:/tmp claude(the bundled cli.js entry exposed by theclaudesymlink in 2.1.112), not throughcli-wrapper.cjs. Step 5 alias updated. "Updating Claude Code" section explains the chmod dance required to upgrade past the pin. "Uninstalling" section adds the chmod -R u+w prerequisite.- README — Path A code blocks updated to the pinned form. Footer version bumped to 2.7.0. Version badge bumped to 2.7.0.
- Step 1 dependencies — added
jqto thepkg installline in install.sh anddocs/install.md(used for safely merging~/.claude/settings.jsonwithout clobbering existing config).
Added
- README top-of-page warning banner linking to recovery instructions for visitors who hit the regression.
- README "April 18 upstream regression — Path A recovery" section under Path A: explains the upstream change, the auto-updater clobber behavior, the recovery steps, and the upgrade path forward.
docs/troubleshooting.mdentry "Claude Code exits: 'native binary not installed'" — full diagnostic, both Path A (pin) and Path B (proot-ubuntu) fixes, and the upgrade-later path.- CHANGELOG note that Path A is now in a maintenance-only state on the upstream side. The strategic move toward Path B (proot-distro Ubuntu) as the primary recommended path is planned for a future release.
Notes
- Path B (proot-distro Ubuntu) and Path C (AVF) are unaffected by the upstream regression. Both run
process.platform === 'linux'which matches the publishedlinux-arm64native binary;npm install -g @anthropic-ai/claude-code(no pin) works normally inside the Ubuntu guest. - The pinned 2.1.112 install was verified end-to-end on Samsung Galaxy S26 Ultra (Android 16, kernel 6.12, Node v25.8.2) on 2026-04-18: install + chmod + auto-updater protection +
claude --versionreturns2.1.112 (Claude Code). - A backup tag
backup/pre-v2.7.0was created onmainbefore this release for rollback. - This guide does not redistribute Anthropic's claude-code package or vendor any binary content; pinning a published npm version and locking permissions on the install directory uses normal npm and POSIX mechanisms.
v2.6.0 — Documentation refresh + SSRF guard rewrite
[2.6.0] - 2026-04-18
Documentation refresh plus security hardening. Corrects stale claims, migrates docs URLs following Anthropic's domain move, updates Path A install instructions for upstream npm package restructure, corrects the hooks-on-Termux section to reflect current behavior, adds an audio-backend section covering /voice mode on vendor-broken devices, and adds an Android 17 Beta status note to the AVF guide. Rewrites examples/ssrf-guard.sh to close real bypasses in the previous regex-based implementation, adds a 47-case test harness, and adds .gitattributes so shell scripts stay LF on Windows checkouts (CRLF breaks them on Termux).
Fixed
- OWASP LLM Top 10 citation in
docs/agent-permissions.md(Excessive Agency is LLM08, not LLM06) - Anthropic pricing URL in README migrated from
anthropic.com/pricingtoclaude.com/pricing - Uncited "approximately 1-2%" prompt injection rate removed from
docs/security-model.md; replaced with "non-zero risk" framing ddbenchmark indocs/avf-guide.mdqualified to note that the read figure includes page cache and overstates real disk throughput- Claude Code version reference refreshed in
.claude/skills/doctor/SKILL.mdexample output - Hooks-on-Termux section in
docs/troubleshooting.mdcorrected. Earlier wording stated PreToolUse/PostToolUse hooks did not fire onprocess.platform === "android"; that is no longer accurate. Replaced with concrete debugging steps. Reference to upstream issue #16615 retained as historical context. examples/ssrf-guard.shrewritten to close real bypasses. The previous regex-based IPv4 check required four dotted octets, so short-form IPs (http://127.1/) and mixed-format IPs (decimal2130706433, hex0x7f000001) passed through to loopback. A prompt-injected WebFetch call using any of those forms would reach internal services. The rewrite offloads URL parsing to Node'snew URL()— the same RFC 3986 normalization Claude Code's WebFetch uses internally — then applies private-range checks on the normalized hostname. Covers short-form IPv4, decimal, hex, IPv4-mapped IPv6, cloud metadata aliases, case-sensitive hostnames, and malformed URLs. WebSearch bare queries (no://) now pass through instead of being blocked as missing-scheme. DNS-rebinding limitation remains and is documented in the header.- Subscription tier list in README and
docs/install.mdexpanded from "Pro or Max" to match upstream's documented list (Pro, Max, Team, Enterprise, or Console/API account). docs/install.mdsoftened "preferred installation method" to "recommended installation method" to match upstream wording exactly.
Changed
- All Anthropic Claude Code docs URLs migrated from
docs.anthropic.com/en/docs/claude-code/*tocode.claude.com/docs/en/*(Anthropic moved the docs domain). Affects README.md,.github/CONTRIBUTING.md,docs/agent-permissions.md,docs/skills.md. - Path A install method updated to route Claude Code through the
cli-wrapper.cjsJavaScript fallback. Anthropic restructured the@anthropic-ai/claude-codenpm package so the CLI is delivered via platform-specific optional native binaries; android-arm64 is not in that distribution list. The install completes but bareclaudeerrors with "claude native binary not installed." The same package shipscli-wrapper.cjs, a JavaScript fallback launcher; invoking it through Node works on android-arm64. The existing proot tmp wrapper is retained. Affects README,install.sh,docs/install.md. - README intro updated from "two ways" to "three ways" to reflect Path C/AVF documentation already present.
- Footer dates refreshed on
docs/install.md,docs/security-model.md,docs/avf-guide.md. - README "Last Verified" badge updated to
2026-04-18; Version badge updated to2.6.0.
Added
- New README section "Audio: /voice mode and the chain underneath" documenting the SoX → PulseAudio → backend → mic chain. Identifies vendor-device failures at the SLES backend layer (termux/termux-packages#28861, termux/termux-packages#27978, termux/termux-packages#27367, termux/termux-packages#26871) and points to termux/termux-packages#29319 (Oboe package + PulseAudio Oboe modules) as the fix path. Includes the user opt-in steps for after the PR lands. Caveat noted that Claude Code's own SoX detection on Termux is a separate concern.
- Android 17 Beta status note at the top of
docs/avf-guide.md. Existing Android 16 content retained as baseline reference; A17 Beta not re-verified end-to-end in this cycle. tests/ssrf-guard-tests.sh— 47-case test harness for the SSRF guard. Feeds JSON PreToolUse payloads covering short-form IPs, private ranges, cloud metadata, IPv6 forms, bad schemes, WebSearch queries, and edge cases. Used to prove the previous guard had real bypasses and that the rewrite closes them..gitattributespinning*.shto LF line endings. Windows checkouts would otherwise get CRLF, which breaks the scripts when they land on Termux (bash errors on$'\r'at end of lines).
Notes
- Path A cli-wrapper.cjs workaround verified on a current Termux install where Claude Code is actively running through it.
- Path B (proot-distro Ubuntu + official curl|bash installer) documentation not freshly re-verified end-to-end in this update; matches Anthropic's current upstream guidance for linux-arm64.
- Per-device verification dates in the README compatibility table left unchanged; individual devices not re-tested in this cycle.
- SSRF guard rewrite tested on-device with 47 test cases; all pass. Previous implementation failed 7. Test harness at
tests/ssrf-guard-tests.shverifies this.
v2.5.1
Added
- Security model document (
docs/security-model.md) -- centralized threat model covering Termux:API permission exposure, ADB capability escalation, the critical difference between app-level and shell-level access, threat scenarios in plain language, existing mitigations, and a minimal-risk setup checklist - README security notice -- visible before Prerequisites, links to security model
- Termux:API permission scoping guidance in install guide -- only grant the permissions your workflow requires
- Download-then-inspect alternative in install guide Path B -- inspect the install script before running it
- shellcheck CI workflow -- runs on push and PR for install.sh and verify-claims.sh
- Markdown link checker CI workflow -- checks for broken links across all documentation
- Executable hook examples --
examples/ssrf-guard.shandexamples/fingerprint-gate.shadapted from documentation into standalone runnable files
Changed
- ADB capabilities table -- added risk/exposure column so capabilities include security context
- SSRF guard and fingerprint gate docs -- reference examples/ files as canonical script location
- Skills documentation -- Android-specific and general-purpose workflow skills separated with descriptive intros
- README navigation bar -- added Security Model link
- Version badge updated to 2.5.1
Security
- Security audit found users could complete installation and grant full device access (SMS, contacts, GPS, camera, screen capture, input injection) without encountering a security warning. This release adds the security model, README warning, permission scoping guidance, and risk context to the ADB capabilities table.