Set up the agent sandbox
Motir asks you to hand work to a coding agent that runs unattended, with approvals off. This is where it should run: a published container that can reach your workspace tree and nothing else on your machine. This page ends when you have a working one.
What it confines — and what it does not
| Surface | |
|---|---|
Filesystem | Confined. The only host surfaces inside the container are a writable /workspace and your agent’s own credential, mounted read-only. No docker socket, no other host bind. |
Network | Open, by design. Every agent needs its provider API, and every dispatched item needs git remotes. This image confines the filesystem blast radius, not egress — reach for docker’s own --network controls if your threat model needs more. |
Privileges | Runs as the unprivileged node user (uid 1000), so files written into the mount stay owned by you rather than by root. |
- Surface
Filesystem- Confined. The only host surfaces inside the container are a writable
/workspaceand your agent’s own credential, mounted read-only. No docker socket, no other host bind.
- Surface
Network- Open, by design. Every agent needs its provider API, and every dispatched item needs git remotes. This image confines the filesystem blast radius, not egress — reach for docker’s own
--networkcontrols if your threat model needs more.
- Surface
Privileges- Runs as the unprivileged
nodeuser (uid 1000), so files written into the mount stay owned by you rather than by root.
Before you start
Two things, and neither is a Motir account detail — you sign in inside the container in step 4, so there is nothing to mint or copy first. You do not need the Motir CLI on this machine either; it ships in the image.
| What you need | How to get it |
|---|---|
Docker, running | Docker Desktop or any engine. The images are built for linux/amd64 and linux/arm64, so Apple Silicon is a first-class machine and nothing is emulated. There is no build step — you pull. |
| Your agent’s own sign-in, on this machine | Sign in to your coding agent once, here, before you start — or have its API key in your environment. Its credential mount is read-only, so the container can use a sign-in and can never perform one. |
- What you need
Docker, running- How to get it
- Docker Desktop or any engine. The images are built for
linux/amd64andlinux/arm64, so Apple Silicon is a first-class machine and nothing is emulated. There is no build step — you pull.
- What you need
- Your agent’s own sign-in, on this machine
- How to get it
- Sign in to your coding agent once, here, before you start — or have its API key in your environment. Its credential mount is read-only, so the container can use a sign-in and can never perform one.
A Motir project usually spans several repositories and the work loop runs across all of them — so what you mount is the folder that contains your checkouts, not any one of them. Start the container from there:
~/work/ ← start the container from HERE ├── motir-core/ ← a checkout └── motir-ai/ ← another
1Pick your profile
One image per agent, each layer fetching that CLI from its own official source at build time. Find the row for the agent you already use — you need its tag and its credential mount in step 2.
Tier is how closely we track the vendor, not whether it works. Every profile here is published, and every one is built and smoke-tested before a release ships. Tier 1 means we pin both the install source and the credential location and verify them on every change. Tier 2 installs from a vendor endpoint we do not control, so day-to-day breakage there is reported rather than blocking — it is caught at release, not necessarily the hour it happens.
| Profile · binary | Tier | Installed from | Credential mount (read-only) |
|---|---|---|---|
claude · claude | Tier 1 | npm install -g @anthropic-ai/claude-code | ~/.claude |
codex · codex | Tier 1 | npm install -g @openai/codex | ~/.codex |
opencode · opencode | Tier 1 | npm, or the OpenCode install script (opencode.ai) | ~/.config/opencode · ~/.local/share/opencode |
kimi · kimi | Tier 1 | npm (@moonshot-ai/kimi-code) — needs Node ≥ 22.19 | ~/.kimi-code |
antigravity · agy | Tier 2 | curl -fsSL https://antigravity.google/cli/install.sh | bash | — |
cursor · agent | Tier 2 | curl https://cursor.com/install -fsS | bash | ~/.local/share/cursor-agent |
aider · aider | Tier 2 | pip (Python) — PyPI aider-chat | ~/.aider.conf.yml |
goose · goose | Tier 2 | the Goose installer (Block) | ~/.config/goose |
- Profile · binary
claude·claude- Tier
- Tier 1
- Installed from
- npm install -g @anthropic-ai/claude-code
- Credential mount (read-only)
~/.claude
- Profile · binary
codex·codex- Tier
- Tier 1
- Installed from
- npm install -g @openai/codex
- Credential mount (read-only)
~/.codex
- Profile · binary
opencode·opencode- Tier
- Tier 1
- Installed from
- npm, or the OpenCode install script (opencode.ai)
- Credential mount (read-only)
~/.config/opencode·~/.local/share/opencode
- Profile · binary
kimi·kimi- Tier
- Tier 1
- Installed from
- npm (@moonshot-ai/kimi-code) — needs Node ≥ 22.19
- Credential mount (read-only)
~/.kimi-code
- Profile · binary
antigravity·agy- Tier
- Tier 2
- Installed from
- curl -fsSL https://antigravity.google/cli/install.sh | bash
- Credential mount (read-only)
- —
- Profile · binary
cursor·agent- Tier
- Tier 2
- Installed from
- curl https://cursor.com/install -fsS | bash
- Credential mount (read-only)
~/.local/share/cursor-agent
- Profile · binary
aider·aider- Tier
- Tier 2
- Installed from
- pip (Python) — PyPI
aider-chat - Credential mount (read-only)
~/.aider.conf.yml
- Profile · binary
goose·goose- Tier
- Tier 2
- Installed from
- the Goose installer (Block)
- Credential mount (read-only)
~/.config/goose
ghcr.io/moooon-b-v/motir-sandbox:base ships the sandbox with no agent at all — bring your own binary. Motir is agent-agnostic; the profiles make 8 CLIs first-class, they do not make Motir depend on any of them.2Start the container
Two commands: pull the image, then start it. The pull is what makes the second one a current sandbox. docker run never goes back to the registry for an image this machine already has — and neither does docker start -ai, which is how this page tells you to come back to the container later.
That matters because the profile tags move. :claude and its siblings always point at the newest release, so a copy pulled weeks ago is out of date and nothing on your machine will say so — you get an older motir with fewer commands than this guide describes. Returning to a container you set up earlier? A pull on its own is not enough: the container was made from the old image and keeps it. Pull, then docker rm motir-sandbox and run the command again (or give the run a new --name). The sign-in from step 4 lives in the old container, so expect to do that step once more.
Two of the run command’s parts come from the row you just picked; everything else is the same for every profile. It drops you into a shell in /workspace.
--rm, and the container has a name. The sign-in in step 4 is written inside the container, so a --rm run would throw it away the moment you exit. Set it up once and come back to it with docker start -ai motir-sandbox.:<profile>-<version> tag beside the moving one — <version> is the @motir/cli release the image was cut from — and pulling that name gets the same bytes every time. Take it when you are pinning a CI runner or reproducing a run, and the moving tag when you would rather have the newest. The published list, with the digest for each, lives beside the image in packages/cli/sandbox/README.md.https://app.motir.co unless told otherwise, so nothing here names a server. Point it at your own instance by adding -e MOTIR_SERVER=https://motir.example.com — Motir is open-core and self-hostable, and that variable is the whole difference.docker pull ghcr.io/moooon-b-v/motir-sandbox:claude
docker run -it --name motir-sandbox \ -v "$PWD:/workspace" \ -v "$HOME/.claude:/home/node/.claude:ro" \ ghcr.io/moooon-b-v/motir-sandbox:claude
3Or start it from VS Code instead
The same confined image, as a dev container: VS Code opens /workspace inside it with the same mounts, so your editor, terminal and agent all run behind the same boundary. Three sub-steps, and they replace step 2 rather than following it — steps 4 onwards are the same either way.
1 · Install the Dev Containers extension. From the Extensions view, or from the command palette — ⇧⌘P on macOS, Ctrl+Shift+P on Windows and Linux, F1 on all three, or View → Command Palette… if you would rather not hold a chord — then Extensions: Install Extensions. The palette is where two of these three sub-steps happen, so it is worth pinning now. The extension is what starts the container on your behalf, and it drives the same Docker engine step 2 uses.
2 · Add .devcontainer/devcontainer.json to the folder you are mounting — the same one step 2 would have started from. It pins the published image and passes the mount your profile needs. Write it from that folder in one command, because a GUI file manager will not do it for you: macOS Finder and most file pickers refuse a name beginning with a dot, and they refuse it without saying why.
mkdir -p .devcontainer
cat > .devcontainer/devcontainer.json <<'JSON'
{
"name": "Motir sandbox (Claude Code)",
"image": "ghcr.io/moooon-b-v/motir-sandbox:claude",
"workspaceFolder": "/workspace",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
"mounts": [
"source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind,readonly"
],
"remoteUser": "node",
"overrideCommand": true
}
JSONThe quotes around <<'JSON' are load-bearing: they are what stops your shell from expanding ${localWorkspaceFolder} and ${localEnv:HOME} before they reach the file. Those are Dev Containers substitutions, and the editor is what resolves them. That command writes exactly this — the same file, if you would rather create it by hand:
{
"name": "Motir sandbox (Claude Code)",
"image": "ghcr.io/moooon-b-v/motir-sandbox:claude",
"workspaceFolder": "/workspace",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
"mounts": [
"source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind,readonly"
],
"remoteUser": "node",
"overrideCommand": true
}Two routes inside VS Code do accept the dot-name where Finder will not: Explorer → New File, typing the whole path .devcontainer/devcontainer.json — it creates the .devcontainer folder for you — or the palette’s Dev Containers: Add Dev Container Configuration Files…, then replace what it scaffolds with the above.
Swap :claude and the mounts entry for your row from step 1. A dev container is not torn down when you close the window, so the sign-in in step 4 persists here without any extra flag — and for exactly that reason it also keeps the image it was first created from. Dev Containers reuses a local image just as docker run does, so step 2’s docker pull is still yours to run, in a terminal on your machine, before you reopen; an existing container then needs the palette’s Dev Containers: Rebuild Container to pick the new image up. Pin the immutable :<profile>-<version> tag here instead if you would rather this folder stay on a known image.
3 · Open the folder in the container. Command palette → Dev Containers: Open Folder in Container…, and pick the folder you just wrote the file into. VS Code pulls the image and attaches; its terminal is the same shell step 2 would have dropped you into. If that folder is already open in VS Code, Dev Containers: Reopen in Container does the same attach without asking which folder — that precondition is the entire difference between the two commands, and it is why the sub-step does not lead with the one that begins “Re”.
devcontainer.json files inside the motir-core repository are not this file. They carry a build block pointing at that repository’s own Dockerfile, because they are its dev containers and a checkout is what those are for. For your own workspace, pin the image as above.4Sign in
From the shell inside the container. It prints a code and a URL, you approve it in a browser on any device, and the container polls until the token is minted — a device grant, headless by construction, which is exactly what a container is.
motir login
-e MOTIR_TOKEN on step 2’s command, minted under Settings → Account → Tokens. And a laptop that has already run motir login can mount its credential read-only — but then motir login has nowhere to write, and says so.5Link the folder to your project
Still inside the container. This binds /workspace — the folder you mounted — to a Motir project, so every command knows what it is working on:
motir link
If your workspace has exactly one project, it is chosen for you and nothing is asked. With more than one, the command lists them and asks which; pass --project <key> to answer up front, which is also what a non-interactive shell needs. Linking then clones the project’s repositories into /workspace, which is the whole point of mounting it: the container starts empty, and the agent needs the code on a filesystem. Pass --no-clone if you mounted checkouts you already had.
6Check it
The last thing, and the one that tells you the setup actually took:
motir doctor
| It checks | Which tells you |
|---|---|
| the workspace | /workspace is writable and the link from step 5 resolves — so you mounted the right folder, not a checkout inside it. |
| the Motir credential | Step 4’s sign-in landed, and it names where the credential came from. |
| your agent | The binary is on PATH, and its own credential is present where that profile keeps it — step 2’s read-only mount actually landed. |
- It checks
- the workspace
- Which tells you
/workspaceis writable and the link from step 5 resolves — so you mounted the right folder, not a checkout inside it.
- It checks
- the Motir credential
- Which tells you
- Step 4’s sign-in landed, and it names where the credential came from.
- It checks
- your agent
- Which tells you
- The binary is on
PATH, and its own credential is present where that profile keeps it — step 2’s read-only mount actually landed.
All three green is the end of this page: you have a confined container that can see your work and hold your credentials, and nothing else on your machine.
What next
Setting the sandbox up and using it are different things, and this page only owns the first. Inside the container the CLI behaves exactly as it does on a host — motir next to take one item, motir run <key> for a specific one, or motir auto to drain the ready set unattended, which is what the confinement is really for. All three are documented with the CLI, because none of them is about the container.
motir auto --agent "<your agent’s command>"). Those drift between vendor releases, so the current set lives beside the image in packages/cli/sandbox/README.md — which is also where the digests to pin, the confinement proof, the validation harness and the escape hatch for an unlisted agent live.Next: API reference · Getting started · Stability & deprecation