Skip to content

michael-duren/boxes

Repository files navigation

Boxes

go gopher building boxes

A minimal Linux container runtime, written in Go.

box is the CLI front-end for the Boxes runtime. It follows the OCI-style lifecycle (createstartkilldelete) and is intended as a learning-oriented, low-level container runtime in the spirit of runc / youki.

Status: early development — APIs, on-disk state layout, and CLI flags are unstable and may change without notice.

Table of Contents

Features

  • OCI-compatible container lifecycle (create, start, kill, delete, state)
  • Linux namespace isolation via cloneflags (PID, mount, UTS, IPC, network, cgroup, time)
  • OCI runtime spec (config.json) consumption
  • OCI lifecycle hooks (prestart, createRuntime, createContainer, startContainer, poststart, poststop)
  • Reexec-based container init (/proc/self/exe re-exec with namespace cloneflags)
  • Unix socket IPC between runtime and container process
  • Structured JSON file logging with debug toggle
  • On-disk state persistence (state.json under XDG dirs)
  • Signal handling (by number or name, e.g. SIGTERM, 9)
  • Image rootfs handling (bring-your-own rootfs)
  • Mount setup (pivot_root, bind mounts) — namespace created but rootfs not mounted
  • User namespace UID/GID mapping (rootless containers)
  • Container environment isolation (currently inherits host env)
  • cgroups v2 resource limits (CPU, memory, pids, IO)
  • Networking (veth + bridge, then CNI)

Architecture

┌──────────────┐      ┌──────────────────────┐      ┌──────────────────┐
│  box (CLI)   │ ───► │  internal/operations │ ───► │  internal/       │
│  cmd/cli     │      │  lifecycle handlers  │      │  container       │
└──────────────┘      └──────────────────────┘      └────────┬─────────┘
                                                             │
                      ┌──────────────────────┐      ┌────────▼─────────┐
                      │  internal/hooks      │      │  Linux kernel    │
                      │  OCI hook execution  │      │  ns / cloneflags │
                      └──────────────────────┘      └──────────────────┘

Container creation uses a reexec pattern: box create forks via /proc/self/exe reexec <id> with cloneflags that set up Linux namespaces. The child signals "ready" over a unix socket. On box start, the parent tells the child to proceed, and the child syscall.Exec()s the user process.

  • cmd/cli/ — Cobra-based CLI entrypoint.
  • internal/cli/ — subcommand definitions and flag wiring.
  • internal/operations/ — orchestration layer for each lifecycle verb.
  • internal/container/ — core Container type, state machine, reexec, socket IPC.
  • internal/hooks/ — OCI lifecycle hook execution.
  • internal/filesystem/ — XDG directory resolution for state/runtime paths.
  • internal/logger/ — structured JSON file logging via slog.
  • internal/errs/ — deferred-close error utilities.
  • internal/assert/ — custom test assertion library (no external test deps).

Requirements

  • Linux kernel ≥ 5.10 (cgroups v2, user namespaces)
  • Go ≥ 1.26.3
  • A rootfs directory (e.g. extracted from docker export or a distro bootstrap)
  • For rootless mode: newuidmap / newgidmap and /etc/subuid + /etc/subgid entries

Installation

From source:

git clone https://github.com/michael-duren/boxes.git
cd boxes
make build              # produces ./bin/box
make install            # installs to $HOME/.local/bin/box

PREFIX can be overridden, e.g. make install PREFIX=/usr/local.

Quick Start

# 1. Prepare a rootfs
mkdir -p /tmp/mybox/rootfs
# ...extract a base image tarball into rootfs/...

# 2. Create the container
box create mybox --bundle /tmp/mybox

# 3. Start it
box start mybox

# 4. Inspect its state
box state mybox

# 5. Stop and clean up
box kill mybox
box delete mybox

Commands

Command Description
box create <id> Create a container from a bundle (does not run)
box start <id> Start the process inside a created container
box state <id> Print the current state of a container
box kill <id> Send a signal to the container's init process
box delete <id> Remove a stopped container and its state

Run box <command> --help for full flag documentation.

Configuration

Containers are described by an OCI runtime config.json inside the bundle directory passed to box create. A minimal example:

{
  "ociVersion": "1.0.2",
  "process": {
    "args": ["/bin/sh"],
    "cwd": "/"
  },
  "root": { "path": "rootfs" }
}

State for running containers is persisted under $XDG_STATE_HOME/boxes/<container-id>/state.json, with runtime sockets at $XDG_RUNTIME_DIR/boxes/<container-id>/.

Development

Cross-compilation environment

Boxes targets Linux. If developing on macOS (or another non-Linux OS), the project uses direnv to automatically set GOOS=linux and GOARCH=amd64 so that gopls and other Go tools resolve Linux-only symbols (e.g. syscall.CLONE_*, unix.SIGKILL).

brew install direnv          # if not already installed
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc   # or your shell rc
direnv allow                 # approve the project .envrc

These variables are scoped to this project directory and do not affect other Go projects.

Make targets

make build      # compile to ./bin/box
make run ARGS="state mybox"
make test       # go test ./...
make vet
make fmt
make lint       # requires golangci-lint
make tidy
make clean

Project layout:

.
├── cmd/cli/                 # CLI entrypoint (Cobra)
├── internal/
│   ├── cli/                 # Subcommand definitions + flag wiring
│   ├── operations/          # Lifecycle operation orchestration
│   ├── container/           # Core Container type, state machine, reexec
│   ├── hooks/               # OCI lifecycle hook execution
│   ├── filesystem/          # XDG directory resolution
│   ├── logger/              # Structured JSON file logging (slog)
│   ├── errs/                # Error utilities
│   └── assert/              # Custom test assertion library
├── alpinefs/                # Alpine rootfs bundle + OCI config.json
├── scripts/                 # Helper scripts (alpine rootfs, OCI validation, Docker)
├── docs/                    # OCI validation research notes
├── slides/                  # Slidev presentation
├── Makefile
└── go.mod

OCI conformance

box is measured against the upstream opencontainers/runtime-tools validation suite. See docs/oci-validation.md for the CLI contract, suite mechanics, and version pin. To drive one validation test against the locally-built box (expected to fail during development — needs root + user namespaces):

./scripts/oci-validation.sh            # runs validation/default
TEST=state ./scripts/oci-validation.sh # run a specific test

Roadmap

  • CLI with OCI lifecycle verbs (create, start, kill, delete, state)
  • Reexec-based init process in new namespaces
  • Parse and honor OCI config.json
  • OCI lifecycle hook execution
  • Unix socket IPC (init sock + container sock)
  • Persisted container state under XDG dirs
  • Mount setup (pivot_root, rootfs, bind mounts)
  • Container environment and hostname configuration
  • cgroups v2 resource limits
  • Rootless support via user namespaces and newuidmap
  • --pid-file and --console-socket flags (OCI validation)
  • Networking (veth + bridge, then CNI)
  • exec into a running container
  • Integration / acceptance tests with a real rootfs

License

See LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors