Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cloud slice pre-baking now bakes CloakBrowser (a stealth-patched Chromium fork) instead of vanilla Playwright-managed Chromium into the box image, matching the engine swap in default-workspace-template's browser fleet. The deferred-install marker split in two (`done.playwright_deps`, `done.cloakbrowser`) so loaded slices still skip the real install correctly.
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@
# Exit code GNU ``timeout`` returns when it kills the wrapped command on timeout.
_COMMAND_TIMEOUT_EXIT_CODE: Final[int] = 124

# The DEFAULT_WORKSPACE_TEMPLATE ``deferred-install`` service writes this marker on success; the bake waits
# for it (see ``wait_for_deferred_install``) before stopping the services agent.
_DEFERRED_INSTALL_MARKER: Final[str] = "/var/lib/minds/deferred-install/done.playwright"
# The DEFAULT_WORKSPACE_TEMPLATE ``deferred-install`` service writes this marker on success (the final one --
# CloakBrowser itself, after the apt-deps marker); the bake waits for it (see
# ``wait_for_deferred_install``) before stopping the services agent.
_DEFERRED_INSTALL_MARKER: Final[str] = "/var/lib/minds/deferred-install/done.cloakbrowser"
# Cap on how long the bake blocks for the deferred install (heavy apt + browser
# download) to finish; on timeout the bake proceeds and the install retries on lease.
_DEFERRED_INSTALL_WAIT_TIMEOUT_SECONDS: Final[int] = 900
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def test_wait_for_deferred_install_polls_for_marker_or_finished_process() -> Non
assert [label for label, _cmd in runner.calls] == ["deferred-install-wait"]
command = runner.calls[0][1]
# The poll checks the success marker and uses a bracketed pgrep pattern (self-match guard).
assert "done.playwright" in command
assert "done.cloakbrowser" in command
assert "[d]eferred_install.sh" in command
assert "timeout 5" in command

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,43 @@
# DEFAULT_WORKSPACE_TEMPLATE image tar -- the box boot disk is shared, so fail early rather than fill it.
_ESTIMATED_DEFAULT_WORKSPACE_TEMPLATE_IMAGE_BYTES: Final[int] = 15 * 1024**3
# Generous cap for the seeding slice's base DEFAULT_WORKSPACE_TEMPLATE image build (the inner create budget
# is 45 min; the build is the long pole, the Playwright derive + save the rest).
# is 45 min; the build is the long pole, the CloakBrowser derive + save the rest).
_SEED_BASE_BUILD_TIMEOUT_SECONDS: Final[float] = 1800.0
# The Playwright-derived image RUNs a chromium download + apt; retry transient
# failures a few times before hard-failing the seed.
_PLAYWRIGHT_BUILD_ATTEMPTS: Final[int] = 3
_PLAYWRIGHT_BUILD_TIMEOUT_SECONDS: Final[float] = 900.0
_PLAYWRIGHT_CTX_DIR: Final[str] = "/tmp/default-workspace-template-playwright-ctx"
# The CloakBrowser-derived image RUNs an apt install + a pinned binary download; retry
# transient failures a few times before hard-failing the seed.
_CLOAKBROWSER_BUILD_ATTEMPTS: Final[int] = 3
_CLOAKBROWSER_BUILD_TIMEOUT_SECONDS: Final[float] = 900.0
_CLOAKBROWSER_CTX_DIR: Final[str] = "/tmp/default-workspace-template-cloakbrowser-ctx"
# Pinned CloakBrowser release -- keep in sync with default-workspace-template's
# scripts/deferred_install.sh (_CLOAKBROWSER_VERSION / _CLOAKBROWSER_SHA256_X64).
# Cloud slices are x86_64 bare metal only (see DEFAULT_IMAGE_URL_X86_64), so this
# bakes the x64 asset only -- no arch branching needed here.
#
# Full context (where CloakBrowser came from, why it replaced Fortress/vanilla
# Chromium, exact variable-by-variable ownership incl. the Playwright-vs-
# browser-use `executable_path` distinction, and step-by-step version-bump /
# engine-swap instructions) lives in default-workspace-template, not here:
# libs/browser/CHROMIUM_ENGINE_DESCRIPTION_AND_FUTURE_GUIDELINES.md
# These constants are that doc's row 7 -- a manually-kept-in-sync mirror.
# Bump one, bump the other.
_CLOAKBROWSER_VERSION: Final[str] = "chromium-v146.0.7680.177.4"
_CLOAKBROWSER_RELEASE_URL: Final[str] = (
f"https://github.com/CloakHQ/CloakBrowser/releases/download/{_CLOAKBROWSER_VERSION}"
)
_CLOAKBROWSER_SHA256_X64: Final[str] = "5af027faafb1fef9933eb784c094b764706de22a372a2cee84bc117fc4ab537f"
_CLOAKBROWSER_INSTALL_DIR: Final[str] = "/opt/cloakbrowser"
_BUILDER_PRUNE_TIMEOUT_SECONDS: Final[float] = 120.0
# The DEFAULT_WORKSPACE_TEMPLATE Dockerfile relocates the built workspace here (off the /mngr volume mount)
# before first boot; the Playwright derive runs ``uv run`` from it. This is a DEFAULT_WORKSPACE_TEMPLATE image
# before first boot; the CloakBrowser derive runs ``uv run`` from it. This is a DEFAULT_WORKSPACE_TEMPLATE image
# contract -- if DEFAULT_WORKSPACE_TEMPLATE moves it, the derive's guard fails fast with a clear message.
_DEFAULT_WORKSPACE_TEMPLATE_BUILD_CODE_DIR: Final[str] = "/docker_build_code"
# Where the DEFAULT_WORKSPACE_TEMPLATE deferred-install service writes its success marker; baking it into
# the seeded image makes ``[program:deferred-install]`` a no-op on every loaded slice.
# Where the DEFAULT_WORKSPACE_TEMPLATE deferred-install service writes its success markers; baking both
# into the seeded image makes ``[program:deferred-install]`` a no-op on every loaded slice.
# Two markers, matching default-workspace-template's scripts/deferred_install.sh split:
# apt libs, then the CloakBrowser binary itself (the one deferred_install_ready() gates on).
_DEFERRED_INSTALL_MARKER_DIR: Final[str] = "/var/lib/minds/deferred-install"
_DEFERRED_INSTALL_MARKER: Final[str] = f"{_DEFERRED_INSTALL_MARKER_DIR}/done.playwright"
_DEFERRED_INSTALL_DEPS_MARKER: Final[str] = f"{_DEFERRED_INSTALL_MARKER_DIR}/done.playwright_deps"
_DEFERRED_INSTALL_MARKER: Final[str] = f"{_DEFERRED_INSTALL_MARKER_DIR}/done.cloakbrowser"


