Skip to content

fix: Termux/Android support — PRoot scroll, SIGSYS sandbox, build docs#509

Merged
kevincodex1 merged 4 commits into
Gitlawb:mainfrom
PatrickNoFilter:fix/tui-scroll-prroot
Jul 5, 2026
Merged

fix: Termux/Android support — PRoot scroll, SIGSYS sandbox, build docs#509
kevincodex1 merged 4 commits into
Gitlawb:mainfrom
PatrickNoFilter:fix/tui-scroll-prroot

Conversation

@PatrickNoFilter

@PatrickNoFilter PatrickNoFilter commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Three Termux/Android portability fixes for Zero:

1. PRoot scroll fix (TUI mouse mode)

Replace Termux env-var detection with isRunningUnderPRoot() via /proc/self/status TracerPid, cached with sync.OnceValue. Under PRoot, falls back to CellMotion instead of AllMotion — fixing broken touch-gesture scrolling while keeping wheel events, clicks, and drag working.

Includes non-controversial TUI scroll fixes:

  • Shift+Up/Shift+Down before plain KeyUp/KeyDown with permission/ask-user cursor and composer guard
  • Ctrl+U/Ctrl+D direction fix (Ctrl+U = up/-1, Ctrl+D = down/+1)
  • Proper modal guards so Ctrl+U/D doesn't intercept wizard/picker/spec-review keybindings
  • Unit tests for direction regression and composer guard

2. Sandbox faccessat2 avoidance

Replace exec.LookPath with a lookupExecutable helper that uses os.Stat instead of the faccessat2 syscall (syscall 439). This syscall is blocked by Samsung's seccomp filter on Android, causing SIGSYS termination. os.Stat uses the newfstatat/statx syscalls which are universally permitted.

3. Termux build documentation

Added docs/INSTALL.md section covering:

  • Building with GOOS=android to avoid SIGSYS (Go 1.26+ stdlib fix)
  • DNS resolution via proot bind-mount for /etc/resolv.conf
  • PRoot scroll behavior explained
  • Free-tier provider setup (OpenCode Zen)

Commits

1306ab5 fix(tui): TracerPid PRoot detection, Ctrl+U/D direction, composer guard
c8f899d fix(sandbox): avoid faccessat2 for Termux/Android portability
9696920 docs(install): add Termux/Android build and configuration guide

Notes

Summary by CodeRabbit

  • New Features

    • Added/extended transcript navigation shortcuts: Shift+Up/Down for line-by-line movement and Ctrl+U/Ctrl+D for half-page scrolling.
  • Bug Fixes

    • Prevented navigation shortcuts from interfering while composing text or when higher-priority dialogs are focused.
    • Improved mouse hover behavior in restricted terminal environments and enhanced helper availability detection on constrained systems.
  • Documentation

    • Added Termux (Android) installation guide, including DNS setup and an example provider configuration.
  • Tests

    • Added regression and unit tests for prompt cursor movement, composer guard behavior, and tracer detection logic.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: afc276c3-b75f-4675-b61f-a9b1e6cb2715

📥 Commits

Reviewing files that changed from the base of the PR and between 9696920 and 85c23cc.

📒 Files selected for processing (1)
  • internal/sandbox/manager.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/sandbox/manager.go

Walkthrough

This PR extends TUI keyboard navigation and PRoot detection, adds related tests and Termux docs, and replaces sandbox helper discovery with manual executable lookup.

Changes

TUI navigation and Termux support

Layer / File(s) Summary
Transcript shortcuts and prompt cursors
internal/tui/model.go
Shift+Up/Down and Ctrl+U/D now scroll the transcript or move permission/ask-user cursors, with guards for composer text, suggestions, and higher-priority modals.
PRoot mouse mode selection
internal/tui/mouse.go, internal/tui/model.go
parseTracerPid and isRunningUnderPRoot read /proc/self/status, and View() chooses mouse mode based on whether the process is running under PRoot.
Tests and Termux notes
internal/tui/mouse_test.go, internal/tui/permission_prompt_test.go, docs/INSTALL.md, .gitignore
Adds parser and keyboard regression tests, Termux install instructions, and ignore rules for Termux/Android build artifacts.

Sandbox executable discovery

