cc

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2026 License: GPL-3.0 Imports: 4 Imported by: 0

README

CrumbleCracker

Experimental Alpha: Public Preview

CrumbleCracker: An experimental embedded virtual machine monitor written in Golang for speed and ease of use.

What is CrumbleCracker?

Firstly the name comes from Apple Crumble Cracker meant as a cross platform virtual machine monitor (this is the 15-20th iteration of a project with that name).

CrumbleCracker is a high performance embeddable virtualization platform designed to deeply integrate the operating system running inside the guest machine with the host-side API. That means unlike QEMU we have a direct API for booting a Linux kernel and running commands with different settings inside the machine. Think of CrumbleCracker like a virtualized version of runc or another OCI runtime but designed to be embedded into existing Golang codebases.

Development Plan

Stage 1 of development on x86_64 Linux is complete

Stage 2 is stability and speed

Part 1

  • Fix macOS arm64
  • Fix Windows amd64
  • Fix Linux arm64
  • Fix Windows arm64
  • Fix Linux Compile Errors (issues with Virtio-fs)
  • Fix Ubuntu boot (issues with /etc/resolv.conf)

Part 2

  • Add benchmarking (both Golang and Tests)
  • Add snapshot support
  • Improve KVM AMD64
  • Improve HVF arm64
  • Improve WHP amd64
  • Improve KVM arm64
  • Improve WHP arm64
  • Improve VirtIO
  • Improve Networking
  • Improve Filesystem

Final Part

  • Get a more advanced desktop running (like XFCE)

Stage 3 is Developer Experience and Public Beta

Cross-Platform Status

Only Linux Guests and bare-metal code are currently supported.

Windows, macOS, and Linux are fully supported as host operating systems.

Relationship to TinyRange

CrumbleCracker is a VMM (Virtual Machine Monitor) while I see TinyRange as a broader build system. The version in https://github.com/tinyrange/tinyrange has been going though experimental changes over the past 6 months and once CrumbleCracker is more stable I expect to use it as a foundation for a new version.

Licensing & Contributions

Currently I've decided to license the code under GPL-3.0 in a public preview period. This is intentionally to limit the spread to interested users primarily looking at tools I've developed internally while also enabling derived code to be shared. I expect the API to change dramatically over time so until that cools down I'll keep the license as GPL-3.0 and won't accept outside code contributions. Once the API is stable I expect to switch the licensing to something more permissive like Apache-2.0 or MPL.

Policy on AI Usage

Update after Stage 1

Note: I'm posting this for transparency and also some advice for other adopting models at a larger scale (the project is about 80k lines of code)

