Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Only after doing all of the above should you begin writing code.
# Important commands and conventions:

- Never run `uv sync`, always run `uv sync --all-packages` instead
- For browser automation, Playwright's Python API is in the root venv (`from playwright.sync_api import sync_playwright`, run via `uv run python`). Chromium installs asynchronously on first container boot (the one-shot `deferred-install` program), so in a fresh workspace confirm it finished -- `supervisorctl status deferred-install` or the marker `/var/lib/minds/deferred-install/done.playwright` -- before launching, or the launch fails with a clear error. It runs as-is under the docker provider's gVisor runtime; if you hit a "No usable sandbox!" error on a runtime without unprivileged user namespaces, pass `chromium.launch(args=["--no-sandbox"])`. See `libs/bootstrap/README.md` for the full deferral contract.
- For browser automation, Playwright's Python API is in the root venv (`from playwright.sync_api import sync_playwright`, run via `uv run python`). The engine is Fortress (a stealth-patched Chromium fork), not Playwright's own managed Chromium -- pass `executable_path="/opt/fortress/tilion-fortress/tilion"` explicitly to `chromium.launch(...)`, since Playwright's own browser-cache lookup only auto-discovers builds it downloaded itself. It installs asynchronously on first container boot (the one-shot `deferred-install` program), so in a fresh workspace confirm it finished -- `supervisorctl status deferred-install` or the marker `/var/lib/minds/deferred-install/done.fortress` -- before launching, or the launch fails with a clear error. It runs as-is under the docker provider's gVisor runtime; if you hit a "No usable sandbox!" error on a runtime without unprivileged user namespaces, pass `chromium.launch(executable_path="/opt/fortress/tilion-fortress/tilion", args=["--no-sandbox"])`. See `libs/bootstrap/README.md` for the full deferral contract.

# Always remember these guidelines:

Expand Down
5 changes: 5 additions & 0 deletions dev/changelog/deferred-install-fortress-arm64.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
`scripts/deferred_install.sh` now fetches the Fortress stealth Chromium fork
per-arch on first container boot instead of vanilla Playwright Chromium: x64
from Fortress's official `v151.0.7908.0` release, arm64 from a fork build
(minhtrinh-imbue/fortress, for the aarch64-support PR tiliondev/fortress#29
until it merges upstream). Both tarballs are SHA256-verified before install.
6 changes: 3 additions & 3 deletions libs/browser/browser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ def test_deferred_install_ready_gates_on_marker(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.delenv("BROWSER_SKIP_INSTALL_CHECK", raising=False)
play = tmp_path / "done.playwright"
monkeypatch.setattr(bsession, "_PLAYWRIGHT_MARKER", play)
marker = tmp_path / "done.fortress"
monkeypatch.setattr(bsession, "_FORTRESS_MARKER", marker)
ready, _ = bsession.deferred_install_ready()
assert ready is False
play.write_text("")
marker.write_text("")
ready, reason = bsession.deferred_install_ready()
assert ready is True
assert reason == "ready"
Expand Down
6 changes: 6 additions & 0 deletions libs/browser/changelog/deferred-install-fortress-arm64.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
The browser fleet's Chromium engine is now Fortress (a from-source, BSD-3,
stealth-patched Chromium fork) instead of Playwright's own managed Chromium.
`session.py` and the agents' direct Playwright calls both launch the same
Fortress binary at `/opt/fortress/tilion-fortress/tilion`, so the whole
workspace runs one engine. The fleet gates browser launches on the Fortress
deferred-install marker.
16 changes: 12 additions & 4 deletions libs/browser/src/browser/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,15 @@
# tab feels snappier. Slightly more bandwidth than skipping frames.
_SCREENCAST_EVERY_NTH_FRAME = 1

# Deferred-install marker (see scripts/deferred_install.sh). Chromium installs
# Deferred-install marker (see scripts/deferred_install.sh). Fortress installs
# asynchronously on first container boot; launching a browser before it exists
# fails, so callers gate on this. No Xvfb: CDP streaming/input are headless.
_PLAYWRIGHT_MARKER = Path("/var/lib/minds/deferred-install/done.playwright")
_FORTRESS_MARKER = Path("/var/lib/minds/deferred-install/done.fortress")

# Fortress's fixed install path (see scripts/deferred_install.sh's
# _install_fortress). A stealth, C++-patched Chromium fork -- replaces
# vanilla Chromium as the engine for every browser the fleet launches.
_FORTRESS_EXECUTABLE = "/opt/fortress/tilion-fortress/tilion"

# Default model. browser-use's own default LLM is ChatBrowserUse (its hosted
# model), so to drive with the user's Anthropic key we pass ChatAnthropic
Expand Down Expand Up @@ -329,7 +334,7 @@ def deferred_install_ready() -> tuple[bool, str]:
"""Return ``(ready, reason)`` once Chromium is installed."""
if os.environ.get("BROWSER_SKIP_INSTALL_CHECK") == "1":
return True, "ready" # host/CI testing without the deferred-install marker
if not _PLAYWRIGHT_MARKER.exists():
if not _FORTRESS_MARKER.exists():
return False, "Chromium is still installing in this workspace; try again in a minute."
return True, "ready"

