Android port: finish remote mux attach and consolidate startup/UI fixes #101

Open
opened 2026-09-25 17:03:48 -04:00 by ober · 0 comments
Owner

Objective

Finish the Android port in /Users/user/work/jerboa-shell-android-impl so the packaged app starts reliably, shows a usable terminal, accepts ordinary Android keyboard input, and supports the intended remote workflow:

  1. Unlock the app.
  2. Press Connect.
  3. Enter HOST:PORT.
  4. The app executes the equivalent of ,mux attach HOST:PORT and reaches a usable remote mux prompt.
  5. A human can enter and run commands through the app.

This issue is a handoff for a batch implementation pass. Treat the current worktree and the handoff document as evidence, not as a claim of completion.

Repository/worktree state

  • Repository: ober/jerboa-shell
  • Current local branch: fix/android-shell-startup
  • Current checkout: /Users/user/work/jerboa-shell-android-impl
  • The checkout is dirty and has not been committed or pushed.
  • No PR was opened for this work.
  • The authoritative handoff is docs/android-startup-unblock-2026-09-24.md.
  • The current APK is dist/android/jsh-0.10.68-arm64-v8a-debug.apk.
  • Latest APK SHA-256: 2c638880fc01c9ad0d409429f13a5912cb1c5fe71e9aab9f3923ab357e581f4.
  • The same APK was copied to the Termux host as /data/data/com.termux/files/home/jsh.apk; the remote checksum matched.

Do not discard the dirty changes. Review and consolidate them before committing.

Verified work already completed

Startup crash diagnosis and fix

  • The Android-created mux pane crashed during prompt construction when HOSTNAME was absent.
  • The prompt path attempted to spawn /bin/hostname; the packaged Android worker then died with SIGSEGV.
  • Supplying HOSTNAME=android before mux server and pane startup keeps the pane alive.
  • The native launcher now supplies HOSTNAME when absent, and the pane environment merge preserves it.
  • A clean-data install without an app .jshrc produced a live user@android prompt and passed echo HOSTNAME_ONLY_FIX_VERIFIED in an independent mux attachment.
  • Do not treat the old app .jshrc workaround as the production fix.

Blank terminal diagnosis and fix

  • TerminalView.onDraw used RGB-only integers as Android ARGB colors. 0x00FFFFFF and 0x00000000 are fully transparent.
  • The terminal therefore appeared blank even though mux output was arriving.
  • The source now uses opaque black/white ARGB constants.
  • The visible prompt, working directory, attach banner, and status text were verified on the emulator.
  • ANSI cleanup now removes the startup cursor/mode fragments observed on the emulator. This remains a transcript renderer, not a complete terminal emulator/grid.

Local command-entry UI

  • The custom terminal view did not reliably summon the soft keyboard.
  • A typed Android native EditText plus Send row was added above the transcript.
  • The row uses the platform IME path and sends command text plus newline through the existing session binder.
  • The row is above the transcript so the keyboard does not cover Send.
  • A weighted LinearLayout.LayoutParams fix was added after discovering that long commands could measure the EditText at full width and push Send off-screen.
  • Emulator verification entered echo IME_COMMAND_OK, pressed Send, and observed the field clear while the local shell prompt remained live.
  • A long ,mux attach 10.66.60.3:1234 --no-mtls string remained editable with Send visible after the weighted-layout fix.

Connect UI

  • The Connect dialog accepts HOST:PORT.
  • Pressing Connect writes ,mux attach HOST:PORT into the live shell pane.
  • Invalid/unsupported endpoint behavior is visible in the terminal transcript.
  • The dialog and callback path were exercised with 10.66.60.1:1234.

Build and packaging

  • make android exists as the human-facing APK target.
  • The target selects all features, uses ~/.embed by default, supports an explicit temporary passphrase file for encrypted embed generation, and cleans an internally created passphrase file.
  • The typed Android generator, Kotlin compilation, packaging, and APK build pass with the current changes.
  • jerboa_check_balance reports Balance OK for android/app.ss.
  • git diff --check is clean.

