Skip to content

OpenMausBot

An open-source chat app where every bot in the sidebar is a real agent running through the CLI already installed on your machine, and each one can be handed a computer: a cloud Linux desktop, a Docker or Podman container on the same host, a container on a VPS you own, or the machine in front of you where the platform can show that it is safe.

Screenshot of OpenMausBot
Editor screenshot, 1 Oct 2026OpenMausBot ↗

What it is

An open-source alternative to Grok Bot: a chat app in which every bot in the sidebar is a real agent process — the Claude, Codex or Grok CLI already installed on your machine — with its own personality, model, memory, connected apps and, the part this record is about, its own computer. A bot can be given a cloud Linux desktop from a third-party service, an isolated container on the same host through Docker or Podman, a container on a VPS you already own, or, only where the platform safety boundary is certified, the machine you are sitting at. Bots drive those desktops through a bundled Cua driver reached over a stdio MCP pipe. The container boundary is explicit: memory, CPU and pid limits, all capabilities dropped except three, private namespaces, one mounted workspace directory, and a lease so that exactly one turn holds the mouse at a time. It was built in seven weeks and had shipped fifty releases by the end of the seventh.

Who built itThe GitHub account was opened on 2018-12-30 and now holds 144 public repositories and 175 followers. His account is credited with 1,562 of the repository’s 3,103 commits, with the rest spread across 102 contributors — robinsonnblack at 345, Brad Hallett at 273, aivsomkar at 175 and KesleyDavid at 105 are the largest. He signs the project as one person rather than a company, and the repository carries a contributor licence agreement.

How it is put together

The parts · 6

A harness process on the loopback interface owns every agent process; the app is a chat shell that sends typed commands over HTTP and folds one SSE event stream into state. A bot’s computer is a provider attached to a turn rather than a property of the bot process, and the same turn can be mounted on a cloud Linux desktop, a container on this host, a container on a VPS reached by docker over SSH, or — only where the platform safety boundary is certified — the machine the person is sitting at. Three things hold that together. The boundary is verified rather than trusted: the container is bounded at creation and re-checked afterwards against the same numbers, and one that fails is refused instead of repaired. Ownership is a lease with a TTL rather than a lock, so a dead provider cannot pin a desktop and a person who takes control can refuse the bot at the pipe. And nothing is snapshotted: the container is disposable, the mounted workspace is the state, and recreating the desktop is the repair. The cost of that last choice is stated plainly for each backend rather than hidden behind the word persistent.

server/
The harness: 597 files and 9.6 MB. Drivers for each engine, the event bus they fan into, the HTTP and SSE API, and the computer providers — container, VPS, host and shared — beside the lease, seat-pool and MCP-bridge modules that decide who may drive one.
src/ and electron/
The chat shell, 383 component files and 3.5 MB over a server-backed store with no client-side transports of its own; and the desktop shell, 160 files and a single 165 KB main process that owns the operating system permission boundary, spawns the Cua driver, and writes a 0600 descriptor the harness reads.
android/, ios/ and companion/
First-party phone clients — 229 Kotlin files in the Android app and 85 in its shared core, 78 Swift files in the iOS app with 38 sources and 76 test files — plus the companion process that pairs a phone to a host over multicast DNS, Tailscale or a managed tunnel.
cloudflare/
Two Workers: a control plane of 23 files with D1 migrations, better-auth, e-mail one-time codes and a custom domain, and a Composio broker whose 28 KB entry point pages a marketplace catalogue that was previously capped at its first 500 toolkits.
docs/verification/
216 files and 7.8 MB: one recipe per user-visible behaviour, each run against a disposable fixture with its own temporary home and data directory. The stated rule is that a green unit test alone does not prove a user workflow, and the directory also carries dated acceptance runs and before-and-after evidence.
.github/workflows/, third_party/ and deploy/
Fourteen workflow files of 123 KB, led by a 35 KB release pipeline and a 26 KB continuous integration file; the reviewed redistribution record for the bundled Cua driver, with pinned commit, archive and inner-binary hashes, generated notices and a CycloneDX inventory; and deployment recipes for Docker Compose, Podman, Fly.io and a local Caddy front.

