kosa8 docs

Guides / Python / Quickstart

Python quickstart

One hello.py, five minutes. You will connect to kosa8 from Python, 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, unchanged, against a real kosa8 daemon 0.9.36 on an Apple silicon Mac (macOS 26.0.1), with kosa8==0.1.1 installed from PyPI by the uv pip install line below into a fresh virtual environment on CPython 3.13.13 (it was also run on 0.1.0, with the same results). The output shown is what the 0.1.1 run printed. Timings will differ on your machine. The pip lines under "Install the package" were not run on that machine (its Homebrew Python could not bootstrap pip; see Errors and troubleshooting).

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 Python SDK (the kosa8 package on PyPI) lets a Python program do all of this: create sandboxes, run commands in them, read the output, save a sandbox's exact state (a checkpoint) and go back to it later (a rewind). It talks to the kosa8 background service, the daemon, that runs on your machine. It uses only Python's standard library, so it adds nothing else to your project.

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.
Python3.9 or later (the package's requires-python is >=3.9). No other packages.
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 4142s)

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 package

Put it in a virtual environment, so it stays out of your system Python. With uv:

$ mkdir hello-kosa8 && cd hello-kosa8
$ uv venv -q .venv
$ uv pip install --python .venv/bin/python kosa8==0.1.1
Resolved 1 package in 496ms
Prepared 1 package in 71ms
Installed 1 package in 3ms
 + kosa8==0.1.1
$ .venv/bin/python -c "import kosa8; print(kosa8.__version__)"
0.1.1

Or with plain pip (not run for this page):

$ python3 -m venv .venv
$ .venv/bin/pip install kosa8==0.1.1

Use 0.1.1 or later: it fixes several bugs found while writing these pages (listed in the package's CHANGELOG.md). In a uv project, uv add kosa8 adds it to pyproject.toml. The import name is the same as the package name: import kosa8. "Resolved 1 package" is the whole story: there are no dependencies.

The walkthrough

The steps below are parts of one file, hello.py. The whole file, and what it printed, is at the end. Everything after the sandbox is created sits inside a try block whose finally deletes the sandbox, so the clean-up runs even when a later step fails.

Connect

from kosa8 import Kosa8Client

# 1. Connect. With no arguments the client uses ~/.kosa8/run/kosa8.sock.
k = Kosa8Client()
print("kosa8", k.version(), "at", k.transport.target)
kosa8 0.9.36 at unix:///Users/dhruvit/.kosa8/run/kosa8.sock

Kosa8Client() 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 Kosa8ConnectionError, 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-py-demo", egress="deny")
sb = k.sandbox("doc-py-demo")
try:
    info = created["sandbox"]
    print(f"sandbox {info['name']}: {info['state']}, egress {info['egress']}, booted in {created['boot_ms']}ms")
sandbox doc-py-demo: running, egress deny, booted in 15310ms

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 Sandbox handle with the name filled in, so later calls are shorter; it sends nothing by itself. The answer is the daemon's JSON as a plain dict. Booting took 15 seconds here because the machine was running other sandboxes; 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"])
    print(f"exit {res.exit_code} ok={res.ok}")
    print(res.stdout, end="")
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. 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 an 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: res.exit_code is 3 and res.ok is False. An exception means 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"])

    def sh(cmd):
        r = sb.exec(cid, ["sh", "-c", cmd])
        return (r.stdout + r.stderr).strip()

    print("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. start_container returns the container's id.

Checkpoint, break something, rewind

    # 5. Checkpoint, break something, rewind.
    cp = sb.checkpoint(label="before the risky bit")
    print(f"checkpoint {cp['checkpoint']['id']} ({cp['checkpoint']['label']}) in {cp['snapshot']['snapshot_ms']}ms")
    sh("rm /note")
    print("after rm:", sh("cat /note"))
    rw = sb.rewind()
    print(f"rewound to {rw['checkpoint']['id']} in {rw['restore_ms']}ms")
    print("after rewind: /note =", sh("cat /note"))
checkpoint doc-py-demo-ckpt-1 (before the risky bit) in 761ms
after rm: cat: can't open '/note': No such file or directory
rewound to doc-py-demo-ckpt-1 in 480ms
after rewind: /note = v1

A checkpoint saves the whole running sandbox: memory, running programs and disks. rewind() with no argument 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 checkpoint is written to ~/.kosa8/checkpoints.json, the same list the kosa8 command line uses.

Delete

finally:
    # 6. Always clean up: the sandbox and its checkpoints.
    print("deleted:", sb.delete())
deleted: {'ok': True, 'checkpoints_dropped': 1}

delete() removes the sandbox and, by default, its checkpoints and their snapshots too: checkpoints_dropped counts them. Pass keep_checkpoints=True to keep them.

The whole file

from kosa8 import Kosa8Client

# 1. Connect. With no arguments the client uses ~/.kosa8/run/kosa8.sock.
k = Kosa8Client()
print("kosa8", k.version(), "at", k.transport.target)

# 2. Create a sandbox: a microVM with its own kernel and no network at all.
created = k.create_sandbox(name="doc-py-demo", egress="deny")
sb = k.sandbox("doc-py-demo")
try:
    info = created["sandbox"]
    print(f"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"])
    print(f"exit {res.exit_code} ok={res.ok}")
    print(res.stdout, end="")

    # 4. Keep a container running, and write a file in it.
    cid = sb.start_container("alpine:latest", ["sh", "-c", "while :; do sleep 3600; done"])

    def sh(cmd):
        r = sb.exec(cid, ["sh", "-c", cmd])
        return (r.stdout + r.stderr).strip()

    print("wrote /note:", sh("echo v1 > /note; cat /note"))

    # 5. Checkpoint, break something, rewind.
    cp = sb.checkpoint(label="before the risky bit")
    print(f"checkpoint {cp['checkpoint']['id']} ({cp['checkpoint']['label']}) in {cp['snapshot']['snapshot_ms']}ms")
    sh("rm /note")
    print("after rm:", sh("cat /note"))
    rw = sb.rewind()
    print(f"rewound to {rw['checkpoint']['id']} in {rw['restore_ms']}ms")
    print("after rewind: /note =", sh("cat /note"))
finally:
    # 6. Always clean up: the sandbox and its checkpoints.
    print("deleted:", sb.delete())
$ .venv/bin/python hello.py
kosa8 0.9.36 at unix:///Users/dhruvit/.kosa8/run/kosa8.sock
sandbox doc-py-demo: running, egress deny, booted in 15310ms
exit 3 ok=False
kernel 6.18.35
no network
wrote /note: v1
checkpoint doc-py-demo-ckpt-1 (before the risky bit) in 761ms
after rm: cat: can't open '/note': No such file or directory
rewound to doc-py-demo-ckpt-1 in 480ms
after rewind: /note = v1
deleted: {'ok': True, 'checkpoints_dropped': 1}

If the program stops halfway (an exception, or Ctrl-C), the finally block still deletes the sandbox. If the process is killed outright, the sandbox is left running: remove it with kosa8 sandbox rm doc-py-demo. Running the program twice at the same time fails on the second create, because the name is taken (Kosa8ConflictError).

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-py-demo
$ kosa8 snapshot ls

A checkpoint taken from Python shows up in kosa8 checkpoint ls, and kosa8 rewind doc-py-demo would go back to it; the other way round works too.

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 Kosa8CheckpointError it raises ends with the restore_snapshot(…) call that brings it back. See Python errors and troubleshooting.

Next

Python developer guide: every capability in detail, with patterns for AI agent loops. Errors and troubleshooting and the API reference list everything else.