> ## Documentation Index
> Fetch the complete documentation index at: https://meepa.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Isolation

> Isolation modes for agent execution

## Overview

MeepaGateway can run each agent inside a Docker container, isolating the entire agent loop — LLM calls, tool execution, shell commands — from the host. A fresh container is spawned per message and destroyed after the response is sent.

Container mode is **disabled by default**. Enable it for agents that run untrusted code, execute shell commands, or need a controlled environment with specific packages installed.

***

## Isolation Models

MeepaGateway supports three isolation levels, from strongest to weakest:

### Container

The agent's full loop runs inside an isolated Docker container. The agent workspace is bind-mounted in; everything else on the host is inaccessible. File access rules do not apply — the container boundary is the enforcement mechanism.

Enable with `container_mode.enabled: true` on the agent.

### Seatbelt / Landlock

The agent runs on the host with kernel-level file access enforcement. This is the **default mode** when container mode is not enabled and `file_access.unrestricted` is not set.

Rules are evaluated in priority order: `deny` always wins, then `allow_read_write`, then `allow_read`. Writes are blocked by default unless explicitly permitted.

* **macOS**: enforced via `sandbox-exec` (seatbelt)
* **Linux**: enforced via Landlock LSM

### Unrestricted

No file access restrictions. Enable with `file_access.unrestricted: true` in config or the `--unrestricted` CLI flag.

***

## How It Works

1. A message arrives from a connector (MeepaChat, Discord, Slack)
2. The host spawns a Docker container running `meepagateway agent-run`
3. Message content, provider secrets, and config are passed via stdin JSON
4. The agent workspace is bind-mounted at `/workspace` (read-write)
5. The agent runs its full loop inside the container — including all tool calls
6. The response is written to stdout as JSON
7. The host reads the response, sends it through the connector, and the container exits

Only one container runs per agent at a time. A per-agent mutex prevents concurrent SQLite writes from overlapping container mounts.

***

## Container Filesystem

| Path            | Type          | Writable | Persists | Purpose                                              |
| --------------- | ------------- | -------- | -------- | ---------------------------------------------------- |
| `/workspace`    | Bind mount    | Yes      | Yes      | Agent files (SOUL.md, MEMORY.md, memory.db, skills/) |
| `/tmp`          | tmpfs (256MB) | Yes      | No       | Temp files (SQLite journals, downloads)              |
| Everything else | Image rootfs  | No       | N/A      | Read-only (OS, packages, meepagateway binary)        |

* `HOME=/workspace` — so `~/SOUL.md` resolves to the agent's persistent files
* `TMPDIR=/tmp` — temp files go to the writable tmpfs
* `WORKDIR=/workspace`

The bind mount maps to the host's `~/.meepagateway/agents/{id}/` directory. Any file the agent writes to `/workspace` persists across container runs.

***

## Container Image

The container image defines what packages are available inside the container. Configure it in the Captain Dashboard under **Settings > Container Mode** or in `config.yaml`:

```yaml theme={null}
image_config:
  enabled: false
  image: ''
  base_image: ghcr.io/bogpad/meepagateway-sandbox:latest
  packages: []
  env_vars: {}
  inject_credentials_env: true
  inject_secrets_env: true
  redact_secrets: true
```

When you click **Build** in the dashboard (or run `meepagateway container build`), MeepaGateway generates a Dockerfile from your package list, builds it, and tags the result as `meepa-sandbox:<content-hash>`. The tag changes automatically when you modify the package list.

***

## Isolation Image

When container mode is enabled, MeepaGateway builds an **isolation image** that layers the `meepagateway` binary on top of the container image. This gives the container both:

* All your configured packages (python3, nodejs, chromium, etc.)
* The `meepagateway agent-run` binary that drives the agent loop

The isolation image is auto-built on first use. If a container image exists, it's used as the base. Otherwise, the default `ghcr.io/bogpad/meepagateway-sandbox:latest` image is used.

The image is tagged as `meepagateway-isolated:{version}-{base}` and cached locally. Changing container packages produces a new isolation image automatically.

***

## Configuration

### Container Mode (per-agent)

Enable container mode in `config.yaml` under each agent:

