A chat box is not an agent OS: five places a 30-person company breaks it
One claim — a chat UI on a single-user harness is not an agent OS — reads like a slogan until you take it apart. Five breakpoints: who is speaking, what it remembers, what survives a crash, which machine it acts on, and whether the reply was delivered. Checked against the Apache-2.0 repo, with the price attached.
In one line: a chat box on a single-user harness moves the interface and nothing else. The difference is five things — who is speaking, what it remembers, what survives the process, which machine it acts on, and whether the reply arrived.
First, the material boundary. The article’s body is behind an X login; what I could read is its own claim plus the opening paragraph. So this piece does not walk through their argument. It takes the claim as a problem and checks the technical claims against the Apache-2.0 repository (shallow clone, 5,890 files) and the docs shipped with it, including a 170-line Tradeoffs and Known Limits. Numbers are labelled by origin: published in the docs, or counted in the clone.
1. The claim, stated plainly#
Their sentence: putting a chat UI on a single-user harness, or dropping a bot into a group chat, is not an agent OS. Run either in a thirty-person company across ops, finance and legal, and it breaks in weeks.
The direction is right. The force is not in “it breaks” but in where. Five places, each with a concrete failure mode.

| Question | Chat box + single-user harness | What a team agent OS answers |
|---|---|---|
| Who is speaking | rules in the system prompt | Principal + runtime AuthZ |
| What it remembers | starts at zero, searches chat logs | structured world model, permission-scoped recall |
| The process dies | the turn’s work is gone | PostgreSQL ledger, resumes from commits |
| Where it acts | your own laptop | one isolated container per Agent |
| Was it delivered | sent is sent | Delivery intent, committed on confirm |
2. Who is speaking: permission is not a prompt#
People, Agents and internal services are all (tenant_id, uid) Principals. Authorization is evaluated at runtime in the control plane: principal, permission groups, Drive ACL anchors, action, and CEL conditions. One line in the docs is worth quoting — access is “never enforced by prompt convention”.
That distinction becomes concrete in the two Agent kinds. A Personal Assistant is paired 1:1 with a human and borrows that owner’s Drive, Pod and group grants, while keeping its own runtime identity so the audit trail shows the hands. An AI Colleague works under its own grants, and when an action exceeds its write authority — revising a protected knowledge object, say — it must post a propose_action_card and wait for a human. In shared rooms, one Agent’s broadcast reply never wakes another; delegation goes through send_to_agent, which opens an isolated peer conversation.
3. What it remembers: a graph with edges, not a pile of chunks#
The knowledge layer (Brain) is not chat excerpts in a vector index. It keeps :knowledge pages plus atomic Facts, Takes, timelines and typed brain_links edges; recall is hybrid BM25 + vector, intersected with the permissions of both the requester and the Agent. Revoke access on a source page and the claims derived from it are filtered on the next read. When new evidence contradicts an older claim, offline Dreaming records the contradiction for a human instead of quietly overwriting it.
What that costs to maintain shows up in the repository:
| Measure | Value | Source |
|---|---|---|
| Files | 5,890 | counted in the clone |
| Elixir | 651,660 lines | counted in the clone |
| TypeScript / TSX | 339,391 lines | counted in the clone |
| Rust | 49,048 lines | counted in the clone |
| Lean (formal verification) | 63,587 bytes | GitHub languages API |
| Test files | 977 | counted in the clone |
| Plugins | 15 (10 channel adapters, 3 enterprise connectors, 2 provider packs) | plugins/ |
| Docs | 42 pages + 27 design docs | docs/ |
| Licence / stars / forks | Apache-2.0 / 174 / 27 | GitHub API, at the time of writing |
4. After it dies: the database is the only source of truth#
A message becomes a Signal: source, source event id, target, attribution and authorization are recorded, duplicates are removed by (target, source, id), and only then does a Session wake or an automation job get queued. A Session runs one Run at a time, and Admission re-checks tenant, Agent state, human access, authorization and token quota every time execution starts.

The recovery semantics are unusually explicit: a monotonic epoch on the Driver fences off stale writes; an interrupted model call may repeat; committed tool results are reused; a Worker tool call whose outcome is unknown is surfaced as interrupted rather than guessed. RuntimeFabric — one Phoenix Channel per Worker — is transport, not a queue: after a disconnect, everything is read back from what PostgreSQL committed.
That is also why the control plane is Elixir. The actor model gives each Session an address, a mailbox, a lifecycle and a recovery checkpoint, and OTP supervision trees put one Agent, one connector stream or one job into its own failure domain.
5. Where it acts: one computer per Agent#

Each Agent gets its own Agent Computer Worker. No shared container, no shared Home volume, no shared cache disk, no shared credentials. Inside, shell, Codex and the headless browser all run as uid 1000, with no sudo and no inner sandbox. The tradeoffs doc says it without decoration: the container boundary is the only security boundary, and the system does not try to defend a Worker against its own Agent — because that machine’s credential represents exactly that one Agent.
Two directories, two fates: /workspace persists (the Agent’s home, kept across instance replacement), /var/cache/ankole is disposable (lost on replacement, rebuilt on demand). /drive mounts four Unified Drive views over FUSE, with Brain, Skills and the AGENTS.md contract read-only. If an instance fails to become ready within agent_workers.ready_timeout_seconds, the control plane replaces it with a fresh container and a clean cache disk.
The platform is not fixed: four Worker provider kinds ship in the core (Docker, Kubernetes, Cloudflare, Lambda), and the docs’ matrix adds Vercel and Alibaba Function Compute. On Kubernetes, an operator can attach a gVisor RuntimeClass to the Worker Pod.
6. Whether it arrived: at-least-once is the reality#
On delivery semantics the docs are more honest than most products. A webhook returning 200 means the event was received, not that the Agent answered. Streaming previews are best effort — if a process dies mid-stream, half a sentence can linger in the external chat. An ambiguous send is marked unknown and automatic retries stop, so a human can requeue it once from the Console instead of the channel getting spammed.
Two platform-level constraints are named as well: WhatsApp closes its 24-hour service window, and the adapter fails the delivery rather than calling the API (paid template messages sit outside adapter scope); and when someone types /steer mid-turn, one Turn spans two Signal rows, so tracing a reply means following the Delivery row’s ai_message_id.
7. The price#
| Decision | What it buys | What it costs |
|---|---|---|
| The control plane launches Workers (Docker socket, Kubernetes Role) | a genuinely isolated computer per Agent | the control plane is root-equivalent infrastructure and must be protected as such |
| PostgreSQL 18+ as the only ledger, Base on physical shards | recoverable, auditable results | backups must cover the object store and every Agent Home, and each shard’s recovery target must be no earlier than the control plane’s |
| One disposable disk, one that must survive | replacing an instance loses no work | durability follows the provider: Docker local volumes do not survive a lost host disk, Cloudflare’s unplanned kill can lose writes since the last snapshot, Alibaba OSS is not full POSIX |
| Three runtimes (Elixir control plane, Bun worker, Rust kernel) | each part in the language that suits it | a wider operations surface: three languages to staff |
| Self-hosted only, Apache-2.0 | data and credentials stay inside | DNS, four random secrets, SMTP or SSO, and someone to own upgrades |
One more, not in the table: the project is young (repository created April 2026), and a 0.x API still moves.
8. What a reader can check#
Four things you can run yourself before believing any “agent OS”:
- Deploy the Docker Compose form and look for a one-time activation code flow (
SETUP ACTIVATION CODE) rather than a default password into an admin panel. - Delete the permission rules from the prompt and see whether the privileged action is still refused. If it goes through, the permission model lives in the prompt.
- Kill a Worker mid-task. The job should resume from
/workspace, not restart from scratch. - Send a group message while the network is flaky. The reply should end up marked uncertain and waiting for a human, not silently vanish — and not be sent three times.
9. Open questions#
- If the control plane is root-equivalent infrastructure, where does a thirty-person company host it, and who operates it, without breaking that trust assumption or having a weekly incident?
- Brain records contradictions for a human to judge. Does that “wait for a human” step still hold at a few thousand pages, or does it have to become an automatic adjudication policy?
References#
- Original: AgentBull’s post and article (the body needs a login)
- Repository: AgentBull/ankole , Apache-2.0
- Docs: Architecture , Agent Computer Worker
- Tradeoffs: docs/TradeoffsAndKnownLimits.md