bots-garden/mini-mepublic Fork
⑂ feature/mcp-support
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. 427799c · on feature/mcp-support · k33g · Sep 21, 2026
run-in-a-docker-sandbox.md · 138 lines · 9.7 KBmarkdown
Blame HistoryOpen raw

How to run mini-me in a Docker Sandbox

This guide shows how to run mm inside a Docker Sandbox, so that every command the model runs and every file it edits happens in a container, on a mounted copy of your project. It covers the two ways of using it: the window mode in your terminal, and from an editor through the Agent Client Protocol. It assumes Docker Desktop with the sbx command, and a model server (Docker Model Runner or llama-server) running on your machine.

Everything lives under docker-sbx/ in the repository:

Piece What it is
template/Dockerfile The template: Docker Sandboxes' shell image with mm built from this checkout, plus the two entry points below.
kits/mm-tui/spec.yaml A sandbox kit: sbx run on it opens mm's window mode on the workspace.
kits/mm-acp/spec.yaml A sandbox kit for an editor: created once with sbx create, the editor then runs mm -acp inside it.
build.sh Builds the template image, k33g/mm-sandbox:<version>; PUSH=1 builds it for linux/amd64 and linux/arm64 and pushes it.
sync-kits.sh, push-kits.sh Point the kits at one image, and publish them to Docker Hub as OCI kit artifacts.

Use the published kits

Every release publishes the image and both kits to Docker Hub at mm's version — k33g/mm-sandbox:0.0.2, k33g/mm-tui:0.0.2, k33g/mm-acp:0.0.2 — so the shortest path needs no build at all. Docker Hub is an allowed kit source by default (sbx settings get kit.allowedSources shows docker.io/), and a reference with a / but no registry host is read as docker.io/…:

cd /path/to/your/project
sbx run k33g/mm-tui:0.0.2 . -- agent.dmr.yaml

for the window mode, and for an editor:

sbx create --name mm-acp k33g/mm-acp:0.0.2 . --kit-arg config=agent.dmr.yaml

then the editor registration of step 3 below. No --pull never: the kit names k33g/mm-sandbox:0.0.2 and sbx pulls it. sbx kit inspect k33g/mm-tui:0.0.2 shows what a kit declares before you run it. The steps that follow build the same thing locally, for a work-in-progress mm.

Steps

1. Build the template

From the repository root:

./docker-sbx/build.sh

This reads mm's tag from release.env (v0.0.2) and builds k33g/mm-sandbox:0.0.2: the image tag is the version without the v, like the release assets, and mm inside is stamped with the tag — mm --version is run in the image as proof. The script then rewrites the kits' default tag argument to 0.0.2, so the kits and the image move together. ./docker-sbx/build.sh v0.0.3 builds another version; an earlier image stays reachable with --kit-arg tag=0.0.1.

The image is in your local Docker image store: every sbx run or sbx create on these kits takes --pull never until the image is pushed (docker push k33g/mm-sandbox:0.0.2), or sbx tries to pull it and fails. If sbx runs on another machine, EXPORT=1 ./docker-sbx/build.sh also writes tmp/mm-sandbox-0.0.2.tar, to load there with sbx template load.

2. Window mode in the terminal (kit mm-tui)

From the project you want mm to work on:

sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml

The first time, sbx creates the sandbox, mounts the current directory as its workspace and attaches; then it runs the kit's entry point with what follows -- as arguments — here mm -tui agent.dmr.yaml, in the workspace. Any mm flag passes the same way: -- -config other.yaml.

Without anything after --, mm reads the kit's config argument, then ./agent.yaml in the workspace, then its built-in defaults:

sbx run --pull never ./docker-sbx/kits/mm-tui . --kit-arg config=agent.dmr.yaml
--kit-arg Default Effect
config=<path> empty The configuration file, as mm's AGENT_CONFIG: relative to the workspace, or absolute inside the sandbox.
tag=<version> the version last built (0.0.2) Which k33g/mm-sandbox:<version> image the sandbox is made from.

The sandbox keeps its name (mm-tui-<directory> unless you pass --name); sbx run --name <name> re-attaches to it later. Inside, the window is the one described in Use the window mode. Sessions, when sessions.enabled is on, are written under the workspace's .mm/sessions — that is your host directory, so they survive the sandbox.

3. From an editor (kit mm-acp)

