Skip to content

desktop: install Podman when a machine has no engine, and run Intelligence on the same engine - #862

Draft
dustywusty wants to merge 10 commits into
dustywusty/desktop-runs-imagesfrom
dustywusty/desktop-engine-choice
Draft

dustywusty wants to merge 10 commits into
dustywusty/desktop-runs-imagesfrom
dustywusty/desktop-engine-choice

Conversation

@dustywusty

Copy link
Copy Markdown
Collaborator

OpenBot Desktop installs Podman, not Docker Desktop, on a machine that has no container engine, and runs local Intelligence on the same engine. Docker Desktop's license needs a paid subscription at larger companies; Podman is Apache 2.0 and has no license window. Docker is still used whenever the machine has it. Stacked on #852.

Needs a CLI release with CopilotKit/Intelligence#2147, which teaches local Intelligence to run on rootful Podman. Until then, the CLI refuses Podman.

What changes

  • Engine choice (install.rs install_engine_with), macOS and Windows:

    1. Docker answering → use it.
    2. Docker Desktop installed but stopped → start it.
    3. Podman present → use it.
    4. A stopped docker that isn't Docker Desktop → ask the person to start it.
    5. Nothing at all → install Podman (until now: Docker Desktop).

    Docker Desktop's install code stays. The install step names the prompt honestly: on macOS, "Enter your Mac password when macOS asks for it". The Windows MSI installs per user. If WSL is missing, the Windows row says Podman will offer to install it, and Windows then asks for administrator approval and a restart.

  • Rootful Podman machine (acquire.rs, engine.rs).

    • New machines are created with podman machine init --rootful, because local Intelligence's k3d needs rootful.
    • OpenBot uses the machine's rootful connection (openbot-root). On the bare name, its containers would land in the rootless engine, and the supervisor's Docker socket mount would fail.
    • A rootful Windows machine gets the host-gateway setting in the system config.
  • Existing rootless machines are not switched. A rootless machine keeps its containers and volumes in rootless storage, which the rootful engine can't see. Switching would bring OpenBot back on an empty database, and releases up to 0.2.0 created openbot rootless. Instead, local Intelligence refuses on a rootless machine with a row saying how to switch (Failure::RootlessPodman). Open question: following that advice also hides the machine's existing OpenBot data, so the wording, or a migration, needs a decision.

  • One engine for both (local_eval.rs). Every Intelligence CLI process gets COPILOTKIT_LOCAL_ENGINE=docker|podman, matching the engine OpenBot runs on. Every process starts through one function. On Podman it also gets CONTAINER_HOST/DOCKER_HOST set to the machine's API socket (Unix socket or named pipe), drops any inherited DOCKER_CONTEXT, and has Podman on its PATH. Nothing secret is added.

  • Readiness text (localReadiness.ts) no longer says "Docker Desktop".

Verification

  • From desktop/src-tauri: cargo fmt --check and cargo clippy --all-targets -- -D warnings are clean. cargo test --lib --bins passes: 666 lib and 185 bin tests.
  • From desktop: Biome is clean, and bun test has 275 pass.
  • New tests:
    • the full engine-choice table, including nothing installed → Podman on macOS and Windows, Docker Desktop stopped → started, and Docker answering → used;
    • machines created rootful, and an existing rootless machine left alone;
    • the rootful Windows gateway config, connection naming, and socket and pipe parsing;
    • the CLI environment for both engines, with nothing secret in it;
    • the rootless refusal.

