Guides / Go / Quickstart
Go quickstart
One main.go, five minutes. You will connect to kosa8 from Go, start a
sealed sandbox with no network, run a command in it, save it, break it, put it back, and delete
it.
The program on this page was run against a real kosa8 daemon 0.9.36 on an Apple silicon Mac
(macOS 26.0.1), with github.com/kosa8ai/kosa8-go v0.1.2 fetched from the Go module proxy
and built with go1.26.9 darwin/arm64. The output shown is what that run printed. Timings are
from that run, on a machine that was 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 Go SDK (kosa8-go) lets a Go 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 Go's
standard library, so it adds no other dependencies 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. |
| Go | 1.21 or later (the module's go line is 1.21). |
| 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 3417s)
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 module
$ mkdir hello-kosa8 && cd hello-kosa8 $ go mod init hello-kosa8 $ go get github.com/kosa8ai/[email protected] go: downloading github.com/kosa8ai/kosa8-go v0.1.2 go: added github.com/kosa8ai/kosa8-go v0.1.2
The package name is kosa8. Import it as
kosa8 "github.com/kosa8ai/kosa8-go".
The walkthrough
The steps below are parts of one file, main.go. The whole file, and what it printed,
is at the end. All the work happens in a run function that returns an error, so that
the clean-up in step 2 always runs, even when a later step fails.
Connect
// 1. Connect. With empty Options the client uses ~/.kosa8/run/kosa8.sock.
k, err := kosa8.New(kosa8.Options{})
if err != nil {
return err
}
v, err := k.Version(ctx)
if err != nil {
return err
}
fmt.Println("kosa8", v, "at", k.Endpoint().Target())
kosa8 0.9.36 at unix:///Users/dhruvit/.kosa8/run/kosa8.sock
kosa8.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: the error says so, and 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, err := k.CreateSandbox(ctx, kosa8.CreateOptions{Name: "doc-go-demo", Egress: "deny"})
if err != nil {
return err
}
sb := k.Sandbox("doc-go-demo")
// Always clean up, even if a later step fails.
defer func() {
out, err := sb.Delete(ctx, kosa8.DeleteOptions{})
fmt.Println("deleted:", out, err)
}()
info := created["sandbox"].(map[string]any)
fmt.Printf("sandbox %s: %s, egress %s, booted in %vms\n", info["name"], info["state"], info["egress"], created["boot_ms"])
sandbox doc-go-demo: running, egress deny, booted in 11965ms
Egress: "deny" closes the network completely. 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 Go map (kosa8.Info). Booting took 12 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.
if _, err := sb.Pull(ctx, "alpine:latest"); err != nil {
return err
}
res, err := sb.Run(ctx, "alpine:latest", []string{"sh", "-c",
"echo kernel $(uname -r); wget -q -T 3 -O- http://example.com || echo no network; exit 3"}, nil)
if err != nil {
return err
}
fmt.Printf("exit %d\n%s", res.ExitCode, res.Stdout())
exit 3 kernel 6.18.35 no network
Code in a sandbox runs in containers on the sandbox's own engine, so it needs an image.
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
its output. 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 error:
err is nil and res.ExitCode is 3. err is set 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, err := sb.StartContainer(ctx, "alpine:latest", []string{"sh", "-c", "while :; do sleep 3600; done"})
if err != nil {
return err
}
sh := func(cmd string) string {
r, err := sb.Exec(ctx, cid, []string{"sh", "-c", cmd}, nil)
if err != nil {
return "error: " + err.Error()
}
return strings.TrimSpace(r.Stdout() + r.Stderr())
}
fmt.Println("wrote /note:", sh("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.
Checkpoint, break something, rewind
// 5. Checkpoint, break something, rewind.
cp, err := sb.Checkpoint(ctx, "before the risky bit")
if err != nil {
return err
}
fmt.Printf("checkpoint %s (%s) in %vms\n", cp.Checkpoint.ID, cp.Checkpoint.Label, cp.Snapshot["snapshot_ms"])
sh("rm /note")
fmt.Println("after rm:", sh("cat /note"))
rw, err := sb.Rewind(ctx, "")
if err != nil {
return err
}
fmt.Printf("rewound to %s in %vms\n", rw.Checkpoint.ID, rw.RestoreMS)
fmt.Println("after rewind: /note =", sh("cat /note"))
return nil
checkpoint doc-go-demo-ckpt-1 (before the risky bit) in 908ms after rm: cat: can't open '/note': No such file or directory rewound to doc-go-demo-ckpt-1 in 487ms after rewind: /note = v1 deleted: map[checkpoints_dropped:1 ok:true] <nil>
A checkpoint saves the whole running sandbox: memory, running programs and disks.
Rewind with "" 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. The last line is the deferred Delete from step 2: it removed
the sandbox and its one checkpoint.
The whole file
package main
import (
"context"
"fmt"
"os"
"strings"
kosa8 "github.com/kosa8ai/kosa8-go"
)
func main() {
if err := run(context.Background()); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
func run(ctx context.Context) error {
// 1. Connect. With empty Options the client uses ~/.kosa8/run/kosa8.sock.
k, err := kosa8.New(kosa8.Options{})
if err != nil {
return err
}
v, err := k.Version(ctx)
if err != nil {
return err
}
fmt.Println("kosa8", v, "at", k.Endpoint().Target())
// 2. Create a sandbox: a microVM with its own kernel and no network at all.
created, err := k.CreateSandbox(ctx, kosa8.CreateOptions{Name: "doc-go-demo", Egress: "deny"})
if err != nil {
return err
}
sb := k.Sandbox("doc-go-demo")
// Always clean up, even if a later step fails.
defer func() {
out, err := sb.Delete(ctx, kosa8.DeleteOptions{})
fmt.Println("deleted:", out, err)
}()
info := created["sandbox"].(map[string]any)
fmt.Printf("sandbox %s: %s, egress %s, booted in %vms\n", info["name"], info["state"], info["egress"], created["boot_ms"])
// 3. Pull an image into the sandbox (fetched on the host), then run a command to completion.
if _, err := sb.Pull(ctx, "alpine:latest"); err != nil {
return err
}
res, err := sb.Run(ctx, "alpine:latest", []string{"sh", "-c",
"echo kernel $(uname -r); wget -q -T 3 -O- http://example.com || echo no network; exit 3"}, nil)
if err != nil {
return err
}
fmt.Printf("exit %d\n%s", res.ExitCode, res.Stdout())
// 4. Keep a container running, and write a file in it.
cid, err := sb.StartContainer(ctx, "alpine:latest", []string{"sh", "-c", "while :; do sleep 3600; done"})
if err != nil {
return err
}
sh := func(cmd string) string {
r, err := sb.Exec(ctx, cid, []string{"sh", "-c", cmd}, nil)
if err != nil {
return "error: " + err.Error()
}
return strings.TrimSpace(r.Stdout() + r.Stderr())
}
fmt.Println("wrote /note:", sh("echo v1 > /note; cat /note"))
// 5. Checkpoint, break something, rewind.
cp, err := sb.Checkpoint(ctx, "before the risky bit")
if err != nil {
return err
}
fmt.Printf("checkpoint %s (%s) in %vms\n", cp.Checkpoint.ID, cp.Checkpoint.Label, cp.Snapshot["snapshot_ms"])
sh("rm /note")
fmt.Println("after rm:", sh("cat /note"))
rw, err := sb.Rewind(ctx, "")
if err != nil {
return err
}
fmt.Printf("rewound to %s in %vms\n", rw.Checkpoint.ID, rw.RestoreMS)
fmt.Println("after rewind: /note =", sh("cat /note"))
return nil
}
$ go run .
kosa8 0.9.36 at unix:///Users/dhruvit/.kosa8/run/kosa8.sock
sandbox doc-go-demo: running, egress deny, booted in 11965ms
exit 3
kernel 6.18.35
no network
wrote /note: v1
checkpoint doc-go-demo-ckpt-1 (before the risky bit) in 908ms
after rm: cat: can't open '/note': No such file or directory
rewound to doc-go-demo-ckpt-1 in 487ms
after rewind: /note = v1
deleted: map[checkpoints_dropped:1 ok:true] <nil>
Look at it from the command line
The SDK 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-go-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 error message ends with the RestoreSnapshot(…) call that brings it
back. See Go errors and troubleshooting.
Go developer guide: every option and capability in detail. Errors and troubleshooting and the API reference list everything else.