Help us shape the future of Go! Take the 2026 Go Developer Survey

arapuca

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

README

go-arapuca

Go bindings for arapuca, a Linux/macOS process sandbox. Wraps the C API via cgo to provide idiomatic Go types for launching sandboxed subprocesses.

What arapuca enforces

On Linux: Landlock filesystem restrictions, seccomp BPF syscall filtering, cgroups v2 resource limits (memory, CPU, PIDs), network namespace isolation, rlimits, pdeathsig, setsid, environment sanitization.

On macOS: Apple's sandbox-exec (Seatbelt) with deny-default profiles, rlimits, memory polling, parent-PID watchdog.

Install

go get github.com/sergio-correia/go-arapuca

Requires CGO_ENABLED=1 and a C compiler (gcc or clang). The static library (libarapuca.a) is vendored via git-lfs — no Rust toolchain needed.

Usage

package main

import (
    "context"
    "fmt"
    "log"

    arapuca "github.com/sergio-correia/go-arapuca"
)

func main() {
    sb, err := arapuca.New()
    if err != nil {
        log.Fatal(err)
    }
    defer sb.Close()

    cfg := arapuca.Config{
        Profile: arapuca.Profile{
            ReadPaths:   []string{"/usr", "/lib", "/lib64", "/bin", "/etc", "/dev"},
            WritePaths:  []string{"/tmp/workspace"},
            MaxMemoryMB: 2048,
            MaxPIDs:     256,
            MaxCPUPct:   200, // 2 cores
            UseNetNS:    true,
        },
        TaskID:  "task-42",
        Phase:   "execute",
        WorkDir: "/tmp/workspace",
    }

    proc, err := sb.Launch(context.Background(), cfg, "/usr/bin/python3", []string{"agent.py"}, nil)
    if err != nil {
        log.Fatal(err)
    }

    exitCode, err := proc.Wait()
    if err != nil {
        log.Printf("process error: %v", err)
    }

    stats := proc.ResourceStats()
    fmt.Printf("exit=%d peak_memory=%d bytes\n", exitCode, stats.MemoryPeakBytes)

    proc.Cleanup()
}

API

Sandbox
arapuca.New() (*Sandbox, error)           // create sandbox handle
sb.Launch(ctx, cfg, cmd, args, extraFiles)  // launch sandboxed subprocess
sb.CgroupsAvailable() bool                 // probe cgroups v2
sb.Close()                                 // release handle
Process
proc.PID() int                     // subprocess PID
proc.Wait() (int, error)           // wait for exit (code, error)
proc.ResourceStats() ResourceUsage // cgroup stats (before Cleanup)
proc.OOMCount() int                // OOM kill count (before Cleanup)
proc.Cleanup()                     // release resources
Utilities
arapuca.MakeSocketDir() (string, error)  // temp dir for Unix sockets
arapuca.MakeTmpDir(taskID) (string, error)
arapuca.WrapperPath() string             // find arapuca binary
arapuca.LandlockABIVersion() int         // 0 if unavailable
arapuca.NetNSAvailable() bool
arapuca.DiskUsageMB(path) uint64
Types
type Profile struct {
    ReadPaths, WritePaths []string
    MaxMemoryMB           uint64  // 0 = no limit
    MaxCPUPct             uint32  // 0 = no limit; 200 = 2 cores
    MaxPIDs               uint32
    MaxFileSizeMB         uint64
    UseNetNS              bool
}

type Config struct {
    Profile            Profile
    SocketDir          string
    TaskID             string
    Phase              string
    WorkDir            string
    Stdout, Stderr     *os.File  // nil = inherit
    NetworkProxySocket string
}

type ResourceUsage struct {
    MemoryCurrentBytes, MemoryPeakBytes int64
    CPUUsageSeconds                     float64
    PIDCount                            int64
    IOReadBytes, IOWriteBytes           int64
}

Thread Safety

  • Sandbox is safe for concurrent use (mutex-protected).
  • Process methods are safe for concurrent use (mutex-protected).
  • Error checking uses runtime.LockOSThread() to pin goroutines to OS threads (arapuca uses thread-local error storage).
  • Context cancellation sends SIGKILL to the process group.

Building from source

To update the vendored libarapuca.a from a local arapuca build:

make update-lib ARAPUCA_DIR=../arapuca

License

Apache-2.0

Documentation

Overview

Package arapuca provides Go bindings for the arapuca process sandbox library via cgo. It wraps the C API to provide idiomatic Go types for sandboxed subprocess management.

Arapuca enforces OS-level isolation (Landlock, seccomp BPF, cgroups v2, network namespaces on Linux; sandbox-exec on macOS) on untrusted subprocesses.

