Guides / Python / API reference
Python API reference
Every name the kosa8 package exports in version 0.1.1 (its
__all__), and the public members of each. Signatures are copied from the source of the
published package. Keys in returned dicts are the daemon's own JSON names, unchanged.
Kosa8ClientEvery method, its endpoint and result.
SandboxThe handle bound to one sandbox.
DataclassesExecResult, ExploreBranch, ExploreResult.
Result shapesThe dicts methods return.
TransportEndpoint rules and raw requests.
Checkpoint storesCheckpointBook and the two stores.
ErrorsThe exception classes.
Package
from kosa8 import (
Kosa8Client, Sandbox, ExecResult, ExploreBranch, ExploreResult,
Transport, default_socket_path, resolve_endpoint,
CheckpointBook, FileCheckpointStore, MemoryCheckpointStore, MAX_CHECKPOINTS_PER_SANDBOX,
Kosa8Error, Kosa8ConnectionError, Kosa8ApiError, Kosa8BadRequestError, Kosa8AuthError,
Kosa8PolicyError, Kosa8ApprovalRequiredError, Kosa8NotFoundError, Kosa8ConflictError,
Kosa8UnsupportedError, Kosa8StreamError, Kosa8CheckpointError, Kosa8TimeoutError,
Kosa8ProtocolError, error_from_response, __version__,
)
| Name | Value |
|---|---|
__version__ | "0.1.1". Changes from 0.1.0 are marked "0.1.1" below and listed in the package's CHANGELOG.md. |
MAX_CHECKPOINTS_PER_SANDBOX | 10. Checkpoints kept per sandbox; the oldest is evicted past it. |
Python 3.9 or later. No dependencies: only the standard library. The package ships
py.typed, so type checkers read its annotations. Type aliases used below, from
kosa8.client and kosa8.transport:
Env = Union[Mapping[str, str], Sequence[str], None] # {"A": "1"} or ["A=1"]
Chunk = Callable[[bytes], None] # output callbacks
EgressPolicy = Union[str, Mapping[str, Sequence[str]], None] # "deny" | "any" | "fakenet" | "allow:a,b" | {"allow": [...]}
Body = Union[bytes, str, Iterable[Union[bytes, str]]] # write_stdin data
Query = Mapping[str, Union[str, int, bool, None]]
Kosa8Client
Kosa8Client(
*,
socket_path: Optional[str] = None,
host: Optional[str] = None,
token: Optional[str] = None,
insecure: bool = False,
actor: str = "tool",
agent_token: Optional[str] = None,
headers: Optional[Mapping[str, str]] = None,
timeout: Optional[float] = None,
checkpoint_store: Any = None,
env: Optional[Mapping[str, str]] = None,
)
All arguments are keyword-only. Constructing the client sends nothing; it resolves the endpoint
(and can raise Kosa8Error for a refused address or a bad actor).
| Parameter | Type | Default | Meaning |
|---|---|---|---|
socket_path | str | ~/.kosa8/run/kosa8.sock | The daemon's unix socket. Ignored when a host is set. |
host | str | KOSA8_HOST | A remote worker: https://…, http://…, tcp://h:p, h:p, or unix:///path. See resolve_endpoint. |
token | str | KOSA8_TOKEN, only when the host came from KOSA8_HOST | Bearer token, sent as Authorization: Bearer … to a TCP host only. |
insecure | bool | False (or KOSA8_INSECURE=1) | Allow plain http to a non-loopback host. |
actor | str | "tool" | "human", "agent" or "tool"; sent as X-Kosa8-Actor and recorded in the audit log. Anything else raises Kosa8Error. |
agent_token | str | KOSA8_AGENT_TOKEN | Agent identity, sent as X-Kosa8-Agent-Token. |
headers | Mapping[str, str] | none | Extra headers on every request. The actor, approval, agent token and authorization headers are set after these and win. |
timeout | float | None (wait forever) | Socket timeout in seconds, applied to connect and to each read. Per-call timeout= overrides it. |
checkpoint_store | store | FileCheckpointStore() | Where checkpoint stacks are kept. Any object with load() and update(fn). |
env | Mapping[str, str] | os.environ | Where KOSA8_* variables are read from. Pass {} to ignore the environment. |
| Attribute | Type | Meaning |
|---|---|---|
transport | Transport | The HTTP layer; transport.target says where requests go. |
checkpoints | CheckpointBook | The checkpoint stack logic over checkpoint_store; checkpoints.store is the store. |
Every method that takes approval: Optional[str] = None sends it, stripped, as the
X-Kosa8-Approval header. Methods without it in their signature send none. Every
method can raise Kosa8ConnectionError (daemon unreachable),
Kosa8TimeoutError (a timeout was set and passed), and the
HTTP error class matching the daemon's status; only the extra errors are
listed per method. Paths below are shortened: … is /kosa8/v1, and every
path segment is percent-encoded.
Status and the shared engine
| Signature | Returns | HTTP |
|---|---|---|
health(*, approval=None) -> Dict[str, Any] | {status, version, commit, go, uptime_seconds, phase} | GET …/health |
version() -> str | health()["version"], such as "0.9.36" | GET …/health |
pull(ref: str, *, approval=None) -> Dict[str, Any] | The daemon's answer. Pulls into the host's shared engine, not a sandbox. | POST …/pull, body {"ref"} |
Sandboxes
| Signature | Returns | HTTP |
|---|---|---|
list_sandboxes() -> List[Dict[str, Any]] | Sandbox records; [] when the daemon answers null. | GET …/sandboxes |
get_sandbox(id_or_name: str) -> Optional[Dict[str, Any]] | The record whose id matches, else whose name matches, else None. Never raises for a missing sandbox. | GET …/sandboxes, filtered in the client |
create_sandbox(*, name=None, cpus=None, mem_mb=None, disk_gb=None, mounts=None, egress=None, egress_allow=None, capture=None, labels=None, broker=None, approval=None) -> Dict[str, Any] | {sandbox, boot_ms}, plus broker when one was asked for | POST …/sandboxes |
start_sandbox(sandbox: str, *, approval=None) -> Dict[str, Any] | {sandbox, boot_ms} (and warning when the daemon has one) | POST …/sandboxes/{id}/up |
stop_sandbox(sandbox: str, *, approval=None) -> Dict[str, Any] | {ok, sandbox} (and warning, already_stopped when they apply) | POST …/sandboxes/{id}/down |
delete_sandbox(sandbox: str, *, wipe: bool = False, keep_checkpoints: bool = False, approval=None) -> Dict[str, Any] | The daemon's answer plus checkpoints_dropped: int | DELETE …/sandboxes/{id} (?wipe=1 when wipe), then DELETE …/snapshots/{ckpt} per checkpoint |
pull_into_sandbox(sandbox: str, ref: str, *, approval=None) -> Dict[str, Any] | {ok, image} | POST …/sandboxes/{id}/pull, body {"ref"} |
sandbox_images(sandbox: str) -> List[str] | Image references in the sandbox's engine | GET …/sandboxes/{id}/images |
sandbox_containers(sandbox: str) -> List[Dict[str, Any]] | [{id, image, status, exit?, ip?}] | GET …/sandboxes/{id}/containers |
sandbox_egress(sandbox: str) -> Dict[str, Any] | {policy, stats, …}: the rule the host enforces and what it decided | GET …/sandboxes/{id}/egress |
create_sandbox parameters
A parameter that is None, empty or zero is left out of the request body, and the
daemon applies its default.
| Parameter | Type | Sent as | Meaning |
|---|---|---|---|
name | str | name | The sandbox name. Omit it and the daemon picks one. |
cpus | int | cpus | Virtual CPUs. |
mem_mb | int | mem_mb | Memory in MiB. |
disk_gb | int | disk_gb | Disk size in GB. |
mounts | List[Dict] | mounts | Host folders: {"host_path", "guest_path", "read_only"}. |
egress | EgressPolicy | egress | "deny", "any", "fakenet", "allow:a.com,b.com", or {"allow": [...]} (sent as "allow:…"). None: the org policy's default. |
egress_allow | Sequence[str] | egress: "allow:…" | Shorthand for an allow-list. Combining it with an egress other than None or "deny" raises Kosa8Error. |
capture | Dict | capture | Packet capture: {"path", "max_bytes"?, "max_files"?}; path absolute, on the host. |
labels | Mapping[str, str] | labels | Free-form labels kept on the record. |
broker | Dict | broker | Egress broker spec, passed through: {"allow"?: [{"host", "ports"?, "methods"?}], "routes"?: [{"name", "upstream", "header", "secret", "methods"?, "paths"?, "scheme"?}]}. |
approval | str | header | A signed approval id. |
Extra errors: Kosa8Error (client side) for egress={"allow": []} ("an
egress allow-list needs at least one host…") and for egress_allow with another
egress.
Running code
| Signature | Returns | HTTP |
|---|---|---|
run(sandbox: str, image: str, args: Sequence[str] = (), *, env: Env = None, on_stdout: Optional[Chunk] = None, on_stderr: Optional[Chunk] = None, on_status: Optional[Chunk] = None, timeout: Optional[float] = None, approval=None) -> ExecResult | ExecResult | POST …/sandboxes/{id}/run, body {"image", "args", "env"?}, NDJSON answer |
start_container(sandbox: str, image: str, args: Sequence[str] = (), *, approval=None) -> str | The container id | POST …/sandboxes/{id}/start, body {"image", "args"} |
exec(sandbox: str, container: str, args: Sequence[str], *, on_stdout=None, on_stderr=None, timeout=None, approval=None) -> ExecResult | ExecResult | POST …/sandboxes/{id}/exec, body {"container", "args"} |
exec_frames(sandbox: str, container: str, args: Sequence[str], *, timeout=None, approval=None) -> Iterator[Dict[str, Any]] | A generator of frames (below). Nothing is sent until you start iterating. | POST …/sandboxes/{id}/exec |
start_interactive(sandbox: str, image: str, args: Sequence[str] = (), *, env: Env = None, tty: bool = False, approval=None) -> str | The container id | POST …/sandboxes/{id}/start-interactive, body {"image", "args", "env", "tty"} |
write_stdin(sandbox: str, container: str, data: Body, *, approval=None) -> Dict[str, Any] | {ok}. The body is the container's whole stdin: the daemon closes stdin when it ends. 0.1.1: a second call for the same (sandbox, container) strings on the same client raises Kosa8Error (stdin of container … was already sent and is closed …) without sending; 0.1.0 sent it and answered {ok} for input never read | POST …/sandboxes/{id}/containers/{cid}/stdin, application/octet-stream; an iterable is sent chunked |
attach(sandbox: str, container: str, *, on_stdout=None, on_stderr=None, timeout=None, approval=None) -> ExecResult | ExecResult when the container exits | POST …/sandboxes/{id}/containers/{cid}/attach |
argsis an argv list; no shell is involved unless you run one.envis a mapping (sent as["K=V", …]) or a sequence of"K=V"strings.- Callbacks receive each chunk as
bytes, in the calling thread, as it arrives.on_status(run only) receives the daemon's progress frames; status bytes are not collected into the result. timeoutis a socket timeout in seconds for connect and for each read, so a stream that keeps producing output within it can run longer in total. When it passes, the call raisesKosa8TimeoutError; the process in the sandbox keeps running.- A non-zero exit is not an error. Extra errors:
Kosa8StreamErrorwhen the stream ends with an error frame, or with no final frame.
A frame is a dict as the daemon sends it, with data base64-decoded to
bytes: output frames {"stream": "stdout" | "stderr" | "status", "data": b"…"},
then a final {"done": True, "ok": …, "exit": int}, or
{"done": True, "error": "…"}. exec_frames yields them unchanged and
raises nothing for an error frame; an HTTP error before streaming starts is raised as its typed
error.
Snapshots, fork, explore
| Signature | Returns | HTTP |
|---|---|---|
snapshot(sandbox: str, *, id: Optional[str] = None, encrypt: bool = False, approval=None) -> Dict[str, Any] | {snapshot, snapshot_ms} (and encrypted_by_policy, encrypt_ms, encryption when encrypted) | POST …/sandboxes/{id}/snapshot, body {"id"?, "encrypt"?: true} |
list_snapshots() -> List[Dict[str, Any]] | Snapshot records | GET …/snapshots |
restore_snapshot(snapshot_id: str, *, name: Optional[str] = None, label: Optional[str] = None, allow_tampered: bool = False, approval=None) -> Dict[str, Any] | {sandbox, restore_ms} (and seal for a sealed snapshot) | POST …/snapshots/{id}/restore, body {"name"?, "label"?, "allow_tampered"?: true} |
delete_snapshot(snapshot_id: str, *, wipe: bool = False, force: bool = False, approval=None) -> Dict[str, Any] | {ok, removed, encrypted, …} | DELETE …/snapshots/{id}, ?wipe=1, ?force=1 when set |
fork(sandbox: str, n: int = 1, *, prefix: Optional[str] = None, label: Optional[str] = None, approval=None) -> Dict[str, Any] | {forks: [sandbox records], snapshot, fork_ms, count} | POST …/sandboxes/{id}/fork, body {"n", "prefix"?, "label"?} |
explore(sandbox: str, variants: Sequence[str], *, image: str = "alpine:latest", keep_all: bool = False, keep: Optional[str] = None, timeout: float = 120.0, prefix: Optional[str] = None, pull: bool = True, approval=None) -> ExploreResult | ExploreResult | pull_into_sandbox (if pull), fork, one run per branch in parallel, then DELETE …/sandboxes/{branch} for the losers |
explore parameters
| Parameter | Default | Meaning |
|---|---|---|
variants | (required) | Shell commands, one per branch, each run as ["sh", "-c", variant]. At least two, or Kosa8Error. |
image | "alpine:latest" | The image each variant runs in. |
keep_all | False | Keep every branch instead of deleting the losers. |
keep | None | Keep this branch name as the winner. Not one of the branches: Kosa8Error, and every branch is left in place. |
timeout | 120.0 | Seconds, per branch, passed to run as its socket timeout. |
prefix | f"{sandbox}-explore-{int(time.time()) % 100000}" | Branch name prefix. |
pull | True | Pull image into the parent first. |
Branches run on a ThreadPoolExecutor with one worker per variant. A branch whose
error message looks transient (connection refused, EOF, reset, broken pipe, "no such file or
directory", "stream ended", unreachable) is retried up to four attempts, waiting 0.5, 1 and 1.5
seconds between them (0.1.0 also waited 2 seconds after the last); a timeout is never retried. The winner is the fastest branch with no error and exit 0;
"" when none succeeded. A failed delete of a loser is not raised; removed
counts the ones that went. Extra errors: Kosa8Error when the fork returned fewer
copies than variants.
Checkpoints
Client-side, over snapshots. The daemon has no checkpoint endpoint.
| Signature | Returns | HTTP |
|---|---|---|
checkpoint(sandbox: str, *, label: str = "", approval=None) -> Dict[str, Any] | {checkpoint, evicted: List[str], snapshot}: the stored record, the ids evicted, and the snapshot() answer | POST …/sandboxes/{id}/snapshot with id <sandbox>-ckpt-<n>, then DELETE …/snapshots/{old} per eviction |
list_checkpoints(sandbox: str) -> List[Dict[str, Any]] | Records {id, sandbox, label, created}, oldest first | none: the store |
rewind(sandbox: str, *, to: Optional[str] = None, approval=None) -> Dict[str, Any] | {sandbox, checkpoint, dropped: List[str], restore_ms} | DELETE …/sandboxes/{id}, POST …/snapshots/{ckpt}/restore with {"name": sandbox}, then DELETE …/snapshots/{later} per dropped checkpoint |
to is a checkpoint id or a label (case-insensitive; the newest match wins); without
it, the latest. Extra errors: Kosa8CheckpointError for no checkpoints, no match, or an
invalid sandbox name (empty, ., .., containing / or
\, or starting with a dot). If the restore fails after the delete, rewind
raises Kosa8CheckpointError (0.1.1; a plain Kosa8Error saying "is now
stopped" in 0.1.0) with the message restore failed and NAME no longer exists (it was deleted
to free its name for the restore): … — the checkpoint is intact; recover with
restore_snapshot('ID', name='NAME'), chained to the daemon's error.
checkpoint (0.1.1): when the snapshot is refused with a 409 whose message contains
"already exists" (a concurrent checkpoint took the id), it tries the next number, up to ten
attempts in all; any other 409, or the tenth, is raised. Failures
deleting evicted or dropped snapshots are not raised.
Helper
| Signature | Returns |
|---|---|
sandbox(sandbox_id: str) -> Sandbox | A handle bound to one sandbox. Sends nothing. |
Sandbox
Sandbox(client: Kosa8Client, sandbox_id: str)
client: Kosa8Client
id: str
repr(sb) # "Sandbox('doc-py-guide')"
Each method calls the client method with the sandbox filled in, passing other keyword arguments
through (**kw), so the options and errors are the client's.
| Method | Calls |
|---|---|
info() -> Optional[Dict] | get_sandbox(id) |
start(**kw) -> Dict | start_sandbox(id, **kw) |
stop(**kw) -> Dict | stop_sandbox(id, **kw) |
delete(**kw) -> Dict | delete_sandbox(id, **kw) |
pull(ref, **kw) -> Dict | pull_into_sandbox(id, ref, **kw) |
run(image, args=(), **kw) -> ExecResult | run(id, image, args, **kw) |
start_container(image, args=(), **kw) -> str | start_container(id, image, args, **kw) |
exec(container, args, **kw) -> ExecResult | exec(id, container, args, **kw) |
containers() -> List[Dict] | sandbox_containers(id) |
images() -> List[str] | sandbox_images(id) |
egress() -> Dict | sandbox_egress(id) |
snapshot(**kw) -> Dict | snapshot(id, **kw) |
fork(n=1, **kw) -> Dict | fork(id, n, **kw) |
explore(variants, **kw) -> ExploreResult | explore(id, variants, **kw) |
checkpoint(**kw) -> Dict | checkpoint(id, **kw) |
checkpoints() -> List[Dict] | list_checkpoints(id) |
rewind(**kw) -> Dict | rewind(id, **kw) |
exec_frames(container, args, **kw) -> Iterator[Dict] (0.1.1) | exec_frames(id, container, args, **kw) |
start_interactive(image, args=(), **kw) -> str (0.1.1) | start_interactive(id, image, args, **kw) |
write_stdin(container, data, **kw) -> Dict (0.1.1) | write_stdin(id, container, data, **kw) |
attach(container, **kw) -> ExecResult (0.1.1) | attach(id, container, **kw) |
Not on the handle: restore_snapshot, delete_snapshot,
list_snapshots, and the host-engine pull. Call them on the client. (In
0.1.0, exec_frames, start_interactive, write_stdin and
attach were missing from the handle too.)
Dataclasses
ExecResult
@dataclass
class ExecResult:
exit_code: int
stdout_bytes: bytes = b""
stderr_bytes: bytes = b""
stdout: str # property: stdout_bytes decoded as UTF-8, errors replaced
stderr: str # property: the same for stderr_bytes
ok: bool # property: exit_code == 0
When the final frame carries no integer exit, exit_code is
0.
ExploreBranch
@dataclass
class ExploreBranch:
name: str # the branch sandbox's id
variant: str # the shell command it ran
exit: int = 0
output: str = "" # stdout + stderr, trailing newlines stripped
duration_ms: int = 0 # including retries
error: Optional[str] = None # the last error, if the run never finished
ExploreResult
@dataclass
class ExploreResult:
parent: str
branches: List[ExploreBranch] = field(default_factory=list)
winner: str = "" # "" when no branch succeeded
removed: int = 0 # branches deleted
Result shapes
Methods other than the three above return the daemon's JSON as plain dicts and lists. The keys you will meet most, as kosa8 0.9.36 sends them:
| Shape | Keys |
|---|---|
| sandbox record | id, name, created, state (running, paused, stopped), cpus, mem_mb, and when set disk_gb, parent, mounts, egress, capture, ip, labels, broker |
| snapshot record | id, sandbox, created, cpus, mem_mb, cmdline, size_bytes, state_path, and optional fields such as disk_gb, mounts, egress, sealed, labels, encryption |
| checkpoint record | id, sandbox, label, created (RFC 3339, UTC). Records written by the Go tools may also carry auto. |
| container | id, image, status, and exit, ip when known |
| egress status | policy, stats: {passed, denied, dropped, dns_answered, dns_refused, …}, and capture, wildcards, fakenet when they apply |
Transport and endpoints
Transport
Transport(
*,
socket_path: Optional[str] = None,
host: Optional[str] = None,
token: Optional[str] = None,
insecure: bool = False,
actor: str = "tool",
agent_token: Optional[str] = None,
headers: Optional[Mapping[str, str]] = None,
timeout: Optional[float] = None,
env: Optional[Mapping[str, str]] = None,
)
The same connection parameters as Kosa8Client, without
checkpoint_store. It opens a new connection per request.
| Member | Meaning |
|---|---|
endpoint | The resolved Endpoint. |
actor, agent_token, extra_headers, timeout | As given (agent_token falls back to KOSA8_AGENT_TOKEN). |
target -> str | Property: unix:///path or scheme://host[:port]. |
raw(method, path, *, query=None, body=None, raw_body=None, approval=None, headers=None, timeout=None) -> Response | Sends a request and returns once the headers arrive. body is JSON-encoded; raw_body (bytes, str or an iterable of them) is sent as application/octet-stream, chunked for an iterable. In query, None and False are dropped and True becomes "1". Does not raise for HTTP errors. Raises Kosa8TimeoutError or Kosa8ConnectionError. |
json(method, path, **kw) -> Any | raw, then the body decoded as JSON (None for an empty body, the text if it is not JSON). A status of 400 or more raises error_from_response(…). |
frames(method, path, **kw) -> Iterator[Dict] | raw, then one decoded frame per NDJSON line, data base64-decoded. An HTTP error before streaming raises the typed error; a read timeout mid-stream raises Kosa8TimeoutError; a line that is not valid JSON, or whose data is not valid base64, raises Kosa8ProtocolError (0.1.1; 0.1.0 let the decoding error escape). |
Response (returned by raw, in kosa8.transport) has
status, headers, read() -> bytes, lines() ->
Iterator[bytes] and close(), and is a context manager; read and
lines close it when done.
resolve_endpoint
resolve_endpoint(
host: Optional[str] = None,
socket_path: Optional[str] = None,
token: Optional[str] = None,
insecure: bool = False,
env: Optional[Mapping[str, str]] = None,
) -> Endpoint
Where to connect, by the kosa8 CLI's rules:
- The value is
hostif given (even""), elseKOSA8_HOST, stripped of spaces and trailing slashes. Empty: the unix socket atsocket_pathordefault_socket_path(). - The scheme is lower-cased.
unix:///pathis a socket.tcp://is read ashttp://. No scheme (host:port) is read ashttp://. Any other scheme raisesKosa8Error(KOSA8_HOST scheme "ftp" is not supported; use http://, https://, tcp:// or unix://). No host name raisesKosa8Error. - Plain
httpto a host other thanlocalhost,::1or127.*raisesKosa8Error(refusing to talk to … over plain http …) unlessinsecureorKOSA8_INSECURE=1. - The token is
tokenif given; otherwiseKOSA8_TOKEN, but only when the host came fromKOSA8_HOST(never with an explicithost). A path in the URL is kept as a prefix for every request.
Endpoint (in kosa8.transport) is a frozen dataclass:
kind ("unix" or "tcp"), socket_path,
scheme, host, port, base_path,
token, and the property target.
Other transport names
| Name | Meaning |
|---|---|
default_socket_path() -> str | os.path.join(os.path.expanduser("~"), ".kosa8", "run", "kosa8.sock") |
kosa8.transport.APPROVAL_HEADER | "X-Kosa8-Approval" |
kosa8.transport.ACTOR_HEADER | "X-Kosa8-Actor" |
kosa8.transport.AGENT_TOKEN_HEADER | "X-Kosa8-Agent-Token" |
kosa8.transport.decode_frame(line) -> Dict | One NDJSON frame with data base64-decoded. |
kosa8.transport.enc(segment) -> str | Percent-encodes one path segment (nothing is safe, so / becomes %2F). |
These six live in kosa8.transport and are not re-exported from kosa8.
Checkpoint stores
| Name | Signature and members |
|---|---|
FileCheckpointStore | FileCheckpointStore(path: Optional[str] = None, *, lock_timeout: float = 10.0) (lock_timeout is 0.1.1). Attributes path, lock_timeout. path defaults to ~/.kosa8/checkpoints.json, the file the CLI, MCP server and desk use. load() -> Dict[str, List[Dict]] ({} if missing or corrupt). update(fn) -> T: takes an in-process lock and an exclusive fcntl.flock on <path>.lock (the same lock the Go tools take; in-process only where fcntl is missing). 0.1.1: the flock is tried without blocking every 50 ms, and after lock_timeout seconds raises Kosa8CheckpointError (timed out after 10s waiting for PATH.lock; another kosa8 process is holding it); 0.1.0 blocked indefinitely, loads, calls fn(data) to change it in place, writes <path>.tmp with mode 0600 and renames it over the file. Creates the directory with mode 0700. |
MemoryCheckpointStore | MemoryCheckpointStore(). load() returns a deep copy; update(fn) runs fn on a copy under a lock and keeps it. Nothing is shared with the CLI. |
CheckpointBook | CheckpointBook(store), available as client.checkpoints. store; next_id(sandbox) -> Tuple[str, List[str]] (the next <sandbox>-ckpt-<n>, with n one past the larger of the stack size and the highest number used, and the ids to evict); record(cid, sandbox, label="", evicted=None) -> Dict; list(sandbox) -> List[Dict] (sorted by created); latest(sandbox) -> Dict; resolve(sandbox, ref) -> Dict; pop(sandbox, cid) -> List[str] (keeps cid on top, returns the later ids); forget(sandbox) -> List[str] (drops the stack, returns its ids). Raises Kosa8CheckpointError. |
The store data is {sandbox_name: [checkpoint record, …]}. Any object with
load() and update(fn) of these shapes can be passed as
checkpoint_store. kosa8.checkpoints.validate_sandbox_name(name) is the
name check used by next_id and record.
Errors
| Class | Base | Raised for |
|---|---|---|
Kosa8Error | Exception | Base class; client-side checks. |
Kosa8ConnectionError | Kosa8Error | Daemon unreachable (any OSError while sending). |
Kosa8TimeoutError | Kosa8Error | A socket timeout passed. |
Kosa8ProtocolError (0.1.1) | Kosa8Error | A streamed frame that is not valid NDJSON (bad JSON or base64). method, path set; the decoding error is __cause__. |
Kosa8StreamError | Kosa8Error | Error frame, or no final frame. stdout, stderr. |
Kosa8CheckpointError | Kosa8Error | Checkpoint bookkeeping; from 0.1.1 also a checkpoint lock timeout and a rewind whose restore failed. |
Kosa8ApiError | Kosa8Error | Any HTTP error status without a more specific class (500, 503, …). |
Kosa8BadRequestError | Kosa8ApiError | 400 |
Kosa8AuthError | Kosa8ApiError | 401 |
Kosa8PolicyError | Kosa8ApiError | 403 |
Kosa8ApprovalRequiredError | Kosa8PolicyError | 403 whose body's approval is a dict with a string why. approval: dict. |
Kosa8NotFoundError | Kosa8ApiError | 404 |
Kosa8ConflictError | Kosa8ApiError | 409 |
Kosa8UnsupportedError | Kosa8ApiError | 501 |
Every class has the attributes message, status,
daemon_message, remediation, method, path and
body (each None when not known). The constructor is
Kosa8Error(message, *, status=None, daemon_message=None, remediation=None, method=None,
path=None, body=None); Kosa8ApprovalRequiredError(message, approval, **kw);
Kosa8StreamError(message, stdout="", stderr="", **kw).
error_from_response(status: int, body: Any, *, method: Optional[str] = None, path: Optional[str] = None) -> Kosa8ApiError
Builds the typed error for an HTTP error answer, as the client does. daemon_message
is the body's error, or else its message, or a non-empty text body;
remediation is the body's remediation. The message is
"<daemon_message> (<remediation>)", or the daemon message alone, or
"request failed with HTTP <status>". Details and real messages:
Python errors and troubleshooting.