Layer / File(s) Summary
Manual executable lookup
internal/sandbox/manager.go
Adds PATH-scanning executable lookup and uses it in platform backend selection when no custom lookup function is provided.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Possibly related PRs

  • Gitlawb/zero#126: Shares the TUI modal-routing and transcript-navigation paths that these keyboard shortcuts extend.
  • Gitlawb/zero#127: Also changes TUI input routing around ask-user and picker-style modal states.
  • Gitlawb/zero#232: Also changes permission prompt cursor behavior and related TUI input routing.

Suggested reviewers: gnanam1990, anandh8x

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main Termux/Android portability fixes, including PRoot scrolling, sandbox compatibility, and install docs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/tui/mouse.go (1)

11-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

No direct test coverage for isRunningUnderPRoot.

The function reads a hardcoded path (/proc/self/status), making it hard to unit-test the parsing logic (missing prefix, malformed TracerPid value, empty file, etc.) directly. Extracting the parsing into a small helper that accepts the file contents (or an io.Reader) as a parameter would let tests exercise the TracerPid-parsing branch without depending on sync.OnceValue's process-wide caching or actual /proc contents.

♻️ Suggested refactor for testability
+func parseTracerPid(data []byte) bool {
+	for _, line := range strings.Split(string(data), "\n") {
+		if strings.HasPrefix(line, "TracerPid:") {
+			pid := strings.TrimSpace(line[10:])
+			return pid != "0"
+		}
+	}
+	return false
+}
+
 var isRunningUnderPRoot = sync.OnceValue(func() bool {
 	data, err := os.ReadFile("/proc/self/status")
 	if err != nil {
 		return false
 	}
-	for _, line := range strings.Split(string(data), "\n") {
-		if strings.HasPrefix(line, "TracerPid:") {
-			pid := strings.TrimSpace(line[10:])
-			return pid != "0"
-		}
-	}
-	return false
+	return parseTracerPid(data)
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/mouse.go` around lines 11 - 23, The parsing logic inside
isRunningUnderPRoot is hard to test because it is tied to os.ReadFile and
sync.OnceValue caching. Extract the TracerPid parsing into a small helper that
accepts the file contents or an io.Reader, and keep isRunningUnderPRoot focused
on reading /proc/self/status and calling that helper. This will let tests cover
missing TracerPid, malformed values, and empty input directly without depending
on real /proc data.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/tui/mouse.go`:
- Around line 11-23: The current isRunningUnderPRoot check in mouse.go uses
TracerPid from /proc/self/status, which also matches debuggers and other ptrace
tracers. Either replace this with a more specific PRoot detection in the same
helper or, if the broader fallback is intended, add a short inline note
documenting that gdb, strace, and dlv will also trigger CellMotion behavior.

---

Nitpick comments:
In `@internal/tui/mouse.go`:
- Around line 11-23: The parsing logic inside isRunningUnderPRoot is hard to
test because it is tied to os.ReadFile and sync.OnceValue caching. Extract the
TracerPid parsing into a small helper that accepts the file contents or an
io.Reader, and keep isRunningUnderPRoot focused on reading /proc/self/status and
calling that helper. This will let tests cover missing TracerPid, malformed
values, and empty input directly without depending on real /proc data.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 02a88ebc-018f-4b19-8157-09485034df0e

📥 Commits

Reviewing files that changed from the base of the PR and between afb5010 and 1306ab5.

📒 Files selected for processing (3)
  • internal/tui/model.go
  • internal/tui/mouse.go
  • internal/tui/permission_prompt_test.go

Comment thread internal/tui/mouse.go Outdated
@anandh8x

anandh8x commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Thanks for coming back with the TracerPid approach — that's exactly the right technical fix for detecting PRoot specifically instead of Termux broadly, and the composer/modal guard ordering looks correct this time (traced it against the fallback dispatch chain). Good work on that part.

Before we go further, we want to understand the actual use case: what's the reason to run Zero inside PRoot at all, given Termux already works natively (#455)? PRoot exists to solve problems like missing packages, FHS path assumptions, or glibc requirements — none of which apply to Zero, since it's a single self-contained Go binary with no runtime dependency on any of that. Genuinely asking — if there's a real workflow reason (something specific PRoot gives you that native Termux doesn't, for running Zero specifically), we'd like to hear it, since that changes the calculus here.

If the answer turns out to be "no strong reason, just how I had my environment set up," then we'd lean toward not carrying this: isRunningUnderPRoot() adds a new detection function and a new mouse-mode branch that has to be reasoned about and tested on every future TUI mouse/keybinding change, for a configuration we don't document or recommend and that currently has one reporter. That's ongoing cost for a case the supported native path doesn't have.

Separately, worth fixing regardless of the outcome above: this PR references #507, which is about zero update hitting GitHub's API rate limit on shared IPs — unrelated to TUI/mouse handling. The actual parent issue for this content (#505) is the one we closed earlier, so this would need its own approved issue either way.

One thing that stands on its own regardless: the Ctrl+U/Ctrl+D/Shift+↑/Shift+↓ scroll shortcuts are useful on any terminal without PgUp/PgDn, not specific to Termux/PRoot. Happy to take those independently if you want to split them out.

Keeping this open for your take on the use-case question above.

@anandh8x anandh8x closed this Jul 5, 2026
@anandh8x anandh8x reopened this Jul 5, 2026
@PatrickNoFilter

Copy link
Copy Markdown
Contributor Author

The use case for running Zero inside PRoot:

Hermes Agent (an AI agent framework by Nous Research) uses Zero as its terminal UI. Hermes runs inside a PRoot/Alpine sandbox on Termux because:

  1. Python ML toolchain — Hermes depends on numpy, pytorch, transformers, etc. Native Termux package management (PEP 668, library conflicts, missing wheels) makes this brittle. A clean Alpine distro via PRoot avoids all of that.

  2. The TracerPid fix is general-purpose anyway — it's the correct way to detect any ptrace-based environment (PRoot, strace, debuggers, some containers). It's not Termux-specific.

  3. The keyboard fixes (composer guard, Ctrl+U/D direction) are entirely orthogonal to PRoot — they're general TUI improvements.

So the PRoot detection is just one part of a larger set of fixes. TracerPid is the right approach for it. Happy to split the PR if you'd prefer the keyboard fixes separate from the environment detection.

@kevincodex1

Copy link
Copy Markdown
Contributor

can you address coderabbit comments @PatrickNoFilter ?

- Replace Termux env var detection with isRunningUnderPRoot()
  via /proc/self/status TracerPid, cached with sync.OnceValue
- Mouse mode falls back to CellMotion (not AllMotion) under PRoot
  to fix broken touch scrolling
- Add Shift+Up/Shift+Down before plain KeyDown/KeyUp for transcript
  scroll with permission/askuser cursor + composer guard
- Add Ctrl+U (scroll up) and Ctrl+D (scroll down) with proper
  direction: Ctrl+U=move up, Ctrl+D=move down
- Add guards for active modals (wizard, picker, spec review, etc.)
  to avoid intercepting their Ctrl+U/D keybindings
- Tests: TestPermissionCursorCtrlU/D (direction regression),
  TestShiftUp/DownComposerGuard (composer guard)
- References issue Gitlawb#507
@PatrickNoFilter PatrickNoFilter force-pushed the fix/tui-scroll-prroot branch from 1306ab5 to 73d69ea Compare July 5, 2026 12:39
@PatrickNoFilter

Copy link
Copy Markdown
Contributor Author

@kevincodex1 — addressed both CodeRabbit items on fix/tui-scroll-prroot:

1. TracerPid matches debuggers — Added an inline comment in mouse.go documenting that TracerPid detects any ptrace tracer (PRoot, gdb, strace, dlv), not just PRoot. That is intentional: under any ptrace tracer the AllMotion (1003) sequence is unreliable, so CellMotion is the safer fallback. The only loss is hover-highlighting, which has no functional impact.

2. Testability — Extracted parseTracerPid(data []byte) bool as a testable helper and added 5 unit tests covering: missing TracerPid, TracerPid: 0, TracerPid: 42, malformed value, and empty input.

All existing mouse + permission tests continue to pass.

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified this locally since CI is gated on fork PRs — build's clean and the tui suite passes. The fix is careful.

The PRoot detection is the good kind of thoughtful. TracerPid via /proc/self/status is more robust than sniffing Termux env vars, and you pre-empted the obvious objection right in the comment: it detects any ptrace tracer (gdb/strace/dlv too), not PRoot specifically, and that's fine because CellMotion-under-a-tracer only costs hover highlighting. parseTracerPid taking []byte instead of reading the file is exactly right for testing, and sync.OnceValue keeps it to one read.

Directions all check out against the existing convention (positive = up, from PgUp): Shift+Up = scrollChat(1), Shift+Down = scrollChat(-1), Ctrl+U = +page (up), Ctrl+D = -page (down). The guard order is right too — shifted arrows and Ctrl+U/D are handled before the plain-arrow/composer path so they aren't swallowed, with proper fallthrough to the permission/ask-user cursors, the wizards, the picker, and a composer guard for multiline navigation.

Two small things, neither blocking:

  • The Ctrl+U/D comments say "half a page," but chatPageScrollLines() is a full page (height-8), so Ctrl+U/D scroll the same amount as PgUp/PgDn. Either make them half or fix the comment — vim/less users expect Ctrl+U/D to be a half page.
  • Heads up that this needs a maintainer to approve the workflow run: the Go CI is gated on fork PRs and hasn't executed yet, so I ran build + the tui suite locally to stand in for it.

On process: the bug issue #505 got closed "completed" at the same moment #503 closed, but #503 never merged, so the bug is still live in main — this is the actual fix. I've reopened #505 and marked it issue-approved.

Good work. Approving.

Replace exec.LookPath with lookupExecutable that uses os.Stat
instead of the faccessat2 syscall. The faccessat2 syscall (439)
is blocked by the Samsung seccomp filter on Android, causing
SIGSYS termination. os.Stat uses the newfstatat/statx syscalls
which are universally permitted.

This is part of broader Termux/Android support for Zero.
Document how to build Zero for native Android/Termux: GOOS=android
to avoid the SIGSYS crash, proot bind-mount for DNS resolution,
PRoot scroll fix, and free-tier provider setup.
@PatrickNoFilter PatrickNoFilter changed the title fix(tui): TracerPid PRoot detection, Ctrl+U/D direction, composer guard fix: Termux/Android support — PRoot scroll, SIGSYS sandbox, build docs Jul 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/sandbox/manager.go`:
- Around line 125-153: The PATH lookup logic in lookupExecutable needs to handle
Windows executables correctly and avoid treating empty PATH entries as the
current directory. Update the PATH-discovery branch in lookupExecutable to use a
platform-aware executable check for Windows instead of relying on
fi.Mode()&0o111, and ensure empty PATH segments are skipped so
selectPlatformBackend and its lookupExecutable fallback do not reintroduce
dot-path resolution.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 713b0a6b-7f27-4862-9fa1-9a7cf2e892b9

📥 Commits

Reviewing files that changed from the base of the PR and between 73d69ea and 9696920.

📒 Files selected for processing (3)
  • .gitignore
  • docs/INSTALL.md
  • internal/sandbox/manager.go
✅ Files skipped from review due to trivial changes (2)
  • docs/INSTALL.md
  • .gitignore

Comment thread internal/sandbox/manager.go

@kevincodex1 kevincodex1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me! thanks

@anandh8x

anandh8x commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

A few updates now that more of this has resolved:

Syscall fix: good catch, but it actually cuts against the PRoot case, not for it — PRoot is the standard workaround for exactly the syscall-blocking problem this fixes. Once Zero stops making the blocked call, that's one less real reason to need PRoot for Zero specifically.

Hermes Agent: asked twice, got the same unsubstantiated claim back twice. Need something checkable this time — a repo link, docs, a config, anything showing Hermes Agent actually embedding Zero as its terminal UI. If it's not shareable, that's a fine answer too, but it changes what we merge.

Process: PR still says "Ref #507" (the unrelated zero update rate-limit issue) — #505 is the one that's reopened and issue-approved. Please fix the reference regardless of the above.

Given all that, let's split it: Ctrl+U/D + Shift+Up/Down + the syscall fix + the Termux docs stand on their own merit and don't need the PRoot question answered — that can merge once the half-page comment @Vasanthdev2004 flagged is fixed. isRunningUnderPRoot() + the mouse-mode fallback stays in its own PR pending real evidence on the use case.

@PatrickNoFilter

Copy link
Copy Markdown
Contributor Author

Update: this PR was actually merged into main as 0f69d99. The sandbox lookupExecutable fix (avoids faccessat2 SIGSYS on Android), the isRunningUnderPRoot() detection via /proc/self/status TracerPid, and the Termux build docs all landed. Thanks for the reviews and merging! 🎉

For anyone running Zero on Termux/Android: build with GOOS=android to avoid the SIGSYS crash. Inside PRoot, the TracerPid detection will automatically fall back to CellMotion mouse mode for working touch scroll.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants