camelAI runs its coding agent inside a Cloudflare Durable Object
camelAI — an AI coding assistant platform built on Cloudflare Workers and Durable Objects
At a glance
- What is it?
- Each chat thread in camelAI is a Durable Object with its own coding agent, project files split between Durable Object SQLite and R2, and a Docker Compose self-hosting target that gives up outbound email and multi-node failover.
- Who is it for?
- camelAI suits teams already committed to Cloudflare who want chat-scoped coding agents with durable project files and a single-machine self-hosting path. It does not suit anyone who needs outbound email from the running instance or multi-node failover, because the Compose target leaves both out on purpose.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Every chat thread is a Durable Object, not a session on a server
Each chat thread in camelAI gets its own Durable Object with its own agent loop, and that one decision shapes most of what the platform can do. A React Router application served over SSR and browser HTTP/SSE sits in front of a main Cloudflare Worker, and the Worker routes work into a ChatThreadDO. There is no long-lived VM behind a conversation and no container kept warm for its duration. The object is the state, so a thread survives the gap between two messages without anyone rehydrating a context window by hand.
What runs in that object is not a vendor CLI in a wrapper. The project is explicit that the harness is its own, built from pi's lower-level agent loop and state-management libraries, and that it is not Claude Code or Codex. Swappable underneath it are Anthropic, OpenAI, OpenRouter, Bedrock, and custom endpoints, configured from organization settings once the app starts. Those providers supply tokens. The loop that picks the next tool, and the set of things that tool is allowed to reach, is code in workers/main/, alongside the MCP wiring and the sandbox services.
That split has a practical consequence for anyone evaluating the project. Swapping a model changes cost and latency and nothing else. Changing what the agent can do means changing the harness or the tools it exposes, which is a fork-level decision, not a settings-page one.
The agent writes JavaScript because there is no bash to call
The agent inside the Durable Object uses native file tools and writes JavaScript instead of bash. Code Mode then runs that JavaScript in fresh V8 isolates, and the platform surface available to it is made explicit rather than ambient: the isolate gets defined platform and connection methods, not a machine it can wander across. Credentials stay outside the execution sandbox, so a script an agent generates has no path to the signing and encryption secrets you placed in `.dev.vars`.
For a reader planning workloads, this is the constraint to sit with. There is no shell, which means no `pip install` inside the agent loop and no arbitrary process spawn, and any task that needs a real Linux userland has to leave the isolate. The project routes those tasks deliberately rather than pretending they do not exist. A short sentence in the README says the agent is not Claude Code or Codex, and the architecture section is where you find out what replaced the missing shell: a separate execution path for code, and containers for everything that cannot be expressed as code.
The gap between an isolate and a shell is also where behaviour gets surprising. Fresh isolates mean no state carried between two tool calls except what the agent writes to the workspace, so anything a script assumed would persist in a process has to come back through the filesystem. Code Mode and the workspace Durable Object are coupled for exactly that reason.
Workspace files split at a size boundary into SQLite and R2
Project files live in WorkspaceFilesystemDO, and the split is by size rather than by type. Small files sit in Durable Object SQLite. Larger files go to R2. Git history comes from Cloudflare Artifacts, so version control for a project is a Cloudflare product rather than something the repository carries.
Around that storage sit the other Workers in the repository, and each one has a job you can point at. `workers/dispatcher/` routes requests to published user applications, which is the piece that makes a preview or a deployed app reachable. `workers/app-usage-guard/` does usage monitoring and reversible app quarantine, meaning a runaway published app can be pulled back rather than deleted. `workers/discord-bridge/` holds the Discord Gateway connection and its control Worker. The main Worker directory also carries the HTTP and SSE transports and the MCP layer.
Publishing runs through Workers for Platforms, and the feature list pairs that with isolated application builds, notebook analysis, and SQL execution. Integrations named at the top level cover APIs, databases, email, Slack, and Discord. Read that as a workspace platform rather than a chat toy: the thread, the files, the built application, and the routes serving it are all separate deployable pieces in one repository.
Linux is reserved for builds, notebooks, and database queries
Container work is not the default and is not the agent's home. The project reserves Linux for short-lived jobs that genuinely need it, and names three: application builds, notebook analysis, and database queries. Those run in dedicated Cloudflare sandbox containers, created for the job and gone afterwards.
Docker therefore appears twice in the prerequisites, once for sandbox-backed features and once for agent evals, and the evaluation suite is gated on it as a result. Running an eval by manifest ID is a separate command from the unit tests:
bun run test:eval <eval-id>Agent evals need Docker and additional credentials, which makes them a heavier thing to wire up than the Vitest suites. The practical reading of the architecture is that the fast path is deliberately narrow. Reading a file, editing a file, and running JavaScript against a connection stay inside the Durable Object. Anything that has to install a package, spin up a notebook, or push a query at a live database crosses over into a container and pays for it in startup time and in configuration you have to supply.
Cloning the repo and seeding a Local Dev identity
The prerequisites are Node.js 22 or newer, Bun, a Cloudflare account with access to the development resources, and Docker for the sandbox-backed features. The install is a clone, a frozen install, and a secrets file:
git clone https://github.com/qaml-ai/camelAI.git
cd camelAI
bun install --frozen-lockfile
cp .dev.vars.example .dev.varsThe placeholder signing and encryption secrets in `.dev.vars` have to be replaced before anything signs a token. For hosted model access you also add a development Cloudflare AI Gateway token. The alternative is to leave that out and configure Anthropic, OpenAI, OpenRouter, Bedrock, or a custom provider from the organization settings after the app starts, which is the path to take if you would rather not route model traffic through a gateway.
Two dev commands, and they are not interchangeable. The local-auth one seeds a `Local Dev` user, organization, and workspace, and is restricted to the Vite development server:
bun run dev:local-authOpen `http://localhost:3001` and you are inside a pre-populated workspace with no OAuth round trip. To exercise the normal OAuth flow, run `bun run dev` instead. The port defaults to 3001 and moves with `VITE_DEV_PORT`.
Four test entry points, and one of them needs a real machine
The command list is unusually explicit about which layer a change lives in. `bun run typecheck` generates route types and runs the TypeScript checks, `bun run lint` covers source and import checks, and the test scripts split along the architecture boundary: `bun run test:run` for application and shared-library tests, `bun run test:workers` for Worker and Durable Object tests, `bun run test:all` for both, and `bun run test:e2e` for Playwright. `bun run test` is Vitest in watch mode.
When a change touches a Durable Object, the project asks for a focused run rather than the whole suite:
bun run test:workers -- <test-file>The build command is not a plain bundle either. In package.json, `build` chains `build:cf` with `check:pi-bedrock-build`, a script that inspects the pi Bedrock build, so a production build is also a check on the harness dependency. The same manifest names `start` as `node scripts/wrangler-dev.mjs`, which is how the built worker gets served locally.
package.json calls it chiridion-app and ships six wrangler configs
The manifest is named `chiridion-app`, version 0.1.0, marked private, MIT licensed, and declared as an ES module. The mismatch between that name and the camelAI branding is worth knowing before you grep a log line or file a traceback.
Six wrangler configuration files sit at the repository root: a base `wrangler.jsonc` plus prod, staging, and test variants, and two developer-named development configs, `wrangler.dev-illiana.jsonc` and `wrangler.dev-miguel.jsonc`. That pair is the clearest signal in the tree that per-person Cloudflare bindings are the normal working arrangement, and that a fresh clone is not going to have your bindings in it. There is a `.dev.vars.example` to start from, and `cloudflare-env.d.ts` plus `cloudflare-container-native.d.ts` for the binding types.
The rest of the tree is operational rather than illustrative. `migrations/` holds database migrations, `infra/` holds the self-hosting and database egress infrastructure, `sandbox/` holds agent skills and project scaffold templates, and `e2e/` holds Playwright tests with separate configs for signup and for staging billing. Slack manifests ship in four flavours, JSON and YAML, plain and staging. There is a `skills-lock.json` and a `.gitleaksignore`, which suggests secrets scanning is part of the normal commit path, and `CUSTOMER_SSO_SETUP_GUIDE.md` sits next to the README as a separate enterprise document.
Self-hosting gives up outbound email and multi-node failover
There is a supported single-machine Docker Compose target, aimed at private networks and on-premises evaluation, driven by three scripts:
bun run selfhost:init
bun run selfhost:doctor
bun run selfhost:upThe capability list is long and specific: the web application, authentication, model providers, durable state, project source files and local history, project builds and deployments, notebooks, SQL execution, and browser rendering. What is missing is stated just as plainly. Outbound email and password-email verification are intentionally unavailable, and so is multi-node failover. That is a product decision rather than an omission, and it is the single fact most worth checking against your own requirements before evaluating the rest.
Operator tooling is more granular than the three entry points. `selfhost:doctor` is the validation step, and there are dedicated smoke scripts for artifacts and for images, plus a container smoke set that runs in four passes named mount, project, analysis, and db-query, which map one to one onto the sandbox jobs described earlier. `selfhost:d1:migrate` handles schema changes. The Compose files come in source and release-image variants, with Caddy and Pomerium fronting them, and the README's repository table hands the detailed contract, backup, and upgrade procedure to SELF_HOSTING.md and infra/selfhost/README.md rather than restating it.
Editorial conclusion
camelAI suits teams already committed to Cloudflare who want chat-scoped coding agents with durable project files and a single-machine self-hosting path. It does not suit anyone who needs outbound email from the running instance or multi-node failover, because the Compose target leaves both out on purpose. Verify two things before adopting: whether your agent work needs a real shell, since the harness writes JavaScript instead of running bash, and whether your model provider is reachable from inside a Durable Object.
Frequently asked questions
How do I use camelAI?
Clone the repository, run `bun install --frozen-lockfile`, copy `.dev.vars.example` to `.dev.vars` and replace the placeholder signing and encryption secrets, then start the app with `bun run dev:local-auth` and open http://localhost:3001. You need Node.js 22 or newer, Bun, a Cloudflare account, and Docker for the sandbox-backed features.
What is an alternative to camelAI?
The project names Claude Code and Codex as the things it is not. Those are separate coding agents with their own harnesses, while camelAI runs its own harness built on pi's lower-level agent loop inside a Durable Object, and only the model provider underneath is swappable.
Where does camelAI keep project files and their history?
Project files live in WorkspaceFilesystemDO, with small files in Durable Object SQLite and larger ones in R2. Git history comes from Cloudflare Artifacts rather than from a repository the workspace carries.
What does the self-hosted camelAI build leave out?
Outbound email and password-email verification are intentionally unavailable, and multi-node failover is not supported. The Compose target covers the web application, authentication, model providers, durable state, project files and local history, builds and deployments, notebooks, SQL execution, and browser rendering.
Which tasks in camelAI need a Linux container?
Application builds, notebook analysis, and database queries run in short-lived Cloudflare sandbox containers. The agent itself stays in a Durable Object and runs JavaScript in fresh V8 isolates, so Docker is needed for the sandbox-backed features and for agent evals.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/qaml-ai-camelai)