DevOps Tools
API reference for the devops package -- portable development environments with LinuxKit, shell access, testing, serving, Claude sandboxing, and image management. See Home for installation.
DevOps Manager
The central DevOps struct orchestrates the portable dev environment backed by LinuxKit QEMU images.
import (
"forge.lthn.ai/core/go-devops/devops"
"forge.lthn.ai/core/go/pkg/io"
)
d, err := devops.New(io.Local)
Lifecycle
// Boot the dev environment
err := d.Boot(ctx, devops.BootOptions{
Memory: 4096, // MB
CPUs: 2,
Name: "core-dev",
Fresh: false, // set true to destroy and recreate
})
// Check status
running, err := d.IsRunning(ctx)
// Get detailed status
status, err := d.Status(ctx)
// status.Installed, status.Running, status.ImageVersion,
// status.ContainerID, status.Memory, status.CPUs,
// status.SSHPort (default 2222), status.Uptime
// Stop
err := d.Stop(ctx)
// Install/update images
err := d.Install(ctx, progressCallback)
current, latest, hasUpdate, err := d.CheckUpdate(ctx)
BootOptions
type BootOptions struct {
Memory int // MB, default 4096
CPUs int // default 2
Name string // container name, default "core-dev"
Fresh bool // destroy existing and start fresh
}
opts := devops.DefaultBootOptions() // sensible defaults
DevStatus
type DevStatus struct {
Installed bool
Running bool
ImageVersion string
ContainerID string
Memory int
CPUs int
SSHPort int // default 2222
Uptime time.Duration
}
Shell Execution
Connect to the dev environment via SSH or serial console.
err := d.Shell(ctx, devops.ShellOptions{
Console: false, // true for serial console, false for SSH
Command: []string{"ls"}, // empty for interactive shell
})
ShellOptions
type ShellOptions struct {
Console bool // Use serial console instead of SSH
Command []string // Command to run (empty = interactive shell)
}
SSH connects to root@localhost:2222 with agent forwarding (-A) and strict host key checking using ~/.core/known_hosts.
Docker Image Management
The ImageManager handles downloading, installing, and updating LinuxKit dev images from multiple sources.
type ImageManager struct { /* ... */ }
// Platform-specific image name
name := devops.ImageName() // e.g. "core-devops-darwin-arm64.qcow2"
// Image paths
dir, err := devops.ImagesDir() // ~/.core/images (or $CORE_IMAGES_DIR)
path, err := devops.ImagePath() // ~/.core/images/core-devops-darwin-arm64.qcow2
Image Sources (devops/sources)
The ImageSource interface defines how images are downloaded:
type ImageSource interface {
Name() string
Available() bool
LatestVersion(ctx context.Context) (string, error)
Download(ctx context.Context, m io.Medium, dest string, progress func(downloaded, total int64)) error
}
Two built-in sources:
| Source | Type | Availability Check |
|---|---|---|
GitHubSource |
GitHub Releases via gh CLI |
gh auth status succeeds |
CDNSource |
HTTP download from CDN/S3 URL | CDN URL configured |
import "forge.lthn.ai/core/go-devops/devops/sources"
cfg := sources.SourceConfig{
GitHubRepo: "host-uk/core-images",
CDNURL: "https://cdn.example.com/images",
ImageName: "core-devops-darwin-arm64.qcow2",
}
gh := sources.NewGitHubSource(cfg)
cdn := sources.NewCDNSource(cfg)
Manifest
Tracks installed images in ~/.core/images/manifest.json:
type ImageInfo struct {
Version string `json:"version"`
SHA256 string `json:"sha256,omitempty"`
Downloaded time.Time `json:"downloaded"`
Source string `json:"source"` // "github" or "cdn"
}
Testing
Run tests inside the dev environment with auto-detection of test frameworks.
err := d.Test(ctx, projectDir, devops.TestOptions{
Name: "", // run named command from .core/test.yaml
Command: []string{}, // override command (highest priority)
})
Auto-Detection Order
DetectTestCommand checks in this order:
.core/test.yaml-- explicitcommandfieldcomposer.jsonwithtestscript --composer testpackage.jsonwithtestscript --npm testgo.mod--go test ./...pytest.iniorpyproject.toml--pytestTaskfile.yaml--task test
Test Config (.core/test.yaml)
version: 1
command: go test ./...
commands:
- name: unit
run: go test -short ./...
- name: integration
run: go test -run Integration ./...
env:
GOFLAGS: -v
Serve Mode
Mount a project and start a development server inside the dev environment.
err := d.Serve(ctx, projectDir, devops.ServeOptions{
Port: 8000, // default 8000
Path: "", // subdirectory (default: project root)
})
Auto-Detection Order
DetectServeCommand checks in this order:
artisan(Laravel) --php artisan octane:start --host=0.0.0.0 --port=8000package.jsonwithdevscript --npm run dev -- --host 0.0.0.0package.jsonwithstartscript --npm startcomposer.json(PHP) --frankenphp php-server -l :8000go.mod+main.go--go run .manage.py(Django) --python manage.py runserver 0.0.0.0:8000- Fallback --
python3 -m http.server 8000
Project is mounted into the VM at /app via reverse SSHFS.
Claude Sandbox
Run Claude Code in an isolated dev environment with selective auth forwarding.
err := d.Claude(ctx, projectDir, devops.ClaudeOptions{
NoAuth: false, // don't forward any auth
Auth: []string{"gh", "anthropic", "ssh", "git"}, // selective (default: all)
Model: "opus", // model override
})
ClaudeOptions
type ClaudeOptions struct {
NoAuth bool // Don't forward any auth
Auth []string // Selective auth: "gh", "anthropic", "ssh", "git"
Model string // Model to use: opus, sonnet
}
Auth Forwarding
| Auth Type | What Gets Forwarded |
|---|---|
ssh |
SSH agent forwarding (-A) -- always on unless NoAuth |
anthropic |
ANTHROPIC_API_KEY env var |
git |
GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL |
gh |
GitHub CLI config copied via CopyGHAuth() using scp |
Auto-boots the dev environment if not running. Project is mounted at /app.
SSH Utilities
Host Key Management
After booting, ensureHostKey automatically scans the VM's SSH host key and adds it to ~/.core/known_hosts (deduplicating entries). This enables StrictHostKeyChecking=yes for all subsequent SSH connections.
Set CORE_SKIP_SSH_SCAN=true to skip in tests.
Configuration
Global devops config is loaded from ~/.core/config.yaml:
type Config struct {
Version int
Images ImagesConfig
}
type ImagesConfig struct {
Source string // "auto", "github", "cdn"
GitHub GitHubConfig // .Repo (owner/repo)
Registry RegistryConfig // .Image (e.g. ghcr.io/host-uk/core-devops)
CDN CDNConfig // .URL (base download URL)
}
cfg, err := devops.LoadConfig(medium) // returns DefaultConfig() if file missing
path, err := devops.ConfigPath() // ~/.core/config.yaml
Defaults
version: 1
images:
source: auto
github:
repo: host-uk/core-images
registry:
image: ghcr.io/host-uk/core-devops
See Infrastructure for the infra package API.