microsandbox

repository·main·Indexed 27 days ago

https://github.com/superradcompany/microsandbox

A system for running untrusted workloads like AI agents, user code, and CI jobs in fast, local microVMs with hardware-level isolation. It includes tools for OCI image management via microsandbox-image, a guest init process and agent daemon (microsandbox-agentd), and agent clients available in Rust and TypeScript. The project features a flexible filesystem system with PassthroughFs for host directory exposure, MemFs for in-memory scratch space, and DualFs for combining backends.

Tokens
211.9K
Snippets
777
Records
1.3K
Agent score
92%

What's inside microsandbox

  1. Overview of microsandbox types contracts

    main

    The microsandbox-types package provides the shared task and wire contract types used across the Rust SDK, CLI, cloud API, and TypeScript frontend. It defines the backend-neutral shapes for sandbox specifications and cloud communication.

    Note: This is a contract layer only; it does not provide a runtime or manage sandbox lifecycles.

    Key Data Models Included:

    • Sandbox Specs: SandboxSpec, SandboxResources, SandboxRuntimeOptions, rootfs sources, mounts, patches, init, and lifecycle policy.
    • Networking: NetworkSpec, published ports, and protocols.
    • Storage: VolumeSpec, SnapshotSpec, and their kinds.
    • Execution & Logging: Rlimit, RlimitResource, LogSource, and SandboxLogLevel.
    • Cloud Wire Contracts: CloudCreateSandboxRequest, CloudSandbox, and various paginated/message/error bodies.
    • Validation: Shared rules for sandbox-name and hostname.

    Exclusions: Backend-private state (registry credentials, local cache paths, DB rows, resolved manifest digests, process handles) is not included in these types.

  2. Understand Sandbox Lifecycle States

    main

    A sandbox transitions through several states during its lifecycle:

    • Creating: The VM is booting, the kernel is loading, and the guest agent is initializing.
    • Running: The guest agent is ready. You can perform exec, shell, and fs operations.
    • Draining: A graceful shutdown is in progress. Existing commands will finish, but new exec calls are rejected.
    • Stopped: The VM has shut down. Configuration and state are persisted and can be restarted.
    • Crashed: The VM exited unexpectedly (e.g., kernel panic or OOM kill).
    • Crashed/Stopped: Once in these states, the sandbox can be remove()d to delete its state.
  3. Understand the default network security posture

    main

    By default, microsandbox employs a deny-by-default egress policy designed to prevent SSRF and lateral movement.

    Egress (Outbound):

    • Allows access to the public internet and DNS via the gateway.
    • Denies access to private IP ranges (RFC 1918, RFC 4193, etc.), loopback (127.0.0.0/8, ::1), link-local (169.254.0.0/16, fe80::/10), cloud metadata endpoints (169.254.169.254), and the host machine.

    Ingress (Inbound):

    • Only reaches ports you explicitly publish.
    • Published ports bind to 127.0.0.1 on your host by default.

    Note on localhost: Inside a sandbox, localhost or 127.0.0.1 refers to the sandbox's own loopback interface, not your host machine. To reach a service on your host, you must use host.microsandbox.internal, which is denied by the default policy.

  4. Understand the microsandbox isolation boundary

    main

    Microsandbox uses a microVM architecture rather than containers to provide strong isolation.

    Key Isolation Features

    • Kernel Isolation: Each sandbox runs its own Linux kernel (supplied via libkrunfw), not the host kernel. Kernel exploits are contained within the guest.
    • Hardware Hypervisor: Uses KVM (Linux) or Apple's Hypervisor.framework (macOS) via libkrun VMM to schedule virtual CPUs and memory.
    • Host Privileges: The host process runs as the same unprivileged user that launched it. On Linux, it only requires access to /dev/kvm (typically via the kvm group).
    • Device Attack Surface: The guest can only interact with the host through a fixed set of paravirtual (virtio) devices:
      • virtio-console: Control channel to agentd.
      • virtio-net: Network frames.
      • virtio-fs: Explicitly mounted host directories.
      • virtio-blk: Root filesystem and attached disks.
      • virtio-rng: Entropy.
    • Host-Guest Control Channel: Communication is driven by the host via virtio-console. The guest (agentd) answers requests but cannot drive the host or initiate host-side connections.
  5. Use the AgentClient for low-level agentd communication

    main

    The AgentClient is a low-level raw transport for communicating with agentd via a sandbox's relay socket. It is intended for building protocol-level tools or higher-level SDK helpers.

    Important Note: All request and response bodies are raw CBOR bytes. The SDK handles framing and correlation IDs, but it does not encode or decode the CBOR message body for you. The raw body is the full CBOR-encoded protocol Message body (v, t, and p).

    For most standard applications, use the Sandbox, exec, or fs modules instead.

    from microsandbox import AgentClient
    
    client = await AgentClient.connect_sandbox("dev")   # 1. connect
    ready = client.ready_bytes()                        # 2. inspect handshake
    frame = await client.request(0, body)               # 3. raw request/response
    await client.close()                                # 4. close
  6. Understand Agent Protocol Versioning and Compatibility

    main

    The microsandbox protocol manages communication between two components that may have different version levels:

    1. The runtime (agentd): Runs inside the sandbox. It is frozen once the sandbox is created and cannot be upgraded without recreating the sandbox.
    2. The host (SDK / CLI): Runs on your machine. It can be upgraded at any time.

    Compatibility Rule: The host (newer side) always adapts to the runtime (older side). The protocol uses a handshake at the start of a connection to agree on a single version number, called the generation. The connection uses the lower of the two versions. This ensures that an old runtime never has to understand new protocol features; the host handles all adaptation or prevents sending unsupported features to avoid crashes.

  7. Understand sandbox filesystem isolation

    main

    By default, a sandbox's storage is private. A guest sandbox only sees:

    • The OCI image's root filesystem.
    • Any explicitly mounted resources (bind directories, named volumes, or disk images).

    There is no implicit passthrough of host paths, environment variables, or credentials. The root filesystem uses a layered approach: shared, read-only image layers from a content-addressed cache, topped with a per-sandbox private writable layer. Writes made by the guest are isolated to this writable layer and are deleted when the sandbox is removed.

  8. Understand the microsandbox security model

    main

    microsandbox uses a microVM architecture to isolate untrusted workloads (AI agents, user submissions, etc.) from your host. The model relies on a hardware hypervisor (KVM on Linux, Apple's Hypervisor.framework on macOS) to create a strict boundary between the Guest (untrusted) and the Host (trusted).

    The Trust Boundary

    • Guest (Untrusted): Contains the workload, its processes, the guest Linux kernel, and agentd. It is assumed to be adversarial.
    • Host (Trusted): Contains your application, the msb CLI, and the per-sandbox process that manages the VMM, network stack, filesystem broker, and secret injection.

    Interaction between the Guest and Host is strictly mediated through a small set of paravirtual (virtio) devices (console, network, files, and disks). The guest cannot make host syscalls, read host memory, or open host connections directly.

  9. Use AgentClient for low-level agentd communication

    main

    The AgentClient is a low-level transport used to communicate with agentd via a running sandbox's relay socket. It is intended for developers building protocol-level integrations or SDK layers.

    For most standard application tasks, you should use the higher-level Sandbox, exec, or fs modules instead.

    AgentClient provides two tiers of interaction:

    • Typed methods: Automatically encode and decode microsandbox protocol messages.
    • Raw methods: Move framed CBOR bytes without decoding the message body (the raw body includes the full CBOR-encoded protocol Message containing v, t, and p).
  10. Understand the microsandbox secret substitution mechanism

    main

    microsandbox uses a placeholder-based system to prevent credentials from ever entering the guest VM. Instead of the real secret, the guest environment receives a placeholder (defaulting to $MSB_<env_var>).

    The real value is only substituted at the network boundary by the host-side proxy when a request is sent to an explicitly allowed host. This ensures that even if a guest is compromised, the attacker only finds meaningless placeholder strings in memory, environment variables, or disk snapshots.

  11. Quickstart: Run commands via SSH in a Python sandbox

    main

    To run commands in a running sandbox using the Python SDK, use sb.ssh().open_client() to establish a connection, then call .exec() on the resulting client.

    Note: SSH is only supported on local sandboxes.

    from microsandbox import Sandbox
    
    async with await Sandbox.create("api", image="python") as sb:
        client = await sb.ssh().open_client()       # 1. open an SSH client
        out = await client.exec("python -V")        # 2. run a command
        print(out.stdout_text)
    
        await client.close()                        # 3. close the session
  12. Quickstart: Access a sandbox via SSH

    main

    To interact with a running sandbox, boot it using the Sandbox builder, open an SSH client via .ssh().openClient(), and use .exec() to run commands. Always remember to close the client when finished.

    import { Sandbox } from "microsandbox";
    
    await using sandbox = await Sandbox.builder("api")   // 1. boot the sandbox
      .image("python")
      .create();
    
    const client = await sandbox.ssh().openClient();     // 2. open an SSH client
    const out = await client.exec("python -V");          // 3. run a command
    console.log(out.stdout.toString());
    
    await client.close();                                // 4. close the client