Current changed files and what they contain

These are the pending dirty changes that must be reviewed/consolidated:

  • Makefile: make android now orchestrates all-feature selection, personal embed source, encrypted passphrase handoff, and APK packaging.
  • android/app.ss: typed terminal transcript/ANSI cleanup, opaque colors, Connect dialog, command-entry EditText/Send row, weighted layout params, content descriptions, and related Android externs.
  • android/native/jsh_android_bridge.c: HOSTNAME injection, Android passphrase-file environment setup, mux debug log path, and existing Android launcher environment changes.
  • docs/android.md: documents the one-command personal Android build and embed/passphrase behavior.
  • support/patch-extras-build.sh: applies Android embed handoff, mux passphrase, worker fork/exec, environment preservation, and related extras build overlays.
  • support/gen-embed-early-passphrase.patch: early encrypted-embed passphrase handling.
  • support/extras-android-embed-handoff.patch: Android embed passphrase handoff support.
  • support/extras-android-mux-passphrase.patch: mux worker passphrase handoff support.
  • support/extras-android-worker-fork.patch: Android worker fork/exec path changes.
  • docs/android-startup-unblock-2026-09-24.md: full diagnostic and test handoff.
  • docs/evidence/android-startup-2026-09-24/: emulator screenshots and trace/log evidence, including terminal-visible.png, terminal-fresh-hostname.png, connect-ip-port.png, command-row-pty.png, command-row-weighted.png, and connect-termux-clean.png.
  • build/: generated Android build output; decide what is intentionally ignored versus deliverable.

There are also pre-existing modifications in the dirty checkout. Do not blindly remove them; determine whether each is required by the Android path and preserve only verified, maintainable changes.

Remaining implementation work

1. Make remote Connect reach a verified remote prompt

A real Termux mux listener was started at 10.66.60.3:1234, and the emulator could reach the port (nc -z succeeded). The Android Connect dialog changed the status to Connecting to 10.66.60.3:1234, but no remote prompt or command output was verified.

Investigate the full path:

  • android/app.ss Connect callback and command dispatch.
  • The local pane spawned by android/native/jsh_android_bridge.c.
  • vendor/jerboa-shell-extras/jerboa-src/src/jsh/mux-client.ss remote TLS, certificate pinning, mTLS, MSG-ATTACH, and password authentication.
  • Personal encrypted embed generation and whether the APK contains the same cert/CA material expected by the Termux server.
  • Error propagation: a failed remote attach must produce a visible terminal error and return the UI from Connecting to a useful state.
  • A successful remote attach must show a remote prompt and run a proof command such as echo ANDROID_REMOTE_OK.

The current JNI bridge itself starts a local mux server with --server --name default and reconnects to ${HOME}/mux/default.sock; it does not directly accept a host, port, certificate, or password and does not itself issue remote MSG-ATTACH. If the intended design is to keep remote attach in the shell pane, prove that path end-to-end. If the intended design is native remote transport, implement the typed/native bridge and lifecycle deliberately rather than changing only the Unix socket path.

Use a server configured with matching embed TLS material for a real acceptance test. Do not claim success from TCP reachability alone.

2. Finish terminal input/output acceptance

  • Verify the Send row with a real shell command after a fresh install.
  • Verify a successful remote command after remote attach is fixed.
  • Verify ordinary Gboard composition, not only adb shell input text.
  • Decide whether to keep the transcript renderer or implement the required terminal grid/cursor semantics. Document limitations if the latter is out of scope.
  • Ensure command output, errors, and connection state remain visible after keyboard resize and task changes.

3. Review Android worker patches