An editor talks to mm -acp over the agent's raw stdin and stdout. sbx run cannot carry that: it always allocates a terminal. So the sandbox is created once without attaching, and the editor starts mm inside it with sbx exec -i, which pipes stdin and stdout untouched.

  1. From the project the editor will open, create the sandbox with the configuration file baked in (. is the workspace, sbx makes it absolute; the kit path must be an explicit ./, ../ or absolute path):

    cd /path/to/your/project
    sbx create --pull never --name mm-acp /path/to/mini-me/docker-sbx/kits/mm-acp . \
        --kit-arg config=agent.dmr.yaml
    

    A workspace path that does not exist on your machine makes sbx ask "The selected workspace does not exist. Would you like to create it?" — answer N and fix the path.

    --kit-arg config becomes the container's AGENT_CONFIG, relative to the workspace because that is where sbx exec starts mm. The workspace is fixed at creation: one sandbox per project, each with its own --name.

  2. Register it in the editor. In Zed's settings.json:

    {
      "agent_servers": {
        "mini-me (sandbox)": {
          "type": "custom",
          "command": "sbx",
          "args": ["exec", "-i", "mm-acp", "mm", "-acp"]
        }
      }
    }
    

    Compared with the host-side setup, the command is sbx and no env is needed: AGENT_CONFIG is already in the container. If the editor cannot find sbx, put the absolute path from which sbx in "command".

  3. Open the agent panel and pick mini-me (sandbox). The editor starts mm -acp in the sandbox for every conversation; sbx exec starts the sandbox first when it is stopped. Tool calls, permission dialogs, @file mentions and the slash-command menu work as they do on the host — the difference is where the commands run.

Attaching to this sandbox with sbx run --name mm-acp does not start mm: its entry point prints the wiring above with the sandbox's real name and paths, then opens a shell inside the sandbox. sbx exec -it mm-acp bash is the shorter way to that shell.

The model server stays on the host

Inside a sandbox localhost is the sandbox itself. Your model server keeps running on your machine and is reached through host.docker.internal, which the sandbox proxy maps to your host's localhost.

  • Docker Model Runner — nothing to change: agent.dmr.yaml already has fallback: http://host.docker.internal:12434/engines/v1, and mm switches to it when localhost:12434 does not answer (the banner says which URL won).
  • llama-server — agent.llamacpp.yaml has no fallback. Either edit its baseUrl to http://host.docker.internal:8080/v1, or pass --env AGENT_BASE_URL=http://host.docker.internal:8080/v1 to sbx run / sbx create (see Run with llama.cpp).

Both kits allow ports 12434 and 8080 on the host in the sandbox's network policy. For another port: sbx policy allow network host.docker.internal:<port>.

Publishing a version

Publishing is a manual step, from a machine with Docker Desktop (buildx and the sbx CLI); the Release workflow builds only the binaries. One script builds the image for linux/amd64 and linux/arm64, pushes it, then validates and pushes the two kits, all at mm's tag:

export DOCKER_HANDLE=k33g          # the Docker Hub account, also the image owner
export DOCKER_PAT=dckr_pat_…       # an access token with read/write scope
./docker-sbx/publish.sh            # TAG from release.env: v0.0.3 → k33g/mm-sandbox:0.0.3, k33g/mm-tui:0.0.3, k33g/mm-acp:0.0.3

With both variables set, the script logs in with them and logs out when it is done; with neither, it reuses your existing docker login. Its two halves run alone as PUSH=1 ./docker-sbx/build.sh v0.0.3 (image) and ./docker-sbx/push-kits.sh v0.0.3 (kits, SBX=/path/to/sbx if the CLI is not on the PATH). Both call sync-kits.sh first, which rewrites the kits' version, default tag and image owner, so a published kit always names the image pushed with it — commit the two spec.yaml afterwards. sbx kit push talks to the registry directly and authenticates from the Docker credential store, no sandbox daemon involved. IMAGE_OWNER=<handle> publishes under another account.

Variants

  • Another configuration for one editor session. Add "-e", "AGENT_CONFIG=agent.lsp.yaml" before the sandbox name in "args"; sbx exec -e overrides the baked-in value for that run only.
  • Skills. skillsDir is relative to the directory mm runs in, which is the workspace in both kits — the same rule as on the host.
  • Docker inside the sandbox. Build the template on the Docker-in-Docker shell instead: SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh.
  • Read the logs. In the window mode they are on screen; over ACP they go to stderr, which Zed shows under dev: open acp logs.
  • Remove a sandbox. sbx rm mm-acp. The project directory is a bind mount and is not touched.

See also

  • The kit format these two files follow: the Docker Sandboxes kit specification (schemaVersion: "2"), bundled with the sbx sources.
  • What mm -acp exposes to the editor: ACP reference.
  • The flags and environment variables mm reads: command line reference.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# How to run mini-me in a Docker Sandbox