Expand Down Expand Up @@ -542,7 +547,10 @@ async def start(
"""
self._playwright = playwright
self._input_enabled.set()
chromium_path = playwright.chromium.executable_path
# Fixed Fortress path, not playwright.chromium.executable_path -- the
# fleet's engine is Fortress, not Playwright's own managed Chromium
# (which vanilla Playwright calls elsewhere in this image still use).
chromium_path = _FORTRESS_EXECUTABLE
profile_dir = _profile_dir(self.browser_id)
profile_dir.mkdir(parents=True, exist_ok=True)
_clear_stale_singleton(profile_dir) # a prior hard kill may have orphaned a lock
Expand Down
2 changes: 1 addition & 1 deletion libs/browser/test_browser_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ def test_http_release_requires_ownership(monkeypatch: pytest.MonkeyPatch) -> Non

def test_http_new_browser_blocked_until_chromium_installed(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("BROWSER_SKIP_INSTALL_CHECK", raising=False)
monkeypatch.setattr(bsession, "_PLAYWRIGHT_MARKER", bsession.Path("/nonexistent/marker"))
monkeypatch.setattr(bsession, "_FORTRESS_MARKER", bsession.Path("/nonexistent/marker"))
client = runner.application.test_client()
resp = client.post("/browsers")
assert resp.status_code == 503
Expand Down
71 changes: 54 additions & 17 deletions scripts/deferred_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,35 +66,72 @@ _recover_interrupted_dpkg() {
fi
}

_install_playwright() {
# Fortress (tiliondev/fortress) stealth Chromium engine, replacing vanilla
# Playwright-managed Chromium. x64 from the official release; arm64 has no
# official release yet (tiliondev/fortress#29, open as of 2026-07-23) so this
# points at a fork build in the meantime -- swap _FORTRESS_ARM64_URL/_SHA256
# to the official tiliondev/fortress release once that PR merges.
# Fork build: https://github.com/minhtrinh-imbue/fortress/releases/tag/linux-arm64-151.0.7908.0-debian12
# PR: https://github.com/tiliondev/fortress/pull/29
readonly _FORTRESS_X64_URL="https://github.com/tiliondev/fortress/releases/download/v151.0.7908.0/tilion-fortress-linux-x64.tar.gz"
readonly _FORTRESS_X64_SHA256="243238b2b8a8b944b7ba2b63533d2b917da7d569dcb290ce96bf28151294b873"
readonly _FORTRESS_ARM64_URL="https://github.com/minhtrinh-imbue/fortress/releases/download/linux-arm64-151.0.7908.0-debian12/tilion-fortress-linux-arm64-debian12.tar.gz"
readonly _FORTRESS_ARM64_SHA256="da6965af8fa8e995d137bcabdca8d163fde7f32ba483eaf5e029223995f19ada"
readonly _FORTRESS_INSTALL_DIR="/opt/fortress"

_install_fortress() {
local marker
marker="$(_marker_for playwright)"
marker="$(_marker_for fortress)"
if [ -f "$marker" ]; then
_log "playwright: marker present at $marker, skipping"
_log "fortress: marker present at $marker, skipping"
return 0
fi
# `playwright install --with-deps` shells out to apt; recover any
# interrupted dpkg state first so an install the bake interrupted can
# actually complete on retry.
# `install-deps` apt-installs the shared libs Chromium needs (libnss3,
# libgbm, etc.) -- Fortress is a Chromium build too, same requirement.
# Recover any interrupted dpkg state first so a bake-interrupted install
# can actually complete on retry.
_recover_interrupted_dpkg
_log "playwright: installing chromium + apt system libs (this may take a few minutes)"
# `--with-deps` apt-installs the system libraries chromium needs.
# `uv run` uses the workspace venv (the playwright Python wheel is
# already installed via the root pyproject.toml's pin). Subshell so
# the cwd change does not leak to other `_install_<name>` functions.
if (cd "$REPO_ROOT" && uv run playwright install --with-deps chromium); then
touch "$marker"
_log "playwright: install complete, marker written to $marker"
else
_log "playwright: install FAILED; marker not written so the next boot retries"
_log "fortress: installing apt system libs"
if ! (cd "$REPO_ROOT" && uv run playwright install-deps chromium); then
_log "fortress: apt install FAILED; marker not written so the next boot retries"
return 1
fi

local url sha256
case "$(uname -m)" in
x86_64) url="$_FORTRESS_X64_URL"; sha256="$_FORTRESS_X64_SHA256" ;;
aarch64) url="$_FORTRESS_ARM64_URL"; sha256="$_FORTRESS_ARM64_SHA256" ;;
*) _log "fortress: unsupported architecture $(uname -m)"; return 1 ;;
esac
_log "fortress: downloading $url"
local tmp_dir
tmp_dir="$(mktemp -d)"
# shellcheck disable=SC2064
trap "rm -rf '$tmp_dir'" RETURN
local asset="$tmp_dir/fortress.tar.gz"
if ! curl -fsSL -o "$asset" "$url"; then
_log "fortress: download FAILED; marker not written so the next boot retries"
return 1
fi
if [ "$(sha256sum "$asset" | awk '{print $1}')" != "$sha256" ]; then
_log "fortress: SHA256 mismatch -- refusing to install"
return 1
fi
rm -rf "$_FORTRESS_INSTALL_DIR"
mkdir -p "$_FORTRESS_INSTALL_DIR"
if ! tar xzf "$asset" -C "$_FORTRESS_INSTALL_DIR"; then
_log "fortress: extract FAILED; marker not written so the next boot retries"
return 1
fi
chmod +x "$_FORTRESS_INSTALL_DIR/tilion-fortress/tilion"
touch "$marker"
_log "fortress: install complete (${_FORTRESS_INSTALL_DIR}/tilion-fortress/tilion), marker written to $marker"
}

main() {
mkdir -p "$MARKER_DIR"
local rc=0
_install_playwright || rc=$?
_install_fortress || rc=$?
if [ "$rc" -eq 0 ]; then
_log "all deferred installs complete"
else
Expand Down