# Getting Started with DynoCap

Install DynoCap on Apple Silicon with Homebrew or curl, prepare a project, and run a command in an isolated Linux microVM.

HTML: https://dynocap.dev/docs/tutorials/getting-started

Install DynoCap on an Apple Silicon Mac, prepare one project, and run a command inside an isolated Linux guest.

You will:

1. Install the CLI with Homebrew, or with curl.
2. Check your setup with `dynocap doctor`.
3. Repair it with `dynocap doctor --fix`.
4. Launch a raw sandbox and execute commands over vsock.
5. Safely destroy the sandbox.

---

## Prerequisites

- **Hardware:** Apple Silicon Mac (M1 or newer). Intel Macs are refused by the installers.
- **Operating System:** macOS 13 (Ventura) or newer.
- **Homebrew**, for the primary install. The formula also installs `e2fsprogs`, which provides `mke2fs`. That formula is keg-only; DynoCap finds it at `/opt/homebrew/opt/e2fsprogs/sbin/mke2fs`.
- **Go 1.22+** (`brew install go`) the first time a runtime is prepared. The guest image ships in the release archive, so a Homebrew or curl install does not need Go to copy it. Swift and Node are for a source build and for this repository's TypeScript packages.

---

## Step 1: Install

DynoCap uses Apple's `Virtualization.framework`, which requires the `com.apple.security.virtualization` entitlement. There is no Apple Developer ID signature. Both installers re-sign the CLI on your Mac with `codesign --sign -` and the bundled entitlement. Neither installer uses a Homebrew bottle, because a poured bottle would skip that re-sign.

### Homebrew

```sh
brew install baires/tap/dynocap
```

