kosa8 docs

Guides / Java / Quickstart

Java quickstart

One Main.java, five minutes. You will connect to kosa8 from Java, start a sealed sandbox with no network, run a command in it, save it, break it, put it back, and delete it.

How this page was tested

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 com.kosa8:kosa8:0.1.2, built from the v0.1.2 tag (commit 14e5fc6) with mvn install, because Maven Central was still answering 404 for 0.1.2 while it was being published, into an empty local repository by Apache Maven 3.10.0. It was compiled with --release 17 and run on OpenJDK 21.0.12.1. The same program, unchanged, was also run earlier on 0.1.1 downloaded from Maven Central into an empty local repository; it needed no extra Maven flags. The output shown is what that run printed. Timings are from a machine that was busy with other sandboxes at the time, and will differ on yours. The Gradle lines were not run: Gradle was not installed on the test machine.

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 Java SDK (com.kosa8:kosa8) lets a Java 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 has no dependencies at all: one 75 kB jar that uses only the JDK.

What you need

PartRequirement
kosa8Installed, with its daemon running. See Install. kosa8 version shows both parts.
MachinemacOS on Apple silicon, or x86-64 Linux with KVM. Sandboxes are real virtual machines.
Java17 or later. The SDK reaches the daemon over a unix socket with java.net.UnixDomainSocketAddress, which arrived in Java 16, and it uses records and pattern matching, so 17 is the floor.
Build toolMaven or Gradle, or none: the jar can go straight on java -cp.
DiskRoom 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 4186s)

If the second line says the daemon is not running, start it with kosa8 daemon start (the Mac app starts it for you).

Add the dependency

The coordinates are group com.kosa8, artifact kosa8, version 0.1.2, on Maven Central. The package is com.kosa8.

Maven

<properties>
  <maven.compiler.release>17</maven.compiler.release>
</properties>
<dependencies>
  <dependency>
    <groupId>com.kosa8</groupId>
    <artifactId>kosa8</artifactId>
    <version>0.1.2</version>
  </dependency>
</dependencies>

Gradle

// build.gradle.kts
dependencies {
    implementation("com.kosa8:kosa8:0.1.2")
}

// build.gradle (Groovy)
dependencies {
    implementation 'com.kosa8:kosa8:0.1.2'
}

No build tool

$ curl -sO https://repo1.maven.org/maven2/com/kosa8/kosa8/0.1.2/kosa8-0.1.2.jar
$ java -cp kosa8-0.1.2.jar Main.java     # Java 11+ runs a single source file directly

If Maven reports NOT allowed from central for the jar, your local repository holds an old copy of Central's prefix list; see Java errors and troubleshooting.

The walkthrough

The steps below are parts of one file, src/main/java/Main.java. The whole file, and what it printed, is at the end. The work after the sandbox is created sits inside try … finally, so the clean-up in step 6 always runs, even when a later step throws.

Connect

// 1. Connect. With no settings the client uses ~/.kosa8/run/kosa8.sock.
Kosa8Client k = Kosa8Client.builder().build();
System.out.println("kosa8 " + k.version() + " at " + k.endpoint().target());
kosa8 0.9.36 at unix:///Users/dhruvit/.kosa8/run/kosa8.sock

build() 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 throws a Kosa8Exception.ConnectionException whose message says so, and says how to start it. Every exception the SDK throws is unchecked, so there is no throws clause to add.

Create a sandbox with no network

