[PR #4519] fix(browser): keep Termux Chromium profiles on a writable temp directory #6281

Open
opened 2026-10-31 01:13:16 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4519
Author: @JoaoPHS
Created: 10/16/2026
Status: 🔄 Open

Base: main ← Head: fix/termux-chromium-temp


📝 Commits (10+)

  • 2a3514e fix(browser): keep Termux Chromium profiles on a writable temp directory
  • cb6761b fix(browser): place Playwright's Termux artifact directory on the writable temp path
  • 8000377 merge: resolve conflicts with main
  • 88a97ce fix(browser): keep overlapping Termux launches on their own temp directories
  • 701ecb3 fix(browser): keep the launched browser type outside the temp-directory callback
  • bb32ec5 fix(browser): isolate Termux temp lookups so a stuck launch cannot block the next
  • 63a5ee9 fix(browser): let Playwright load on Termux before Chromium starts
  • cfac433 fix(browser): keep a selected Android browser unless it is Termux Chromium
  • de00779 merge: resolve conflicts with main
  • 67fcb13 test(browser): give the Termux Android launch test a writable PREFIX

📊 Changes

10 files changed (+670 additions, -63 deletions)

View changed files

📝 src/browser/PlaywrightScenarioBrowser.test.ts (+472 -7)
📝 src/browser/PlaywrightScenarioBrowser.ts (+180 -40)
📝 src/native/AppleDispatchCoverage.ts (+4 -2)
📝 src/native/android/AndroidJniLibrary.ts (+1 -1)
📝 src/setupEnvironment.test.ts (+5 -5)
📝 tests/boundary/android/androidJniLibraries.test.ts (+1 -1)
📝 tests/boundary/filesystem/clientConfigurationDialects.test.ts (+4 -2)
📝 tests/boundary/filesystem/setupLauncherAlias.test.ts (+1 -1)
📝 tests/support/analysis-efficiency/android-jni.mjs (+1 -2)
📝 tests/support/analysis-efficiency/packet-entry.mjs (+1 -2)

📄 Description

Summary

Termux Chromium scenario launches created the browser profile under Node's default temp directory. On Android that is /tmp, which the Termux app user cannot write, and Chromium also tried to use /dev/shm, which Android does not provide. The profile and Playwright's host playwright-artifacts-* directory now stay on a writable temp directory, and shared-memory files are disabled.

Problem and expected behavior

On process.platform === "android", os.tmpdir() is /tmp when TMPDIR, TMP, and TEMP are unset. /tmp is owned by the Android shell user, so the Termux app cannot mkdtemp a Chromium profile there. Android also has no usable /dev/shm, so Chromium's default shared-memory path fails the launch.

A caller-selected temp directory must still win: TMPDIR, then TMP, then TEMP. Empty values are absent. When none is set, the writable location is $PREFIX/tmp. Playwright 1.58.2 also creates playwright-artifacts-* with the host os.tmpdir() after an await, before Chromium starts. That lookup is process-wide, so two overlapping launches must not share one directory, and one launch that never finishes must not block the next.

Change and scope

openPlaywrightScenarioBrowser creates the profile under the caller temp directory, or $PREFIX/tmp when that is the only writable default. The Chromium child receives that directory as TMPDIR when the caller did not select one. The host process does not rewrite TMPDIR, TMP, or TEMP. Playwright's os.tmpdir() call is scoped to the launch that is creating the artifact directory, so two Android launches can use different directories at the same time. Before Playwright is imported, a launch on Android publishes PLAYWRIGHT_BROWSERS_PATH under that writable directory so the 1.58.2 registry can load. If the caller selected Termux's $PREFIX/bin/chromium-browser wrapper and $PREFIX/lib/chromium/chrome exists, that binary is launched instead so Chromium can see icudtl.dat. Android launch args are --no-sandbox, --disable-dev-shm-usage, and --single-process. Existing Termux LD_PRELOAD / TERMUX_EXEC__PROC_SELF_EXE behavior is unchanged.

Follow the contribution scope.
This is an ordinary fix inside the existing browser scenario launcher. No new provider, target execution, injection, mutation, or protection-recovery capability. Maintainer scope decision: not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Playwright scenario browser launch owns the profile directory and Chromium environment. The change is at profile creation and the host temp lookup, before the browser process starts.
  • Contribution scope fit and linked maintainer decision: ordinary fix within the existing browser launcher. Not applicable.
  • CLI and MCP/tool-catalog contract: none. Scenario tool inputs and result shapes are unchanged.
  • Provider, bridge, target-format, or platform compatibility: Android/Termux Chromium launch only. Other platforms keep the previous profile parent (os.tmpdir()) and do not gain the Android args.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): the profile directory is still removed with the existing cleanup owner. A caller-selected TMPDIR is preserved. $PREFIX/tmp is created only when it is the fallback parent.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact (CI generates and validates ignored catalogs and skills/; edit authored instructions in .agents/skills/reverse-engineer-anything/ and never commit build outputs): none.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