Choices, and what they beat

  • Cua is the only local desktop-control provider over a second path beside it — cliclick, a robotjs-class library, or a Python computer server

    Written down as a decision dated 2026-08-12, with no fallbacks: one permission identity, one binary to sign and notarise, and one behaviour contract are worth more than covering a missing driver capability locally. If something is missing it goes upstream or becomes a driver tool. The rejected options are named in a table with the reason each one lost.

  • Refuse a container that fails the hardening check rather than repairing it over adjusting the container until it matches

    The check inspects the running container and compares it against the memory, CPU, pid and capability bounds and the labels set at creation; the failure message tells the user to recreate it. A container someone else created under the managed name is refused outright. The same shape appears on the VPS, where a container that publishes a port is refused before every attach rather than only at creation.

  • No snapshot and no resume; recreate the desktop and keep the workspace over saving and restoring container state

    The start action is refused with an explanation, and what is durable is the host directory bind-mounted into the container plus the browser profiles inside it. The VPS guide draws the conclusion the design implies instead of softening it: treat the container filesystem as disposable, and get anything worth keeping off the server before the container is removed.

  • Treat the fleet-wide container count as operator policy and raise it over holding the per-bot maximum at four as a safety limit

    Every container is already individually bounded and re-verified, so the pull request raising the ceiling to eight argues that the app-wide count is not a safety guard; the pooled design needed the higher ceiling first. It also carries a rollback note, because an older build reads an out-of-range value and falls back to first-run defaults for the whole configuration file.

  • Let the elevated approval level cross a private desktop channel, not the HTTP API over letting a bot reach the permissive mode through the same API it already talks to

    Full access can only be enabled from a packaged local desktop app, and the documentation states what it does not bypass: operating system privacy controls, authentication, service permissions, and workspace or computer-sharing grants. Approval levels are passed straight through to the provider rather than judged by the app, which keeps no allowlist or classifier of its own, and the level set is credited in the document to another project’s permission modes.

  • Ship pooled desktops behind the configuration file, with the gaps written into the pull request over waiting for a settings picker and a removal path

    The pull request states that the picker still offers the other two modes and the panel renders pool like shared; that a switch away from pool has no deletion fence, so seat containers can linger within the instance cap with no interface to remove them; and that the seat affinity table is per process and resets on restart. Each is labelled a known follow-up rather than an oversight.

Read fromdocs/computer-use-integration.md (a decision document dated 2026-08-12), apps/docs/content/docs/computers/local-vm.mdx, docs/byo-vps.md, docs/verification/group-local-vm.md, docs/approval-levels.md, server/container-computer.ts, server/local-vm-lease.ts, server/local-vm-seat-pool.ts, server/local-vm-idle.ts, server/mcp-bridge.ts, server/container-mcp.ts, electron/cua-connection.cjs, and the pull requests that introduced pool mode and raised the instance ceiling.

Build log

