• Serhii-Set a day ago |
    The sandboxed execution part sounds really interesting.

    How are you handling isolation in practice — is it more like WASM-based sandboxing or OS-level isolation (containers, seccomp, etc.)?

    Feels like this is one of the hardest parts to get right for agent-based systems.

    • chrismeurer a day ago |
      Hey! It's OS-level, no WASM. Three tiers depending on what's available on the host:

      The full-isolation path is Firecracker. Each tool execution spins a microVM (1 vCPU, 512MB), and the runner communicates with a statically-linked guest agent over AF_VSOCK — the host process never shells out directly. Inside the VM there's a second allowlist before spawn() is called, so even if you somehow escaped the first filter you'd still hit a second one. In prod the pod itself runs under gVisor (runsc), so the microVM guest kernel is one layer inside gVisor's user-space kernel. That's three kernel-boundary crossings before you touch the real host.

      When Firecracker isn't available (dev laptops, restricted CI), it falls back to unshare-based namespace isolation. If that's also absent, it's just process-level controls — allowlist, CWD scoping, cgroup v2 limits (512MB memory, 50% CPU, 256 pids), and a prompt-injection scan that looks for bidi overrides and things like reverse.*ssh patterns.

      The hard part honestly wasn't the VM layer, it was the cgroup v2 writes being gracefully no-op'd when not root so the same binary works in dev and prod without ifdef soup. The AF_VSOCK path was also fiddly — you need a raw fd via libc::socket(AF_VSOCK, SOCK_STREAM, 0) and then wrap it into tokio's async I/O, which isn't exactly well-documented (got to fix that).

      Kata Containers RuntimeClass is also defined if you want hardware-VM pods instead of gVisor, but it defaults to gVisor since it doesn't require nested virt support on the nodes.

      Feel free to ask if you have anymore questions!