Guides / Ruby / Quickstart
Ruby quickstart
One hello.rb, five minutes. You will connect to kosa8 from Ruby, start a
sealed sandbox with no network, run a command in it, save it, break it, put it back, and delete
it.
The script on this page was run against a real kosa8 daemon 0.9.36 on an Apple silicon Mac
(macOS 26.0.1), with the kosa8 gem 0.1.2 and the system Ruby, 2.6.10. The install
steps were run from RubyGems into fresh scratch folders, both with gem install and
through Bundler, and their output is shown as it printed; the installed files were identical to
the v0.1.2 source the script ran against (ruby -I lib). The output of the
script is what that run printed. Timings are from that run, on a machine busy with other
sandboxes at the time, and will differ on yours.
What this is, in plain words
A sandbox is a small, separate computer that runs inside your computer. It has its own operating system, its own files and its own network settings. Code running inside it cannot see your files, your passwords or your network unless you allow it. If the code does something bad, the damage stays inside the sandbox, and you can throw the sandbox away.
That matters when code comes from somewhere you do not fully trust: an AI agent writing and running its own commands, a user's script on your server, a package you have not reviewed. kosa8 runs that code in a sandbox on your own machine, not in someone else's cloud.
The Ruby gem (kosa8) lets a Ruby program do all of this: create sandboxes,
run commands in them, read the output, save a sandbox's exact state and go back to it later. It
talks to the kosa8 background service (the daemon) that runs on your machine. It uses only
Ruby's standard library, so it adds no other gems to your project.
What you need
| Part | Requirement |
|---|---|
| kosa8 | Installed, with its daemon running. See Install. kosa8 version shows both parts. |
| Machine | macOS on Apple silicon, or x86-64 Linux with KVM. Sandboxes are real virtual machines. |
| Ruby | 2.6 or later (the gemspec's required_ruby_version). The Ruby that ships with macOS (2.6) works. |
| Disk | Room for the guest image the daemon downloads on first use (about 1.5 GB), plus sandboxes and checkpoints. |
$ kosa8 version kosa8 client 0.9.36 (f6124c0, darwin/arm64) kosa8 daemon 0.9.36 (up 4173s) $ ruby -v ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin25]
If the second line says the daemon is not running, start it with kosa8 daemon start
(the Mac app starts it for you).
Install the gem
This page uses version 0.1.2. Install it directly:
$ gem install kosa8 -v 0.1.2 --no-document
Successfully installed kosa8-0.1.2
1 gem installed
Or, in a project, add it to the Gemfile and let Bundler install it:
source "https://rubygems.org" gem "kosa8", "~> 0.1.2"
$ bundle install --path vendor/bundle Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Using bundler 1.17.2 Fetching kosa8 0.1.2 Installing kosa8 0.1.2 Bundle complete! 1 Gemfile dependency, 2 gems now installed. Bundled gems are installed into `./vendor/bundle` $ bundle exec ruby -e 'require "kosa8"; puts Kosa8::VERSION' 0.1.2
Use 0.1.2 or later. In 0.1.0 a command that prints nothing for 60 seconds fails with a timeout; 0.1.1 and 0.1.2 fix that and more (see the developer guide).
On macOS's own Ruby, a plain gem install wants to write to a system folder and
fails without sudo. Do not use sudo: install into your home with
gem install --user-install kosa8, use a Ruby version manager, or use Bundler with a
project folder as above. (The tests for this page used a scratch folder.) The gem's name, its
require and its module are all kosa8 / Kosa8.
The walkthrough
The steps below are parts of one file, hello.rb. The whole file, and what it printed,
is at the end. Everything after the sandbox is created sits in a begin … ensure block,
so the clean-up in step 6 always runs, even when a step fails.
Connect
# 1. Connect. With no options the client uses ~/.kosa8/run/kosa8.sock.
k = Kosa8::Client.new
puts "kosa8 #{k.version} at #{k.transport.endpoint.target}"
kosa8 0.9.36 at unix:///Users/dhruvit/.kosa8/run/kosa8.sock
Kosa8::Client.new only works out where to connect; nothing is sent until the
first call. version asks the daemon for its version, which proves the connection
works. If the daemon is not running, this is where you find out: it raises
Kosa8::ConnectionError, and the message says how to start it.
Create a sandbox with no network
# 2. Create a sandbox: a microVM with its own kernel and no network at all.
created = k.create_sandbox(name: "doc-rb-demo", egress: "deny")
sb = k.sandbox("doc-rb-demo")
begin
info = created["sandbox"]
puts "sandbox #{info['name']}: #{info['state']}, egress #{info['egress']}, booted in #{created['boot_ms']}ms"
sandbox doc-rb-demo: running, egress deny, booted in 10564ms
egress: "deny" closes the network completely. ("Egress" means traffic going
out.) The rule is enforced on your computer, outside the sandbox, so code inside cannot turn it
off. k.sandbox(name) returns a handle with the sandbox's name filled in, so later
calls are shorter. The answer is the daemon's JSON as a Ruby hash with string keys. Booting took
11 seconds here because the machine was busy; on an idle Mac it is usually a second or two.
Pull an image and run a command
# 3. Pull an image into the sandbox (fetched on the host), then run a command to completion.
sb.pull("alpine:latest")
res = sb.run("alpine:latest", ["sh", "-c",
"echo kernel $(uname -r); wget -q -T 3 -O- http://example.com || echo no network; exit 3"])
puts "exit #{res.exit_code} (ok? #{res.ok?})"
print res.stdout
exit 3 (ok? false) kernel 6.18.35 no network
Code in a sandbox runs in containers on the sandbox's own engine, so it needs an
image (a packaged set of files and programs; alpine is a tiny Linux).
pull fetches it on the host and copies it in, which works even though the sandbox
has no network. run starts a fresh container, waits for it to finish and returns a
Kosa8::ExecResult. The kernel is the sandbox's own, and the download failed, as it
should.
A command that exits with a non-zero code is a result, not an exception:
exit_code is 3 and ok? is false. An exception is raised only when the
command could not run at all.
Keep a container running
# 4. Keep a container running, and write a file in it.
cid = sb.start_container("alpine:latest", ["sh", "-c", "while :; do sleep 3600; done"])
sh = lambda do |cmd|
r = sb.exec(cid, ["sh", "-c", cmd])
(r.stdout + r.stderr).strip
end
puts "wrote /note: #{sh.call('echo v1 > /note; cat /note')}"
wrote /note: v1
run starts from a clean image every time. To keep files between commands, start
a container that stays up and exec commands in it. start_container
returns the container's id.
Checkpoint, break something, rewind
# 5. Checkpoint, break something, rewind.
cp = sb.checkpoint(label: "before the risky bit")
puts "checkpoint #{cp['checkpoint']['id']} (#{cp['checkpoint']['label']}) in #{cp['snapshot']['snapshot_ms']}ms"
sh.call("rm /note")
puts "after rm: #{sh.call('cat /note')}"
rw = sb.rewind
puts "rewound to #{rw['checkpoint']['id']} in #{rw['restore_ms']}ms"
puts "after rewind: /note = #{sh.call('cat /note')}"
checkpoint doc-rb-demo-ckpt-1 (before the risky bit) in 520ms after rm: cat: can't open '/note': No such file or directory rewound to doc-rb-demo-ckpt-1 in 556ms after rewind: /note = v1
A checkpoint saves the whole running sandbox: memory, running programs and disks.
rewind with no arguments goes back to the latest one. The sandbox returns under
the same name with the same container still running, so the same container id works and the
deleted file is back. Checkpoints are kept in the same list the kosa8 command line uses, so
kosa8 rewind doc-rb-demo would have done the same.
Delete
ensure
# 6. Always clean up, even if a step above failed.
puts "deleted: #{sb.delete}"
end
deleted: {"ok"=>true, "checkpoints_dropped"=>1}
delete removes the sandbox and, unless you pass
keep_checkpoints: true, its checkpoints too: here, the one taken in step 5.
The whole file
require "kosa8"
# 1. Connect. With no options the client uses ~/.kosa8/run/kosa8.sock.
k = Kosa8::Client.new
puts "kosa8 #{k.version} at #{k.transport.endpoint.target}"
# 2. Create a sandbox: a microVM with its own kernel and no network at all.
created = k.create_sandbox(name: "doc-rb-demo", egress: "deny")
sb = k.sandbox("doc-rb-demo")
begin
info = created["sandbox"]
puts "sandbox #{info['name']}: #{info['state']}, egress #{info['egress']}, booted in #{created['boot_ms']}ms"
# 3. Pull an image into the sandbox (fetched on the host), then run a command to completion.
sb.pull("alpine:latest")
res = sb.run("alpine:latest", ["sh", "-c",
"echo kernel $(uname -r); wget -q -T 3 -O- http://example.com || echo no network; exit 3"])
puts "exit #{res.exit_code} (ok? #{res.ok?})"
print res.stdout
# 4. Keep a container running, and write a file in it.
cid = sb.start_container("alpine:latest", ["sh", "-c", "while :; do sleep 3600; done"])
sh = lambda do |cmd|
r = sb.exec(cid, ["sh", "-c", cmd])
(r.stdout + r.stderr).strip
end
puts "wrote /note: #{sh.call('echo v1 > /note; cat /note')}"
# 5. Checkpoint, break something, rewind.
cp = sb.checkpoint(label: "before the risky bit")
puts "checkpoint #{cp['checkpoint']['id']} (#{cp['checkpoint']['label']}) in #{cp['snapshot']['snapshot_ms']}ms"
sh.call("rm /note")
puts "after rm: #{sh.call('cat /note')}"
rw = sb.rewind
puts "rewound to #{rw['checkpoint']['id']} in #{rw['restore_ms']}ms"
puts "after rewind: /note = #{sh.call('cat /note')}"
ensure
# 6. Always clean up, even if a step above failed.
puts "deleted: #{sb.delete}"
end
$ ruby hello.rb
kosa8 0.9.36 at unix:///Users/dhruvit/.kosa8/run/kosa8.sock
sandbox doc-rb-demo: running, egress deny, booted in 10564ms
exit 3 (ok? false)
kernel 6.18.35
no network
wrote /note: v1
checkpoint doc-rb-demo-ckpt-1 (before the risky bit) in 520ms
after rm: cat: can't open '/note': No such file or directory
rewound to doc-rb-demo-ckpt-1 in 556ms
after rewind: /note = v1
deleted: {"ok"=>true, "checkpoints_dropped"=>1}
With Bundler, run it as bundle exec ruby hello.rb. (The test ran it as
ruby -I ~/kosa8-rb/lib hello.rb, against the v0.1.2 source, which is the same code as
the published gem.)
Look at it from the command line
The gem and the CLI drive the same daemon and share the same checkpoint list, so you can watch what your program does, or finish a job by hand:
$ kosa8 sandbox ls $ kosa8 checkpoint ls doc-rb-demo $ kosa8 audit ls -n 10 # your program's calls, recorded as actor "tool"
Restoring a saved machine needs the screen to be unlocked. The checkpoint succeeds and the
rewind fails with macOS blocks restoring a VM while the screen is locked; unlock and
retry. By then the rewind has already removed the sandbox; the checkpoint's snapshot is
intact, and the Kosa8::CheckpointError message ends with the
restore_snapshot(…) call that brings it back. See
Ruby errors and troubleshooting.
Ruby developer guide: every capability in detail, with recipes. Errors and troubleshooting and the API reference list everything else.