Turn protobuf RPCs into capabilities a model can compose inside a sandbox.
Ordinary tool use makes a model work one call at a time: fifty tasks, fifty round trips, every intermediate result back through the model. Code mode has the model write a program instead. The program loops and filters at machine speed inside a sandbox, and only the final answer returns.
const { todos } = codemode.execute("todos.list", {});
const pending = todos.filter(todo => !todo.completed);
codemode.execute("todos.complete", { id: pending[0].id });
return "completed " + pending[0].title;The sandbox cannot reach your database, network, or secrets. It gets one function, and that function goes back to Go, which checks a generated registry before anything runs.
go get github.com/mukezhz/codemodeAnnotate a protobuf service with (codemode.namespace) and a
(codemode.description) per method, then:
full, err := registry.FromFiles(ordersv1.File_orders_v1_orders_proto) // 1. registry from generated Go
// 2. your code: the only place that touches data
execute := func(ctx context.Context, rpc string, input json.RawMessage) (json.RawMessage, error) {
switch rpc {
case "orders.v1.OrderService/List":
return listOrders(ctx, input)
}
return nil, fmt.Errorf("RPC %q is not implemented", rpc)
}
// 3. per request: only what this caller may call
allowed := full.For(capability.Caller{Roles: user.Roles, Permissions: user.Permissions})
mode, err := codemode.New(allowed, capability.Keyword(allowed), execute)
// 4. model writes a program, sandbox runs it
model, err := llm.NewFromEnv() // ANTHROPIC_API_KEY or OPENAI_API_KEY
runner, err := sandbox.NewFromEnv(sandbox.Options{}) // in-process JS by default
result, err := agent.Run(ctx, mode, model, "how much have paid orders brought in?",
agent.Options{Runner: runner})
fmt.Println(agent.Answer(result.Answer))Blank-import the backends you want (llm/claude, sandbox/goja, …); nothing
else is linked. Quickstart builds this as
complete files in fifteen minutes.
mise install
make demo-todoRuns end to end with no API keys and no database: search falls back to keyword ranking, and a fixed program stands in for the model. Then give a model the pen:
export ANTHROPIC_API_KEY=...
go run ./examples/todo -request "finish anything about docs"For something complete — a task tracker with a web UI, SQLite storage,
twenty-three capabilities, per-user scoping, and charts rendered from a prompt —
see examples/taskreport. Go is the only requirement;
the frontend is built and committed:
go run ./examples/taskreport -serve :8080 \
-base-url https://api.deepseek.com/v1 -api-key sk-... -model deepseek-chatAsk it "what is overdue, and which of it is high priority?" and one model turn produces one program, which returns a document the browser renders as stat cards, a chart, and a table. Works against any OpenAI-compatible endpoint.
Each example has a step-by-step README: examples/todo
(no key, no database, also an MCP server for Claude Code) and
examples/taskreport (web UI, roles, rate
limits, audit log). Both run programs in-process by default; add
SANDBOX_ISOLATION=process after make worker to run them in codemode-worker.
Full docs are a VitePress site under docs/.
Get started
- Quickstart — an empty directory to a model answering, in eight steps
- Integration guide — every integration choice, explained
- Adding it to an existing app — a CRM walked through: contract, adapter, roles, tenants, confirmed writes, and a non-Go CRM via a sidecar
Ship it
- Development and production · Running the worker — same binary, different isolation
- What your binary links — the blank imports, and why
- Permissions · Roles and tenants — roles in the contract, rows in your code
- Writes and retries · Caching programs
Grow it
How it works
Reference
make docs # serve locally with hot reload
make docs-build # build the static site| Path | What it is |
|---|---|
proto/ |
codemode.proto: the service and method annotations (namespace, description, roles, permissions), embedded for consumers |
registry/ |
Builds a registry from linked descriptors or a Buf descriptor set; CLI in cmd/codemode-registry/ |
gen/ |
Generated Go for codemode.proto. Each example generates into its own tree |
capability/ |
Registry types and the vector index; backends in sqlitestore/ and pgstore/ |
codemode/ |
The two-method public interface: Search and Execute |
llm/ |
The Client interface; providers in claude/ and openai/ |
sandbox/ |
The Runner interface; engines in goja/, starlark/, lua/ |
agent/ |
Search → prompt → model → sandbox, with a repair loop |
examples/todo/ |
The smallest end-to-end example, also served over MCP |
examples/taskreport/ |
A complete app: SQLite tasks, 23 capabilities, roles, reporting |
docs/ |
The published documentation site |
notes/ |
Internal writing: PRDs, milestones, decisions. Never published. |
| Command | What it does |
|---|---|
make generate |
Regenerate Go from every proto module and rebuild the example's registry.json |
make test |
Buf lint plus all Go tests |
make test-vector |
SQLite vector search; needs SQLITE_VECTOR_EXTENSION |
make test-consumer |
Checks a consumer links only what it blank-imports |
make test-env |
Checks no library package reads the environment outside env.go |
make test-postgres |
pgvector search against a throwaway container |
make docs |
Serve the documentation site |
Configuration is entirely environment-driven and entirely optional — see
.env.example.
Warning
The default engines run in-process and are not a defence against a determined
attacker. For programs steered by untrusted users, use process isolation (SANDBOX_ISOLATION=process),
and a container for multi-tenant input. See SECURITY.md.