The code in this repository has remained about 95% AI coded with a mixture of models (Opus 4.5, GPT-5.2, and a little Composer 1) mostly via Cursor. Honestly this surprised me, weighing up the increase of engineering debt with the development speed improvements is a interesting tradeoff. By that I mean the models are still not great at architecting (4 generations on the same problem have 4 different solutions suggesting they don't settle or meaningfully derive an architecture). But this same approach is great at fixing problems and with the average problem in the project being difficult (on a personal scale) that feels like a easy trade-off while the project is not fully released.

All of this is not without it's flaws. AI is expensive at this scale. I'd estimate I spent about $500-600 USD on Stage 1 in generation credits across the models. Half of that was spent in a single debugging session as Opus 4.5 got stuck on a problem for hours and burned tokens until I stopped it. I also continue to see behavior from models showing a lack of reasoning depth and understanding. The odd thing about that is as the models get more advanced the flaws feel more human. The models don't understand taking a step back on a problem and coming back with a new perspective so a small punt in the right direction is often key to solving hard problems.

I'll leave the accomplishments of the project as a testament to the success of state of the art models with the caveat that most of the core architectural decisions I made despite the models efforts.

A take away and future direction is with the evolution of AI models the quality of tooling becomes essential. The issues I experienced with debugging forced me to invest time into better debug tooling designed to capture dense logging. Also the adoption of Greptile has been surprisingly effective. Besides issues with outdated training data (it likes hating on new methods in Golang and ranking my PRs 1/5 when it sees them) it spots hard to find bugs on a regular basis and the reports are a very helpful read to improve overall code quality. It also encouraged me (indirectly) to protect the main branch so I was forced to make and polish pull requests.

As someone who spends most of their time writing tooling I see a bright future adopting AI models in areas that speed up development but I worry the immaturity of current tooling holds back adoption. That is one of the use cases I see for CrumbleCracker in the future once it's more stable though.

Getting Start Notes

To run Alpine in a default VM ./tools/build.go -run -- alpine.

You can run tests with...

  • ./tools/build.go -quest: Basic tests, should print "Hello, World" at the end.
  • ./tools/build.go -bringup: Advanced Linux Tests, tests FS, Networking, and others.
  • ./tools/build.go -bringup-gpu: GPU Tests, should work on headless systems as well.
  • ./tools/build.go -runtest <test>: Runs a test in the tests/ dir. These are Dockerfiles meant for advanced tests.
    • Notably...
      • sway. When run with ./tools/build.go -runtest sway -- -exec -gpu should start a window with a Sway desktop.

Special Thanks

Documentation

Overview

Package cc provides virtualization primitives with APIs that mirror the Go standard library. An Instance represents an isolated virtual machine that can be interacted with using familiar os, os/exec, io, and net patterns.

Index

Constants

View Source
const (
	PullIfNotPresent = api.PullIfNotPresent
	PullAlways       = api.PullAlways
	PullNever        = api.PullNever
)

Pull policy constants.

Variables

View Source
var (
	ErrNotRunning    = api.ErrNotRunning
	ErrAlreadyClosed = api.ErrAlreadyClosed
	ErrTimeout       = api.ErrTimeout

	// ErrHypervisorUnavailable indicates the hypervisor is not available.
	// This can happen when:
	// - Running on a platform without hypervisor support
	// - Missing permissions (e.g., macOS entitlements, Linux /dev/kvm access)
	// - Running in a VM or container without nested virtualization
	//
	// Use errors.Is(err, cc.ErrHypervisorUnavailable) to check and skip tests in CI.
	ErrHypervisorUnavailable = api.ErrHypervisorUnavailable
)

Common sentinel errors.

Functions

func EnsureExecutableIsSigned

func EnsureExecutableIsSigned() error

EnsureExecutableIsSigned checks if the current executable is signed with the hypervisor entitlement (macOS only). If not, it signs the executable and re-executes itself. This is useful for test binaries.

On non-macOS platforms, this is a no-op.

Call this at the start of TestMain(). If signing and re-exec succeed, this function does not return. If already signed, it returns nil.

Example:

func TestMain(m *testing.M) {
    if err := cc.EnsureExecutableIsSigned(); err != nil {
        log.Fatalf("Failed to sign executable: %v", err)
    }
    os.Exit(m.Run())
}

func SupportsHypervisor

func SupportsHypervisor() error

SupportsHypervisor checks if the hypervisor is available on this system. Returns nil if available, or an error describing why not. Use this for early startup checks to show a friendly error message.

Example:

if err := cc.SupportsHypervisor(); err != nil {
    log.Fatal("Hypervisor unavailable:", err)
}

Types

type CacheDir

type CacheDir = api.CacheDir

CacheDir represents a cache directory configuration. It provides a unified way to configure cache directories for both OCIClient and Instance, ensuring they share the same cache location.

func NewCacheDir

func NewCacheDir(path string) (CacheDir, error)

NewCacheDir creates a cache directory config. If path is empty, uses the platform-specific default cache directory.

type Cmd

type Cmd = api.Cmd

Cmd represents a command ready to be run in the guest. It mirrors *os/exec.Cmd.

type DockerAndRuntimeOption

type DockerAndRuntimeOption interface {
	Option
	DockerfileOption
}

func WithMemoryMB

func WithMemoryMB(size uint64) DockerAndRuntimeOption

WithMemoryMB sets the memory size in megabytes.

type DockerfileBuildContext

type DockerfileBuildContext = api.DockerfileBuildContext

DockerfileBuildContext provides access to files during COPY/ADD operations.

func NewDirBuildContext

func NewDirBuildContext(dir string) (DockerfileBuildContext, error)

NewDirBuildContext creates a new directory-based build context.

type DockerfileOption

type DockerfileOption = api.DockerfileOption

DockerfileOption configures Dockerfile building.

func WithBuildArg

func WithBuildArg(key, value string) DockerfileOption

WithBuildArg sets a build argument (ARG instruction).

func WithBuildContext

func WithBuildContext(ctx DockerfileBuildContext) DockerfileOption

WithBuildContext sets the build context for COPY/ADD operations.

func WithBuildContextDir

func WithBuildContextDir(dir string) DockerfileOption

WithBuildContextDir creates a build context from a directory path.

func WithDockerfileCacheDir

func WithDockerfileCacheDir(dir string) DockerfileOption

WithDockerfileCacheDir sets the cache directory for filesystem snapshots.

type DockerfileRuntimeConfig

type DockerfileRuntimeConfig = api.DockerfileRuntimeConfig

DockerfileRuntimeConfig holds metadata from CMD, ENTRYPOINT, USER, etc.

func BuildDockerfileRuntimeConfig

func BuildDockerfileRuntimeConfig(dockerfileContent []byte, opts ...DockerfileOption) (*DockerfileRuntimeConfig, error)

BuildDockerfileRuntimeConfig parses a Dockerfile and returns the runtime configuration (CMD, ENTRYPOINT, USER, etc.) without building the image.

type DownloadProgress

type DownloadProgress = api.DownloadProgress

DownloadProgress represents the current state of a download.

type Error

type Error = api.Error

Error represents a cc operation error with structured information.

type Exec

type Exec = api.Exec

Exec provides process execution on a guest.

type FS

type FS = api.FS

FS provides filesystem operations on a guest. It mirrors functions from the os package.

type File

type File = api.File

File represents an open file in a guest filesystem. It mirrors *os.File.

type FilesystemSnapshot

type FilesystemSnapshot = api.FilesystemSnapshot

FilesystemSnapshot represents a point-in-time snapshot of a filesystem. It can be used as an InstanceSource to create new instances with the captured filesystem state.

func BuildDockerfileSource

func BuildDockerfileSource(ctx context.Context, dockerfileContent []byte, client OCIClient, opts ...DockerfileOption) (FilesystemSnapshot, error)

BuildDockerfileSource builds an InstanceSource from Dockerfile content. It parses the Dockerfile, converts instructions to filesystem operations, and executes them to produce a cached filesystem snapshot.

Example:

dockerfile := []byte(`
    FROM alpine:3.19
    RUN apk add --no-cache curl
    COPY app /usr/local/bin/
    CMD ["app"]
`)
source, err := cc.BuildDockerfileSource(ctx, dockerfile, client,
    cc.WithBuildContextDir("./build"),
    cc.WithDockerfileCacheDir(cacheDir),
)

type FilesystemSnapshotFactory

type FilesystemSnapshotFactory = api.FilesystemSnapshotFactory

FilesystemSnapshotFactory builds filesystem snapshots using Dockerfile-like operations. It supports caching intermediate layers to speed up repeated builds.

func NewFilesystemSnapshotFactory

func NewFilesystemSnapshotFactory(client OCIClient, cacheDir string) *FilesystemSnapshotFactory

NewFilesystemSnapshotFactory creates a new factory for building filesystem snapshots. The factory uses the provided cacheDir to store and retrieve cached layers.

Example:

snap, err := cc.NewFilesystemSnapshotFactory(client, cacheDir).
    From("alpine:3.19").
    Run("apk", "add", "--no-cache", "gcc", "musl-dev").
    Exclude("/var/cache/*", "/tmp/*").
    Build(ctx)

type FilesystemSnapshotOption

type FilesystemSnapshotOption = api.FilesystemSnapshotOption

FilesystemSnapshotOption configures a snapshot operation.

func WithSnapshotCacheDir

func WithSnapshotCacheDir(dir string) FilesystemSnapshotOption

WithSnapshotCacheDir sets the directory for snapshot cache storage.

func WithSnapshotExcludes

func WithSnapshotExcludes(patterns ...string) FilesystemSnapshotOption

WithSnapshotExcludes specifies path patterns to exclude from snapshots. Patterns use glob-style matching (*, ?, []).

type GPU

type GPU = api.GPU

GPU provides access to guest display and input devices.

type Helper

type Helper = api.Helper

Helper manages a connection to a cc-helper process.

func SpawnHelper

func SpawnHelper() (Helper, error)

SpawnHelper starts a new cc-helper process and returns a Helper for creating and managing VM instances through it.

type ImageConfig

type ImageConfig = api.ImageConfig

ImageConfig contains OCI image configuration metadata. This provides access to container runtime settings like environment, entrypoint, and working directory.

func SourceConfig

func SourceConfig(source InstanceSource) *ImageConfig

SourceConfig returns the ImageConfig for a source, or nil if unavailable. This is a convenience function that performs a type assertion to OCISource.

type Instance

type Instance = api.Instance

Instance represents a running virtual machine.

func New

func New(source InstanceSource, opts ...Option) (Instance, error)

New creates and starts a new Instance from the given source.

The instance is ready for use when New returns. The caller must call Close when finished to release resources.

type InstanceSource

type InstanceSource = api.InstanceSource

InstanceSource is the source for creating a new Instance.

type MountConfig

type MountConfig struct {
	// Tag is the virtio-fs tag used to mount in the guest.
	// The guest mounts it with: mount -t virtiofs <tag> /mnt/path
	Tag string

	// HostPath is the host directory to expose. If empty, an empty writable
	// filesystem is created.
	HostPath string

	// Writable makes the mount writable. By default, mounts are read-only.
	Writable bool
}

MountConfig configures a host directory mount via virtio-fs.

type Net

type Net = api.Net

Net provides network operations on a guest.

type OCIClient

type OCIClient = api.OCIClient

OCIClient pulls OCI images and converts them to InstanceSources.

func NewOCIClient

func NewOCIClient() (OCIClient, error)

NewOCIClient creates a new OCI client for pulling images. Uses the default cache directory (platform-specific user config directory).

func NewOCIClientWithCache

func NewOCIClientWithCache(cache CacheDir) (OCIClient, error)

NewOCIClientWithCache creates a new OCI client using the provided CacheDir. This ensures the OCI client uses the same cache location as other components.

type OCIPullOption

type OCIPullOption = api.OCIPullOption

OCIPullOption configures an OCI pull operation.

func WithAuth

func WithAuth(username, password string) OCIPullOption

WithAuth provides authentication credentials for private registries.

func WithPlatform

func WithPlatform(os, arch string) OCIPullOption

WithPlatform specifies the platform to pull (e.g., "linux/amd64", "linux/arm64"). Defaults to the host platform.

func WithProgressCallback

func WithProgressCallback(fn ProgressCallback) OCIPullOption

WithProgressCallback sets a callback function for download progress updates. The callback receives progress updates with current/total bytes, filename, and blob index information. Set to nil to disable progress reporting.

Example:

source, err := client.Pull(ctx, "alpine:latest",
    cc.WithProgressCallback(func(p cc.DownloadProgress) {
        fmt.Printf("Downloading %s: %d/%d bytes\n", p.Filename, p.Current, p.Total)
    }),
)

func WithPullPolicy

func WithPullPolicy(policy PullPolicy) OCIPullOption

WithPullPolicy sets when to pull from the registry vs use cache.

type OCISource

type OCISource = api.OCISource

OCISource extends InstanceSource with OCI-specific metadata. Use type assertion or SourceConfig() to access the ImageConfig.

type Option

type Option = api.Option

Option configures an Instance.

func WithCPUs

func WithCPUs(count int) Option

WithCPUs sets the number of virtual CPUs. Default is 1.

func WithCache

func WithCache(cache CacheDir) Option

WithCache sets the cache directory for the instance. This is used for QEMU emulation binaries and other cached resources.

func WithDmesg

func WithDmesg() Option

WithDmesg enables kernel dmesg output (loglevel=7). When enabled, kernel messages are printed to the console which is useful for debugging boot issues and driver problems.

func WithGPU

func WithGPU() Option

WithGPU enables virtio-gpu and virtio-input devices for graphical output. When enabled, the instance's GPU() method returns a non-nil GPU interface. The caller must run the display loop on the main thread using Poll/Render/Swap.

Example:

runtime.LockOSThread() // Required for windowing on macOS
inst, err := cc.New(source, cc.WithGPU())
if gpu := inst.GPU(); gpu != nil {
    win := createWindow() // Platform-specific
    gpu.SetWindow(win)
    for {
        if !gpu.Poll() { break }
        gpu.Render()
        gpu.Swap()
    }
}

func WithInteractiveIO

func WithInteractiveIO(stdin io.Reader, stdout io.Writer) Option

WithInteractiveIO enables interactive terminal mode and sets the stdin/stdout. When enabled, stdin/stdout connect to virtio-console for live I/O instead of the default vsock-based capture mode. This is suitable for running interactive commands like shells.

Example:

inst, err := cc.New(source,
    cc.WithInteractiveIO(os.Stdin, os.Stdout),
)

func WithMount

func WithMount(config MountConfig) Option

WithMount adds a virtio-fs mount to the guest. The guest can mount it with: mount -t virtiofs <tag> /mnt/path

Example:

inst, err := cc.New(source,
    cc.WithMount(cc.MountConfig{Tag: "shared", HostPath: "/host/data"}),
)

Then in guest: mount -t virtiofs shared /mnt/shared

func WithPacketCapture

func WithPacketCapture(w io.Writer) Option

WithPacketCapture enables packet capture (pcap format) to the given writer. The captured packets can be analyzed with tools like Wireshark or tcpdump.

Example:

f, _ := os.Create("capture.pcap")
defer f.Close()
inst, err := cc.New(source, cc.WithPacketCapture(f))

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets a maximum lifetime for the instance. After this duration, the instance is forcibly terminated.

func WithUser

func WithUser(user string) Option

WithUser sets the user (and optionally group) to run as inside the guest. Format: "user" or "user:group" or numeric "1000" or "1000:1000".

type ProgressCallback

type ProgressCallback = api.ProgressCallback

ProgressCallback is called periodically during downloads.

type PullPolicy

type PullPolicy = api.PullPolicy

PullPolicy determines when images are fetched from the registry.

Directories

Path Synopsis
bindings
c command
cmd
cc-helper command
cc-helper is a codesigned helper process that runs VMs on behalf of libcc clients.
cc-helper is a codesigned helper process that runs VMs on behalf of libcc clients.
ccapp command
ccinstaller command
ccinstaller is a small binary that handles replacing the main application binary.
ccinstaller is a small binary that handles replacing the main application binary.
debug command
helper-bench command
helper-bench spawns multiple cc-helper processes concurrently, each running its own VM via the IPC protocol.
helper-bench spawns multiple cc-helper processes concurrently, each running its own VM via the IPC protocol.
helper-fstest command
helper-fstest validates filesystem operations through the helper API.
helper-fstest validates filesystem operations through the helper API.
snapshot-e2e command
timeslice command
examples
getting-started/compile_c command
compile_c demonstrates compiling and running C code in a sandboxed environment.
compile_c demonstrates compiling and running C code in a sandboxed environment.
getting-started/compile_go command
compile_go demonstrates compiling and running Go code in a sandboxed environment.
compile_go demonstrates compiling and running Go code in a sandboxed environment.
getting-started/compile_rust command
compile_rust demonstrates compiling and running Rust code in a sandboxed environment.
compile_rust demonstrates compiling and running Rust code in a sandboxed environment.
getting-started/env_vars command
env_vars demonstrates passing environment variables to sandboxed processes.
env_vars demonstrates passing environment variables to sandboxed processes.
getting-started/file_ops command
file_ops demonstrates filesystem operations in a sandboxed environment.
file_ops demonstrates filesystem operations in a sandboxed environment.
getting-started/multi_file command
multi_file demonstrates working with multi-file projects in a sandboxed environment.
multi_file demonstrates working with multi-file projects in a sandboxed environment.
getting-started/run_node command
run_node demonstrates running JavaScript/Node.js scripts in a sandboxed environment.
run_node demonstrates running JavaScript/Node.js scripts in a sandboxed environment.
getting-started/run_python command
run_python demonstrates running Python scripts in a sandboxed environment.
run_python demonstrates running Python scripts in a sandboxed environment.
getting-started/shell_script command
shell_script demonstrates running shell scripts in a sandboxed environment.
shell_script demonstrates running shell scripts in a sandboxed environment.
getting-started/stdin_echo command
stdin_echo demonstrates processing stdin in a sandboxed environment.
stdin_echo demonstrates processing stdin in a sandboxed environment.
shared
Package shared provides common utilities for all examples.
Package shared provides common utilities for all examples.
shared/testrunner
Package testrunner provides a YAML-driven test framework for example services.
Package testrunner provides a YAML-driven test framework for example services.
shared/testrunner/cmd/runtest command
Command runtest executes YAML-driven tests for example services.
Command runtest executes YAML-driven tests for example services.
internal
api
Package api provides the internal implementation for the public cc API.
Package api provides the internal implementation for the public cc API.
asm
cmd/cc command
cmd/codesign command
Package codesign provides basic functionalities for ad-hoc code signing of Mach-O files.
Package codesign provides basic functionalities for ad-hoc code signing of Mach-O files.
cmd/gowin command
cmd/html command
Command html is a kitchen sink demo for the HTML renderer.
Command html is a kitchen sink demo for the HTML renderer.
cmd/kernel command
cmd/miniplayer command
Command miniplayer displays markdown files with an embedded VM terminal.
Command miniplayer displays markdown files with an embedded VM terminal.
cmd/quest command
cmd/term command
cmd/termbench command
cmd/tstest command
dockerfile
Package dockerfile provides a parser and builder for converting Dockerfiles into cc.InstanceSource objects.
Package dockerfile provides a parser and builder for converting Dockerfiles into cc.InstanceSource objects.
fdt
fslayer
Package fslayer provides filesystem layer storage and management for snapshots.
Package fslayer provides filesystem layer storage and management for snapshots.
git
Package git provides functionality for reading and writing git repositories.
Package git provides functionality for reading and writing git repositories.
gowin/third_party/truetype
This library processes TrueType files:
This library processes TrueType files:
gowin/ui
Package ui provides a widget-based UI framework for graphics rendering.
Package ui provides a widget-based UI framework for graphics rendering.
gowin/ui/html
Package html provides a minimal HTML renderer that generates gowin/ui widgets.
Package html provides a minimal HTML renderer that generates gowin/ui widgets.
helper
Package helper implements the cc-helper process that runs VMs on behalf of libcc clients.
Package helper implements the cc-helper process that runs VMs on behalf of libcc clients.
hv
ipc
Package ipc provides the inter-process communication protocol for the cc-helper out-of-process architecture.
Package ipc provides the inter-process communication protocol for the cc-helper out-of-process architecture.
ir
netstack
Package raw implements a tiny, purpose-built, in-VM L2/L3 network stack.
Package raw implements a tiny, purpose-built, in-VM L2/L3 network stack.
oci
rtg
rtg/runtime
Package runtime provides stub declarations for the RTG runtime.
Package runtime provides stub declarations for the RTG runtime.
update
Package update handles checking for and downloading application updates.
Package update handles checking for and downloading application updates.
vfs
docs command
test-doc-examples extracts Go code examples from documentation and verifies they compile.
test-doc-examples extracts Go code examples from documentation and verifies they compile.

Jump to

Keyboard shortcuts

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