Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@

An atomic Linux host keeps its base system read-only and replaceable. You still need somewhere to `apt install` a project's dependencies, try a toolchain packaged for another distro, or run a service. nsl gives you persistent Linux machines for that work, as WSL does on Windows. Each machine is a whole distro with its own packages and services. It opens in the directory you were in, works on your files, and stops when you stop using it.

```sh
nsl create debian --distro debian:13 # verify and cache the signed images; the first machine is the default
nsl # a login shell in the machine, in this directory
nsl run make test # one command, with its exit status
```
[Create your first machine](https://frostyard.github.io/nsl/getting-started/first-machine/) walks through creation, a login shell, running a command and stopping the VM.

**Documentation: [frostyard.github.io/nsl](https://frostyard.github.io/nsl/)**

Expand Down Expand Up @@ -38,7 +34,7 @@ It is **pre-release**. v0.4.0 was the first release of the current design; v0.3.

## Start

1. [Install nsl](https://frostyard.github.io/nsl/getting-started/install/) with Homebrew (`brew tap frostyard/tap`, then `brew install --cask frostyard/tap/nsl`) or from the [latest release](https://github.com/frostyard/nsl/releases/latest).
1. [Install nsl](https://frostyard.github.io/nsl/getting-started/install/) with Homebrew or a release tarball.
2. [Create your first machine](https://frostyard.github.io/nsl/getting-started/first-machine/).
3. Look up [commands](https://frostyard.github.io/nsl/reference/cli/) and [configuration](https://frostyard.github.io/nsl/reference/configuration/) as you need them.

Expand Down
2 changes: 1 addition & 1 deletion machines.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (a *app) idleTimeout() (*int, error) {
// create prepares a machine from a verified catalogue selection or a local
// machine image.
func (a *app) create(args []string) error {
usage := errors.New("usage: create NAME --distro DISTRO:RELEASE [--offline] [--default] [--user NAME] | create NAME --image FILE --digest sha256:HEX [--default] [--user NAME]")
usage := errors.New("usage: create NAME --distro DISTRO:RELEASE [--offline] [--isolated] [--default] [--user NAME] | create NAME --image FILE --digest sha256:HEX [--isolated] [--default] [--user NAME]")
if len(args) == 0 || strings.HasPrefix(args[0], "-") {
return usage
}
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,15 @@ func usage(w io.Writer) {

[-m NAME] login shell in NAME or the default machine
run [-m NAME] [--root] [--cd PATH] COMMAND [ARGS...]
create NAME --distro DISTRO:RELEASE [--offline] [--default] [--user NAME]
create NAME --image FILE --digest sha256:HEX [--default] [--user NAME]
create NAME --distro DISTRO:RELEASE [--offline] [--isolated] [--default] [--user NAME]
create NAME --image FILE --digest sha256:HEX [--isolated] [--default] [--user NAME]
list [--json]
default NAME
start NAME
stop NAME
remove NAME [--yes]
export NAME FILE
import NAME FILE
import NAME FILE [--isolated]
ports [NAME]
ssh-config NAME
logs [NAME]
Expand Down
8 changes: 7 additions & 1 deletion site/content/concepts/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ flowchart LR
Waypipe --> Desktop[Host Wayland desktop]
```

## Terms

- **Shared VM:** the single virtual machine that runs all ordinary machines. Isolated machines each have their own VM.
- **Machine image:** a distro root filesystem used to create a machine with its own packages, services and guest home. Updating that image does not change existing machines.
- **Default machine:** the machine that bare `nsl` and `nsl run` enter when you omit `-m NAME`. The first machine you create or import becomes the default; `nsl default NAME` changes it.

## The VM

The shared VM boots the signed nsl VM image with systemd-vmspawn and QEMU/KVM, under a user unit named `nsl-UID-vm-ID.service`. It has two disks:
Expand All @@ -43,7 +49,7 @@ Once the images are cached, creation needs no network. The VM imports the image

The CLI reaches the VM over SSH on vsock, where the VM's sshd accepts only nsl's key and only runs the nsl agent. Before doing anything, the CLI checks the VM's identity: its ID, your UID and GID, its role, and the image's protocol and architecture. It checks this even when the VM was already running.

The agent runs each command as a transient systemd unit in the machine, with a PAM login session. It passes arguments literally, keeps streams separate or uses a terminal, and returns the exit status, with 128+N for a command killed by signal N.
The agent runs each command as a transient systemd unit in the machine, with a PAM login session for the machine account. Commands run with `--root` have no PAM session. It passes arguments literally, keeps streams separate or uses a terminal, and returns the exit status, with 128+N for a command killed by signal N.

## Host integration

Expand Down
2 changes: 1 addition & 1 deletion site/content/concepts/trust.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The VM separates the machine's kernel, packages, services and root from your hos
- Become root on the host. `--root` and `sudo` mean root in the machine. Files it creates through `/mnt/host` belong to you, and it cannot exceed your permissions.
- See host `/usr`, `/etc`, `/tmp`, the rest of `/run`, devices or pseudo-filesystems.
- Reach host sockets: D-Bus, the SSH agent, the GPU and the display are not shared, and Unix sockets do not connect across `/mnt/host`.
- Run arbitrary commands on the host. The only host action is opening a link or a shared file.
- Run arbitrary commands through the host-action broker, which only opens links and shared files. A machine can still write shell startup files or other code in your shared home that later runs as you on the host.
- Change host sudoers, device permissions or packages. nsl itself never changes them either.

## Machines share a VM
Expand Down
6 changes: 4 additions & 2 deletions site/content/contributing/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,18 @@ source build/poc/env.sh

# The VM image.
scripts/build-image.sh --role vm
image=build/image/share/nsl-vm-trixie-x86-64-r9.raw
image=build/image/share/nsl-vm-trixie-x86-64-r10.raw
build/nsl update --image "$image" --digest "sha256:$(sha256sum "$image" | cut -d' ' -f1)"
build/nsl recover

# A machine image, and a machine from it.
scripts/build-image.sh --role machine --distribution debian
machine=build/image/share/nsl-machine-debian-trixie-x86-64-r4.tar.zst
machine=build/image/share/nsl-machine-debian-trixie-x86-64-r5.tar.zst
build/nsl create debian --image "$machine" --digest "sha256:$(sha256sum "$machine" | cut -d' ' -f1)"
```

Each build prints a `Built PATH` line. Use that path for `image` or `machine` if the profile revision differs from these examples.

`--distribution` also takes `ubuntu`, `fedora`, `centos`, `arch` and `opensuse`; `--distribution opensuse --release 16.0` builds Leap. The [image build notes ↗](https://github.com/frostyard/nsl/blob/main/image/README.md) describe the layers and the builder.

## Accept images in disposable VMs
Expand Down
36 changes: 26 additions & 10 deletions site/content/getting-started/first-machine.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,53 @@ description: Create a machine from a signed image, enter it, run commands and st

# Your first machine

Let's create a Debian 13 machine, run a few commands and stop it. The first creation downloads the VM image and the Debian machine image. Once both are cached, creating another machine from them needs no network.
Let's create a Debian 13 machine, run a few commands and stop it. The first creation downloads the VM image and the Debian [machine image](../concepts/how-it-works.md#terms). Allow several minutes for the first download and setup; the time depends on your connection and host. Download progress is printed in bytes, followed by verification messages. Once both are cached, creating another machine from them needs no network.

<div class="steps" markdown>

1. **Create the machine.**

```sh
nsl create [name] [flags]
nsl create debian --distro debian:13
```

nsl downloads the signed image catalogue, checks that both images came from the Frostyard publishing workflow, and caches them. Then it starts the shared VM and creates your Debian machine. Since this is your first machine, it becomes the default.
nsl downloads the signed image catalogue, checks that both images came from the Frostyard publishing workflow, and caches them. Then it starts the [shared VM](../concepts/how-it-works.md#terms) and creates your Debian machine. Since this is your first machine, it becomes the [default](../concepts/how-it-works.md#terms). The name and an image selection are required; the [command reference](../reference/cli.md#machines) also covers local images.

Success ends with a line like this (`BUILD_ID` varies with the published image):

```text
Created debian from BUILD_ID
```

If startup stalls or fails, check [`nsl logs`](../guides/storage.md#logs). For a VM that cannot become ready, [`nsl recover`](../guides/storage.md#recover-a-vm) restarts it from a fresh root. See [troubleshooting](../reference/limits.md#troubleshooting).

2. **Open a shell.**

```sh
mkdir -p ~/src/project
cd ~/src/project
nsl # a login shell at /mnt/host/home/src/project
nsl
```

You're now in a login shell in the default machine, working in the same files. Their path starts with `/mnt/host`, followed by the canonical host path. Your username, UID and GID match the host's, and `sudo` needs no password. This works for directories under your home, `/run/media/USER` and `/mnt`. From anywhere else, nsl opens the shell in your guest home and tells you.

For example, with host username `you` and home `/var/home/you`, a session looks like this (your prompt may differ):

```console
you@debian:/mnt/host/var/home/you/src/project$ pwd
/mnt/host/var/home/you/src/project
you@debian:/mnt/host/var/home/you/src/project$ exit
```

You're now in a login shell in the default machine, working in the same files. Their path starts with `/mnt/host`, followed by the host path. Your username, UID and GID match the host's, and `sudo` needs no password. This works for directories under your home, `/run/media/USER` and `/mnt`. From anywhere else, nsl opens the shell in your guest home and tells you.
Type `exit` or press Ctrl-D to return to your host shell. Leaving the shell does not stop the machine immediately; it stays available until you stop it or the idle timeout expires. Run the remaining steps on the host.

3. **Run one command.**
3. **Run one command from the host.**

```sh
nsl run sudo apt-get update
nsl run sudo apt-get install -y podman
nsl run podman run --rm docker.io/library/alpine echo hello
nsl run cat /etc/os-release
```

These commands install Podman in the machine and use it to run an Alpine container. `run` passes arguments literally, keeps stdout and stderr separate, and returns the command's exit status.
The output identifies Debian 13. `run` passes arguments literally, keeps stdout and stderr separate, and returns the command's exit status. For installing packages and running Podman, see [software in a machine](../guides/machines.md#software-in-a-machine).

4. **Look around.**

Expand Down
22 changes: 18 additions & 4 deletions site/content/getting-started/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Host requirements, installing with Homebrew, downloading and verify

# Install nsl

nsl is a single binary that runs as your user. You'll need an x86-64 Linux host with KVM to run the x86-64 VM and machine images. The tested setup is Snow Linux 13 with systemd 261.2, QEMU 10.0.13, virtiofsd 1.13.2 and GNOME Wayland.
nsl is a single binary that runs as your user. You'll need an x86-64 Linux host with KVM to run the x86-64 VM and [machine images](../concepts/how-it-works.md#terms). Releases also include a `linux_arm64` CLI binary, but it cannot run machines: `create` rejects non-x86-64 hosts. The tested setup is Snow Linux 13 with systemd 261.2, QEMU 10.0.13, virtiofsd 1.13.2 and GNOME Wayland.

## Host requirements

Expand All @@ -16,7 +16,7 @@ nsl is a single binary that runs as your user. You'll need an x86-64 Linux host
| virtiofsd at `/usr/libexec/virtiofsd` | Sharing your files and the image cache with the VM |
| OpenSSH (`ssh`, `ssh-keygen`) and `/usr/lib/systemd/systemd-ssh-proxy` | Reaching the VM's agent over vsock |
| `sg` or util-linux `newgrp` (on Debian 14, both from `util-linux-extra`), and util-linux `unshare` | Opening the KVM devices through your `kvm` membership |
| `getent` and `id` | Checking `kvm` membership in the host account database |
| `getent` and `id` | Checking `kvm` membership in the host account database |
| Membership in `kvm`, with access to `/dev/kvm` and `/dev/vhost-vsock` | Hardware virtualization and vsock |
| Unprivileged user namespaces | Launching the VM without root |
| Waypipe (optional) | Wayland windows from machines |
Expand All @@ -37,7 +37,6 @@ nsl doctor

The cask installs only the CLI; the host requirements above still apply.
VM and machine images are downloaded and verified when you create a machine.
Uninstalling the cask leaves your machines and nsl state intact.

To update the CLI:

Expand All @@ -48,7 +47,7 @@ brew upgrade --cask frostyard/tap/nsl

## Download a release

Each release has a tarball for `linux_amd64`, a `checksums.txt`, and GitHub build provenance for both. The files have the same names in every release, so these steps always fetch the newest one.
Each release has tarballs for `linux_amd64` and `linux_arm64`, a `checksums.txt`, and GitHub build provenance. The files have the same names in every release, so these steps always fetch the newest one. Use `linux_amd64` on the supported x86-64 hosts, as below.

<div class="steps" markdown>

Expand All @@ -72,6 +71,7 @@ Each release has a tarball for `linux_amd64`, a `checksums.txt`, and GitHub buil

```sh
tar -xzf nsl_linux_amd64.tar.gz nsl
mkdir -p ~/.local/bin
install -m 0755 nsl ~/.local/bin/nsl
nsl version
```
Expand All @@ -94,10 +94,24 @@ make build # writes build/nsl
nsl doctor
```

A passing check looks like this (one line of the full output):

```text
OK kvm group membership
```

For a `MISSING` result, follow [troubleshooting](../reference/limits.md#troubleshooting).

`doctor` prints `OK` or `MISSING` for each tool and for the UEFI firmware systemd-vmspawn would use. It checks device access in your current session, looks up your `kvm` membership in the host account database, then tries KVM and vsock access through that group. It also checks user namespaces and the user systemd manager. A missing VM prerequisite makes it exit with an error.

Having firmware installed isn't enough on its own. systemd-vmspawn needs a QEMU firmware descriptor in `/usr/share/qemu/firmware` or `/etc/qemu/firmware`; without one, `doctor` reports the firmware as missing. Waypipe is optional, but you'll need it to display machine windows on the host.

If you aren't a member of `kvm`, doctor and VM launch report the missing membership with a command such as `sudo usermod -aG kvm 'YOUR_USERNAME'` for an administrator to run. They skip the group switch so it cannot ask for a group password. Once membership is added, retry nsl; no new login is needed. Device permissions must still allow the `kvm` group access.

Next, [create your first machine](first-machine.md).

## Uninstall

Run `nsl shutdown` before removing the CLI. For Homebrew, run `brew uninstall --cask frostyard/tap/nsl`. For the tarball installation above, remove `~/.local/bin/nsl`; if you installed it elsewhere, remove that binary instead. You can also delete the downloaded tarball, checksums and unpacked copy.

Both methods leave your machines and state in `NSL_HOME`, along with your separate `nsl.conf` settings. See [where state lives](../guides/storage.md#where-state-lives). To delete machines too, [remove them](../guides/machines.md#remove-a-machine) with nsl before uninstalling.
2 changes: 1 addition & 1 deletion site/content/guides/host-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ In an ordinary machine, you can read and write files from three places on the ho
| `/run/media/you` | `/mnt/host/run/media/you` |
| `/mnt` | `/mnt/host/mnt` |

Every ordinary machine sees all three trees. Creating a machine for a project doesn't limit it to that project's directory. Host `/usr`, `/etc`, `/tmp`, the rest of `/run` and pseudo-filesystems are not shared.
Every ordinary machine sees the trees that exist as directories when the shared VM launches. For example, if `/run/media/USER` is created after launch, restart the VM with `nsl shutdown` and then `nsl` to share it. Creating a machine for a project doesn't limit it to that project's directory. Host `/usr`, `/etc`, `/tmp`, the rest of `/run` and pseudo-filesystems are not shared.

## Paths and aliases

Expand Down
9 changes: 2 additions & 7 deletions site/content/guides/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,6 @@ No flag or environment variable turns these checks off. Images are rebuilt at le

## Local images

Image developers can select files they built:
Image developers can select local files with `nsl create NAME --image FILE --digest sha256:HEX` or `nsl update --image FILE --digest sha256:HEX`. The [build walkthrough](../contributing/build.md#the-images) shows how to build both images, select the output paths and calculate their digests.

```sh
nsl create test --image nsl-machine-debian-trixie-x86-64-r4.tar.zst --digest sha256:HEX
nsl update --image nsl-vm-trixie-x86-64-r9.raw --digest sha256:HEX
```

The digest checks that the file matches the bytes you selected. It can't tell you who built the image. See [build from source](../contributing/build.md).
The digest checks that the file matches the bytes you selected. It can't tell you who built the image.
10 changes: 8 additions & 2 deletions site/content/guides/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,14 @@ Keep backups of machines you need. `recover` can't rebuild deleted keys or repai
## Logs

```sh
nsl logs # the VM, its port forwarder and every desktop session
nsl logs dev # those of one machine
nsl logs # every VM, its port forwarder and desktop sessions
nsl logs dev # the desktop session of ordinary machine dev
```

For an isolated machine, `nsl logs NAME` also includes its own VM and forwarder. With a name, `logs` prints a hint for reading the machine's own journal:

```sh
nsl run -m dev --root journalctl -n 100
```

A VM that fails to become ready keeps its disks, and the error points to `logs` and `recover`.
18 changes: 11 additions & 7 deletions site/content/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@ hide:
[How it works](concepts/how-it-works.md){ .md-button }

</div>
<img class="fy-hero__icon" src="assets/nsl.svg" alt="NSL">
<img class="fy-hero__icon" src="assets/nsl.svg" alt="">
</div>

```sh
nsl create debian --distro debian:13 # verify the signed images; the first machine is the default
nsl # a login shell in the machine, in this directory
nsl run make test # one command, with its exit status
```
[Create your first machine](getting-started/first-machine.md) walks through creation, a login shell, running a command and stopping the VM.

## What a machine gives you

Expand Down Expand Up @@ -81,10 +77,18 @@ nsl run make test # one command, with its exit status

How the VM runs your machines and connects them to the host.

- <span class="fy-index">03</span> **[Command reference](reference/cli.md)**
- <span class="fy-index">03</span> **[Guides](guides/machines.md)**

Work with machines, host files, ports, desktop applications and backups.

- <span class="fy-index">04</span> **[Command reference](reference/cli.md)**

Look up commands, settings in `nsl.conf` and published images.

- <span class="fy-index">05</span> **[Limits and troubleshooting](reference/limits.md)**

Check known limits and find help when a command fails.

</div>

!!! note "Pre-release"
Expand Down
Loading
Loading