The current patch stack includes speculative or risky changes from the startup investigation. Audit each one against a clean build and runtime evidence:

  • Android fork/exec versus posix_spawn.
  • Descriptor closing behavior. A previous global Android close_fds_except bypass did not fix the startup crash and can leak descriptors; do not ship it without proof.
  • Sparse pane environment reconstruction.
  • Native memfd executable permissions.
  • Passphrase file lifecycle and permissions.
  • Debug logging and whether it is acceptable in a release build.

Remove dead experiments and narrow compatibility overlays to the smallest verified implementation.

4. Reproducible build/release workflow

  • Run the required pristine features-all gate from absent vendor/ and the platform build gate before PR creation.
  • Confirm the APK contains the intended encrypted personal embed data without publishing secrets.
  • Synchronize VERSION and user-visible/package versions as required by repository policy.
  • Create a feature branch from current default branch, commit only reviewed changes, push, open a Forgejo PR, and check CI until green.
  • Do not merge the PR; a human must review and merge it.

Required acceptance evidence

The implementation is not complete until a fresh install demonstrates all of the following on the emulator:

  1. App starts without an app .jshrc workaround.
  2. Prompt and terminal text are visible.
  3. Unlock works.
  4. Connect accepts HOST:PORT.
  5. A reachable remote mux endpoint completes TLS/auth/attach.
  6. The app displays the remote prompt.
  7. echo ANDROID_REMOTE_OK returns visible output through the Android UI.
  8. Send remains reachable with the keyboard open and long commands.
  9. A clean make android build reproduces the APK from the documented inputs.
  10. Evidence and limitations are recorded in docs/android-startup-unblock-2026-09-24.md.

Useful commands and evidence

cd /Users/user/work/jerboa-shell-android-impl
make android
adb devices
adb -s emulator-5554 install -r dist/android/jsh-0.10.68-arm64-v8a-debug.apk
adb -s emulator-5554 shell am start -n org.jerboa.shell/.MainActivity
adb -s emulator-5554 shell 'run-as org.jerboa.shell tail -100 files/mux-debug.log'
ssh termux 'ps -ef | grep [j]sh'

The handoff document has the detailed timeline and screenshot references. Keep secrets such as the personal embed passphrase out of the issue and out of committed documentation.