The checked launch effect is the existing scenario Chromium process. This change only chooses its profile directory and the Android Chromium flags already used elsewhere.

Evidence and regression coverage

  • Tests added or updated: src/browser/PlaywrightScenarioBrowser.test.ts covers $PREFIX/tmp, caller-selected TMPDIR, TMP, TEMP, empty values, and TMPDIR precedence. It expects --disable-dev-shm-usage alongside the existing Termux args and environment. One case calls the production Playwright launcher and expects playwright-artifacts-* under the Termux temp directory. Overlapping launches each get an artifact directory in their own prefix. A launch that stays inside launchPersistentContext does not block a later launch, and each launch still sees its own temp directory. An Android launch with an empty PLAYWRIGHT_BROWSERS_PATH publishes $PREFIX/tmp/ms-playwright. A Termux wrapper at $PREFIX/bin/chromium-browser is rewritten to $PREFIX/lib/chromium/chrome when that file exists. These cases set process.platform to android. They do not boot Android.
  • Base reproduction or other evidence: Node documents os.tmpdir() as /tmp when the temp variables are unset, and Android's /tmp is the shell temp directory. The device section below records what happened when the production launch function ran under Termux on an emulator.
  • User-visible CLI/MCP output (if applicable): none. Failure before this change is the existing browser launch error when profile creation or Chromium startup fails.
  • Remaining proof gaps: see the device section. A page was not loaded.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Device run

Observed on 2026-10-20. The bundled production function is openPlaywrightScenarioBrowser from commit 63a5ee998.

Host and image:

  • AVD Pixel_7_API_35, adb device emulator-5554, ro.build.version.release 15, ABI x86_64, product sdk_phone64_x86_64. This image has no Play Store. adb root was used to install and run Termux.
  • Termux v0.118.3 github-debug x86_64 (com.termux).
  • After apt-get upgrade with the maintainer config kept: Node v26.4.0, process.platform android. Chromium 155.0.8059.79 from the Termux x11 repository, command $PREFIX/bin/chromium-browser.

The scenario was headless, with start_url https://example.com, PREFIX=/data/data/com.termux/files/usr, and TMPDIR, TMP, and TEMP unset.

  1. Before this commit, import("playwright-core") from playwright-core@1.58.2 threw Unsupported platform: android at lib/server/registry/index.js:486. Chromium was not started.
  2. After this commit, with TMPDIR, TMP, TEMP, and PLAYWRIGHT_BROWSERS_PATH unset, the same function published PLAYWRIGHT_BROWSERS_PATH=/data/data/com.termux/files/usr/tmp/ms-playwright and imported Playwright. It launched $PREFIX/lib/chromium/chrome with --user-data-dir=/data/data/com.termux/files/usr/tmp/rea-browser-scenario-zx3dv8 and --no-sandbox, --disable-dev-shm-usage, --single-process. Chromium still logged Invalid file descriptor to ICU data received. and exited with SIGTRAP. No page title was read. The failed launch removed the profile. /tmp remained empty.

