> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirage.strukto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Apple Container

> Run captured programs in containers under Apple's container tool on macOS.

The `apple_container` machine provider reaches a running container under
Apple's `container` tool, and the `machine` runtime runs captured programs in
it. You start the containers; Mirage only runs programs in them. Needs Apple silicon and macOS 26 or later. Python SDK and Node.

## Installation

Install and start Apple's `container`. From the Mirage repository root,
build [the machine image](https://docs.mirage.strukto.ai/home/setup/machine/overview.md#the-machine-image), which
carries Mirage and `fuse3`, and start a container from it:

```bash theme={null}
brew install container
container system start
container build -f docker/sandbox/Dockerfile --target fuse \
  -t mirage-python-fuse .
container run -d --cap-add SYS_ADMIN \
  --name my-sandbox mirage-python-fuse sleep infinity
```

## Usage

`captures` defaults to [`@external`](https://docs.mirage.strukto.ai/home/setup/runtime/overview#captures):
every program Mirage does not have itself. Name `@python` to run
Python in the container too. For one container per agent, make each
container a machine of its own and send each session to its machine with a
[route](https://docs.mirage.strukto.ai/home/policy/route-runtime.md#admit-then-route).

> [tab] Python
>
> ```python theme={null}
> from mirage import RAMVFS, MountMode, Workspace
> from mirage.runtime.machine import MachineRuntime
>
> ws = Workspace(
>     {"/data": RAMVFS()},
>     mode=MountMode.EXEC,
>     machines={"box": {"provider": "apple_container", "container": "my-sandbox"}},
>     runtimes=[MachineRuntime(["@python", "@external"], machine="box")],
> )
> await ws.shell("python3 -c 'print(1 + 1)'")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { MountMode, MachineRuntime, RAMVFS, Workspace } from '@struktoai/mirage-node'
>
> const ws = new Workspace(
>   { '/data': new RAMVFS() },
>   {
>     mode: MountMode.EXEC,
>     machines: { box: { provider: 'apple_container', container: 'my-sandbox' } },
>     runtimes: [new MachineRuntime({ captures: ['@python', '@external'], machine: 'box' })],
>   },
> )
> await ws.shell("python3 -c 'print(1 + 1)'")
> ```

> [tab] YAML
>
> ```yaml theme={null}
> mode: exec
> machines:
>   box:
>     provider: apple_container
>     container: my-sandbox
> runtimes:
>   - name: machine
>     captures: ["@python", "@external"]
>     machine: box
> mounts:
>   /data:
>     vfs: ram
> ```

## Connecting the workspace

A program in the container opens workspace files at the same paths as in
Mirage, so the container must see the mounts there. `mount_type` picks how, as
for [Docker](https://docs.mirage.strukto.ai/home/setup/machine/docker.md#connecting-the-workspace):
`ssh` serves every mount over `container exec` (the image needs `sshfs`),
`fuse` leaves them to a Mirage inside, and `both` mixes the two.

The default image lacks `sshfs`; add it with `apt-get install sshfs`.

For `fuse`, write the same mounts at the same paths into `sandbox.yaml`, and
start Mirage with it in the container:

```yaml theme={null}
mounts:
  /data:
    vfs: s3
    config:
      bucket: my-bucket
    backend: fuse
    mountpoint: /data
```

```bash theme={null}
container cp sandbox.yaml my-sandbox:/tmp/sandbox.yaml
container exec my-sandbox mirage workspace create /tmp/sandbox.yaml
```

* This workspace's mount modes and permissions do not reach those files;
  `sandbox.yaml` decides.
* Keep the container's paths in step with the host's mounts yourself.

## Runtime behavior

| Capability | In the container |
| - | - |
| Command | The captured program and its words, as typed |
| Piped input | stdin |
| Environment | The session's, over the config `env` |
| Working directory | The shell's, unchanged |
| Workspace files | By `mount_type`; see [above](#connecting-the-workspace) |
| Disk and network | The container's own |

## Notes

* Set `container` to the name of a running container.
* The image needs a POSIX-compatible `/bin/sh`. The session's environment wins
  over the image's. The working directory must already exist in the container.
* Host directories reach the container only through `--volume`. A service on
  your Mac is not at `127.0.0.1` inside; Apple's
  [host integration guide](https://github.com/apple/container/blob/main/docs/host-integration.md)
  sets up `host.container.internal`.
* Each container is its own VM, so agents with one each share no files,
  processes, CPU or memory. Give each one that agent's rules: the `default`
  profile in its `sandbox.yaml`.
* A [timeout](https://docs.mirage.strukto.ai/home/setup/machine/overview.md#timeouts) does not kill the program: `container` 1.4.1 does
  not deliver signals into the guest
  ([apple/container#1941](https://github.com/apple/container/issues/1941)).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.