# Configuration

marsh keeps configuration and runtime state per `MARSH_HOME`. Use this page
to change job limits, choose a shell, or find the files used by your
[installation](https://runmar.sh/docs/install.md).

For a separate guest home and set of VMs, launch with
`MARSH_HOME=~/.marsh-work marsh`. To raise the per-job memory limit to 16 GiB,
finish active work, exit open shells, run `marsh stop`, then launch with
`MARSH_JOB_MEMORY_BYTES=17179869184 marsh`.

`marsh status` shows the limits in effect and whether each came from a built-in
default or the daemon's launch environment.

## Scopes

A *scope* is one canonical `MARSH_HOME` path. It has its own daemon, VMs,
guest home, and job records. The default scope is `~/.marsh`.
Persistent-home shells in that scope share the shell VM; an
[ephemeral-home shell](https://runmar.sh/docs/shell.md#ephemeral-home) gets a separate VM and home.

Use separate scopes when work must not share a VM or guest home, for example
a client project and your personal agent logins. Scopes do not separate your
project files: a job in any scope can write the project it runs in.

For skills, dotfiles, MCP connections, and extra directories, see
[Set up your marsh home](https://runmar.sh/docs/guides/set-up-your-home.md).

## Environment

| Variable | Meaning |
|---|---|
| `MARSH_HOME` | Absolute scope root. Default `~/.marsh`. The guest home is `$MARSH_HOME/home`. |
| `MARSH_SHELL` | Shell to run: `marsh` (Brush, the default), `bash`, or `zsh`. See [Choosing your shell](https://runmar.sh/docs/shells.md). |
| `MARSH_SBX` | Path to `sbx`. Default: `sbx` on `PATH`. |
| `MARSH_CONTROL_HOME` | Host-only state root. See [Control home](#control-home). |
| `MARSH_SPAWN` | Names that jobs started from this shell may start. A comma-separated list, or `none`. See [spawn restrictions and refusal statuses](https://runmar.sh/docs/agents.md#limits-and-messages). |
| `MARSH_PLACE` | Where registered commands run. Only `local` is accepted; any other value is an error (`MARSH_PLACE must be local`). |
| `MARSH_KIT_IMAGE_CACHE` | Cache of Kit images built from source. Default `~/Library/Caches/marsh/kit-images`. |

### Job limits

The daemon reads these once, when it starts for a scope. They apply to every
job in the scope. Values are positive integers.

| Variable | Default |
|---|---|
| `MARSH_JOB_CPU_MILLIS` | 4000 (4 CPUs) |
| `MARSH_JOB_MEMORY_BYTES` | 8589934592 (8 GiB) |
| `MARSH_JOB_PIDS` | 4096 |
| `MARSH_JOB_WRITABLE_BYTES` | 10737418240 (10 GiB). Counts the container's own files, not mounted directories. |
| `MARSH_JOB_OUTPUT_BYTES` | 268435456 (256 MiB). stdout and stderr together. |
| `MARSH_JOB_WALL_SECONDS` | 86400 (24 hours) |
| `MARSH_TREE_KIT_VMS` | 4. Distinct Kit VMs used by live jobs in one tree, 1 to 64. Aliases of the same Kit share a VM. |

To see the values in effect, run `marsh status` (or `marsh status --json` and
read `job_defaults`).

To change them:

1. Let running work finish and exit open shells.
2. Run `marsh stop`.
3. Start marsh with the new values, as in the example at the top.

A shell started with a value that conflicts with the running daemon is
refused. `marsh status` shows the daemon's values.

#### Writable limit

marsh periodically measures the container's writable layer and kills a job
that exceeds the limit (`limit:writable`). A fast writer can overshoot between
checks. Files in mounted directories do not count toward this limit.

#### Fixed limits

These cannot be changed. A call that exceeds one is refused with exit 125
([messages](https://runmar.sh/docs/agents.md#limits-and-messages)).

- 8 jobs at once per daemon
- depth 4
- 4 live children per job
- the same Kit at most twice in a row
- 64 launches per tree, including refused child calls

Children also inherit their parent's wall-time deadline. With less than one
second remaining, a new child is refused. Parallel composition has separate
[fanout](https://runmar.sh/docs/fanout.md#limits) and [split capture](https://runmar.sh/docs/split.md#what-is-captured)
limits.

### Control home

`MARSH_CONTROL_HOME` must be an existing directory with mode 0700. Under it:

- scope control directories (`HASH/`);
- MCP and ACP publications: `published-mcp/`, `published-acp/`, and
  `publication-locks/`.

Generated MCP server scopes go beside it, in
`$MARSH_CONTROL_HOME-mcp-scopes/`.

When unset, control directories are in
`~/Library/Application Support/marsh/control`. Publications are in
`~/Library/Application Support/marsh`.

## Files

| Path | Contents |
|---|---|
| `$MARSH_HOME/config.json` | The default shell, set by `marsh config shell NAME`. Default path `~/.marsh/config.json`. |
| `$MARSH_HOME/home/` | The guest home. Agent logins and settings (`.claude`, `.codex`) live here. Every job in the scope can read it. Default path `~/.marsh/home/`. |
| `$MARSH_HOME/sbxenv.yaml` | Shared MCP servers and extra directories for every Kit and the shell. |
| `$MARSH_HOME/sbxenvs/NAME.yaml` | Additional servers and directories for registered command `NAME`; `shell.yaml` also configures the shell VM. |
| `CONTROL/HASH/` | Host-only control directory for a scope. `marsh status` prints it. Never mounted into a VM. |
| `CONTROL/HASH/commands.json` | Your command registry. Overrides packaged entries by name. See [Commands and Kits](https://runmar.sh/docs/kits.md#adding-a-command). |
| `CONTROL/HASH/agents.json` | Your ACP agent list. Replaces the packaged list; `[]` disables ACP. See [ACP](https://runmar.sh/docs/acp.md#adding-agents). |
| `CONTROL/HASH/state/results.journal` | Structural job receipts, for `marsh results`. |
| `PREFIX/libexec/marsh/` | Packaged `commands.json`, `agents.json`, `shell-image`, and the Linux binaries. |
| `~/Library/Application Support/marsh/published-mcp/`, `published-acp/` | Host-only MCP and ACP publication declarations. Under `MARSH_CONTROL_HOME` when it is set. |
| `~/Library/Application Support/marsh-mcp-scopes/` | Generated scope roots for `marsh mcp install sbx` and `marsh-mcp` broker modes. |
| `PROJECT/.marsh/split/ID/` | A split's forks and results. Ignored by Git. |

`CONTROL` is `$MARSH_CONTROL_HOME`, or `~/Library/Application Support/marsh/control`
when unset. `HASH` is the SHA-256 of the canonical `MARSH_HOME` path.

A malformed `commands.json` or `agents.json` stops the daemon from starting.
The error names the file. Fix that file; deleting other state does not help.

## Servers and extra directories

The optional `sbxenv.yaml` and `sbxenvs/NAME.yaml` files use these fields:

| Field | Meaning |
|---|---|
| `schemaVersion` | Required; the string `"1"` |
| `mcp.servers` | List of servers, each with a `name` and either `url` or `command` |
| `mcp.servers[].args` | Literal argument list for a local `command` server |
| `additionalWorkspaces` | List of existing Mac directories, each with `path` and optional `readOnly` |
| `additionalWorkspaces[].readOnly` | Defaults to `false`; use `true` for reference material |

Other fields are rejected. For examples, see the [home guide](https://runmar.sh/docs/guides/set-up-your-home.md).

### Files and merging

- Each YAML file must be a regular file owned by your Mac account, not a
  symlink, and not writable by group or others. The `sbxenvs` directory must
  also be a real directory, not a symlink.
- Shared lists are followed by the command's lists. Entries cannot remove or
  override shared entries. Duplicate server names or overlapping directories
  in the combined lists are errors.
- The same MCP name must have the same definition across Kits. Aliases of one
  Kit must have identical combined configuration because they share a VM.
- Each file is limited to 64 KiB. A combined environment permits at most 32
  MCP servers and 16 extra directories.

### Paths and access

Extra directories appear at their canonical Mac paths in Linux. Relative paths
are resolved from the YAML file's directory; `~` and variables are not expanded.
Directories cannot overlap the project, the guest home's backing directory,
another extra directory, or marsh's install and host-control files. They cannot
replace the guest home itself.

Root jobs receive the shared directories plus their command's directories.
Child jobs inherit the parent's mounts, without adding directories from the
child Kit's file. Split Kit jobs omit extra directories.
`marsh jobs show JOB --json` includes the mount targets and access modes.

A local MCP `command` is an absolute path or an executable name on the Mac's
`PATH`; its arguments are literal strings. Its resolved executable and the
environment files cannot be guest-mounted. The YAML ownership and symlink rules
above apply to the configuration files, not the executable.

MCP servers are loaded per VM. The shell VM can load servers too, but you need
an MCP client to call them. See [MCP access](https://runmar.sh/docs/security.md#mcp-connections).

### Applying changes

Files are read when the daemon starts. After editing them or adding a file for
a newly registered command, finish jobs, exit shells, run `marsh stop` on the
Mac, and launch marsh again. A worker reset reloads the daemon's existing
configuration; it does not reread the YAML. Ephemeral-home sessions skip these
files' servers and directories.

A declared server must register and load successfully before its VM can be
used. [Setup errors](https://runmar.sh/docs/troubleshooting.md#environment-files-and-mcp-connections)
explain authentication and name conflicts.

## Shell prompt and startup files

The shell reads the usual startup files from the guest home. Set `PS1` there
to change the prompt. See [The shell](https://runmar.sh/docs/shell.md#startup-files).

## Results

`marsh results` lists job receipts with the registered command name, where it
ran, exit status, cleanup verification, and timing. The journal retains up to
1,000 terminal receipts, including at most 16 arguments of 200 characters
each. Prompts passed as arguments can be stored; stdin, stdout, and stderr
are not recorded. marsh does not resume work after a daemon restart.

Next: [Commands and Kits](https://runmar.sh/docs/kits.md), [Troubleshooting](https://runmar.sh/docs/troubleshooting.md).