Not verified: needs a live run

  1. Fresh Mac or PC with nothing installed:
    • the Podman install prompt;
    • the rootful machine starts, and OpenBot's containers run on it;
    • model sign-in can read and renew the bind-mounted .openbot/model-oauth.json (the server runs as uid 1001; whether Podman's file sharing on macOS, or WSL's on Windows, lets it read the person's 0600 file is untested);
    • local Intelligence starts, and pods reach host.docker.internal;
    • on Windows: missing WSL, plus the gateway setting on a rootful machine.
  2. Machine with Docker Desktop: stopped → started, Podman never installed, and the CLI gets COPILOTKIT_LOCAL_ENGINE=docker.
  3. Existing rootless Podman: OpenBot keeps its data, and local Intelligence shows the rootful row. Also: a stack created under Docker while OpenBot now runs on Podman is refused by the CLI.
  4. A first-run race: local setup starting before the engine is ready.

The server image already carries the API and the built app. Add worker/src and
its node_modules (the isolated install's link to @openbot/shared), so the same
published image can run the worker with `bun ../worker/src/index.ts`.

Nothing uses it yet; this is step 1 of running OpenBot Desktop's server, app and
worker as containers instead of building them on the customer's machine.
OpenBot's own server (the API and the built app it serves) and the routines
worker, from the published server image, behind an `openbot` profile so
scripts/start.sh and a plain `docker compose up -d` keep running them on the
host as before.

The server reads the same .env a host server does, with every address
overridden to a service on the compose network, PORT and SERVER_PORT both
pinned to 3001 (the server refuses to start when they disagree), NODE_ENV
cleared so a local OpenBot may still reach private addresses, and the app and
server host ports both mapped to it on loopback. The worker gets its three
settings and sits on the data network only.

Bots keep their host.docker.internal tool URL by default; whoever starts the
server service points them at server:3001.
The runtime sends the browser the gateway's /client socket in /info, and the
browser opens it itself. A server in a container reaches local Intelligence at
host.docker.internal, which the browser on the host cannot resolve, while the
browser's localhost is the container from inside it.

INTELLIGENCE_BROWSER_GATEWAY_WS_URL, when set, is the address /info (and a
trajectory grant) gives the browser; the server's own runner and channel
sockets stay on INTELLIGENCE_GATEWAY_WS_URL. The compose server service sets it
to the deployment's own gateway URL and reads its container address from
INTELLIGENCE_CONTAINER_GATEWAY_WS_URL.
A Google or xAI sign-in leaves .openbot/model-oauth.json on the host, and the
server reads and renews it itself (temp file plus rename, and a .lock folder
beside it), so the folder is bind-mounted rather than the file. Not created by
Compose, which would make it root-owned on Linux. The user is settable so the
Linux desktop can run the server as the host user who owns that 0600 file.
…n addresses

SERVICES now includes server and worker, and Stop names the openbot profile
they sit behind. The settings point every Bot at server:3001, give the server
host.docker.internal addresses for local Intelligence (the browser keeps the
localhost ones), and give an installed harness its container address. The
macOS Podman overlay publishes the server's two ports on IPv4 only.
… host

Start now hands everything to Compose and waits for the `server` container
to answer `/health` on both the app and API host ports, for up to 180 s.
Docker's restart policy supervises the server and worker, so the host
launch, readiness, pid recording and restart loop are gone.

- The desktop host token is minted before `up` and reaches Compose, which
  names it for the server alone; it is still never persisted.
- COMPUTER_NETWORK is `<compose project>_default` (new
  `stack::project_name`), so Bot computers join the server's network. On
  Linux OPENBOT_SERVER_USER is the deployment folder's uid:gid.
- `.openbot/` is created before `up`, since its bind mount does not
  create it.
- App and server ports count as ours when our container publishes them;
  the pre-spawn port check is gone.
- Ownership for showing, restoring and asking OpenBot is now "this root's
  server container publishes the ports" (`stack::server_publishes`), not
  recorded host pids.
- Status lists server and worker as containers; Restart runs
  `compose restart server worker`.
- What is left of the supervisor polls the server for refused
  connections (`watch_connections`).
- Start no longer requires the host Bun or packages.
- Old Bun host processes from earlier installs are still stopped on Start
  and Stop, which is how an upgrade frees 3001/3010.

Also brings tests left stale by the previous commit up to date
(server/worker in the up list, `--profile openbot` on down).
Installing OpenBot is now pulling images. Bun and bun install are prepared only
when somebody starts the self-hosted Intelligence sign-in, the one thing on the
host that still runs a Bun script, and reused after that.
Rootless Podman maps container root to the person who started it and any
other uid to one of their subordinate ids, so passing the owner's uid:gid
made the server a stranger to the owner-only .openbot sign-in folder.
Docker keeps the owner's numbers; Podman now gets 0:0.
A release from before the server and worker were Compose services has no
server or worker to raise, and its server image has no worker, so keeping
its pin left a working installation unable to start. Such a deployment now
reads as needing installation, Install resolves the latest release instead
of repairing the old pin, and if even the latest still runs the server on
the host the app says so rather than failing inside Compose.
…n Intelligence on the same engine

Docker Desktop's licence needs a paid subscription at larger companies, so a Mac or Windows machine
with no engine now gets Podman instead of Docker Desktop. Docker is still used whenever somebody has
it: an answering Docker as it is, an installed Docker Desktop is started, a Podman somebody has is
used, and a stopped OrbStack or Colima is left for its owner to start. Docker Desktop's installer is
kept. The step row says when macOS will ask for a password, and on Windows that Podman may offer to
install WSL behind an administrator prompt and a restart.

Local Intelligence's k3d needs rootful Podman (CopilotKit/Intelligence#2147), so the machine
OpenBot creates is `--rootful`, and OpenBot addresses it through its `<name>-root` connection, the
rootful engine, both for its own machine and for a running machine it adopts. On a rootful Windows
machine the host-gateway setting goes to the system configuration. An existing rootless machine is
never switched, ours included: its OpenBot lives in the rootless engine's storage, which the rootful
engine does not see. Local Intelligence setup and start decline there instead, with a new
`rootless-podman` failure whose row names `podman machine set --rootful`.

Every Intelligence CLI process gets COPILOTKIT_LOCAL_ENGINE for the engine OpenBot runs on, recorded
when the engine is ready and when Start pins it (or detected, before either). For a Podman machine
it also gets CONTAINER_HOST and DOCKER_HOST naming the machine's forwarded API, drops an inherited
DOCKER_CONTEXT, and has Podman's directory on PATH. The window's runtime row no longer says Docker
Desktop.

This branch has not been deployed

No deployments
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.

1 participant