{"id":47310729,"url":"https://github.com/stacklok/go-microvm","last_synced_at":"2026-04-02T12:01:39.144Z","repository":{"id":344881083,"uuid":"1157363463","full_name":"stacklok/go-microvm","owner":"stacklok","description":"Go framework for running OCI images as microVMs with libkrun","archived":false,"fork":false,"pushed_at":"2026-03-24T11:35:46.000Z","size":2531,"stargazers_count":2,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-03-25T14:09:37.082Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stacklok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-13T18:25:06.000Z","updated_at":"2026-03-24T11:35:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stacklok/go-microvm","commit_stats":null,"previous_names":["stacklok/go-microvm"],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/stacklok/go-microvm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacklok%2Fgo-microvm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacklok%2Fgo-microvm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacklok%2Fgo-microvm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacklok%2Fgo-microvm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stacklok","download_url":"https://codeload.github.com/stacklok/go-microvm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacklok%2Fgo-microvm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31305971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T09:48:21.550Z","status":"ssl_error","status_checked_at":"2026-04-02T09:48:19.196Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-17T11:15:33.766Z","updated_at":"2026-04-02T12:01:38.910Z","avatar_url":"https://github.com/stacklok.png","language":"Go","funding_links":[],"categories":["Security \u0026 Compliance"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"assets/go-microvm.png\" alt=\"go-microvm mascot\" width=\"250\" /\u003e\n\n  \u003ch1\u003ego-microvm\u003c/h1\u003e\n\n  \u003cp\u003e\u003cstrong\u003eRun OCI container images as microVMs with libkrun.\u003c/strong\u003e\u003c/p\u003e\n\n  \u003cp\u003e\n    \u003ca href=\"https://github.com/stacklok/go-microvm/actions/workflows/ci.yaml\"\u003e\u003cimg src=\"https://github.com/stacklok/go-microvm/actions/workflows/ci.yaml/badge.svg\" alt=\"CI status\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://pkg.go.dev/github.com/stacklok/go-microvm\"\u003e\u003cimg src=\"https://pkg.go.dev/badge/github.com/stacklok/go-microvm.svg\" alt=\"Go Reference\" /\u003e\u003c/a\u003e\n    \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-Apache--2.0-blue.svg\" alt=\"License: Apache-2.0\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/stacklok/go-microvm\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/stacklok/go-microvm\" alt=\"Go Report Card\" /\u003e\u003c/a\u003e\n  \u003c/p\u003e\n\n  \u003cp\u003e\n    \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e \u0026middot;\n    \u003ca href=\"#architecture\"\u003eArchitecture\u003c/a\u003e \u0026middot;\n    \u003ca href=\"docs/ARCHITECTURE.md\"\u003eDocs\u003c/a\u003e \u0026middot;\n    \u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e \u0026middot;\n    \u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n\u003e [!WARNING]\n\u003e **Experimental** -- go-microvm is under active development. APIs, configuration\n\u003e formats, and behavior may change without notice between releases. It is not\n\u003e yet recommended for production use.\n\n---\n\ngo-microvm is a Go library and runner binary that turns any OCI container image\ninto a lightweight virtual machine. It pulls the image, flattens its layers\ninto a rootfs, configures in-process networking, and boots the result using\n[libkrun](https://github.com/containers/libkrun) -- all in a single function\ncall.\n\nYou would use go-microvm when you need stronger isolation than containers provide\nbut want to keep the OCI image workflow you already have. The library handles\nimage caching, preflight validation, port forwarding, virtio-fs mounts, and\nprocess lifecycle so you can focus on what runs inside the VM.\n\n## Table of Contents\n\n- [Prerequisites](#prerequisites)\n- [Quick Start](#quick-start)\n- [Advanced Usage](#advanced-usage)\n- [Package Overview](#package-overview)\n- [Build](#build)\n- [Architecture](#architecture)\n- [Security Model](#security-model)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Prerequisites\n\ngo-microvm requires hardware virtualization support and a few system packages.\n\n### Linux -- Fedora\n\n```bash\n# Install libkrun development headers\nsudo dnf install libkrun-devel\n\n# Ensure your user has KVM access\nsudo usermod -aG kvm $USER\n# Log out and back in for the group change to take effect\n```\n\n### Linux -- Ubuntu / Debian\n\nlibkrun is not yet packaged for Debian-based distributions. You must build it\nfrom source:\n\n```bash\n# Install build dependencies\nsudo apt install build-essential libssl-dev pkg-config python3 patchelf\n\n# Clone and build libkrun\ngit clone https://github.com/containers/libkrun.git\ncd libkrun\nmake\nsudo make install\nsudo ldconfig\n\n# Ensure your user has KVM access\nsudo usermod -aG kvm $USER\n```\n\n### macOS (Apple Silicon)\n\n```bash\n# Install libkrun via Homebrew\nbrew install libkrun\n\n# Or build from source:\ngit clone https://github.com/containers/libkrun.git\ncd libkrun\nmake\nsudo make install\n```\n\nOn macOS, Hypervisor.framework provides hardware virtualization and is available\non all supported Apple Silicon Macs. No `/dev/kvm` equivalent is needed.\n\n### All Platforms -- Verify KVM Access (Linux)\n\n```bash\n# Check that /dev/kvm exists and is accessible\nls -la /dev/kvm\n\n# If you get \"permission denied\", add your user to the kvm group:\nsudo usermod -aG kvm $USER\n# Then log out and log back in.\n\n# Verify KVM modules are loaded\nlsmod | grep kvm\n# If empty, load them:\nsudo modprobe kvm kvm_intel   # Intel CPUs\nsudo modprobe kvm kvm_amd     # AMD CPUs\n```\n\n### Go Toolchain\n\ngo-microvm requires **Go 1.26** or later. The library packages (everything\nexcept `krun` and `go-microvm-runner`) do not require CGO and compile with\n`CGO_ENABLED=0`. The runner binary requires `CGO_ENABLED=1` and `libkrun-devel`.\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log\"\n\n    \"github.com/stacklok/go-microvm\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    vm, err := microvm.Run(ctx, \"alpine:latest\",\n        microvm.WithPorts(microvm.PortForward{Host: 8080, Guest: 80}),\n    )\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer vm.Stop(ctx)\n\n    info, _ := vm.Status(ctx)\n    log.Printf(\"VM %s running (id %s)\", info.Name, info.ID)\n\n    // The VM is now serving on localhost:8080.\n    // Block until interrupted, or integrate with your own lifecycle.\n    select {}\n}\n```\n\n`microvm.Run` executes the full pipeline: preflight checks, OCI image pull,\nlayer extraction, rootfs caching, networking setup, subprocess spawn, and\npost-boot hooks. It returns a `*VM` handle that you use to query status, stop,\nor remove the VM.\n\n## Advanced Usage\n\nFor appliance-style deployments, go-microvm exposes hooks and overrides at every\nstage of the pipeline:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"os\"\n    \"path/filepath\"\n\n    \"github.com/stacklok/go-microvm\"\n    \"github.com/stacklok/go-microvm/hypervisor/libkrun\"\n    \"github.com/stacklok/go-microvm/image\"\n    \"github.com/stacklok/go-microvm/preflight\"\n    \"github.com/stacklok/go-microvm/ssh\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    vm, err := microvm.Run(ctx, \"my-appliance:latest\",\n        // Name the VM (defaults to \"go-microvm\").\n        microvm.WithName(\"my-appliance\"),\n\n        // Configure VM resources.\n        // vCPUs default to 1, memory defaults to 512 MiB.\n        // Stock libkrunfw caps vCPUs at 8.\n        microvm.WithCPUs(4),\n        microvm.WithMemory(2048),\n\n        // Port forwards from host to guest.\n        microvm.WithPorts(\n            microvm.PortForward{Host: 443, Guest: 443},\n            microvm.PortForward{Host: 2222, Guest: 22},\n        ),\n\n        // Replace the OCI ENTRYPOINT/CMD with a custom init script.\n        // The command is written into /.krun_config.json and executed\n        // by libkrun's built-in init process (PID 1).\n        microvm.WithInitOverride(\"/sbin/my-init\"),\n\n        // Inject files into the rootfs before boot.\n        // Hooks run after image extraction but before .krun_config.json\n        // is written, so they can modify anything in the filesystem.\n        microvm.WithRootFSHook(func(rootfs string, cfg *image.OCIConfig) error {\n            return os.WriteFile(\n                filepath.Join(rootfs, \"etc\", \"my-app.conf\"),\n                []byte(\"key=value\\n\"), 0o644,\n            )\n        }),\n\n        // Run setup after the VM process is alive.\n        // Common use: wait for SSH, push configuration, run health checks.\n        microvm.WithPostBoot(func(ctx context.Context, vm *microvm.VM) error {\n            keyPath := filepath.Join(vm.DataDir(), \"id_ecdsa\")\n            sshClient := ssh.NewClient(\"127.0.0.1\", 2222, \"root\", keyPath)\n            return sshClient.WaitForReady(ctx)\n        }),\n\n        // Mount a host directory into the guest via virtio-fs.\n        microvm.WithVirtioFS(microvm.VirtioFSMount{\n            Tag: \"shared\", HostPath: \"/srv/data\",\n        }),\n\n        // Use a custom data directory for state, caches, and logs.\n        // Defaults to ~/.config/go-microvm or $GO_MICROVM_DATA_DIR.\n        microvm.WithDataDir(\"/var/lib/my-appliance\"),\n\n        // Configure the libkrun backend with a specific runner binary\n        // and library search path. These options are backend-specific.\n        microvm.WithBackend(libkrun.NewBackend(\n            libkrun.WithRunnerPath(\"/usr/local/bin/go-microvm-runner\"),\n            libkrun.WithLibDir(\"/opt/libs\"),\n        )),\n\n        // Add custom preflight checks beyond the built-in defaults\n        // (KVM access, disk space, system resources, port availability).\n        microvm.WithPreflightChecks(\n            preflight.PortCheck(443, 2222),\n            preflight.Check{\n                Name:        \"connectivity\",\n                Description: \"Verify registry is reachable\",\n                Run: func(ctx context.Context) error {\n                    // Custom validation logic here.\n                    return nil\n                },\n                Required: true,\n            },\n        ),\n\n        // Provide a custom image cache location.\n        microvm.WithImageCache(image.NewCache(\"/var/cache/go-microvm\")),\n    )\n    if err != nil {\n        panic(err)\n    }\n    defer vm.Stop(ctx)\n\n    // VM lifecycle methods:\n    //   vm.Stop(ctx)      -- SIGTERM, then SIGKILL after 30s\n    //   vm.Status(ctx)    -- returns VMInfo{Name, Active, ID, Ports}\n    //   vm.Remove(ctx)    -- stop + clean up\n    //   vm.Name()         -- VM name\n    //   vm.ID()           -- backend-specific identifier (e.g. PID string for libkrun)\n    //   vm.DataDir()      -- data directory path\n    //   vm.RootFSPath()   -- extracted rootfs path\n    //   vm.Ports()        -- configured port forwards\n}\n```\n\n### Complete Option Reference\n\n| Option | Description | Default |\n|--------|-------------|---------|\n| `WithName(s)` | VM name for identification | `\"go-microvm\"` |\n| `WithCPUs(n)` | Virtual CPUs (max 8 with stock libkrunfw, max 255 hard limit) | `1` |\n| `WithMemory(mib)` | RAM in MiB | `512` |\n| `WithPorts(...)` | TCP port forwards from host to guest | none |\n| `WithInitOverride(cmd...)` | Replace OCI ENTRYPOINT/CMD | OCI config |\n| `WithRootFSPath(path)` | Use pre-built rootfs directory, skip OCI image pull | none |\n| `WithRootFSHook(...)` | Modify rootfs before boot | none |\n| `WithPostBoot(...)` | Run logic after VM process starts | none |\n| `WithNetProvider(p)` | Replace default runner-side networking with a custom provider | runner-side vnet |\n| `WithFirewallRules(...)` | Firewall rules for frame-level packet filtering | none |\n| `WithFirewallDefaultAction(action)` | Default firewall action when no rule matches | `Allow` |\n| `WithPreflightChecker(c)` | Replace entire preflight checker | platform defaults |\n| `WithPreflightChecks(...)` | Add custom pre-boot checks | KVM + resources |\n| `WithVirtioFS(...)` | Host directory mounts via virtio-fs | none |\n| `WithDataDir(p)` | State, cache, and log directory | `~/.config/go-microvm` |\n| `WithCleanDataDir()` | Remove existing data dir contents before boot | disabled |\n| `WithEgressPolicy(p)` | Restrict outbound traffic to allowed DNS hostnames | none |\n| `WithImageCache(c)` | Custom image cache instance | `$dataDir/cache/` |\n| `WithImageFetcher(f)` | Custom image fetcher for OCI retrieval | local-then-remote |\n| `WithLogLevel(n)` | libkrun log verbosity (0=off, 1=error, ..., 5=trace) | `0` |\n| `WithBackend(b)` | Hypervisor backend (e.g. `libkrun.NewBackend(...)`) | libkrun |\n\n## Package Overview\n\n| Package | CGO? | Description |\n|---------|------|-------------|\n| `microvm` (root) | No | Top-level API: `Run()`, `VM` type, functional options, hook types |\n| `hypervisor` | No | `Backend` and `VMHandle` interfaces, `VMConfig`, `InitConfig` types |\n| `hypervisor/libkrun` | No | libkrun backend: spawns go-microvm-runner subprocess, `WithRunnerPath`/`WithLibDir`/`WithSpawner` |\n| `image` | No | OCI image pull via `ImageFetcher`, layer flattening, rootfs extraction |\n| `image/disk` | No | Disk image download with decompression (gzip/bzip2/xz) |\n| `krun` | **Yes** | CGO bindings to libkrun C API (context, VM config, `StartEnter`) |\n| `hooks` | No | RootFS hook factories: `InjectAuthorizedKeys`, `InjectFile`, `InjectBinary`, `InjectEnvFile` |\n| `extract` | No | Binary bundle caching with SHA-256 versioning and cross-process locking |\n| `guest/*` | No | Guest-side boot orchestration, hardening, SSH server (Linux-only, `//go:build linux`) |\n| `net` | No | `Provider` interface and `Config`/`PortForward` types |\n| `net/firewall` | No | Frame-level packet filtering with stateful connection tracking |\n| `net/egress` | No | DNS-based egress policy: intercepts DNS, creates dynamic firewall rules |\n| `net/hosted` | No | Hosted `net.Provider` running VirtualNetwork in caller's process with HTTP services |\n| `net/topology` | No | Shared network topology constants (subnet, gateway, IPs, MTU) |\n| `preflight` | No | `Checker` interface, `Check` struct, built-in KVM/HVF and port checks |\n| `runner` | No | `Spawner` / `ProcessHandle` interfaces for managing the go-microvm-runner subprocess |\n| `runner/cmd/go-microvm-runner` | **Yes** | The runner binary (calls `krun.StartEnter`, never returns) |\n| `ssh` | No | ECDSA key generation and SSH client for guest communication |\n| `state` | No | flock-based state persistence with atomic JSON writes |\n| `rootfs` | No | Rootfs cloning with reflink (copy-on-write) support |\n| `internal/pathutil` | No | Path traversal validation for safe file operations |\n| `internal/xattr` | No | Extended attribute helpers for `override_stat` ownership mapping |\n\nOnly `krun` and `runner/cmd/go-microvm-runner` require CGO and `libkrun-devel`.\nAll other packages are pure Go and can be imported and tested with\n`CGO_ENABLED=0`.\n\n## Build\n\ngo-microvm uses [Task](https://taskfile.dev/) as its build tool. Run\n`task --list` for all available commands.\n\n| Command | Description |\n|---------|-------------|\n| `task build-dev` | Build runner for development on Linux (requires system `libkrun-devel`, `CGO_ENABLED=1`) |\n| `task build-dev-darwin` | Build runner on macOS (requires Homebrew libkrun, signs with entitlements) |\n| `task build-runner` | Build runner + libs using builder container (no system libkrun needed) |\n| `task fetch-runtime` | Download pre-built runtime from GitHub Release |\n| `task fetch-firmware` | Download pre-built firmware from GitHub Release |\n| `task test` | Run all tests with race detector (`go test -v -race ./...`) |\n| `task test-coverage` | Run tests with coverage, generates `coverage.html` |\n| `task lint` | Run `golangci-lint` |\n| `task lint-fix` | Run linter and auto-fix issues |\n| `task fmt` | Format code (`go fmt` + `goimports`) |\n| `task tidy` | Run `go mod tidy` |\n| `task verify` | Run fmt, lint, and test in sequence (CI pipeline) |\n| `task version` | Print version, commit, and build date from git |\n| `task clean` | Remove `bin/`, `dist/`, and coverage files |\n\n### Testing Without CGO\n\nThe library packages do not require CGO and can be validated separately:\n\n```bash\n# Test pure-Go packages only (no libkrun needed)\nCGO_ENABLED=0 go test $(go list ./... | grep -v krun | grep -v go-microvm-runner)\n\n# Vet pure-Go packages\nCGO_ENABLED=0 go vet $(go list ./... | grep -v krun | grep -v go-microvm-runner)\n```\n\n## Architecture\n\ngo-microvm uses a **two-process model**:\n\n```\n+---------------------------+         +---------------------------+\n|     Your application      |         |     go-microvm-runner       |\n|  (links go-microvm lib)   |  spawn  |  (CGO binary, links      |\n|                           |--------\u003e|   libkrun)                |\n|  microvm.Run()           |  JSON   |                           |\n|                           |  config |  1. Parse Config (argv[1])|\n|  Pure Go, no CGO          |         |  2. krun.CreateContext()  |\n|                           |         |  3. SetVMConfig, SetRoot  |\n|  Monitors runner PID      |         |  4. AddNetUnixStream      |\n|  In-process networking    |         |  5. krun_start_enter()    |\n|  Runs hooks               |         |     (never returns)       |\n+---------------------------+         +---------------------------+\n         |                                      |\n         |  SIGTERM / SIGKILL                    |  VM runs inside\n         +-------------------------------------\u003e|  this process\n```\n\n1. **Your application** links the go-microvm library (pure Go, no CGO). It pulls\n   the OCI image, configures networking, runs preflight checks, and spawns a\n   subprocess.\n\n2. **go-microvm-runner** is a small CGO binary that receives the VM configuration\n   as JSON in `argv[1]`. It calls libkrun's C API to configure the VM context,\n   then calls `krun_start_enter()` -- which **never returns** on success. The\n   calling process becomes the VM supervisor until the guest shuts down.\n\nThis separation exists because `krun_start_enter()` takes over the process. If\nit were called from your application directly, you would lose control of the\nGo runtime.\n\n### OCI-to-VM Pipeline\n\n```\n  Pull image (crane)\n       |\n  Flatten layers (mutate.Extract)\n       |\n  Extract to rootfs directory (with security checks)\n       |\n  Run rootfs hooks (optional, caller-provided)\n       |\n  Write /.krun_config.json\n       |\n  Start networking (in-process vnet)\n       |\n  Spawn go-microvm-runner subprocess\n       |\n  Runner calls krun_start_enter()\n       |\n  Run post-boot hooks (optional, caller-provided)\n```\n\n### Networking\n\n```\n+-------------------+     Unix socket      +-------------------+\n|   Host machine    | (SOCK_STREAM, 4-byte |   Guest VM        |\n|                   |  BE length-prefix)   |                   |\n|  VirtualNetwork ------\u003e  virtio-net  -------\u003e eth0            |\n|  (in-process)     |                      |  192.168.127.2    |\n|  192.168.127.1    |                      |                   |\n|                   |                      |  DHCP from        |\n|  Port forwards:   |                      |  VirtualNetwork   |\n|  localhost:8080 --|---------------------\u003e|  gateway           |\n|       -\u003e guest:80 |                      |                   |\n+-------------------+                      +-------------------+\n```\n\nBy default, the runner creates an in-process VirtualNetwork (gvisor-tap-vsock)\nproviding a virtual network (192.168.127.0/24), DHCP, DNS, and TCP port\nforwarding. For advanced use cases, `WithNetProvider()` moves the network stack\nto the caller's process -- the `net/hosted` package provides a ready-made\nprovider that also supports HTTP services on the gateway IP. An optional\nframe-level firewall with stateful connection tracking can be enabled via\n`WithFirewallRules()`. See [docs/NETWORKING.md](docs/NETWORKING.md) for a\ndeep dive.\n\n### Extension Points\n\n- **`hypervisor.Backend`** -- pluggable hypervisor backend (default: libkrun)\n- **`net.Provider`** -- replace default in-process networking\n- **`preflight.Checker`** -- add custom pre-boot validations\n- **`RootFSHook`** -- modify the rootfs before VM boot\n- **`PostBootHook`** -- run logic after the VM process is confirmed alive\n- **`WithInitOverride`** -- replace the OCI ENTRYPOINT/CMD entirely\n- **`WithEgressPolicy`** -- restrict outbound traffic to allowed DNS hostnames\n\nFor a detailed architecture walkthrough, see\n[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).\n\n## Security Model\n\n### Guest-VMM Trust Boundary\n\nlibkrun runs the guest and VMM in the same process and security context. The\nmicroVM provides hardware-level isolation via KVM (Linux) or\nHypervisor.framework (macOS), but the VMM itself is not sandboxed from the\nhost process. This is the same model used by krunvm and crun+libkrun. Treat\nthe VM as a stronger isolation boundary than containers but weaker than a fully\nsandboxed hypervisor like Firecracker.\n\n### Tar Extraction Security\n\nWhen extracting OCI image layers, go-microvm applies multiple defenses against\nmalicious tar archives:\n\n- **Path traversal prevention**: `sanitizeTarPath` rejects absolute paths and\n  paths containing `..` components that would resolve outside the rootfs.\n- **Symlink traversal prevention**: `mkdirAllNoSymlink` creates directories one\n  component at a time and refuses to follow symlinks when creating parent\n  directories. `validateNoSymlinkLeaf` prevents writing through symlinks.\n- **Hardlink boundary enforcement**: hard links are validated to ensure both\n  source and target remain within the rootfs directory.\n- **Decompression bomb limit**: extraction is capped at 30 GiB via an\n  `io.LimitedReader` to prevent resource exhaustion.\n\n### Process Identity Verification\n\nWhen stopping a VM, the `runner.Process.IsAlive()` method sends signal 0 to the\nPID to verify the process exists before sending SIGTERM. This prevents sending\nsignals to unrelated processes if the PID has been reused. The stop sequence\nuses SIGTERM first, then falls back to SIGKILL after a 30-second timeout.\n\n## Troubleshooting\n\n### VM Fails to Start\n\n```bash\n# 1. Check KVM availability (Linux)\nls -la /dev/kvm\n# If missing: sudo modprobe kvm kvm_intel  (or kvm_amd)\n# If permission denied: sudo usermod -aG kvm $USER\n\n# 2. Check console output for guest-side errors\ncat ~/.config/go-microvm/console.log\n\n# 3. Check runner stderr for host-side errors\ncat ~/.config/go-microvm/vm.log\n\n# 4. Verify the runner binary is available\nwhich go-microvm-runner\n# Or check next to your binary\n```\n\n### Image Pull Fails\n\n```bash\n# Check registry connectivity\ncrane manifest alpine:latest\n\n# Check Docker/Podman auth for private registries\ncat ~/.docker/config.json\n\n# Try pulling manually to see detailed errors\ncrane pull alpine:latest /tmp/test.tar\n```\n\n### Port Conflicts\n\n```bash\n# Check which process is using a port\nss -tlnp | grep ':8080'\n\n# Or use the go-microvm preflight check directly:\n# microvm.WithPreflightChecks(preflight.PortCheck(8080))\n```\n\n### macOS-Specific Issues\n\n- The runner binary must be code-signed with three entitlements (hypervisor,\n  disable-library-validation, allow-dyld-environment-variables). The\n  `task build-dev-darwin` command handles signing automatically.\n- If using bundled libraries, set `DYLD_LIBRARY_PATH` (not `LD_LIBRARY_PATH`).\n  The `libkrun.WithLibDir` backend option handles this for the runner subprocess.\n- libkrun internally calls `dlopen` with versioned filenames (e.g.,\n  `libkrunfw.5.dylib`). If you see \"library not loaded\" errors, ensure the\n  versioned dylib names are present, not just unversioned symlinks.\n- See [docs/MACOS.md](docs/MACOS.md) for details on filesystem permissions\n  (virtiofs xattr), guest networking differences, and troubleshooting.\n\n## Contributing\n\nContributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for\ndevelopment setup, build commands, code conventions, and the workflow for\nsubmitting changes.\n\n## License\n\nApache 2.0 -- see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstacklok%2Fgo-microvm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstacklok%2Fgo-microvm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstacklok%2Fgo-microvm/lists"}