Contributing

stereOS welcomes contributions.

Development setup

Prerequisites

  • Nix with flakes enabled — Add experimental-features = nix-command flakes to ~/.config/nix/nix.conf.
  • direnv — See Local Build for installation.

Getting started

git clone https://github.com/<your-username>/stereos
cd stereos
direnv allow

make help
make build
make test

The Nix dev shell provides Go, linters, and all other tools.

Testing

make test

Follow existing test patterns. Use standard Go tests:

func TestConfigParsing(t *testing.T) {
    cfg, err := config.Load("testdata/valid.toml")
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if cfg.VM.Name != "test-vm" {
        t.Errorf("expected name 'test-vm', got '%s'", cfg.VM.Name)
    }
}

Linting and formatting

gofmt -w .
go vet ./...

Code that doesn’t pass these will be flagged during review.

Code style

  • Error handling — Return errors, don’t panic. Wrap with context: fmt.Errorf("loading config: %w", err).
  • Naming — Clear, descriptive. Avoid abbreviations except common ones (ctx, err, cfg).
  • Package structure — Single responsibility per package. No circular dependencies.
  • Comments — Doc comments on exported types and functions. Focus on what, not how.
  • Constructor pattern — Use New* functions. Return pointers. Initialize maps and slices.
func NewVMManager(imagePath string) *VMManager {
    return &VMManager{
        imagePath: imagePath,
        vms:       make(map[string]*VM),
    }
}

Pull request process

Branch naming

git checkout -b feat/my-feature
# or: fix/issue-description, docs/page-name, refactor/component-name

Before submitting

  1. make build — compiles without errors
  2. make test — all tests pass
  3. gofmt -w . and go vet ./...
  4. One logical change per commit

PR description

Cover what the change does, why it’s needed, and testing beyond the automated suite.

Review process

All PRs need at least one review. Reviewers check correctness, test coverage, code style, docs updates, and regressions.

Project structure

stereos/
├── flake.nix                  # Entry point — delegates to flake/ modules (flake-parts)
├── flake/                     # flake-parts modules (split flake.nix logic)
│   ├── devshell.nix           # Developer shell for direnv
│   ├── images.nix             # Image build targets (raw, qcow2, kernel-artifacts)
│   └── checks.nix             # CI verification builds
├── modules/                   # NixOS modules — the core of the OS
│   ├── default.nix            # Aggregator (imports all sub-modules)
│   ├── base.nix               # Core OS: filesystem, SSH, nix settings, hardening
│   ├── boot.nix               # Boot config + boot-time optimizations
│   ├── services/              # Service overrides (stereosd, agentd)
│   └── users/                 # User definitions (agent, admin)
├── profiles/                  # Composable configuration presets
│   ├── base.nix               # Shared foundation (imports image formats)
│   └── dev.nix                # Dev-only: SSH key injection, debug tools
├── mixtapes/                  # Mixtapes — spins with specific AI agents
│   ├── claude-code/
│   ├── opencode/
│   ├── gemini-cli/
│   └── full/
├── formats/                   # Image format definitions (raw, qcow2, kernel)
├── lib/                       # Shared Nix helpers (mkMixtape, SSH key logic)
├── cmd/                       # CLI entry points (mb)
├── internal/                  # Private Go packages
├── Makefile                   # Build and test targets
├── .envrc                     # direnv integration
└── go.mod                     # Go module definition