6 stages
  1. 01

    Seven weeks, 3,103 commits, and a rename on the second day

    The repository was created on 2026-08-11 at 18:58 UTC and its first commit landed nineteen minutes later: a scaffold described as the Vite, React, TypeScript and Tailwind v4 base for OpenGrokBot. The rename came the next morning, followed by a commit whose message was "Own the identity: scrub pre-rename product references" — and the old name survives in one place, the bundle identifier com.opengrokbot.app that the desktop writes into the driver descriptor the harness reads. From there the pace never settled: 1,168 commits in August and 1,935 in September, 3,103 in total, against a project that is seven weeks old. The canonical repository carries fifty releases, from v0.1.46 on 2026-09-01 to v0.1.91 on 2026-09-29, plus Android builds up to 1.5.0, and a second public mirror that exists only so builds installed before the repository migration can still update — the newest commit in the history is a version bump to 0.1.92 with no published release behind it. Around that sit 3,862 stars, 654 forks, 1,692 pull requests of which 1,231 were merged, 408 issues of which 212 are closed, and 102 contributors. Of the 875 co-author trailers, the largest group is 217 for Claude Fable 5, then 178 for Claude Opus 5 with a million-token context, 144 for Claude Fable 5.1 and 77 for Claude Opus 5.5.

  2. 02

    Isolation is a container, and how many of them is a setting

    The README promises that every bot gets a computer; the implementation makes that a container with a count attached. Three modes exist. Shared is a single container named openmausbot-computer, what the early versions shipped and still the default. Per-bot gives each bot its own, named from the first sixteen hex characters of a SHA-256 of the bot id rather than from anything a person typed, with its own host directory. Pool gives a configurable number of seats that conversations share, addressed by index. The image is pinned by digest to an official Cua XFCE desktop at an exact multi-architecture manifest, and OpenMausBot builds a derivative of it on the user machine: it installs the exact-version Cua driver wheel from the Python package index after checking its SHA-256, adds a hash-checked CJK font so Japanese renders inside the guest, and starts the driver under a supervisor as an unprivileged user on display :1. The build refuses a defective base image before anything uses it, because some published ARM64 layers shipped zero-byte OpenSSL libraries and the symptom, much later, is a curl error that reads like a network fault. At run time the container gets four gigabytes of memory and matching swap, two CPUs, a pid limit of 512, every capability dropped and three added back — set-user-id, set-group-id, and on Podman the chroot capability Firefox needs for its own sandbox — private IPC and cgroup namespaces, and one bind mount from the host workspace into the guest. The viewer is published on loopback only. A check then inspects the running container against those numbers and its labels, and a container that fails is refused with a message telling the user to recreate it, never quietly adjusted.

  3. 03

    There is no snapshot, so the workspace is the state

    Most products in this space snapshot; this one deliberately does not. The lifecycle verbs are create, start, stop and remove, and start is refused outright with the message that the desktop image cannot safely resume — so stale runtime state is repaired by deleting the container and building a new one. What survives that is the host directory bind-mounted into the guest and the browser profiles inside it: the image moves any profile directories it finds into the mounted workspace, symlinks them back and deletes the stale singleton locks first, which is how a bot stays signed in to a site across a recreation. The documentation states the consequence instead of burying it. For the local container: recreating the desktop repairs stale runtime state without deleting the durable workspace. For the VPS: treat the container filesystem as disposable, and get anything worth keeping off the server before the container is removed, because removal — and the recreate that follows an image upgrade — wipes it. Idling is a timer that re-arms on activity. The default window is eight hours, and a change merged on 2026-10-01 turns it into a setting of five minutes to twenty-four hours; changing the value re-arms timers that are already pending, measured from the last activity, so a shorter window takes effect at once and a longer one extends the current deadline. A desktop with work in flight is never recycled and defers by a full window instead, and a suspend that fails retries after another full window rather than switching the cost backstop off. Deleting a VPS container is not something the app will ever do for you.

  4. 04

    The pipe between a bot and its desktop, and its three exceptions

    A bot does not call an API to move the mouse. The harness adds one entry to the agent CLI configuration whose command is a small bundled Node script; that script validates its arguments, spawns the container runtime, runs the driver in the guest and pipes standard input and output straight through. It defines no tools and parses no protocol messages, with three deliberate exceptions. It answers the ping method itself, because the bundled driver does not implement it and would otherwise break the handshake. It rewrites the tool-list response into a plain-object root, because strict model providers reject a schema whose root is not an object and fail the whole turn. And it enforces the who-is-driving rule: while a person has taken control of the desktop, a tool call from the agent is refused on the near side and never reaches the driver. The control endpoint and its per-boot token travel in the environment rather than the argument list, because an argument list is readable by any process on the machine. Two further details are the kind that only come from being burned. The bridge never calls the process-exit function from a close handler, since that discards buffered output and cuts a final protocol frame in half; it sets an exit code, unpipes and lets the streams drain. And where the local container gets no liveness watchdog, because its runtime CLI talks to a local daemon and fails fast, the VPS variant does: the transport helper accepts no connect timeout, so a dropped connection would otherwise leave a turn hanging in silence, and forty-five seconds of total quiet triggers a probe that ends the bridge only if it fails.

  5. 05

    One desktop, one driver at a time, and a seat to come back to

    Ownership is a short renewable lease rather than a lock held for a session, and its shape follows the race it exists to settle: its methods are synchronous on purpose, so a lifecycle route and a turn dispatch can each claim their side before either of them awaits anything. A lease expires by itself, and it also drops the moment its owning bot stops being busy, so a provider that dies mid-turn cannot pin a desktop forever. There is one lease lane per target — one for the shared container, one per bot in per-bot mode — so separate desktops never block each other while each one stays a strict singleton. Pool mode adds a softer decision: which seat a conversation should address. A thirty-minute affinity keeps a thread on the desktop holding its login state, and the code comments the reasoning as a judgement about people rather than machines — long enough for the pause between two messages, short enough that an abandoned conversation stops steering assignment within the hour. Assignment prefers live affinity, so a thread waits behind the current holder rather than moving to a free desktop and losing its session; then a seat nobody holds and nobody has affinity with; then any unheld seat; and when every seat is held, the one whose lease expires soonest. Only a claim that wins renews the affinity, because a retry that has not won must not extend how long it waits. A person sees the result: a thread that reaches for a screen another thread is using waits, names the holder, and gives up after thirty minutes. The gaps are in the pull request rather than left to be discovered — pool mode shipped behind the configuration file with the picker still offering the other two modes, a switch away from pool has no deletion fence so seat containers can linger inside the instance cap, and the affinity table lives in memory and resets on restart.

  6. 06

    What the repository writes down about its own failures

    The release documentation is unusual in that every verification step is annotated with the incident that produced it. Those came from the hand-cut releases between 0.1.15 and 0.1.25: build output that went stale and broke the code signature, a bare import that killed the packaged server on launch while every check stayed green, helper paths that resolved outside the app after bundling, a stapling step that silently invalidated every published hash, and a finished release left invisible as a draft. The instruction above them is not to remove a gate without reading the comment first. Smaller failures are recorded at the same level of detail. A Windows test failed because NTFS reported a folder modified time late, so it is skipped there with a comment saying the tracker it covers only runs on Linux; another tore down a fixture directory before closing the server that held it as a working directory and collected a permission error; and an npm install that exited zero after quietly dropping a platform-specific optional dependency now gets its installed command probed for a version before the install is called successful. Contention is measured rather than guessed: a pull request used to queue seven macOS jobs against an account that runs at most five, and with twenty-five open pull requests those jobs waited a median of six and a half hours, so the heavy shards moved to Ubuntu and Windows for pull requests. On 2026-09-07 the project rented a disposable Hetzner server and wrote down what a blank-host install proved and what it did not, including a browser sandbox failure that needed an exact-path profile fix, a reboot that recovered on its own, and a list of what the run does not certify. The security policy describes the boundary as it currently is, open items included: the harness binds loopback only, and on a shared workspace a caller with no session, which includes every bot shell, may use only an allow-listed set of routes — with a note that this caller still keeps one worker’s guarded routes, and that a relay token scoped to that worker is the planned fix.

Adjacent records

All records →