That installs the formula from [baires/homebrew-tap](https://github.com/baires/homebrew-tap). On Homebrew 6, this fully qualified name trusts that formula.

Trust it explicitly when you want later commands to use the short name `dynocap`, such as `brew upgrade dynocap`:

```sh
brew trust --formula baires/tap/dynocap
```

The formula downloads the versioned release archive, checks its sha256, and re-signs `dynocap`. Then run doctor in your project, which the formula also prints as a caveat:

```sh
dynocap doctor --fix
```

### Curl

```sh
curl -fsSL https://dynocap.dev/install.sh | sh
```

The site serves [`scripts/install.sh`](https://github.com/baires/dynocap/blob/main/scripts/install.sh). The script downloads `dynocap-darwin-arm64.tar.gz` from the latest GitHub release, checks its sha256, installs it under `~/.local/dynocap`, and symlinks `~/.local/bin/dynocap`. The checksum catches a truncated download. When `gh` is installed and logged in, the script also requires a build attestation from `baires/dynocap`. Its last step is `dynocap doctor --fix`. If `mke2fs` is missing, it tells you to run `brew install e2fsprogs`. If `~/.local/bin` is not on `PATH`, it tells you to add it.

### From source

A checkout has no bundled guest image. Building needs Xcode Command Line Tools (Swift 5.9+), Go 1.22+, and `mke2fs` (`brew install e2fsprogs`).

```sh
./scripts/build-macos.sh
export PATH="$PWD/.build/release:$PATH"
```

Re-run `./scripts/build-macos.sh` after every rebuild so the binary stays signed. `npm ci` is for the repository's TypeScript packages, not for the CLI.

---

## Step 2: Check Your Setup

Go to your project and run `dynocap doctor`. It checks the platform, code signing, companion binaries, guest image, runtime, and daemon, and changes nothing:

```sh
cd ~/code/my-app
dynocap doctor
```

Doctor reads your project's files to pick its runtime, the same way `clone` does:

| File at the project root                                               | Runtime       |
| :--------------------------------------------------------------------- | :------------ |
| `package.json`                                                         | `node:24`     |
| `pyproject.toml`, `uv.lock`, `requirements.txt`, `setup.py`, `Pipfile` | `python:3.14` |
| `go.mod`                                                               | `go:1.27`     |

On a fresh source build, the guest image and runtime are not built yet. A Homebrew or curl install already contains the guest image, and `--fix` copies it into your home. Each failure says what is wrong and what to run:

```text
ERROR   images             The guest image for aarch64 has not been built yet.
                           Next: Run dynocap doctor --fix to build the guest image (downloads pinned Alpine files; needs Go).
ERROR   runtime            Runtime python:3.14 is not prepared for aarch64 yet. This project's pyproject.toml needs it.
                           Next: Run dynocap doctor --fix to prepare python:3.14 (downloads verified inputs; takes a few minutes).

2 required checks failed. Run dynocap doctor --fix to repair them.
```

To prepare a different runtime, pass `--runtime`, for example `dynocap doctor --fix --runtime node:22`. Run outside a project, doctor requires no runtime. It lists the prepared ones instead, because raw sandboxes need none.

---

## Step 3: Repair It with `doctor --fix`

```sh
dynocap doctor --fix
```

`--fix` copies a bundled guest image when the install has one, and builds it otherwise. It prepares your project's runtime and restarts a daemon left over from an older build. Every download is pinned and verified. It then runs the checks again and ends with `Required checks passed.`

Doctor finds that prefix from the `dynocap` binary, whether it is a source checkout or a Homebrew or curl install, so you can run it in any directory. You can also name the project instead of changing directory: `dynocap doctor --fix ~/code/my-app`.

> **Tip:** After you rebuild `dynocap`, run `dynocap doctor --fix` again. It restarts the daemon so it runs the new build.

---

## Step 4: Boot a Raw Sandbox

A raw sandbox boots the minimal Alpine Linux image without importing a project workspace.

Start a sandbox named `my-first-box`:

```sh
dynocap run --name my-first-box
```

Output:

```text
running my-first-box
```

Behind the scenes:

1. The `dynocap` CLI launched the per-user background daemon (`~/.dynocap/daemon.sock`).
2. The daemon booted a Linux virtual machine with 1 vCPU and 1024 MB RAM via Virtualization.framework.
3. The guest booted its initramfs in under 200ms and launched the internal Go `dynocap-agent`.
4. The agent connected back over `virtio-vsock` (guest port 5252).

Verify the sandbox status:

```sh
dynocap ps
```

You will see:

```text
NAME/ID       KIND     PHASE    RUNTIME  NETWORK  TTL    RETENTION
my-first-box  sandbox  running  minimal  none     29m    -
```

Notice that `NETWORK` is `none`. The sandbox is completely offline. No network interface exists inside the guest.

---

## Step 5: Execute Commands Inside the Sandbox

Run commands inside your guest with `dynocap exec`:

```sh
dynocap exec my-first-box -- uname -a
```

Output:

```text
Linux dynocap 6.18.62-0-virt #1-Alpine SMP PREEMPT_DYNAMIC aarch64 Linux
```

Test running an isolated shell command:

```sh
dynocap exec my-first-box -- sh -c "echo 'Hello from DynoCap' && id"
```

Output:

```text
Hello from DynoCap
uid=0(root) gid=0(root) groups=0(root)
```

DynoCap streams stdout and stderr separately, and exits with the exact status code returned by the guest command.

---

## Step 6: Destroy the Sandbox

When your work is done, remove the sandbox:

```sh
dynocap rm my-first-box --force
```

Output:

```text
removed my-first-box
```

Because `my-first-box` was actively running, `--force` explicitly confirms discarding running processes. The daemon terminates the virtual machine and cleans up runtime descriptors.

---

## Next Steps

Now that the CLI is installed and a raw sandbox runs, proceed to:

- **[End-to-End Agent Task Workflow](/tutorials/agent-task-workflow)**: Copy a project into a task, install from a dependency seed, and export a verified diff. The host checkout is never mounted.
- **[Interactive Shells & Terminal Sessions](/how-to/interactive-shells-and-terminal)**: Learn how to attach interactive TTY sessions for live debugging.