## Objective Finish the Android port in `/Users/user/work/jerboa-shell-android-impl` so the packaged app starts reliably, shows a usable terminal, accepts ordinary Android keyboard input, and supports the intended remote workflow: 1. Unlock the app. 2. Press **Connect**. 3. Enter `HOST:PORT`. 4. The app executes the equivalent of `,mux attach HOST:PORT` and reaches a usable remote mux prompt. 5. A human can enter and run commands through the app. This issue is a handoff for a batch implementation pass. Treat the current worktree and the handoff document as evidence, not as a claim of completion. ## Repository/worktree state - Repository: `ober/jerboa-shell` - Current local branch: `fix/android-shell-startup` - Current checkout: `/Users/user/work/jerboa-shell-android-impl` - The checkout is dirty and has not been committed or pushed. - No PR was opened for this work. - The authoritative handoff is `docs/android-startup-unblock-2026-09-24.md`. - The current APK is `dist/android/jsh-0.10.68-arm64-v8a-debug.apk`. - Latest APK SHA-256: `2c638880fc01c9ad0d409429f13a5912cb1c5fe71e9aab9f3923ab357e581f4`. - The same APK was copied to the Termux host as `/data/data/com.termux/files/home/jsh.apk`; the remote checksum matched. Do not discard the dirty changes. Review and consolidate them before committing. ## Verified work already completed ### Startup crash diagnosis and fix - The Android-created mux pane crashed during prompt construction when `HOSTNAME` was absent. - The prompt path attempted to spawn `/bin/hostname`; the packaged Android worker then died with SIGSEGV. - Supplying `HOSTNAME=android` before mux server and pane startup keeps the pane alive. - The native launcher now supplies HOSTNAME when absent, and the pane environment merge preserves it. - A clean-data install without an app `.jshrc` produced a live `user@android` prompt and passed `echo HOSTNAME_ONLY_FIX_VERIFIED` in an independent mux attachment. - Do not treat the old app `.jshrc` workaround as the production fix. ### Blank terminal diagnosis and fix - `TerminalView.onDraw` used RGB-only integers as Android ARGB colors. `0x00FFFFFF` and `0x00000000` are fully transparent. - The terminal therefore appeared blank even though mux output was arriving. - The source now uses opaque black/white ARGB constants. - The visible prompt, working directory, attach banner, and status text were verified on the emulator. - ANSI cleanup now removes the startup cursor/mode fragments observed on the emulator. This remains a transcript renderer, not a complete terminal emulator/grid. ### Local command-entry UI - The custom terminal view did not reliably summon the soft keyboard. - A typed Android native `EditText` plus `Send` row was added above the transcript. - The row uses the platform IME path and sends command text plus newline through the existing session binder. - The row is above the transcript so the keyboard does not cover Send. - A weighted `LinearLayout.LayoutParams` fix was added after discovering that long commands could measure the EditText at full width and push Send off-screen. - Emulator verification entered `echo IME_COMMAND_OK`, pressed Send, and observed the field clear while the local shell prompt remained live. - A long `,mux attach 10.66.60.3:1234 --no-mtls` string remained editable with Send visible after the weighted-layout fix. ### Connect UI - The Connect dialog accepts `HOST:PORT`. - Pressing Connect writes `,mux attach HOST:PORT` into the live shell pane. - Invalid/unsupported endpoint behavior is visible in the terminal transcript. - The dialog and callback path were exercised with `10.66.60.1:1234`. ### Build and packaging - `make android` exists as the human-facing APK target. - The target selects all features, uses `~/.embed` by default, supports an explicit temporary passphrase file for encrypted embed generation, and cleans an internally created passphrase file. - The typed Android generator, Kotlin compilation, packaging, and APK build pass with the current changes. - `jerboa_check_balance` reports `Balance OK` for `android/app.ss`. - `git diff --check` is clean. ## Current changed files and what they contain These are the pending dirty changes that must be reviewed/consolidated: - `Makefile`: `make android` now orchestrates all-feature selection, personal embed source, encrypted passphrase handoff, and APK packaging. - `android/app.ss`: typed terminal transcript/ANSI cleanup, opaque colors, Connect dialog, command-entry EditText/Send row, weighted layout params, content descriptions, and related Android externs. - `android/native/jsh_android_bridge.c`: HOSTNAME injection, Android passphrase-file environment setup, mux debug log path, and existing Android launcher environment changes. - `docs/android.md`: documents the one-command personal Android build and embed/passphrase behavior. - `support/patch-extras-build.sh`: applies Android embed handoff, mux passphrase, worker fork/exec, environment preservation, and related extras build overlays. - `support/gen-embed-early-passphrase.patch`: early encrypted-embed passphrase handling. - `support/extras-android-embed-handoff.patch`: Android embed passphrase handoff support. - `support/extras-android-mux-passphrase.patch`: mux worker passphrase handoff support. - `support/extras-android-worker-fork.patch`: Android worker fork/exec path changes. - `docs/android-startup-unblock-2026-09-24.md`: full diagnostic and test handoff. - `docs/evidence/android-startup-2026-09-24/`: emulator screenshots and trace/log evidence, including `terminal-visible.png`, `terminal-fresh-hostname.png`, `connect-ip-port.png`, `command-row-pty.png`, `command-row-weighted.png`, and `connect-termux-clean.png`. - `build/`: generated Android build output; decide what is intentionally ignored versus deliverable. There are also pre-existing modifications in the dirty checkout. Do not blindly remove them; determine whether each is required by the Android path and preserve only verified, maintainable changes. ## Remaining implementation work ### 1. Make remote Connect reach a verified remote prompt A real Termux mux listener was started at `10.66.60.3:1234`, and the emulator could reach the port (`nc -z` succeeded). The Android Connect dialog changed the status to `Connecting to 10.66.60.3:1234`, but no remote prompt or command output was verified. Investigate the full path: - `android/app.ss` Connect callback and command dispatch. - The local pane spawned by `android/native/jsh_android_bridge.c`. - `vendor/jerboa-shell-extras/jerboa-src/src/jsh/mux-client.ss` remote TLS, certificate pinning, mTLS, `MSG-ATTACH`, and password authentication. - Personal encrypted embed generation and whether the APK contains the same cert/CA material expected by the Termux server. - Error propagation: a failed remote attach must produce a visible terminal error and return the UI from `Connecting` to a useful state. - A successful remote attach must show a remote prompt and run a proof command such as `echo ANDROID_REMOTE_OK`. The current JNI bridge itself starts a local mux server with `--server --name default` and reconnects to `${HOME}/mux/default.sock`; it does not directly accept a host, port, certificate, or password and does not itself issue remote `MSG-ATTACH`. If the intended design is to keep remote attach in the shell pane, prove that path end-to-end. If the intended design is native remote transport, implement the typed/native bridge and lifecycle deliberately rather than changing only the Unix socket path. Use a server configured with matching embed TLS material for a real acceptance test. Do not claim success from TCP reachability alone. ### 2. Finish terminal input/output acceptance - Verify the Send row with a real shell command after a fresh install. - Verify a successful remote command after remote attach is fixed. - Verify ordinary Gboard composition, not only `adb shell input text`. - Decide whether to keep the transcript renderer or implement the required terminal grid/cursor semantics. Document limitations if the latter is out of scope. - Ensure command output, errors, and connection state remain visible after keyboard resize and task changes. ### 3. Review Android worker patches The current patch stack includes speculative or risky changes from the startup investigation. Audit each one against a clean build and runtime evidence: - Android fork/exec versus `posix_spawn`. - Descriptor closing behavior. A previous global Android `close_fds_except` bypass did not fix the startup crash and can leak descriptors; do not ship it without proof. - Sparse pane environment reconstruction. - Native memfd executable permissions. - Passphrase file lifecycle and permissions. - Debug logging and whether it is acceptable in a release build. Remove dead experiments and narrow compatibility overlays to the smallest verified implementation. ### 4. Reproducible build/release workflow - Run the required pristine `features-all` gate from absent `vendor/` and the platform build gate before PR creation. - Confirm the APK contains the intended encrypted personal embed data without publishing secrets. - Synchronize `VERSION` and user-visible/package versions as required by repository policy. - Create a feature branch from current default branch, commit only reviewed changes, push, open a Forgejo PR, and check CI until green. - Do not merge the PR; a human must review and merge it. ## Required acceptance evidence The implementation is not complete until a fresh install demonstrates all of the following on the emulator: 1. App starts without an app `.jshrc` workaround. 2. Prompt and terminal text are visible. 3. Unlock works. 4. Connect accepts `HOST:PORT`. 5. A reachable remote mux endpoint completes TLS/auth/attach. 6. The app displays the remote prompt. 7. `echo ANDROID_REMOTE_OK` returns visible output through the Android UI. 8. Send remains reachable with the keyboard open and long commands. 9. A clean `make android` build reproduces the APK from the documented inputs. 10. Evidence and limitations are recorded in `docs/android-startup-unblock-2026-09-24.md`. ## Useful commands and evidence ```sh cd /Users/user/work/jerboa-shell-android-impl make android adb devices adb -s emulator-5554 install -r dist/android/jsh-0.10.68-arm64-v8a-debug.apk adb -s emulator-5554 shell am start -n org.jerboa.shell/.MainActivity adb -s emulator-5554 shell 'run-as org.jerboa.shell tail -100 files/mux-debug.log' ssh termux 'ps -ef | grep [j]sh' ``` The handoff document has the detailed timeline and screenshot references. Keep secrets such as the personal embed passphrase out of the issue and out of committed documentation.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ober/jerboa-shell#101
No description provided.