description: Internal design of go-container -- types, data flow, hypervisor abstraction, state management, and template engine.
---
# Architecture
go-container is organised into three packages with clear responsibilities. The root `container` package owns the core abstractions. The `devenv` package composes those abstractions into a higher-level development environment. The `sources` package provides pluggable image download backends.
`DetectHypervisor()` auto-selects the best available hypervisor. On macOS it prefers Hyperkit, falling back to QEMU. On Linux it uses QEMU with KVM if `/dev/kvm` is present.
## Data flow: running a container
When `LinuxKitManager.Run()` is called, the following sequence occurs:
1.**Validate** -- Checks the image file exists via `io.Medium` and detects its format from the file extension (`.iso`, `.qcow2`, `.vmdk`, `.raw`, `.img`).
2.**Generate ID** -- Creates an 8-character hex identifier using `crypto/rand`.
3.**Apply defaults** -- Memory defaults to 1024 MB, CPUs to 1, SSH port to 2222.
4.**Build command** -- Delegates to the `Hypervisor.BuildCommand()` method, which constructs the full command line including:
- Memory and CPU allocation
- Disk image attachment (format-specific flags)
- Network with port forwarding (SSH + user-defined ports)
- 9p volume shares (QEMU only)
- Hardware acceleration flags
5.**Start process** -- In **detached** mode, stdout/stderr are redirected to a log file under `~/.core/logs/<id>.log`, and a background goroutine monitors the process for exit. In **foreground** mode, output is tee'd to both the log file and the terminal.
6.**Persist state** -- The container record is written to `~/.core/containers.json` via the `State` store.
7.**Monitor** -- For detached containers, `waitForExit()` runs in a goroutine, updating the container status to `stopped` or `error` when the process terminates.
## State management
The `State` struct provides a thread-safe, JSON-persisted container registry:
`LinuxKitManager.Stop()` performs a graceful shutdown:
1. Sends `SIGTERM` to the hypervisor process.
2. Waits up to 10 seconds for the process to exit.
3. If the process does not exit in time, sends `SIGKILL`.
4. Respects context cancellation throughout -- if the context is cancelled, the process is killed immediately.
## Template engine
LinuxKit templates are YAML files that define a complete VM image configuration (kernel, init, services, files). The template engine adds variable substitution on top.
### Variable syntax
Two forms are supported:
-`${VAR}` -- Required variable. Produces an error if not provided.
-`${VAR:-default}` -- Optional variable with a default value.
### Resolution order
`GetTemplate(name)` searches for templates in this order:
1.**Embedded templates** -- Compiled into the binary via `//go:embed templates/*.yml`. These always take precedence.
2.**Workspace templates** -- `.core/linuxkit/` relative to the current working directory.
3.**User templates** -- `~/.core/linuxkit/` in the user's home directory.
User-defined templates that share a name with a built-in template are ignored (built-ins win).
### Variable extraction
`ExtractVariables(content)` parses a template and returns two collections:
- A sorted slice of required variable names (those using `${VAR}` syntax with no default).
- A map of optional variable names to their default values (those using `${VAR:-default}` syntax).
This powers the `core vm templates vars <name>` command.
## Development environment (devenv)
The `DevOps` struct in the `devenv` package composes the lower-level primitives into a complete development workflow.
### Boot sequence
1. Checks the dev image is installed (platform-specific qcow2 file: `core-devops-{os}-{arch}.qcow2`).
2. Launches the image via `LinuxKitManager.Run()` in detached mode with 4096 MB RAM, 2 CPUs, and SSH on port 2222.
3. Polls for up to 60 seconds until the VM's SSH host key can be scanned, then writes it to `~/.core/known_hosts`.
### Shell access
Two modes are available:
- **SSH** (default) -- Connects via `ssh -A -p 2222 root@localhost` with agent forwarding and strict host key checking against `~/.core/known_hosts`.
- **Serial console** -- Attaches to the QEMU serial console socket via `socat`.
### Project mounting
Projects are mounted into the VM at `/app` using a reverse SSHFS tunnel. The VM opens an SSH reverse tunnel back to the host (port 10000) and mounts the host directory via SSHFS.
### Auto-detection
Several operations auto-detect the project type by inspecting files on disk:
| File detected | Serve command | Test command |
|---------------|---------------|--------------|
| `artisan` | `php artisan octane:start` | -- |
| `package.json` with `dev` script | `npm run dev -- --host 0.0.0.0` | `npm test` |
Download(ctx context.Context, m io.Medium, dest string, progress func(downloaded, total int64)) error
}
```
| Source | Backend | Availability check |
|--------|---------|--------------------|
| `CDNSource` | HTTP GET from a configured CDN URL | CDN URL is configured |
| `GitHubSource` | `gh release download` via the GitHub CLI | `gh` is installed and authenticated |
The `ImageManager` in `devenv` maintains a `manifest.json` in `~/.core/images/` that tracks installed image versions, SHA256 checksums, download timestamps, and source names. When the source is set to `"auto"` (the default), it tries GitHub first, then CDN.