// 2. Create a sandbox: a microVM with its own kernel and no network at all.
Map<String, Object> created = k.createSandbox(new Options.Create().name("doc-java-demo").egress("deny"));
Sandbox sb = k.sandbox("doc-java-demo");
try {
  @SuppressWarnings("unchecked")
  Map<String, Object> info = (Map<String, Object>) created.get("sandbox");
  System.out.printf("sandbox %s: %s, egress %s, booted in %sms%n",
      info.get("name"), info.get("state"), info.get("egress"), created.get("boot_ms"));
sandbox doc-java-demo: running, egress deny, booted in 13096ms

egress("deny") closes the network completely. The rule is enforced on your computer, outside the sandbox, so code inside cannot turn it off. Options are small builder objects: every setter returns the same object, and anything you do not set is left to the daemon's default. k.sandbox(name) returns a Sandbox handle with the name filled in, so later calls are shorter. The answer is the daemon's JSON decoded into plain Java maps and lists. Booting took 13 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");
  Results.Exec res = sb.run("alpine:latest", List.of("sh", "-c",
      "echo kernel $(uname -r); wget -q -T 3 -O- http://example.com || echo no network; exit 3"));
  System.out.print("exit " + res.exitCode() + "\n" + 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 exception: run returns normally and res.exitCode() is 3 (res.ok() is false). It throws only when the command could not run at all.

Keep a container running

  // 4. Keep a container running, and write a file in it.
  String cid = sb.startContainer("alpine:latest", List.of("sh", "-c", "while :; do sleep 3600; done"));
  System.out.println("wrote /note: " + sh(sb, cid, "echo v1 > /note; cat /note"));

// ...and, as a method of the class:
static String sh(Sandbox sb, String cid, String cmd) {
  Results.Exec r = sb.exec(cid, List.of("sh", "-c", cmd), null);
  return (r.stdout() + r.stderr()).strip();
}
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. The last argument of exec is an optional Options.Run; null means none.

Checkpoint, break something, rewind

  // 5. Checkpoint, break something, rewind.
  Results.CheckpointSaved cp = sb.checkpoint("before the risky bit");
  System.out.printf("checkpoint %s (%s) in %sms%n",
      cp.checkpoint().id(), cp.checkpoint().label(), cp.snapshot().get("snapshot_ms"));
  sh(sb, cid, "rm /note");
  System.out.println("after rm: " + sh(sb, cid, "cat /note"));
  Results.Rewind rw = sb.rewind(null);
  System.out.printf("rewound to %s in %sms%n", rw.checkpoint().id(), rw.restoreMs());
  System.out.println("after rewind: /note = " + sh(sb, cid, "cat /note"));
checkpoint doc-java-demo-ckpt-1 (before the risky bit) in 548ms
after rm: cat: can't open '/note': No such file or directory
rewound to doc-java-demo-ckpt-1 in 480ms
after rewind: /note = v1

A checkpoint saves the whole running sandbox: memory, running programs and disks. rewind(null) goes back to the latest one; pass a label or id to pick another. 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.

Delete

} finally {
  // 6. Always clean up, even if a step above threw.
  System.out.println("deleted: " + sb.delete());
}
deleted: {ok=true, checkpoints_dropped=1}

delete() removes the sandbox and, by default, its checkpoints and their snapshots too. checkpoints_dropped says how many went. Kosa8Client and Sandbox are not AutoCloseable: there is no connection to close (each call opens and closes its own), and deleting a sandbox is a decision, so it is written out in finally.

The whole file

import com.kosa8.Kosa8Client;
import com.kosa8.Options;
import com.kosa8.Results;
import com.kosa8.Sandbox;
import java.util.List;
import java.util.Map;

public class Main {
  public static void main(String[] args) {
    // 1. Connect. With no settings the client uses ~/.kosa8/run/kosa8.sock.
    Kosa8Client k = Kosa8Client.builder().build();
    System.out.println("kosa8 " + k.version() + " at " + k.endpoint().target());

    // 2. Create a sandbox: a microVM with its own kernel and no network at all.
    Map<String, Object> created = k.createSandbox(new Options.Create().name("doc-java-demo").egress("deny"));
    Sandbox sb = k.sandbox("doc-java-demo");
    try {
      @SuppressWarnings("unchecked")
      Map<String, Object> info = (Map<String, Object>) created.get("sandbox");
      System.out.printf("sandbox %s: %s, egress %s, booted in %sms%n",
          info.get("name"), info.get("state"), info.get("egress"), created.get("boot_ms"));

      // 3. Pull an image into the sandbox (fetched on the host), then run a command to completion.
      sb.pull("alpine:latest");
      Results.Exec res = sb.run("alpine:latest", List.of("sh", "-c",
          "echo kernel $(uname -r); wget -q -T 3 -O- http://example.com || echo no network; exit 3"));
      System.out.print("exit " + res.exitCode() + "\n" + res.stdout());

      // 4. Keep a container running, and write a file in it.
      String cid = sb.startContainer("alpine:latest", List.of("sh", "-c", "while :; do sleep 3600; done"));
      System.out.println("wrote /note: " + sh(sb, cid, "echo v1 > /note; cat /note"));

      // 5. Checkpoint, break something, rewind.
      Results.CheckpointSaved cp = sb.checkpoint("before the risky bit");
      System.out.printf("checkpoint %s (%s) in %sms%n",
          cp.checkpoint().id(), cp.checkpoint().label(), cp.snapshot().get("snapshot_ms"));
      sh(sb, cid, "rm /note");
      System.out.println("after rm: " + sh(sb, cid, "cat /note"));
      Results.Rewind rw = sb.rewind(null);
      System.out.printf("rewound to %s in %sms%n", rw.checkpoint().id(), rw.restoreMs());
      System.out.println("after rewind: /note = " + sh(sb, cid, "cat /note"));
    } finally {
      // 6. Always clean up, even if a step above threw.
      System.out.println("deleted: " + sb.delete());
    }
  }

  // Run a shell command in the running container; return its output, trimmed.
  static String sh(Sandbox sb, String cid, String cmd) {
    Results.Exec r = sb.exec(cid, List.of("sh", "-c", cmd), null);
    return (r.stdout() + r.stderr()).strip();
  }
}

With the exec-maven-plugin (<mainClass>Main</mainClass>) in the pom.xml:

$ mvn -q compile exec:java
kosa8 0.9.36 at unix:///Users/dhruvit/.kosa8/run/kosa8.sock
sandbox doc-java-demo: running, egress deny, booted in 13096ms
exit 3
kernel 6.18.35
no network
wrote /note: v1
checkpoint doc-java-demo-ckpt-1 (before the risky bit) in 548ms
after rm: cat: can't open '/note': No such file or directory
rewound to doc-java-demo-ckpt-1 in 480ms
after rewind: /note = v1
deleted: {ok=true, checkpoints_dropped=1}

Look at it from the command line

The SDK and the CLI drive the same daemon and share the same checkpoint list (~/.kosa8/checkpoints.json), so you can watch what your program does, or finish a job by hand:

$ kosa8 sandbox ls
$ kosa8 checkpoint ls doc-java-demo
$ kosa8 audit ls -n 10    # your program's calls, recorded as actor "tool"
If the rewind fails on a Mac

Restoring a saved machine needs the screen to be unlocked. With the screen locked the checkpoint succeeds and the rewind fails. By then the rewind has already removed the sandbox; the checkpoint's snapshot is intact, and the CheckpointException message ends with the restoreSnapshot(…) call that brings it back. See Java errors and troubleshooting.

Next

Java developer guide: every capability in detail. Errors and troubleshooting and the API reference list everything else.