class SliceVpsDockerProviderConfig(VpsProviderConfig):
Expand Down Expand Up @@ -444,7 +465,7 @@ def _seed_box_image(
image_tag: str,
build_args: Sequence[str] | None,
) -> None:
"""Build the DEFAULT_WORKSPACE_TEMPLATE image (+ baked Playwright) and seed the box tar; this slice runs that image too."""
"""Build the DEFAULT_WORKSPACE_TEMPLATE image (+ baked CloakBrowser) and seed the box tar; this slice runs that image too."""
logger.info("Building + seeding box tar {} (first slice on this box for this tag)", image_tag)
parsed = self._parse_build_args(build_args)
# Build the base DEFAULT_WORKSPACE_TEMPLATE image via the same shared helper the realizer's build path
Expand All @@ -461,7 +482,7 @@ def _seed_box_image(
builder=self.config.builder,
build_timeout_seconds=_SEED_BASE_BUILD_TIMEOUT_SECONDS,
)
self._build_playwright_derived_image(outer=outer, base_image=base_image, target_tag=image_tag)
self._build_cloakbrowser_derived_image(outer=outer, base_image=base_image, target_tag=image_tag)
cache.check_free_disk(_ESTIMATED_DEFAULT_WORKSPACE_TEMPLATE_IMAGE_BYTES)
with self._transfer_key_authorized(cache, outer) as transfer_key:
cache.save_image_from_slice(image_tag, vm_ssh_port=vm_ssh_port, transfer_key=transfer_key)
Expand All @@ -471,58 +492,75 @@ def _seed_box_image(

@retry(
retry=retry_if_exception_type(MngrError),
stop=stop_after_attempt(_PLAYWRIGHT_BUILD_ATTEMPTS),
stop=stop_after_attempt(_CLOAKBROWSER_BUILD_ATTEMPTS),
wait=wait_exponential(multiplier=1, min=1, max=10),
reraise=True,
)
def _build_playwright_derived_image(self, *, outer: OuterHostInterface, base_image: str, target_tag: str) -> None:
"""Build target_tag as base_image + a baked Playwright/Chromium layer (and the done marker).
def _build_cloakbrowser_derived_image(
self, *, outer: OuterHostInterface, base_image: str, target_tag: str
) -> None:
"""Build target_tag as base_image + a baked CloakBrowser layer (and the done markers).

Playwright is deliberately not in the DEFAULT_WORKSPACE_TEMPLATE Dockerfile (it is shared with the
CloakBrowser is deliberately not in the DEFAULT_WORKSPACE_TEMPLATE Dockerfile (it is shared with the
desktop Lima path); baking it cloud-side here keeps the desktop path
unchanged while letting every loaded slice skip the deferred install. The
browser cache lands in /root/.cache/ms-playwright -- outside the /mngr volume
and untouched by default-workspace-template-seed, so it survives into every container.
unchanged while letting every loaded slice skip the deferred install. It
lands at /opt/cloakbrowser -- outside the /mngr volume and untouched by
default-workspace-template-seed, so it survives into every container.

The RUN first guards that the DEFAULT_WORKSPACE_TEMPLATE build-code dir exists, so a future DEFAULT_WORKSPACE_TEMPLATE image
that relocates it fails fast with a clear message instead of a confusing
``cd``-not-found build failure buried in retries.

Playwright is invoked as ``python -m playwright`` (not the ``playwright``
console script) on purpose: the DEFAULT_WORKSPACE_TEMPLATE Dockerfile builds the uv venv at
``/mngr/code`` and then ``mv``\\s the workspace to ``/docker_build_code``. A uv
venv is path-bound -- its console-script shebangs hardcode
``/mngr/code/.venv/bin/python``, which does not exist here, so ``uv run
playwright`` would fail with ``Failed to spawn: playwright``. ``python -m``
goes through the venv's interpreter symlink (location-independent), so it works
from the relocated path. (DEFAULT_WORKSPACE_TEMPLATE's own deferred-install runs the console script,
but only at runtime after default-workspace-template-seed restores the workspace to ``/mngr/code``.)
Playwright's own apt-only ``install-deps`` step is invoked as ``python -m
playwright`` (not the ``playwright`` console script) on purpose: the
DEFAULT_WORKSPACE_TEMPLATE Dockerfile builds the uv venv at ``/mngr/code`` and then ``mv``\\s the
workspace to ``/docker_build_code``. A uv venv is path-bound -- its
console-script shebangs hardcode ``/mngr/code/.venv/bin/python``, which does
not exist here, so ``uv run playwright`` would fail with ``Failed to spawn:
playwright``. ``python -m`` goes through the venv's interpreter symlink
(location-independent), so it works from the relocated path.
(DEFAULT_WORKSPACE_TEMPLATE's own deferred-install runs the console script, but only at runtime
after default-workspace-template-seed restores the workspace to ``/mngr/code``.) CloakBrowser
itself is a plain pinned-release curl + sha256 + tar, no venv involved.
"""
guard = (
f"test -d {_DEFAULT_WORKSPACE_TEMPLATE_BUILD_CODE_DIR} || "
f"{{ echo 'DEFAULT_WORKSPACE_TEMPLATE build-code dir {_DEFAULT_WORKSPACE_TEMPLATE_BUILD_CODE_DIR} missing; DEFAULT_WORKSPACE_TEMPLATE image layout changed -- "
"update _DEFAULT_WORKSPACE_TEMPLATE_BUILD_CODE_DIR' >&2; exit 1; }"
)
# Cloud slices are x86_64 bare metal only -- one asset, no arch branching
# (contrast default-workspace-template's own deferred_install.sh, which also covers desktop arm64).
asset = "cloakbrowser-linux-x64.tar.gz"
install_cloakbrowser = (
f"curl -fsSL -o /tmp/{asset} {_CLOAKBROWSER_RELEASE_URL}/{asset} "
f"&& echo '{_CLOAKBROWSER_SHA256_X64} /tmp/{asset}' | sha256sum -c - "
f"&& mkdir -p {_CLOAKBROWSER_INSTALL_DIR} "
f"&& tar xzf /tmp/{asset} -C {_CLOAKBROWSER_INSTALL_DIR} "
f"&& chmod +x {_CLOAKBROWSER_INSTALL_DIR}/chrome "
f"&& rm /tmp/{asset}"
)
dockerfile = (
f"FROM {base_image}\n"
f"RUN {guard} "
f"&& cd {_DEFAULT_WORKSPACE_TEMPLATE_BUILD_CODE_DIR} && uv run python -m playwright install --with-deps chromium "
f"&& mkdir -p {_DEFERRED_INSTALL_MARKER_DIR} && touch {_DEFERRED_INSTALL_MARKER}\n"
f"&& cd {_DEFAULT_WORKSPACE_TEMPLATE_BUILD_CODE_DIR} && uv run python -m playwright install-deps chromium "
f"&& mkdir -p {_DEFERRED_INSTALL_MARKER_DIR} && touch {_DEFERRED_INSTALL_DEPS_MARKER} "
f"&& {install_cloakbrowser} "
f"&& touch {_DEFERRED_INSTALL_MARKER}\n"
)
encoded_dockerfile = base64.b64encode(dockerfile.encode()).decode()
stage_command = (
f"rm -rf {_PLAYWRIGHT_CTX_DIR} && mkdir -p {_PLAYWRIGHT_CTX_DIR} && "
f"echo {shlex.quote(encoded_dockerfile)} | base64 -d > {_PLAYWRIGHT_CTX_DIR}/Dockerfile"
f"rm -rf {_CLOAKBROWSER_CTX_DIR} && mkdir -p {_CLOAKBROWSER_CTX_DIR} && "
f"echo {shlex.quote(encoded_dockerfile)} | base64 -d > {_CLOAKBROWSER_CTX_DIR}/Dockerfile"
)
stage_result = outer.execute_idempotent_command(stage_command, timeout_seconds=30.0)
if not stage_result.success:
raise BoxImageCacheError(
f"failed to stage the Playwright Dockerfile on the slice: {stage_result.stderr.strip()}"
f"failed to stage the CloakBrowser Dockerfile on the slice: {stage_result.stderr.strip()}"
)
run_docker(
outer,
["build", "-t", target_tag, "-f", f"{_PLAYWRIGHT_CTX_DIR}/Dockerfile", _PLAYWRIGHT_CTX_DIR],
timeout_seconds=_PLAYWRIGHT_BUILD_TIMEOUT_SECONDS,
["build", "-t", target_tag, "-f", f"{_CLOAKBROWSER_CTX_DIR}/Dockerfile", _CLOAKBROWSER_CTX_DIR],
timeout_seconds=_CLOAKBROWSER_BUILD_TIMEOUT_SECONDS,
)

@contextmanager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
from imbue.mngr.primitives import HostId
from imbue.mngr_imbue_cloud.errors import BoxImageCacheError
from imbue.mngr_imbue_cloud.providers.slice_provider import SliceVpsDockerProvider
from imbue.mngr_imbue_cloud.providers.slice_provider import _CLOAKBROWSER_CTX_DIR
from imbue.mngr_imbue_cloud.providers.slice_provider import _DEFAULT_WORKSPACE_TEMPLATE_BUILD_CODE_DIR
from imbue.mngr_imbue_cloud.providers.slice_provider import _DEFERRED_INSTALL_DEPS_MARKER
from imbue.mngr_imbue_cloud.providers.slice_provider import _DEFERRED_INSTALL_MARKER
from imbue.mngr_imbue_cloud.providers.slice_provider import _PLAYWRIGHT_CTX_DIR
from imbue.mngr_imbue_cloud.slices.box_image_cache import BoxImageCacheInterface
from imbue.mngr_imbue_cloud.slices.mock_box_image_cache_test import MockBoxImageCache

Expand Down Expand Up @@ -134,23 +135,26 @@ def execute_idempotent_command(
return CommandResult(stdout="", stderr="", success=True)


def test_build_playwright_derived_image_renders_marker_and_build_command() -> None:
def test_build_cloakbrowser_derived_image_renders_marker_and_build_command() -> None:
provider = SliceVpsDockerProvider.model_construct()
outer = _RecordingOuter()
provider._build_playwright_derived_image(
provider._build_cloakbrowser_derived_image(
outer=cast(OuterHostInterface, outer), base_image="mngr-build-xyz", target_tag=_TAG
)
# The staged context Dockerfile is shipped base64-encoded; decode it and assert the
# baked-Playwright + deferred-install-marker contract every loaded slice relies on.
# baked-CloakBrowser + deferred-install-marker contract every loaded slice relies on.
stage_command = next(c for c in outer.recorded if "base64 -d" in c)
encoded = stage_command.split("echo ")[1].split(" | base64 -d")[0].strip().strip("'")
dockerfile = base64.b64decode(encoded).decode()
assert dockerfile.startswith("FROM mngr-build-xyz")
# Must invoke playwright via ``python -m`` (not the ``playwright`` console script): the DEFAULT_WORKSPACE_TEMPLATE
# venv is built at /mngr/code and ``mv``\\d to /docker_build_code, so the script's hardcoded
# shebang is broken here -- only the interpreter (reached via ``python -m``) is relocatable.
assert "uv run python -m playwright install --with-deps chromium" in dockerfile
assert "uv run python -m playwright install-deps chromium" in dockerfile
assert _DEFERRED_INSTALL_DEPS_MARKER in dockerfile
assert _DEFERRED_INSTALL_MARKER in dockerfile
assert "cloakbrowser-linux-x64.tar.gz" in dockerfile
assert "sha256sum -c -" in dockerfile
# Guards the DEFAULT_WORKSPACE_TEMPLATE build-code path so a relocated layout fails fast with a clear message.
assert f"test -d {_DEFAULT_WORKSPACE_TEMPLATE_BUILD_CODE_DIR}" in dockerfile
# The RUN body must be valid shell -- catches f-string brace-escaping bugs in the guard.
Expand All @@ -159,7 +163,7 @@ def test_build_playwright_derived_image_renders_marker_and_build_command() -> No
assert syntax_check.returncode == 0, syntax_check.stderr
build_command = next(c for c in outer.recorded if "docker build" in c)
assert _TAG in build_command
assert f"{_PLAYWRIGHT_CTX_DIR}/Dockerfile" in build_command
assert f"{_CLOAKBROWSER_CTX_DIR}/Dockerfile" in build_command


def test_transfer_key_authorize_and_deauthorize_render_expected_commands() -> None:
Expand Down