This guide shows how to run `mm` inside a [Docker Sandbox](https://docs.docker.com/ai/sandboxes/), so that every command the model runs and every file it edits happens in a container, on a mounted copy of your project. It covers the two ways of using it: the window mode in your terminal, and from an editor through the Agent Client Protocol. It assumes Docker Desktop with the `sbx` command, and a model server (Docker Model Runner or llama-server) running on your machine.

Everything lives under [`docker-sbx/`](../../../docker-sbx/README.md) in the repository:

| Piece | What it is |
|---|---|
| `template/Dockerfile` | The **template**: Docker Sandboxes' `shell` image with `mm` built from this checkout, plus the two entry points below. |
| `kits/mm-tui/spec.yaml` | A **sandbox kit**: `sbx run` on it opens `mm`'s window mode on the workspace. |
| `kits/mm-acp/spec.yaml` | A **sandbox kit** for an editor: created once with `sbx create`, the editor then runs `mm -acp` inside it. |
| `build.sh` | Builds the template image, `k33g/mm-sandbox:<version>`; `PUSH=1` builds it for linux/amd64 and linux/arm64 and pushes it. |
| `sync-kits.sh`, `push-kits.sh` | Point the kits at one image, and publish them to Docker Hub as OCI kit artifacts. |

## Use the published kits

Every release publishes the image and both kits to Docker Hub at mm's version — `k33g/mm-sandbox:0.0.2`, `k33g/mm-tui:0.0.2`, `k33g/mm-acp:0.0.2` — so the shortest path needs no build at all. Docker Hub is an allowed kit source by default (`sbx settings get kit.allowedSources` shows `docker.io/`), and a reference with a `/` but no registry host is read as `docker.io/…`:

```bash
cd /path/to/your/project
sbx run k33g/mm-tui:0.0.2 . -- agent.dmr.yaml
```

for the window mode, and for an editor:

```bash
sbx create --name mm-acp k33g/mm-acp:0.0.2 . --kit-arg config=agent.dmr.yaml
```

then the editor registration of step 3 below. No `--pull never`: the kit names `k33g/mm-sandbox:0.0.2` and sbx pulls it. `sbx kit inspect k33g/mm-tui:0.0.2` shows what a kit declares before you run it. The steps that follow build the same thing locally, for a work-in-progress mm.

## Steps

### 1. Build the template

From the repository root:

```bash
./docker-sbx/build.sh
```

This reads mm's tag from `release.env` (`v0.0.2`) and builds `k33g/mm-sandbox:0.0.2`: the image tag is the version without the `v`, like the release assets, and `mm` inside is stamped with the tag — `mm --version` is run in the image as proof. The script then rewrites the kits' default `tag` argument to `0.0.2`, so the kits and the image move together. `./docker-sbx/build.sh v0.0.3` builds another version; an earlier image stays reachable with `--kit-arg tag=0.0.1`.

The image is in your local Docker image store: every `sbx run` or `sbx create` on these kits takes `--pull never` until the image is pushed (`docker push k33g/mm-sandbox:0.0.2`), or `sbx` tries to pull it and fails. If `sbx` runs on another machine, `EXPORT=1 ./docker-sbx/build.sh` also writes `tmp/mm-sandbox-0.0.2.tar`, to load there with `sbx template load`.

### 2. Window mode in the terminal (kit `mm-tui`)

From the project you want `mm` to work on:

```bash
sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml
```

The first time, `sbx` creates the sandbox, mounts the current directory as its workspace and attaches; then it runs the kit's entry point with what follows `--` as arguments — here `mm -tui agent.dmr.yaml`, in the workspace. Any `mm` flag passes the same way: `-- -config other.yaml`.

Without anything after `--`, `mm` reads the kit's `config` argument, then `./agent.yaml` in the workspace, then its built-in defaults:

```bash
sbx run --pull never ./docker-sbx/kits/mm-tui . --kit-arg config=agent.dmr.yaml
```

| `--kit-arg` | Default | Effect |
|---|---|---|
| `config=<path>` | empty | The configuration file, as `mm`'s `AGENT_CONFIG`: relative to the workspace, or absolute inside the sandbox. |
| `tag=<version>` | the version last built (`0.0.2`) | Which `k33g/mm-sandbox:<version>` image the sandbox is made from. |

The sandbox keeps its name (`mm-tui-<directory>` unless you pass `--name`); `sbx run --name <name>` re-attaches to it later. Inside, the window is the one described in [Use the window mode](use-the-window-mode.md). Sessions, when `sessions.enabled` is on, are written under the workspace's `.mm/sessions` — that is your host directory, so they survive the sandbox.

### 3. From an editor (kit `mm-acp`)

An editor talks to `mm -acp` over the agent's raw stdin and stdout. `sbx run` cannot carry that: it always allocates a terminal. So the sandbox is created once without attaching, and the editor starts `mm` inside it with `sbx exec -i`, which pipes stdin and stdout untouched.

1. From the project the editor will open, create the sandbox with the configuration file baked in (`.` is the workspace, sbx makes it absolute; the kit path must be an explicit `./`, `../` or absolute path):

   ```bash
   cd /path/to/your/project
   sbx create --pull never --name mm-acp /path/to/mini-me/docker-sbx/kits/mm-acp . \
       --kit-arg config=agent.dmr.yaml
   ```

   A workspace path that does not exist on your machine makes sbx ask "The selected workspace does not exist. Would you like to create it?" — answer `N` and fix the path.

   `--kit-arg config` becomes the container's `AGENT_CONFIG`, relative to the workspace because that is where `sbx exec` starts `mm`. The workspace is fixed at creation: one sandbox per project, each with its own `--name`.

2. Register it in the editor. In Zed's `settings.json`:

   ```json
   {
     "agent_servers": {
       "mini-me (sandbox)": {
         "type": "custom",
         "command": "sbx",
         "args": ["exec", "-i", "mm-acp", "mm", "-acp"]
       }
     }
   }
   ```

   Compared with [the host-side setup](use-from-an-editor.md), the command is `sbx` and no `env` is needed: `AGENT_CONFIG` is already in the container. If the editor cannot find `sbx`, put the absolute path from `which sbx` in `"command"`.

3. Open the agent panel and pick **mini-me (sandbox)**. The editor starts `mm -acp` in the sandbox for every conversation; `sbx exec` starts the sandbox first when it is stopped. Tool calls, permission dialogs, `@file` mentions and the slash-command menu work as they do on the host — the difference is where the commands run.

Attaching to this sandbox with `sbx run --name mm-acp` does not start `mm`: its entry point prints the wiring above with the sandbox's real name and paths, then opens a shell inside the sandbox. `sbx exec -it mm-acp bash` is the shorter way to that shell.

## The model server stays on the host

Inside a sandbox `localhost` is the sandbox itself. Your model server keeps running on your machine and is reached through `host.docker.internal`, which the sandbox proxy maps to your host's `localhost`.

- **Docker Model Runner** — nothing to change: `agent.dmr.yaml` already has `fallback: http://host.docker.internal:12434/engines/v1`, and `mm` switches to it when `localhost:12434` does not answer (the banner says which URL won).
- **llama-server** — `agent.llamacpp.yaml` has no fallback. Either edit its `baseUrl` to `http://host.docker.internal:8080/v1`, or pass `--env AGENT_BASE_URL=http://host.docker.internal:8080/v1` to `sbx run` / `sbx create` (see [Run with llama.cpp](run-with-llama-cpp.md)).

Both kits allow ports 12434 and 8080 on the host in the sandbox's network policy. For another port: `sbx policy allow network host.docker.internal:<port>`.

## Publishing a version

Publishing is a manual step, from a machine with Docker Desktop (buildx and the `sbx` CLI); the Release workflow builds only the binaries. One script builds the image for linux/amd64 and linux/arm64, pushes it, then validates and pushes the two kits, all at mm's tag:

```bash
export DOCKER_HANDLE=k33g          # the Docker Hub account, also the image owner
export DOCKER_PAT=dckr_pat_…       # an access token with read/write scope
./docker-sbx/publish.sh            # TAG from release.env: v0.0.3 → k33g/mm-sandbox:0.0.3, k33g/mm-tui:0.0.3, k33g/mm-acp:0.0.3
```

With both variables set, the script logs in with them and logs out when it is done; with neither, it reuses your existing `docker login`. Its two halves run alone as `PUSH=1 ./docker-sbx/build.sh v0.0.3` (image) and `./docker-sbx/push-kits.sh v0.0.3` (kits, `SBX=/path/to/sbx` if the CLI is not on the PATH). Both call `sync-kits.sh` first, which rewrites the kits' `version`, default `tag` and image owner, so a published kit always names the image pushed with it — commit the two `spec.yaml` afterwards. `sbx kit push` talks to the registry directly and authenticates from the Docker credential store, no sandbox daemon involved. `IMAGE_OWNER=<handle>` publishes under another account.

## Variants

- **Another configuration for one editor session.** Add `"-e", "AGENT_CONFIG=agent.lsp.yaml"` before the sandbox name in `"args"`; `sbx exec -e` overrides the baked-in value for that run only.
- **Skills.** `skillsDir` is relative to the directory `mm` runs in, which is the workspace in both kits — the same rule as on the host.
- **Docker inside the sandbox.** Build the template on the Docker-in-Docker shell instead: `SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh`.
- **Read the logs.** In the window mode they are on screen; over ACP they go to stderr, which Zed shows under `dev: open acp logs`.
- **Remove a sandbox.** `sbx rm mm-acp`. The project directory is a bind mount and is not touched.

## See also

- The kit format these two files follow: the Docker Sandboxes kit specification (`schemaVersion: "2"`), bundled with the `sbx` sources.
- What `mm -acp` exposes to the editor: [ACP reference](../reference/acp.md).
- The flags and environment variables `mm` reads: [command line reference](../reference/cli.md).