Thread safety: arapuca_last_error() uses thread-local storage. All cgo call sequences that check errors use runtime.LockOSThread() to prevent Go's goroutine scheduler from moving the goroutine to a different OS thread between the call and the error check.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DiskUsageMB

func DiskUsageMB(path string) uint64

DiskUsageMB returns the disk usage of a directory in MB.

func LandlockABIVersion

func LandlockABIVersion() int

LandlockABIVersion probes the kernel's Landlock ABI version. Returns 0 if Landlock is unavailable.

func MakeSocketDir

func MakeSocketDir() (string, error)

MakeSocketDir creates a temporary directory with mode 0700 and a random suffix for per-agent Unix sockets. The caller is responsible for removing the directory when done.

func MakeTmpDir

func MakeTmpDir(taskID string) (string, error)

MakeTmpDir creates a temporary directory for the given task with a random suffix. The caller is responsible for removing it.

func NetNSAvailable

func NetNSAvailable() bool

NetNSAvailable probes whether network namespace isolation works.

func WrapperPath

func WrapperPath() string

WrapperPath returns the path to the arapuca binary, or empty string if not found.

Types

type Config

type Config struct {
	Profile            Profile
	SocketDir          string   // Per-agent socket directory.
	TaskID             string   // Task identifier.
	Phase              string   // Current phase (opaque to arapuca).
	WorkDir            string   // Working directory (empty = inherit).
	Stdout             *os.File // Redirect stdout (nil = inherit).
	Stderr             *os.File // Redirect stderr (nil = inherit).
	NetworkProxySocket string   // Path to network proxy Unix socket.
}

Config holds the full configuration for launching a sandboxed process.

type Process

type Process struct {
	// contains filtered or unexported fields
}

Process represents a running sandboxed subprocess.

func (*Process) Cleanup

func (p *Process) Cleanup()

Cleanup releases the subprocess's resources (temp directory, cgroup). Must only be called after Wait() returns. Safe to call multiple times.

func (*Process) OOMCount

func (p *Process) OOMCount() int

OOMCount reads the OOM kill count from the subprocess's cgroup. Must be called before Cleanup().

func (*Process) PID

func (p *Process) PID() int

PID returns the process ID of the sandboxed subprocess.

func (*Process) ResourceStats

func (p *Process) ResourceStats() ResourceUsage

ResourceStats reads resource usage from the subprocess's cgroup. Must be called before Cleanup(). Returns zero values if cgroups are unavailable.

func (*Process) Wait

func (p *Process) Wait() (int, error)

Wait waits for the subprocess to exit. Returns the exit code and any error. If the process was killed by a signal, the exit code is 0 and the error describes the signal.

type Profile

type Profile struct {
	ReadPaths     []string // Allowed read-only paths.
	WritePaths    []string // Allowed read-write paths.
	MaxMemoryMB   uint64   // Memory limit in MB (0 = no limit).
	MaxCPUPct     uint32   // CPU percentage (0 = no limit; 200 = 2 cores).
	MaxPIDs       uint32   // Max processes (0 = no limit).
	MaxFileSizeMB uint64   // Max file size in MB (0 = no limit).
	UseNetNS      bool     // Use network namespace isolation.
}

Profile defines the restrictions applied to a sandboxed subprocess.

type ResourceUsage

type ResourceUsage struct {
	MemoryCurrentBytes int64
	MemoryPeakBytes    int64
	CPUUsageSeconds    float64
	PIDCount           int64
	IOReadBytes        int64
	IOWriteBytes       int64
}

ResourceUsage holds cgroup v2 resource usage statistics.

type Sandbox

type Sandbox struct {
	// contains filtered or unexported fields
}

Sandbox wraps the arapuca sandbox handle. It is the entry point for launching sandboxed subprocesses. Create with New().

func New

func New() (*Sandbox, error)

New creates a new sandbox for the current platform.

func (*Sandbox) CgroupsAvailable

func (s *Sandbox) CgroupsAvailable() bool

CgroupsAvailable reports whether cgroups v2 resource limits are available on this system.

func (*Sandbox) Close

func (s *Sandbox) Close()

Close releases the sandbox handle. Safe to call multiple times.

func (*Sandbox) Launch

func (s *Sandbox) Launch(ctx context.Context, cfg Config, cmd string, args []string, extraFiles []*os.File) (*Process, error)

Launch starts a sandboxed subprocess. The subprocess is isolated according to the Config's Profile. Extra file descriptors in extraFiles are inherited by the subprocess at FD positions 3, 4, ...

The returned Process must be waited on and cleaned up.

Context cancellation: if ctx is cancelled, the subprocess is killed via SIGKILL to the process group.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL