Kobako: Letting Agents Safely Operate Rails
This article is translated by AI, if have any corrections please let me know.
At RubyKaigi 2026 this year, while chatting with friends about Harness Engineering and the Sandbox aspect of it, I realized that Ruby still lacks a solid solution in this space. On the second night, I happened to be assigned to the druby (Ruby’s built-in distributed object system) group at the Code Party — and that’s how the idea for Kobako was born.
Choosing a Sandbox: Why WebAssembly
There are many routes to build an environment where AI Agents can run — Virtual Machines, container technology, and WebAssembly all work.
For an overview of the available routes, I recommend reading Agent Sandbox Architecture: On Two Design Patterns and Seven Isolation Approaches.
The technology you choose shapes the road ahead and what you can ultimately accomplish. After surveying existing solutions and technologies, I ended up having to build my own.
For example, Shopify has mruby-engine, which is one of the more ideal options. But this approach only guarantees that the Host and Guest are not directly interconnected — if the running Ruby script targets memory or launches certain attacks, it’s easy to break through, making it not quite strong enough for Agent use.
If you choose Virtual Machines or container technology, the biggest difficulty is the strict environment requirements, which can be quite limiting for small projects or development. So Kobako ultimately chose the WebAssembly route, which provides VM-like isolation through the WASI standard. Compared to running mruby directly, you sacrifice some startup and runtime speed in exchange for much better isolation — a reasonable trade-off.
Later, Shopify also invested in wasmtime so that ruby.wasm could replace their original scripting functionality. ruby.wasm was therefore one of the options I considered, but its inherent limitations eventually pushed me down a different path.
WASI’s Limitations and Kobako’s Trade-offs
WASI stands for WebAssembly System Interface. Simply put, it’s an architecture that gives us a UNIX-like interface inside the WebAssembly environment. ruby.wasm, for example, uses the Preview 1 version of the standard and is currently testing Preview 2.
However, this interface is poorly designed. With Preview 1, aside from Stdin / Stdout / Stderr and a VFS-like (Virtual File System) mechanism, there’s basically not much you can do — it doesn’t even support I/O-style behaviors. This is why, when evaluating whether to build Kobako on top of ruby.wasm, I had to abandon that route.
Even if ruby.wasm were usable, it wouldn’t necessarily be a good choice — it requires loading a Ruby environment of roughly 20 MB, and the startup time might even be slower than container technology.
WASI currently has versions Preview 1 through Preview 3, and each comes with different headaches around building and compilation. In the end, Kobako chose Preview 1 — the more stable and widespread standard — to implement. The key difference from ruby.wasm is an additional ABI (Application Binary Interface) that enables an in-memory RPC channel, letting the Ruby script inside the Sandbox call methods provided by the Host.
This is the main reason for not choosing ruby.wasm: adding an ABI would require recompiling ruby.wasm entirely, and the development cost and difficulty behind that would skyrocket. mruby’s design for embedded systems, on the other hand, turns out to fit perfectly.
At this year’s RubyKaigi there was also a talk titled Uzumibi: Reinventing mruby for the Edges, which echoes Kobako’s goal of implementing something like Cloudflare’s Code Mode idea for the MCP problem. Both start from running in Edge environments, and mruby’s footprint is just right for that scale.
RPC: How the Sandbox Talks to the Host
What inspired Kobako from druby is the clever way druby leverages Ruby’s language features to implement an RPC mechanism. Here’s an example of how Kobako actually uses it:
1User = Data.define(:name)
2
3sandbox = Kobako::Sandbox.new
4sandbox.define(:App).bind(:User, User.new(name: "Aotoki"))
5
6sandbox.run(<<~RUBY)
7 "Hello, #{App::User.name}"
8RUBY
9# => "Hello, Aotoki"Inside the Sandbox, how does it know that the Host provides App::User with a #name method?
Every time Sandbox#run is called, Kobako automatically injects code like this:
1class App::User < Kobako::RPC::Client; end
2# ...And that’s it — nothing more is needed. Our Sandbox environment naturally knows it can call #name, because Ruby supports this kind of implementation out of the box. Kobako::RPC::Client makes it work like this:
1class Kobako::RPC::Client
2 def method_missing(name, *args, &block)
3 __kobako_rpc_call__(name, *args)
4 end
5endThis __kobako_rpc_call__ uses an ABI defined in Rust, which is responsible for forwarding the method call out of WebAssembly and routing the reply back in. Through this mechanism, we strike a balance between isolation and extensibility.
Applied to a Rails scenario: integrating AI would normally require redesigning and defining tools from scratch. But if you already have well-designed Service Objects with proper encapsulation and authorization, you only need to use RBS to describe the interface to the AI, and the Agent can automatically generate scripts that operate them.
1sandbox = Kobako::Sandbox.new
2ns = sandbox.define(:Merchant)
3ns.bind(:Product, ProductManagementService.new(actor: current_user))
4# - ProductManagementService#where
5# - ProductManagementService#update
6# - ...
7
8# Agent Tool
9def execute_code(code)
10 sandbox.run(code)
11end
12
13# Merchant::Product.where(name: "%macOS%")
14# => [#<Product id=1>, #<Product id=2>]This is exactly why Cloudflare had to leverage the underlying technology of Cloudflare Workers to build Code Mode — it lets you quickly integrate large numbers of APIs without having to expose them as a long list of tools.
Cloudflare Code Mode offers a thought-provoking choice — using the LLM’s own programming ability to solve the problem. Kobako, on the other hand, draws on experience from the Ruby ecosystem to make this kind of integration even more seamless. The Kobako project already includes an examples/codemode example, where you can use any OpenAI-compatible API or local Ollama / LMStudio with Gemma 4 to try out KeyValue Store + WebFetch. Worth giving it a try if you’re interested.
Enjoyed this article? Buy me a milk tea 🧋