```yaml theme={null}
agents:
  - id: my-agent
    container_mode:
      enabled: true
      image: ''             # Empty = auto-build from image_config
      memory_limit: 512m
      timeout_seconds: 300
      network: bridge       # "bridge" (allows outbound) or "none" (no network)
```

### File Access (Seatbelt / Landlock mode)

When not using container mode, configure host-level file access restrictions:

```yaml theme={null}
agents:
  - id: my-agent
    file_access:
      unrestricted: true
      allow_read: []
      allow_read_write: []
      deny: []
      allow_home_dotfiles: false
      allow_network: true
```

### Configuration Fields

<ParamField path="container_mode.enabled" type="boolean" default="false">
  Run this agent's entire loop inside a Docker container.
</ParamField>

<ParamField path="container_mode.image" type="string" default="">
  Docker image for the container. Leave empty to auto-build from `image_config` (recommended).
</ParamField>

<ParamField path="container_mode.timeout_seconds" type="integer" default="300">
  Maximum seconds before the container is killed.
</ParamField>

<ParamField path="container_mode.memory_limit" type="string" default="512m">
  Docker memory limit (e.g. `"512m"`, `"1g"`).
</ParamField>

<ParamField path="container_mode.network" type="string" default="bridge">
  Container network mode. `"bridge"` allows outbound connections (needed for LLM API calls from inside the container). `"none"` blocks all network access.
</ParamField>

<ParamField path="file_access.unrestricted" type="boolean" default="false">
  Disable all file access restrictions. Equivalent to the `--unrestricted` CLI flag.
</ParamField>

<ParamField path="file_access.allow_read" type="string[]" default="[]">
  Paths the agent may read (but not write).
</ParamField>

<ParamField path="file_access.allow_read_write" type="string[]" default="[]">
  Paths the agent may read and write.
</ParamField>

<ParamField path="file_access.deny" type="string[]" default="[]">
  Paths always denied, regardless of other rules.
</ParamField>

<ParamField path="file_access.allow_home_dotfiles" type="boolean" default="false">
  Allow read access to dotfiles in the home directory.
</ParamField>

<ParamField path="file_access.allow_network" type="boolean" default="true">
  Allow outbound network access.
</ParamField>

***

## Security

Isolation containers run with hardened defaults:

* **Read-only rootfs** — the image filesystem is immutable at runtime
* **All capabilities dropped** — `--cap-drop=ALL`
* **No privilege escalation** — `--security-opt=no-new-privileges`
* **Memory capped** — containers exceeding `memory_limit` are OOM-killed
* **Ephemeral** — each container is destroyed after the response

The only writable paths are the agent workspace bind mount (`/workspace`) and the `/tmp` tmpfs. Provider API keys are passed via stdin JSON and never written to disk.

***

## Managing Images

### Captain Dashboard

Go to **Settings > Container Mode** to:

* Configure base image and packages
* Preview the generated Dockerfile
* Build, rebuild, or delete container images
* Monitor build progress

### CLI

```bash theme={null}
# Build the container image from configured packages
meepagateway container build

# Check container status
meepagateway container status

# List all container images
meepagateway container images

# Clean all container images
meepagateway container clean
```

### Docker

```bash theme={null}
# List isolation + container images
docker images | grep meepa

# Remove a stale isolation image (forces rebuild on next message)
docker rmi meepagateway-isolated:0.0.1-meepa-sandbox-abc123

# Remove all container images
docker rmi $(docker images -q meepa-sandbox)
```

***

## Troubleshooting

### "exec format error"

The isolation image contains a binary built for the wrong architecture. Delete the image and let it rebuild:

```bash theme={null}
docker rmi meepagateway-isolated:*
```

### Packages not available in container

If the agent can't find python3, nodejs, etc., the isolation image was built without the container base. Ensure the container image is built first, then delete the isolation image so it rebuilds:

```bash theme={null}
meepagateway container build
docker rmi meepagateway-isolated:*
```

### SQLite disk I/O errors

Usually caused by a stale isolation image without proper `HOME`/`TMPDIR` settings. Delete and rebuild:

```bash theme={null}
docker rmi meepagateway-isolated:*
```