chromium-browser --version printed Chromium 155.0.8059.79 before the scenario launch.

Validation performed

  • npm run test:focused -- src/browser/PlaywrightScenarioBrowser.test.ts — passed, 28 tests, on Windows (Node from this checkout). The Android cases stub process.platform. The host-artifact case executes Playwright's directory allocation on Windows. It does not start Chromium on Android.
  • Device commands above, on the Pixel 7 API 35 emulator. The production function was the bundled openPlaywrightScenarioBrowser from this commit. PLAYWRIGHT_BROWSERS_PATH was not set by the caller.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Callers that set TMPDIR, TMP, or TEMP keep that directory. Callers that set none on Android move from /tmp to $PREFIX/tmp.
  • Real Hopper/Ghidra, browser, or OS coverage: one headless Chromium process was spawned on the emulator described above and exited during startup. A document was not loaded.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the profile remains mode 0700 and is still removed by the existing cleanup owner. No new network, injection, or target-mutation authority.

Author readiness

Human owner: @JoaoPHS

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4519 **Author:** [@JoaoPHS](https://github.com/JoaoPHS) **Created:** 10/16/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/termux-chromium-temp` --- ### 📝 Commits (10+) - [`2a3514e`](https://github.com/morluto/rea/commit/2a3514ea4c54f75bedb93b71606fec8aeac2f58e) fix(browser): keep Termux Chromium profiles on a writable temp directory - [`cb6761b`](https://github.com/morluto/rea/commit/cb6761b1319c78f7bc7f4fbe88654171f662aabe) fix(browser): place Playwright's Termux artifact directory on the writable temp path - [`8000377`](https://github.com/morluto/rea/commit/80003772de39fa5a12e4409b4f451c522a610922) merge: resolve conflicts with main - [`88a97ce`](https://github.com/morluto/rea/commit/88a97cebffed3b1eb3a99deabca08491d0e29de5) fix(browser): keep overlapping Termux launches on their own temp directories - [`701ecb3`](https://github.com/morluto/rea/commit/701ecb318747825e10cf4ccfba4c3f3e8de7dcaf) fix(browser): keep the launched browser type outside the temp-directory callback - [`bb32ec5`](https://github.com/morluto/rea/commit/bb32ec5fed8f4d080667a64e5124ea4faa48de88) fix(browser): isolate Termux temp lookups so a stuck launch cannot block the next - [`63a5ee9`](https://github.com/morluto/rea/commit/63a5ee998a19181a092c23742428664642d2ab72) fix(browser): let Playwright load on Termux before Chromium starts - [`cfac433`](https://github.com/morluto/rea/commit/cfac4333cdee2287b24a3a1c9be769f370945a04) fix(browser): keep a selected Android browser unless it is Termux Chromium - [`de00779`](https://github.com/morluto/rea/commit/de00779422af46590348939cbc6677c914bf7c4e) merge: resolve conflicts with main - [`67fcb13`](https://github.com/morluto/rea/commit/67fcb13d26dd1da046d63bea43e26a3486b97f30) test(browser): give the Termux Android launch test a writable PREFIX ### 📊 Changes **10 files changed** (+670 additions, -63 deletions) <details> <summary>View changed files</summary> 📝 `src/browser/PlaywrightScenarioBrowser.test.ts` (+472 -7) 📝 `src/browser/PlaywrightScenarioBrowser.ts` (+180 -40) 📝 `src/native/AppleDispatchCoverage.ts` (+4 -2) 📝 `src/native/android/AndroidJniLibrary.ts` (+1 -1) 📝 `src/setupEnvironment.test.ts` (+5 -5) 📝 `tests/boundary/android/androidJniLibraries.test.ts` (+1 -1) 📝 `tests/boundary/filesystem/clientConfigurationDialects.test.ts` (+4 -2) 📝 `tests/boundary/filesystem/setupLauncherAlias.test.ts` (+1 -1) 📝 `tests/support/analysis-efficiency/android-jni.mjs` (+1 -2) 📝 `tests/support/analysis-efficiency/packet-entry.mjs` (+1 -2) </details> ### 📄 Description ## Summary Termux Chromium scenario launches created the browser profile under Node's default temp directory. On Android that is `/tmp`, which the Termux app user cannot write, and Chromium also tried to use `/dev/shm`, which Android does not provide. The profile and Playwright's host `playwright-artifacts-*` directory now stay on a writable temp directory, and shared-memory files are disabled. ## Problem and expected behavior On `process.platform === "android"`, `os.tmpdir()` is `/tmp` when `TMPDIR`, `TMP`, and `TEMP` are unset. `/tmp` is owned by the Android shell user, so the Termux app cannot `mkdtemp` a Chromium profile there. Android also has no usable `/dev/shm`, so Chromium's default shared-memory path fails the launch. A caller-selected temp directory must still win: `TMPDIR`, then `TMP`, then `TEMP`. Empty values are absent. When none is set, the writable location is `$PREFIX/tmp`. Playwright 1.58.2 also creates `playwright-artifacts-*` with the host `os.tmpdir()` after an await, before Chromium starts. That lookup is process-wide, so two overlapping launches must not share one directory, and one launch that never finishes must not block the next. ## Change and scope `openPlaywrightScenarioBrowser` creates the profile under the caller temp directory, or `$PREFIX/tmp` when that is the only writable default. The Chromium child receives that directory as `TMPDIR` when the caller did not select one. The host process does not rewrite `TMPDIR`, `TMP`, or `TEMP`. Playwright's `os.tmpdir()` call is scoped to the launch that is creating the artifact directory, so two Android launches can use different directories at the same time. Before Playwright is imported, a launch on Android publishes `PLAYWRIGHT_BROWSERS_PATH` under that writable directory so the 1.58.2 registry can load. If the caller selected Termux's `$PREFIX/bin/chromium-browser` wrapper and `$PREFIX/lib/chromium/chrome` exists, that binary is launched instead so Chromium can see `icudtl.dat`. Android launch args are `--no-sandbox`, `--disable-dev-shm-usage`, and `--single-process`. Existing Termux `LD_PRELOAD` / `TERMUX_EXEC__PROC_SELF_EXE` behavior is unchanged. Follow the contribution scope. This is an ordinary fix inside the existing browser scenario launcher. No new provider, target execution, injection, mutation, or protection-recovery capability. Maintainer scope decision: not applicable. ## Contract and boundary impact - Semantic owner and earliest changed stage: Playwright scenario browser launch owns the profile directory and Chromium environment. The change is at profile creation and the host temp lookup, before the browser process starts. - Contribution scope fit and linked maintainer decision: ordinary fix within the existing browser launcher. Not applicable. - CLI and MCP/tool-catalog contract: none. Scenario tool inputs and result shapes are unchanged. - Provider, bridge, target-format, or platform compatibility: Android/Termux Chromium launch only. Other platforms keep the previous profile parent (`os.tmpdir()`) and do not gain the Android args. - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details (if applicable): the profile directory is still removed with the existing cleanup owner. A caller-selected `TMPDIR` is preserved. `$PREFIX/tmp` is created only when it is the fallback parent. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `.agents/skills/reverse-engineer-anything/` and never commit build outputs): none. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [x] Read existing files or captured data. - [x] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. The checked launch effect is the existing scenario Chromium process. This change only chooses its profile directory and the Android Chromium flags already used elsewhere. ## Evidence and regression coverage - Tests added or updated: `src/browser/PlaywrightScenarioBrowser.test.ts` covers `$PREFIX/tmp`, caller-selected `TMPDIR`, `TMP`, `TEMP`, empty values, and `TMPDIR` precedence. It expects `--disable-dev-shm-usage` alongside the existing Termux args and environment. One case calls the production Playwright launcher and expects `playwright-artifacts-*` under the Termux temp directory. Overlapping launches each get an artifact directory in their own prefix. A launch that stays inside `launchPersistentContext` does not block a later launch, and each launch still sees its own temp directory. An Android launch with an empty `PLAYWRIGHT_BROWSERS_PATH` publishes `$PREFIX/tmp/ms-playwright`. A Termux wrapper at `$PREFIX/bin/chromium-browser` is rewritten to `$PREFIX/lib/chromium/chrome` when that file exists. These cases set `process.platform` to `android`. They do not boot Android. - Base reproduction or other evidence: Node documents `os.tmpdir()` as `/tmp` when the temp variables are unset, and Android's `/tmp` is the shell temp directory. The device section below records what happened when the production launch function ran under Termux on an emulator. - User-visible CLI/MCP output (if applicable): none. Failure before this change is the existing browser launch error when profile creation or Chromium startup fails. - Remaining proof gaps: see the device section. A page was not loaded. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Device run Observed on 2026-10-20. The bundled production function is `openPlaywrightScenarioBrowser` from commit `63a5ee998`. Host and image: - AVD `Pixel_7_API_35`, `adb` device `emulator-5554`, `ro.build.version.release` `15`, ABI `x86_64`, product `sdk_phone64_x86_64`. This image has no Play Store. `adb root` was used to install and run Termux. - Termux `v0.118.3` github-debug `x86_64` (`com.termux`). - After `apt-get upgrade` with the maintainer config kept: Node `v26.4.0`, `process.platform` `android`. Chromium `155.0.8059.79` from the Termux x11 repository, command `$PREFIX/bin/chromium-browser`. The scenario was headless, with `start_url` `https://example.com`, `PREFIX=/data/data/com.termux/files/usr`, and `TMPDIR`, `TMP`, and `TEMP` unset. 1. Before this commit, `import("playwright-core")` from `playwright-core@1.58.2` threw `Unsupported platform: android` at `lib/server/registry/index.js:486`. Chromium was not started. 2. After this commit, with `TMPDIR`, `TMP`, `TEMP`, and `PLAYWRIGHT_BROWSERS_PATH` unset, the same function published `PLAYWRIGHT_BROWSERS_PATH=/data/data/com.termux/files/usr/tmp/ms-playwright` and imported Playwright. It launched `$PREFIX/lib/chromium/chrome` with `--user-data-dir=/data/data/com.termux/files/usr/tmp/rea-browser-scenario-zx3dv8` and `--no-sandbox`, `--disable-dev-shm-usage`, `--single-process`. Chromium still logged `Invalid file descriptor to ICU data received.` and exited with `SIGTRAP`. No page title was read. The failed launch removed the profile. `/tmp` remained empty. `chromium-browser --version` printed `Chromium 155.0.8059.79` before the scenario launch. ## Validation performed - `npm run test:focused -- src/browser/PlaywrightScenarioBrowser.test.ts` — passed, 28 tests, on Windows (Node from this checkout). The Android cases stub `process.platform`. The host-artifact case executes Playwright's directory allocation on Windows. It does not start Chromium on Android. - Device commands above, on the Pixel 7 API 35 emulator. The production function was the bundled `openPlaywrightScenarioBrowser` from this commit. `PLAYWRIGHT_BROWSERS_PATH` was not set by the caller. ## Compatibility, safety, and release - Breaking changes or migration steps: none. Callers that set `TMPDIR`, `TMP`, or `TEMP` keep that directory. Callers that set none on Android move from `/tmp` to `$PREFIX/tmp`. - Real Hopper/Ghidra, browser, or OS coverage: one headless Chromium process was spawned on the emulator described above and exited during startup. A document was not loaded. - Package or release metadata impact: none. - Security, privacy, process, or containment review: the profile remains mode `0700` and is still removed by the existing cleanup owner. No new network, injection, or target-mutation authority. ## Author readiness Human owner: @JoaoPHS - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [ ] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [ ] The PR has one focused outcome and the title follows `type(scope): outcome`. - [ ] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [ ] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
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
morluto/rea#6281
No description provided.