OpenBot
An open-source platform from CopilotKit that gives each AI coworker a computer of its own — a container with Chromium, a workspace volume and its own logins — where a coworker is any endpoint speaking AG-UI, and every browser, file, MCP or shell action passes through one gateway that decides it against a CEL policy, writes an audit row and only then acts, or refuses and names the rule.

What it is
OpenBot is CopilotKit’s open-source platform for AI coworkers, each of which gets a computer of its own: a container holding a Chromium, a /workspace volume, its own logins and its own browser profile. A coworker is any endpoint that speaks AG-UI, so an agent written with LangGraph, CrewAI, Pydantic AI, Google ADK or by hand arrives the same way, and thirteen ship in the example package as YAML configuration rather than code. Everything a Bot does to a browser, a file, an MCP server or a component passes through one server-side gateway, which resolves the target, evaluates a CEL action policy, writes an audit row and only then calls the computer — and a refusal names the rule that caused it. The repository is a template rather than a product: there is no hosted version, nothing is published as a package, and the README says to clone it and replace the example tenant package with your own coworkers, channels and skills. It comes up on a laptop from Docker Compose, needs a CopilotKit Intelligence project and a single model key, and includes an optional Tauri desktop app.
Who built itAn organization account rather than a person. The repository lists 33 contributors across 451 commits in about six weeks: davidmckayv wrote 126 of them, kevin9327 91, Ayush7614 48 and zopeVaibhav 37, with a GitHub Actions bot on 12 and Renovate on 6. The history carries 242 co-author trailers, and the name most often in that list is David McKay, on 130 of them, with a further 13 written as davidmckayv — so the busiest account and the most frequently credited co-author carry the same name, and the next largest group of co-authors after him is a model.
How it is put together
The parts · 6A Bot is a process on the far side of an AG-UI endpoint, and the product is everything around it: a supervisor that makes a container for it, a computer inside that container holding a real browser, and a server that stands between the two and the rest of the deployment. Three consequences shape the code. The first is that the gateway is the only way in, which is why the largest file in the server is the gateway and why target resolution, policy and audit are modules of their own rather than checks sprinkled through routes. The second is that a Bot is treated as a process that can be careless or hostile: it has a shell, so the database is put on a network the computer is not on; computers bind to loopback behind a token; the supervisor holds the Docker socket and offers four operations; the credential store encrypts at rest and never returns what it holds; and a secret is recorded as having been asked for and how long it was, not what it said. The third is that this is a template rather than a service — thirteen coworkers ship as YAML, no model ships at all, no workspace is published as a package, and the repository is written to be cloned and rewritten rather than operated by its authors for other people.
- server/
- The API and the decision point: 143 source files, led by a 105 KB CopilotKit runtime, a 67 KB app, a 66 KB entry point and a 55 KB configuration module, with the 49,664-byte computer gateway, a 37 KB audit module, policy, snapshot and supervisor clients, plus plugins, channels, routines, voice, auth and learning. Beside it sit 222 tests — one,
plugin-store.integration.test.ts, is 319,249 bytes — and 97 Drizzle files holding migrations 0000 to 0047, whose names are sentences such astruncate_is_not_a_way_around_append_only. - app/
- The interface: 272 files of React and Vite covering the composer, transcripts, the computer panel with its live screen, the skills and agents surfaces and eighteen admin route files, with 132 tests next to them, one of which is 114,325 bytes. The largest sources are the composer at 82,677 bytes, the chat transcript at 74,777 and the channel chat at 45,055 — this is where the product spends its surface area.
- agent-computer/ and supervisor/
- The computer and the thing that hands out one per Bot.
agent-computeris 25 source files around a 60,722-byte entry point, with profiles at 27,263 bytes, the workspace at 15,459, the shell at 13,819, control handover at 11,892 and screenshots at 9,177, watched by 32 test files. The supervisor is seven files whose 27,198-byte Docker module creates, stops, resets and lists containers, with one memory limit, one identity and one name allocator. - The fourteen agent-* directories
- The harness fleet:
agent-botas a proof of concept,agent-langgraphin TypeScript,agent-mastra,agent-langgraph-agui, and eleven Python Bots each with its own Dockerfile, requirements files and a tests directory holding a main test, a learning-delivery test and a model-spec test. They exist so that the README can say a Bot is any AG-UI endpoint and mean it — the largest,agent-crewai/tests/test_main.py, is 26,918 bytes. - desktop/
- A Tauri desktop application: 70 files and 4,563 KB of Rust in
src-tauri, withmain.rsat 416,470 bytes,stack.rsat 318,549 andenv.rsat 101,450, plus Windows code signing and signature-verification scripts in PowerShell, a macOS microphone note, a telemetry document and its validator, and a provider picker with its own 35,009-byte test. - shared/, charts/openbot/, docker/s6/ and scripts/
- Everything that has to agree across languages and deployments.
shared/model-providers.jsonis the single file naming each provider’s key variable, endpoint variable and default model, read by a TypeScript loader and a Python one.charts/openbotis 33 files of Helm with a 28,138-byte values file, a 19,951-byte validation template, a 13,888-byte network policy and CI values for AKS, EKS, GKE and self-hosted.docker/s6is 27 files of s6-rc definitions that make one image carry the API, the computer, the migrations and optionally PostgreSQL, andscripts/start.shat 21,826 bytes is what a clone actually runs.
Choices, and what they beat
A computer per Bot rather than one shared computer over pointing every Bot at the same browser container
With
COMPUTER_SUPERVISOR_URLeach Bot gets its own container, its own workspace volume and its own browser profile; without it every Bot shares oneAGENT_COMPUTER_URL. The README treats the per-Bot case as the point of the feature, and the computer is where the difference between an agent that can use your tools and an agent you can let near them is enforced.The gateway is the only path, so the record exists before the act does over letting the computer decide what it is allowed to do
The computer itself does not decide policy: the server resolves the target from its own snapshot, evaluates the policy, writes an audit row, and calls the computer only when the decision forwards. The README states the consequence directly — there is no path that acts without the record existing first — and the computer’s own lower-level endpoints are documented as ones not to use to bypass the gateway.
One spec file for provider facts, read by two languages over a provider table kept separately in TypeScript and in Python
A single JSON file carries which environment variable holds a provider’s key, which holds its endpoint and which model runs when nothing else does, plus a row per Bot. Both loaders validate the whole file at startup and stop the Bot with the path of the offending key rather than failing at its first model call, and each language has its own test pinning its provider list to that same file, so adding a provider to one side and forgetting the other fails a test.
A failed read must not look like an empty result over letting a pending query fall through to its empty state
Three separate changes take the same position:
isPendinggoes false on a failed fetch exactly as on a successful one, so the skills pages told people with a dozen skills that they had none, the connector pages said a connector does not exist, and the Boundaries screen showed a title over nothing. Each fix routes the failure to an error state with the server’s own reason where there is one, because an empty state shown before the read arrives is, in the repository’s own words, a claim the page has not yet earned.Defer signing and notarization deliberately rather than ship a half-signed build over signing the macOS desktop build in the workflow that produces it
The desktop workflow carries the comment that the bundle step signs and notarizes and is deliberately not run there, because it needs certificates the workflow does not hold; it uses the ad-hoc identity
-instead, and signing is tracked as a named step of its own. The distinction matters to the issue that asked for a signed DMG: it is a deferred piece of work with a label, not something nobody noticed.Cut text between characters rather than at a code unit over slicing a fixed number of UTF-16 code units
Voice caps the context it joins, the captions it shows and the answers that come back, and every cap was a plain
slice, which can land between the two halves of an emoji and hand the model or the transcript half a character. The caps now cut between characters, at most one unit shorter and never over the cap, matching the rule the routing text and the server already applied.
Read fromdocs/architecture.md (28,069 characters), README.md (28,799 characters), docs/configuration.md (54 KB), CHANGELOG.md (291,939 bytes), .github/workflows/ci.yml (30,219 bytes), .env.example (26,511 bytes), docker-compose.yml (22,056 bytes), scripts/start.sh (21,826 bytes), the two skill documents under .claude/skills/, and the complete 1,382-file tree with sizes.
Build log
6 stages- 01
Six weeks, fifteen releases and a 292 KB changelog
The repository was created on 2026-08-17, and its oldest commit is dated two days earlier than that. In the six weeks since, 451 commits landed — 178 in August and 273 in September — released as fifteen versions, from
v0.0.1on the day it was created tov0.0.15on 2026-09-22, withv0.0.2,v0.0.3andv0.0.4all cut on the same day, 2026-08-22. The tree holds 1,382 files.CHANGELOG.mdis 291,939 bytes and.env.exampleis 26,511, so the running account of what changed is larger than most of the code it describes. By the end of September it had 5,761 stars, 765 forks, 23 watchers and 18 open issues, under MIT and written in TypeScript, and the README carries a badge for third place on a daily trending ranking. A repository that grows this fast is usually a pile of scripts; this one arrives with a Helm chart, a Tauri desktop application, an s6 service tree that lets one container carry the app, the API, the browser and optionally PostgreSQL, and 222 test files in the server directory alone. - 02
One monorepo, fourteen agent harnesses and a browser in a box
The layout says what the product is.
app/is a React and Vite interface of 272 files,server/is a Hono API of 143 source files with 222 test files and 97 Drizzle files beside it, andagent-computer/is the piece the whole idea rests on: 25 source files that own a Chromium, a/workspacevolume, browser profiles, screenshots, ARIA snapshots, a shell and the file tools.supervisor/is seven files that hold the Docker socket and do four things — ensure, stop, reset and list — one container per Bot, bound to loopback because its token is a shared secret rather than a network boundary. Around those sit fourteen harness directories: eleven Python Bots (ADK, AG2, Agno, the Claude SDK, CrewAI, LangGraph AG-UI, Langroid, LlamaIndex, the Microsoft Agent Framework, Pydantic AI and Strands), a LangGraph Bot in TypeScript, a Mastra Bot, and a hand-written proof of concept. Each Python one carries a Dockerfile, a requirements file and three tests, one of which pins it to a shared model specification. None of the fleet is privileged, because a Bot is only an AG-UI endpoint: the governance rides the protocol rather than the framework. - 03
The gateway decides, records, and only then acts
The product claim is one sentence, and the code is arranged around it: every action a Bot takes against a browser, a file, an MCP server or a component comes back to the server first.
server/src/computer/gateway.tsis 49,664 bytes and runs a five-step loop — resolve the target from a server-held snapshot, evaluate the current policy, write an audit row for the decision, call the computer only when the decision forwards, and write a second row if a forwarded action fails. The policy is CEL, deny is evaluated before allow, and the engine fails closed: a missing or empty policy permits nothing, a broken deny rule denies, and a broken allow rule does not permit. A malformed configured policy stops server startup, and the shipped default is written out explicitly asdeny: []withallow: ["true"]rather than left to be inferred. A rule can inspecttool.name,intent,page.host,element.*,key,command,file.*,mcp.*andinitiator.*— what started the run, which is the only field that can tell an unattended routine from somebody typing. The network is part of the design as well: a Bot has a shell, and a shell reaches whatever its container reaches, so PostgreSQL sits on a Docker network of its own, computers bind to127.0.0.1behind a per-container token, and a host that supports it can run them under gVisor withCOMPUTER_RUNTIME=runsc. - 04
Six weeks of bug reports, and the same mistake three times
The pull requests read like a catalogue of ways an interface can lie to the person using it. In #665 the skills page, the admin skills page and the skill editor all branch on
isPending, which goes false on a failed fetch just as on a successful one — so somebody with a dozen skills was told “You don’t have any skills yet.”, while the page’s own comment already called an empty state before the read arrives a claim the page has not yet earned. #664 is the same bug on three connector pages, and #656 is the same again, a regression from #60, leaving the Boundaries screen with a title over nothing while it stays open. The rest is equally specific: #657 refuses an administrator’s grant of a skill nobody has written, because the upsert stored the row anyway; #653 deletes the temporary file left behind when a Bot’s browser-control state fails to save; #651 resets a live screen’s reconnect counter only when a frame arrives; #652 cuts voice text between characters rather than at UTF-16 code units. Three more are portability: the start script ends on macOS with a bad substitution, because${name^^}is bash 4 syntax; Docker Compose v5 needsCOMPOSE_PROGRESS=quietwhere a flag would be rejected by older versions; and a health check that shelled out topython3breaks on Git Bash, where that name is a Microsoft Store alias that exits 49. - 05
Thirty-three contributors, and model names in the trailers
All 451 commits carry a linked account, and 33 people have contributed. The largest share is davidmckayv with 126, then kevin9327 with 91, Ayush7614 with 48 and zopeVaibhav with 37; a workflow bot accounts for 12 and Renovate for 6. What makes the history unusual is the trailers: 242 co-author trailers, and after David McKay, credited on 130 of them, the next largest group is a model. Claude Opus 5 appears on 41, Claude Opus 5.5 on 8, Claude Fable 5.1 on 5, Claude Opus 5 in a million-token context on 3, Claude Opus 4.8 on 3 and Claude Fable 5 once, with one mention of Warp and two of something called pi. Review is automated too: on eight of kevin9327’s pull requests the only comment is a Codex connector reporting that it has reached its usage limits for code reviews. Outside contributors are not waved through — on #649, which makes one JSON file the single place that names a provider’s key variable, endpoint variable and default model for both TypeScript and Python, a commenter says he checked it on
mainat a given commit rather than taking it on trust, and then points at the two tests that pin each language’s provider list to that file. Two further pull requests, #670 and #671, arrive from an automated security scanner, and the maintainer answers both by reproducing the failures, pushing a follow-up commit and adding a test that fails against the old code. - 06
The architecture document says what does not work yet
There are ten documents under
docs/, led by a 54 KB configuration reference and a 28,069-character architecture document, and the second is worth reading for what it admits rather than what it promises. It records thatchannels.allowed_groupsis declared and stored but takes no part in the access decision, becauseusers.groupsis never populated by any sign-in path — so a group rule has nothing to evaluate and should be treated as a declaration waiting on the identity provider rather than a control that is running. It calls the Activity tab a window rather than a record, since it lives in the browser and is gone on reload while the audit trail is server-side and survives restarts. It explains that a saved file contributes its path and size and never its contents, matching a write route that declines to echo them because a Bot may be saving something it was told in confidence. It notes that computer actions carry no initiator, and correctly so, because they are executed by the person’s own browser session. The desktop side shows the same habit from the other direction: a commenter points out that the macOS signing workflow holds no certificates on purpose, leaves the ad-hoc identity-in place, and tracks signing and notarization as a named, deliberately deferred step rather than an oversight.
Adjacent records
All records →No. 077
Lody
A workspace where a team shares the coding agents it already runs: connect a machine, bring Claude Code, Codex, Kimi or any other agent that speaks the protocol, and dispatch work from desktop, phone, web or terminal while sessions delegate to each other and the code stays on the machine its owner connected.
No. 073
Zeron
A Rust desktop app that runs the coding agents you already use — Claude Code, Codex, Cursor, Devin, Grok, Hermes, Pi and Antigravity — on your own machine, with no account required, and syncs the sessions to your other devices only if you sign in.
No. 070
OpenChatCut
A local-first video editor whose editing surface is a conversation: the built-in agent and external Codex or Claude Code sessions call the same editing tools the interface itself uses, so every change lands on a real multi-track timeline as a clip, transition, caption, effect or audio item that can still be dragged, undone and exported. Projects and media stay on the machine, and preview and final render both come out of Remotion.