docs: añadir guías paso a paso (01-05) y scripts faltantes
Cubre el flujo completo desde Termux limpio (F-Droid/GitHub) hasta personalizaciones de Claude Code, con base en la restauración de 2026-06-15 y el handoff curado del 2026-06-10. - bin/: añade mdns-publish.py y ssh-fallback - config/: añade smb.conf.example y aliases samba/mDNS en bashrc.snippet - docs/: nuevos 01-termux-fresh-install, 02-base-packages, 03-ssh-mdns-tailscale, 04-samba, 05-claude-code - install.sh: copia los 7 scripts e instala zeroconf vía pip - README.md: índice de docs y sanitización de identificadores personales Gotchas documentados: - Samba msg.lock 0755 tras tar restore - zeroconf no viaja en backup tar - fallos silenciosos por 2>/dev/null en .bashrc Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0d5d099dee
commit
f7ad71bd3c
19
README.md
19
README.md
|
|
@ -21,7 +21,7 @@ Claude Code v2.1.113+ switched to native binaries, breaking Termux/Android compa
|
||||||
## Quick Install
|
## Quick Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone <your-gitea-url>/andres/claude-code-termux.git
|
git clone <your-git-host>/<your-user>/claude-code-termux.git
|
||||||
cd claude-code-termux
|
cd claude-code-termux
|
||||||
bash install.sh
|
bash install.sh
|
||||||
```
|
```
|
||||||
|
|
@ -40,7 +40,9 @@ bash install.sh --dry-run
|
||||||
| `claude-check-env` | `~/.local/bin/` | Environment validator |
|
| `claude-check-env` | `~/.local/bin/` | Environment validator |
|
||||||
| `claude-update` | `~/.local/bin/` | Safe updater (ceiling-aware) |
|
| `claude-update` | `~/.local/bin/` | Safe updater (ceiling-aware) |
|
||||||
| `mdns-resolve` | `~/.local/bin/` | Pure Python mDNS resolver |
|
| `mdns-resolve` | `~/.local/bin/` | Pure Python mDNS resolver |
|
||||||
| `ssh-mdns-proxy` | `~/.local/bin/` | SSH ProxyCommand with mDNS+Tailscale |
|
| `mdns-publish.py` | `~/.local/bin/` | Publish this device as `movil.local` (needs `zeroconf`) |
|
||||||
|
| `ssh-mdns-proxy` | `~/.local/bin/` | SSH ProxyCommand: mDNS → Tailscale |
|
||||||
|
| `ssh-fallback` | `~/.local/bin/` | SSH ProxyCommand: Tailscale → LAN fallback |
|
||||||
| `settings.json` | `~/.claude/` | Disables auto-updater |
|
| `settings.json` | `~/.claude/` | Disables auto-updater |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
@ -71,6 +73,19 @@ export TAILSCALE_DOMAIN="your-tailnet.ts.net"
|
||||||
|
|
||||||
`claude-update` enforces this automatically. See `docs/TROUBLESHOOTING.md` for details.
|
`claude-update` enforces this automatically. See `docs/TROUBLESHOOTING.md` for details.
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
Step-by-step guides for each piece of the stack:
|
||||||
|
|
||||||
|
| # | Doc | Covers |
|
||||||
|
|---|-----|--------|
|
||||||
|
| 01 | [docs/01-termux-fresh-install.md](docs/01-termux-fresh-install.md) | Clean Termux install (F-Droid / GitHub Releases, not Play) |
|
||||||
|
| 02 | [docs/02-base-packages.md](docs/02-base-packages.md) | Required `pkg` / `pip` / `npm` packages |
|
||||||
|
| 03 | [docs/03-ssh-mdns-tailscale.md](docs/03-ssh-mdns-tailscale.md) | SSH, `.local` resolution, Tailscale |
|
||||||
|
| 04 | [docs/04-samba.md](docs/04-samba.md) | Samba server on port 4450 + `msg.lock` gotcha |
|
||||||
|
| 05 | [docs/05-claude-code.md](docs/05-claude-code.md) | Claude Code internals (version ceiling, `clauded`, settings) |
|
||||||
|
| — | [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) | Common errors and fixes |
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
See [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) for:
|
See [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) for:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/data/data/com.termux/files/usr/bin/python
|
||||||
|
"""Publica servicios via mDNS/Zeroconf: SSH + Samba"""
|
||||||
|
import socket
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
from zeroconf import ServiceInfo, Zeroconf, IPVersion
|
||||||
|
|
||||||
|
HOSTNAME = 'movil'
|
||||||
|
|
||||||
|
SERVICES = [
|
||||||
|
{
|
||||||
|
'type': '_ssh._tcp.local.',
|
||||||
|
'port': 8022,
|
||||||
|
'props': {'description': 'Termux SSH'},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': '_smb._tcp.local.',
|
||||||
|
'port': 4450,
|
||||||
|
'props': {'description': 'Termux Samba', 'path': '/'},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_local_ip():
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect(('8.8.8.8', 80))
|
||||||
|
ip = s.getsockname()[0]
|
||||||
|
s.close()
|
||||||
|
return ip
|
||||||
|
except Exception:
|
||||||
|
return '127.0.0.1'
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ip = get_local_ip()
|
||||||
|
addr = socket.inet_aton(ip)
|
||||||
|
zc = Zeroconf(ip_version=IPVersion.V4Only)
|
||||||
|
infos = []
|
||||||
|
|
||||||
|
for svc in SERVICES:
|
||||||
|
info = ServiceInfo(
|
||||||
|
svc['type'],
|
||||||
|
f'{HOSTNAME}.{svc["type"]}',
|
||||||
|
addresses=[addr],
|
||||||
|
port=svc['port'],
|
||||||
|
properties=svc['props'],
|
||||||
|
server=f'{HOSTNAME}.local.',
|
||||||
|
)
|
||||||
|
zc.register_service(info)
|
||||||
|
infos.append(info)
|
||||||
|
print(f'Publicando {HOSTNAME}.local {svc["type"]} -> {ip}:{svc["port"]}')
|
||||||
|
|
||||||
|
def cleanup(sig, frame):
|
||||||
|
print('Deteniendo mDNS...')
|
||||||
|
for info in infos:
|
||||||
|
zc.unregister_service(info)
|
||||||
|
zc.close()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, cleanup)
|
||||||
|
signal.signal(signal.SIGTERM, cleanup)
|
||||||
|
|
||||||
|
try:
|
||||||
|
signal.pause()
|
||||||
|
except AttributeError:
|
||||||
|
import time
|
||||||
|
while True:
|
||||||
|
time.sleep(3600)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/data/data/com.termux/files/usr/bin/bash
|
||||||
|
#
|
||||||
|
# ssh-fallback: Proxy para SSH con fallback automático
|
||||||
|
# Usado como ProxyCommand en ~/.ssh/config
|
||||||
|
#
|
||||||
|
# Uso: ssh-fallback <tailscale_host> <lan_ip> <port>
|
||||||
|
|
||||||
|
TAILSCALE_HOST="$1"
|
||||||
|
LAN_IP="$2"
|
||||||
|
PORT="${3:-22}"
|
||||||
|
|
||||||
|
# Intentar Tailscale primero (1 segundo timeout)
|
||||||
|
if ping -c 1 -W 1 "$TAILSCALE_HOST" &>/dev/null 2>&1; then
|
||||||
|
exec nc -w 5 "$TAILSCALE_HOST" "$PORT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback a IP LAN
|
||||||
|
if ping -c 1 -W 1 "$LAN_IP" &>/dev/null 2>&1; then
|
||||||
|
exec nc -w 5 "$LAN_IP" "$PORT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Último intento: Tailscale sin verificar
|
||||||
|
exec nc -w 10 "$TAILSCALE_HOST" "$PORT"
|
||||||
|
|
@ -13,3 +13,24 @@ export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
|
||||||
# Prevent Claude Code auto-updater (enforced at shell level too)
|
# Prevent Claude Code auto-updater (enforced at shell level too)
|
||||||
export DISABLE_AUTOUPDATER=1
|
export DISABLE_AUTOUPDATER=1
|
||||||
|
|
||||||
|
# === Optional: helper aliases for samba + mDNS services ===
|
||||||
|
# Only useful if you set up smbd and mdns-publish (see docs/04-samba.md, docs/03-ssh-mdns-tailscale.md).
|
||||||
|
|
||||||
|
alias smb-start='smbd -D -s ~/.config/samba/smb.conf 2>>~/.config/samba/samba-startup.log && echo "Samba started on port 4450"'
|
||||||
|
alias smb-stop='pkill smbd && echo "Samba stopped"'
|
||||||
|
alias smb-restart='pkill smbd 2>/dev/null; sleep 1; smbd -D -s ~/.config/samba/smb.conf 2>>~/.config/samba/samba-startup.log && echo "Samba restarted"'
|
||||||
|
alias smb-status='pgrep -a smbd && echo "Port: 4450" || echo "Samba is not running"'
|
||||||
|
alias smb-log='tail -50 ~/.config/samba/samba.log'
|
||||||
|
|
||||||
|
alias mdns-status='pgrep -af mdns-publish || echo "mDNS publisher is not running"'
|
||||||
|
alias mdns-restart='pkill -f mdns-publish; sleep 1; nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 & disown && echo "mDNS restarted"'
|
||||||
|
alias mdns-stop='pkill -f mdns-publish && echo "mDNS stopped"'
|
||||||
|
|
||||||
|
# === Optional: auto-start samba + mDNS on shell login ===
|
||||||
|
# Uncomment the block below if you want services up automatically.
|
||||||
|
# Failures are logged (NOT silenced) — silent failures after a tar restore are the worst class of bug.
|
||||||
|
#
|
||||||
|
# mkdir -p ~/.cache
|
||||||
|
# pgrep -x smbd >/dev/null 2>&1 || smbd -D -s ~/.config/samba/smb.conf 2>>~/.config/samba/samba-startup.log
|
||||||
|
# pgrep -f "mdns-publish" >/dev/null 2>&1 || nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 & disown
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Samba on Termux — port 4450 (445 requires root)
|
||||||
|
#
|
||||||
|
# Replace YOUR_USER with the Samba user you created via `smbpasswd -a YOUR_USER`.
|
||||||
|
# Replace HOST_NAME with whatever you want this device to advertise as.
|
||||||
|
# Connect from clients with: \\<ip>:4450 or smb://<ip>:4450/
|
||||||
|
|
||||||
|
[global]
|
||||||
|
workgroup = WORKGROUP
|
||||||
|
server string = Termux Mobile
|
||||||
|
netbios name = HOST_NAME
|
||||||
|
|
||||||
|
# Non-privileged port
|
||||||
|
smb ports = 4450
|
||||||
|
|
||||||
|
security = user
|
||||||
|
map to guest = never
|
||||||
|
passdb backend = tdbsam
|
||||||
|
|
||||||
|
# SMB2/3 only — Windows 11 and modern Linux/macOS prefer these
|
||||||
|
client min protocol = SMB2
|
||||||
|
server min protocol = SMB2
|
||||||
|
server max protocol = SMB3
|
||||||
|
client max protocol = SMB3
|
||||||
|
|
||||||
|
smb encrypt = desired
|
||||||
|
server signing = desired
|
||||||
|
client signing = desired
|
||||||
|
|
||||||
|
# Termux paths (these MUST exist before smbd starts)
|
||||||
|
private dir = /data/data/com.termux/files/home/.config/samba/private
|
||||||
|
lock directory = /data/data/com.termux/files/home/.config/samba/lock
|
||||||
|
state directory = /data/data/com.termux/files/home/.config/samba/state
|
||||||
|
cache directory = /data/data/com.termux/files/home/.config/samba/cache
|
||||||
|
pid directory = /data/data/com.termux/files/home/.config/samba
|
||||||
|
|
||||||
|
log file = /data/data/com.termux/files/home/.config/samba/samba.log
|
||||||
|
max log size = 500
|
||||||
|
log level = 1
|
||||||
|
|
||||||
|
socket options = TCP_NODELAY IPTOS_LOWDELAY
|
||||||
|
use sendfile = yes
|
||||||
|
aio read size = 16384
|
||||||
|
aio write size = 16384
|
||||||
|
|
||||||
|
unix charset = UTF-8
|
||||||
|
dos charset = CP850
|
||||||
|
|
||||||
|
server multi channel support = yes
|
||||||
|
mdns name = mdns
|
||||||
|
|
||||||
|
# Share: Android shared storage (DCIM, Downloads, etc.)
|
||||||
|
[Storage]
|
||||||
|
comment = Android shared storage
|
||||||
|
path = /storage/emulated/0
|
||||||
|
browseable = yes
|
||||||
|
writable = yes
|
||||||
|
valid users = YOUR_USER
|
||||||
|
create mask = 0644
|
||||||
|
directory mask = 0755
|
||||||
|
|
||||||
|
# Share: Termux $HOME (be careful, this exposes shell config too)
|
||||||
|
[Termux]
|
||||||
|
comment = Termux home
|
||||||
|
path = /data/data/com.termux/files/home
|
||||||
|
browseable = yes
|
||||||
|
writable = yes
|
||||||
|
valid users = YOUR_USER
|
||||||
|
create mask = 0644
|
||||||
|
directory mask = 0755
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
# 01 — Fresh Termux install (Android)
|
||||||
|
|
||||||
|
This guide covers a clean Termux install **without** Google Play, which is required for Termux:API / Termux:Boot compatibility.
|
||||||
|
|
||||||
|
## Why not Google Play
|
||||||
|
|
||||||
|
Google Play freezes Termux on an old version. Addons (Termux:API, Termux:Boot, Termux:Widget) require signature parity with the base app, so the Play version is incompatible with the F-Droid / GitHub-Release addons.
|
||||||
|
|
||||||
|
**Decision:** install everything from F-Droid or GitHub Releases (Termux team's official builds).
|
||||||
|
|
||||||
|
## Recommended source
|
||||||
|
|
||||||
|
Use one of:
|
||||||
|
- [F-Droid](https://f-droid.org/en/packages/com.termux/) — auto-updates, signed by F-Droid
|
||||||
|
- [GitHub Releases](https://github.com/termux/termux-app/releases) — latest, signed by the Termux team
|
||||||
|
|
||||||
|
Both share the same signing key, so addons work across them. **Don't mix Play and F-Droid/GitHub.**
|
||||||
|
|
||||||
|
## APK list (download to the device beforehand)
|
||||||
|
|
||||||
|
| Package | Purpose | Required |
|
||||||
|
|---------|---------|----------|
|
||||||
|
| `termux-app_*.apk` | Base shell | yes |
|
||||||
|
| `termux-api-app_*.apk` | Sensors, camera, clipboard | recommended |
|
||||||
|
| `termux-boot-app_*.apk` | Auto-start on boot | optional |
|
||||||
|
| `termux-widget-app_*.apk` | Home-screen launchers | optional |
|
||||||
|
|
||||||
|
## First boot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Allow Termux access to shared storage (~/storage symlink)
|
||||||
|
termux-setup-storage
|
||||||
|
|
||||||
|
# 2. Prevent Android from killing the process when screen is off
|
||||||
|
termux-wake-lock
|
||||||
|
|
||||||
|
# 3. Update and install the minimum
|
||||||
|
pkg update -y
|
||||||
|
pkg install -y openssh nodejs git proot python
|
||||||
|
```
|
||||||
|
|
||||||
|
## SSH for remote access (optional but useful)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
passwd # set an SSH password
|
||||||
|
sshd # start the daemon on port 8022
|
||||||
|
whoami # note the u0_a*** username for remote login
|
||||||
|
```
|
||||||
|
|
||||||
|
From another machine:
|
||||||
|
```bash
|
||||||
|
ssh -p 8022 u0_a***@<device-ip>
|
||||||
|
```
|
||||||
|
|
||||||
|
If the device is on Tailscale, use the Tailscale IP instead of the LAN one. Termux itself can't run the Tailscale CLI; use the Android Tailscale app and the device will appear in your tailnet automatically.
|
||||||
|
|
||||||
|
## What's next
|
||||||
|
|
||||||
|
Continue with `02-base-packages.md` to install the rest of the stack used by Claude Code and helper scripts.
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
# 02 — Base packages
|
||||||
|
|
||||||
|
Minimum set of packages needed by Claude Code and the helper scripts in this repo. Everything here is idempotent — re-running is safe.
|
||||||
|
|
||||||
|
## Required (Claude Code itself)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pkg install -y \
|
||||||
|
nodejs \
|
||||||
|
python \
|
||||||
|
git \
|
||||||
|
proot \
|
||||||
|
ripgrep
|
||||||
|
```
|
||||||
|
|
||||||
|
| Package | Why |
|
||||||
|
|---------|-----|
|
||||||
|
| `nodejs` | Claude Code runs on Node.js |
|
||||||
|
| `python` | `mdns-resolve`, `mdns-publish.py` |
|
||||||
|
| `git` | Cloning this repo, version control inside Claude |
|
||||||
|
| `proot` | Provides `termux-chroot` used by `clauded` (fixes `/tmp`) |
|
||||||
|
| `ripgrep` | Faster search inside Claude (the bundled one may not run on aarch64) |
|
||||||
|
|
||||||
|
## Recommended (SSH + mDNS)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pkg install -y openssh termux-api
|
||||||
|
pip install zeroconf
|
||||||
|
```
|
||||||
|
|
||||||
|
| Package | Why |
|
||||||
|
|---------|-----|
|
||||||
|
| `openssh` | SSH server (`sshd`) and client |
|
||||||
|
| `termux-api` | Backs the `termux-*` Android-bridge commands |
|
||||||
|
| `zeroconf` (pip) | Required by `mdns-publish.py`; **does not survive a `tar` backup** of `$HOME`, reinstall after restore |
|
||||||
|
|
||||||
|
## Optional (file sharing)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pkg install -y samba
|
||||||
|
```
|
||||||
|
|
||||||
|
See `04-samba.md` for the rest of the setup (custom port 4450, share definitions).
|
||||||
|
|
||||||
|
## Optional (image support in Claude)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g @img/sharp-wasm32 sharp --force
|
||||||
|
```
|
||||||
|
|
||||||
|
Enables image input (screenshots, diagrams). Sometimes flaky on aarch64; safe to skip.
|
||||||
|
|
||||||
|
## Verifying
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --version # >= 22 LTS recommended
|
||||||
|
python --version # 3.11+
|
||||||
|
proot --version
|
||||||
|
ssh -V
|
||||||
|
```
|
||||||
|
|
||||||
|
If any of these fail, re-run the relevant `pkg install`.
|
||||||
|
|
||||||
|
## What's next
|
||||||
|
|
||||||
|
`03-ssh-mdns-tailscale.md` — set up dynamic hostname resolution so you never hardcode IPs.
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# 03 — SSH, mDNS and Tailscale on Termux
|
||||||
|
|
||||||
|
Termux's Bionic libc does **not** resolve `.local` names. This repo ships small Python helpers that fix this without needing root or a custom resolver.
|
||||||
|
|
||||||
|
## Scripts involved
|
||||||
|
|
||||||
|
| Script | Role |
|
||||||
|
|--------|------|
|
||||||
|
| `mdns-resolve` | Resolve `<host>.local` via raw multicast UDP. Returns the IP or non-zero. |
|
||||||
|
| `mdns-publish.py` | Publish this device on the LAN as `movil.local` (or whatever you choose) using `zeroconf`. |
|
||||||
|
| `ssh-mdns-proxy` | SSH `ProxyCommand` that tries mDNS first, then Tailscale DNS. Used from `~/.ssh/config`. |
|
||||||
|
| `ssh-fallback` | Lower-level fallback: tries Tailscale, then LAN. Useful when Tailscale is flaky. |
|
||||||
|
|
||||||
|
## SSH config
|
||||||
|
|
||||||
|
Drop the contents of `config/ssh_config.example` into `~/.ssh/config` (or merge it).
|
||||||
|
|
||||||
|
The key entries:
|
||||||
|
|
||||||
|
```ssh-config
|
||||||
|
Host *.local
|
||||||
|
ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p
|
||||||
|
|
||||||
|
Host lenovo-ideapad
|
||||||
|
ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p
|
||||||
|
|
||||||
|
Host *.YOUR-TAILNET.ts.net
|
||||||
|
User YOUR_USER
|
||||||
|
```
|
||||||
|
|
||||||
|
Adjust the username and the tailnet domain. The proxy command will try mDNS first, then fall through to your Tailscale magic-DNS name (env `TAILSCALE_DOMAIN`).
|
||||||
|
|
||||||
|
## Publishing this device as `movil.local`
|
||||||
|
|
||||||
|
If you want the device to be reachable as `movil.local` from your LAN:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# One-shot foreground (to see errors):
|
||||||
|
python ~/.local/bin/mdns-publish.py
|
||||||
|
|
||||||
|
# Background (typical use, from ~/.bashrc):
|
||||||
|
nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 &
|
||||||
|
disown
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha:** `zeroconf` (the pip package) is **not** part of a `pkg`-managed install. If you restore Termux from a `tar` backup, `zeroconf` will be missing from `$PREFIX/lib/python*/site-packages` and the script will silently fail with `ModuleNotFoundError`. Re-run `pip install zeroconf` after every restore.
|
||||||
|
|
||||||
|
## Logging, not silencing
|
||||||
|
|
||||||
|
The `.bashrc` auto-start lines in this repo (`config/bashrc.snippet`) deliberately log to a file instead of `/dev/null`. Silent failures are the single most painful class of bug after a restore — keep the logs.
|
||||||
|
|
||||||
|
## Tailscale
|
||||||
|
|
||||||
|
Tailscale on Termux runs **as the Android app**, not as a CLI inside Termux. You can't `tailscale up` from a shell. The app provides magic DNS for `*.YOUR-TAILNET.ts.net`, which Bionic-libc resolves fine (it's just a regular DNS query).
|
||||||
|
|
||||||
|
When you list hosts in `~/.ssh/config`, prefer:
|
||||||
|
1. `<host>.local` for fast LAN access
|
||||||
|
2. `<host>.YOUR-TAILNET.ts.net` for off-LAN
|
||||||
|
|
||||||
|
Never hardcode IPs — they rotate.
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
# 04 — Samba server on Termux (port 4450)
|
||||||
|
|
||||||
|
Optional but useful: expose a few folders over SMB so other devices on the LAN can read/write files without ADB.
|
||||||
|
|
||||||
|
## Why port 4450
|
||||||
|
|
||||||
|
Ports below 1024 require root. Samba on Termux runs unprivileged, so we use a high port (`4450`) and configure clients to point at it explicitly.
|
||||||
|
|
||||||
|
```
|
||||||
|
\\<device-ip>:4450
|
||||||
|
```
|
||||||
|
|
||||||
|
Most file managers accept `smb://<ip>:4450/`.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pkg install -y samba
|
||||||
|
mkdir -p ~/.config/samba/lock ~/.config/samba/private
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Copy `config/smb.conf.example` to `~/.config/samba/smb.conf` and edit the share paths to match your device. Then add a Samba user:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
smbpasswd -a YOUR_USER
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates `~/.config/samba/private/passdb.tdb`.
|
||||||
|
|
||||||
|
## Start / stop
|
||||||
|
|
||||||
|
```bash
|
||||||
|
smbd -D -s ~/.config/samba/smb.conf 2>>~/.config/samba/samba-startup.log
|
||||||
|
pkill smbd
|
||||||
|
```
|
||||||
|
|
||||||
|
The aliases in `config/bashrc.snippet` (`smb-start`, `smb-stop`, `smb-restart`, `smb-status`, `smb-log`) wrap these.
|
||||||
|
|
||||||
|
## Critical gotcha — `msg.lock` permissions after restore
|
||||||
|
|
||||||
|
If you restore `~/.config/samba/` from a `tar` backup, the `lock/msg.lock` directory ends up with mode `0700` because `tar` preserves the source attributes. `smbd` requires `0755` on this directory and refuses to start with:
|
||||||
|
|
||||||
|
```
|
||||||
|
invalid permissions on directory '.../msg.lock': has 0700 should be 0755
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod 0755 ~/.config/samba/lock/msg.lock
|
||||||
|
```
|
||||||
|
|
||||||
|
This must be re-run after every restore from a backup that includes the samba lock dir.
|
||||||
|
|
||||||
|
## Connecting from another machine
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux
|
||||||
|
smbclient -L //<device-ip> -p 4450 -U YOUR_USER
|
||||||
|
mount -t cifs //<device-ip>/Home /mnt -o port=4450,username=YOUR_USER
|
||||||
|
|
||||||
|
# Windows: \\<device-ip>:4450 in Explorer
|
||||||
|
|
||||||
|
# macOS: Finder → Go → Connect to Server → smb://<device-ip>:4450
|
||||||
|
```
|
||||||
|
|
||||||
|
## Discovery via mDNS
|
||||||
|
|
||||||
|
If you also run `mdns-publish.py` (see `03-ssh-mdns-tailscale.md`), the device shows up as `movil.local` and SMB clients can use that name directly.
|
||||||
|
|
||||||
|
## Don't bind to `0.0.0.0` if you don't need to
|
||||||
|
|
||||||
|
The default `smb.conf` example binds to all interfaces. If the device roams across hostile WiFi networks, restrict `interfaces =` and `bind interfaces only = yes` to the LAN interface (`wlan0` on most devices).
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
# 05 — Claude Code on Termux
|
||||||
|
|
||||||
|
The reason this whole repo exists. Two non-obvious things break Claude Code on Termux:
|
||||||
|
|
||||||
|
1. **Version ceiling: v2.1.112.** Starting v2.1.113, the CLI ships as a native binary (SEA) for `linux-x64`, `linux-arm64-glibc`, `linux-arm64-musl`, `darwin-*`, `win32-*`. Termux's `process.platform === 'android'` is not in that list. Even `--force`-installing the `linux-arm64` package fails because the binary needs `/lib/ld-linux-aarch64.so.1` or `/lib/ld-musl-aarch64.so.1`, neither of which exist in Bionic libc.
|
||||||
|
2. **`/tmp` is hardcoded.** Claude Code writes to `/tmp/claude/...` for background tasks. Termux doesn't have a writable `/tmp`. `termux-chroot` exposes `/tmp -> /usr/tmp` to **all child processes** (which `proot -b` alone does not).
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
If you just want it working, run the top-level installer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise, the manual steps are:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Pin to the last compatible version
|
||||||
|
npm install -g @anthropic-ai/claude-code@2.1.112
|
||||||
|
|
||||||
|
# 2. Protect against the auto-updater (it can ignore env vars)
|
||||||
|
chmod -R a-w $PREFIX/lib/node_modules/@anthropic-ai/claude-code/
|
||||||
|
|
||||||
|
# 3. Make the wrapper available
|
||||||
|
cp bin/clauded ~/.local/bin/
|
||||||
|
chmod +x ~/.local/bin/clauded
|
||||||
|
```
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
`~/.claude/settings.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"env": { "DISABLE_AUTOUPDATER": "1" },
|
||||||
|
"skipDangerousModePermissionPrompt": true,
|
||||||
|
"effortLevel": "xhigh"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`DISABLE_AUTOUPDATER` is also exported from `bashrc.snippet`. Belt and suspenders — the auto-updater has been observed to ignore the env var in some releases, which is why `chmod -R a-w` is the real defense.
|
||||||
|
|
||||||
|
## How `clauded` works
|
||||||
|
|
||||||
|
```
|
||||||
|
clauded
|
||||||
|
└── termux-chroot
|
||||||
|
└── claude --dangerously-skip-permissions "$@"
|
||||||
|
```
|
||||||
|
|
||||||
|
`termux-chroot` simulates a Linux FHS where `/tmp`, `/etc`, `/usr` etc. point to their Termux equivalents. Crucially, every child process inherits this view, so any background task Claude spawns also sees a working `/tmp`.
|
||||||
|
|
||||||
|
## Updating safely
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude-update
|
||||||
|
```
|
||||||
|
|
||||||
|
This script:
|
||||||
|
- Checks npm for the latest version.
|
||||||
|
- Compares against the ceiling (`2.1.112`).
|
||||||
|
- Refuses to upgrade past the ceiling.
|
||||||
|
- Reapplies `chmod -R a-w` after a successful install.
|
||||||
|
|
||||||
|
If a future Anthropic release re-supports Android (or restores the pure-Node fallback), bump the ceiling in `claude-update` and the README.
|
||||||
|
|
||||||
|
## Authenticating
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clauded
|
||||||
|
# Inside Claude: /login
|
||||||
|
```
|
||||||
|
|
||||||
|
Subscription accounts and API keys both work. The OAuth flow opens a URL; long-press to copy it on Android and paste it in a real browser (Chromium, Firefox).
|
||||||
|
|
||||||
|
## When the wheels come off
|
||||||
|
|
||||||
|
See `TROUBLESHOOTING.md` for:
|
||||||
|
- `EACCES: /tmp/claude/...`
|
||||||
|
- `native binary not installed`
|
||||||
|
- `claude --version` works but `clauded` errors
|
||||||
|
- Sharp / image support
|
||||||
|
- `proot-distro` escape hatch (lets you run newer versions inside a real glibc rootfs)
|
||||||
14
install.sh
14
install.sh
|
|
@ -36,7 +36,7 @@ if [[ -z "$PREFIX" || ! -d "$PREFIX" ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TOTAL=8
|
TOTAL=9
|
||||||
echo -e "${GREEN}=== Claude Code for Termux - Installer ===${NC}"
|
echo -e "${GREEN}=== Claude Code for Termux - Installer ===${NC}"
|
||||||
echo "Version ceiling: v$CLAUDE_VERSION"
|
echo "Version ceiling: v$CLAUDE_VERSION"
|
||||||
$DRY_RUN && echo -e "${YELLOW}(dry-run mode - no changes will be made)${NC}"
|
$DRY_RUN && echo -e "${YELLOW}(dry-run mode - no changes will be made)${NC}"
|
||||||
|
|
@ -59,7 +59,7 @@ fi
|
||||||
# 4. Install scripts
|
# 4. Install scripts
|
||||||
step 4 "Installing scripts to $BIN_DIR..."
|
step 4 "Installing scripts to $BIN_DIR..."
|
||||||
run mkdir -p "$BIN_DIR"
|
run mkdir -p "$BIN_DIR"
|
||||||
for script in clauded claude-check-env claude-update mdns-resolve ssh-mdns-proxy; do
|
for script in clauded claude-check-env claude-update mdns-resolve mdns-publish.py ssh-mdns-proxy ssh-fallback; do
|
||||||
run cp "$SCRIPT_DIR/bin/$script" "$BIN_DIR/$script"
|
run cp "$SCRIPT_DIR/bin/$script" "$BIN_DIR/$script"
|
||||||
run chmod +x "$BIN_DIR/$script"
|
run chmod +x "$BIN_DIR/$script"
|
||||||
done
|
done
|
||||||
|
|
@ -91,8 +91,14 @@ run npm install -g @img/sharp-wasm32 sharp --force 2>/dev/null || {
|
||||||
echo -e " ${YELLOW}Sharp install failed (optional, images won't work)${NC}"
|
echo -e " ${YELLOW}Sharp install failed (optional, images won't work)${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 8. Done
|
# 8. Install zeroconf for mDNS publisher (needed by mdns-publish.py)
|
||||||
step 8 "Verifying installation..."
|
step 8 "Installing zeroconf (pip) for mDNS publishing..."
|
||||||
|
run pip install --quiet zeroconf 2>/dev/null || {
|
||||||
|
echo -e " ${YELLOW}zeroconf install failed (optional, mdns-publish won't work)${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 9. Done
|
||||||
|
step 9 "Verifying installation..."
|
||||||
if ! $DRY_RUN; then
|
if ! $DRY_RUN; then
|
||||||
echo ""
|
echo ""
|
||||||
"$BIN_DIR/claude-check-env"
|
"$BIN_DIR/claude-check-env"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue