Kobako: Script Execution
With standard output in place, whether a script runs correctly can finally be verified, which means we can start genuinely running Ruby inside Kobako’s sandbox environment.
With standard output in place, whether a script runs correctly can finally be verified, which means we can start genuinely running Ruby inside Kobako’s sandbox environment.
Having sorted out the basic conditions for running mruby on WebAssembly, it’s time to start thinking about more practical uses. At the very least, a script should be able to run and print a message like Hello World, but mruby itself wasn’t designed with the WASI (WebAssembly System Interface) environment in mind, so many Gems simply can’t be used.
In Kobako: Exchanging Memory in WebAssembly we solved the problem of Ruby and WebAssembly interacting, and gave Ruby and mruby the ability to exchange values with each other, which basically cleared up the “sandbox” problem.
Kobako’s development is still at a very early stage, though, because there are plenty of problems that only show up in real use, and one of them is the problem of “deciding how to stop.”
Sumitsubo is the tool I use to turn a “spec” into code that can actually be verified. In the early attempt covered by Sumitsubo: Turning Specs into a Linter I used JSON to describe the spec, but while looking into whether similar tools already existed, I unexpectedly found that making Markdown itself the structured source of information lets you design the tool by writing documents.
Midway through building Kobako, the question of whether the spec and the implementation were still aligned kept bothering me. Spec-Driven Development is ideal in theory, but language models are not deterministic by design, and even with a spec to refer to they still drift, or leave things out.
So Sumitsubo came together recently as a general-purpose checking tool.
The past few posts covered how MessagePack was chosen, but interacting through WebAssembly works a little differently from interacting with a native extension.
The reason is that what we’re building is a sandbox to isolate untrusted code, which means the operations are essentially close to one-directional. Code running inside the sandbox cannot touch any of the host’s memory, so the method calls we originally had in mind need some special handling.
Last weekend at COSCUP I gave a talk, “Let AI Take Over Your Application: Building a Seamless, Microsecond-Scale Ruby Sandbox,” covering some of the considerations behind Kobako’s design. During the Q&A afterwards, someone asked: in an era where AI makes the language matter less, why pick Ruby?
The short answer is simply “I like writing Ruby.” But there are much deeper considerations in how Kobako is designed.
Choosing mruby as the sandbox language came out of ruby.wasm’s limitations, but sharing the same language standard (ISO/IEC 30170:2012) with CRuby doesn’t mean the goal comes easily. Compared to CRuby, mruby comes with plenty of restrictions.
Those are the trade-offs mruby has to make to run in environments like embedded systems, and the lightweight nature that comes with them happens to be an advantage for an Embedded Sandbox. It turns the idea of embedding a Ruby sandbox into any language into a viable option.
I’ve been working on Kobako for a few months now, and I’ll also be presenting the project at COSCUP. The process has been interesting enough that I think it deserves a series of posts about how I set out to build a sandbox for the AI era.
By the time of writing, there have been around 20 releases. From here on I’ll walk through Kobako’s evolution as close to chronological order as I can, along with the decisions made along the way.
Kobako is a sandbox I recently built on WebAssembly and mruby for the Ruby ecosystem, in support of Harness Engineering, to fill the gap where AI-written code has no safe environment to run in.
I already introduced Kobako’s design in a previous post, so this time I want to talk about performance. In its early versions, the Cold Start (the initial startup) took roughly 500 ms. That’s a lot slower than the 200 ms response time you’d usually aim for as a best practice. Even though AI generally tolerates slower responses, this isn’t about waiting on an LLM, so it still deserves to be judged by traditional API standards.