# gaol

Cross-platform container and VM management tool supporting Docker, systemd-nspawn, and libvirt/QEMU.

## Development

```bash
# Install dependencies
uv sync --dev

# Run tests
uv run pytest

# Lint and format
uv run ruff check src/ tests/
uv run ruff format src/ tests/

# Type check
uv run mypy src/
```

## Architecture

### Key directories

- `src/gaol/models/container.py` — Core Pydantic models: `ContainerConfig`, `NetworkConfig`, `NetworkMode`
- `src/gaol/gpu/models.py` — `GPUConfig`, `GPUVendor`, `DeviceMapping`
- `src/gaol/store.py` — `StoredContainer` model and `ContainerStore` (YAML persistence)
- `src/gaol/runtimes/nspawn.py` — systemd-nspawn runtime (`.gaol.json` metadata)
- `src/gaol/runtimes/base.py` — Abstract `ContainerRuntime` base class
- `src/gaol/cli.py` — Typer CLI (large file, search by command name)
- `profiles/` — Built-in container profile YAMLs
- `specs/` — Declarative container spec files

### Data locations

- **Store**: `~/.config/gaol/containers/{name}.yaml` — YAML, written via `StoredContainer.model_dump(mode="json")` then `yaml.dump`
- **Marker**: `/var/lib/machines/{name}/.gaol.json` — JSON metadata inside each nspawn container
- **nspawn configs**: `/etc/systemd/nspawn/{name}.nspawn` — systemd-nspawn unit configs

### Network modes

| gaol mode | nspawn config |
|---|---|
| `shared` | `VirtualEthernet=yes` (NAT, default) |
| `bridged` | `VirtualEthernet=yes` + `Bridge=br0` |
| `host` | `Private=no` or `VirtualEthernet=no` |
| `none` | `Private=yes` (no veth) |

## Container adoption

To adopt pre-existing nspawn containers that were created outside gaol:

```bash
# Preview what will be adopted
python scripts/adopt-containers.py --dry-run

# Run adoption (requires root for /var/lib/machines/ access)
sudo python scripts/adopt-containers.py

# Verify
gaol list --runtime nspawn
cat ~/.config/gaol/containers/research.yaml
sudo cat /var/lib/machines/research/.gaol.json
```

The script reads each `.nspawn` config, extracts distro from `os-release`, gets creation timestamps from `machinectl list-images`, and writes both store YAMLs and `.gaol.json` markers. It skips containers already in the store and those in the `--skip` list (default: `alertmanager`, `dx-template`).
