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

# Mounts

> Git mounts: repository checkouts inside the sandbox, ref templating, GitHub App capability scoping, and how mounts gate GitHub MCP tools and git credentials.

Mounts give a session working checkouts of git repositories inside its sandbox. A mount declares which repository to fetch, where to place it, which ref to check out, and — for GitHub repositories — a capability-scoped GitHub App grant that becomes the session's entire GitHub permission surface: both the brokered [GitHub MCP tools](/runtime/github-mcp) and plain `git push` inherit exactly the capabilities the mount declares.

## Field reference

`mounts` is an array on the agent (default `[]`). `kind: git` is the only mount kind.

<ParamField path="mounts[].kind" type="&#x22;git&#x22;" required>
  The mount kind. Only `git` exists.
</ParamField>

<ParamField path="mounts[].repository" type="string" required>
  The repository to fetch. A bare `owner/repo` resolves to `https://github.com/owner/repo.git`; a full URL (`https://…`) or `git@…` address passes through unchanged. GitHub App auth requires the `owner/repo` (GitHub) form.
</ParamField>

<ParamField path="mounts[].mountPath" type="string" required>
  Absolute path inside the sandbox where the checkout lands, for example `/workspace/widgets`. Must start with `/`. When the agent sets no `workingDirectory`, the first mount's `mountPath` becomes the session's working directory.
</ParamField>

<ParamField path="mounts[].ref" type="string">
  The ref to fetch and check out — a branch name (`main`), a fully qualified ref (`refs/pull/128/head`), or a template (see [Ref templates](#ref-templates)).

  When `ref` is omitted, the staging fetch runs with no refspec, so the checkout resolves to whatever `FETCH_HEAD` happens to point at. Always set `ref` explicitly; every shipped template and example does.
</ParamField>

<ParamField path="mounts[].depth" type="integer" default="1">
  Fetch depth, a positive integer. Mounts are shallow by default — the checkout contains only the fetched ref at the requested depth, with no tags. Agents that need more history fetch it explicitly inside the sandbox.
</ParamField>

<ParamField path="mounts[].auth" type="object" default="{ kind: &#x22;none&#x22; }">
  Authentication for fetching and pushing. Two kinds:

  * `kind: none` — unauthenticated. Works for public repositories; the session gets no GitHub credentials for this mount.
  * `kind: githubApp` — authenticated through the project's GitHub App connection. This is what unlocks pushes, the git credential helper, and GitHub MCP tools. Fields below.
</ParamField>

<ParamField path="mounts[].auth.installationId" type="string">
  Pins the GitHub App installation to mint tokens from. Rarely needed: when absent, the platform resolves the installation from the spawning event's payload (GitHub-triggered sessions carry one), and otherwise from the project's connection that covers the repository.
</ParamField>

<ParamField path="mounts[].auth.commitAuthor" type="object">
  Sets the git identity for commits made in this mount: `{ name: <string>, email: <string containing @> }`. Staging writes it as the checkout's local `user.name`/`user.email`.

  When the session has an attributable human requester, the platform splits the identity — the requester becomes the git *author* and `commitAuthor` the *committer* — and installs a `prepare-commit-msg` hook that stamps a `Co-Authored-By:` trailer for the agent, so commit history records both who asked and which agent wrote the change.
</ParamField>

<ParamField path="mounts[].auth.capabilities" type="object">
  The permission grant for this repository. Each capability is `none`, `read`, or `write` (merge is `none` or `write` only):

  | Capability     | Default | Grants                                                                                                                                                                                                                                   |
  | -------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `contents`     | `write` | Repository contents — git fetch/push, file reads and writes                                                                                                                                                                              |
  | `pullRequests` | `write` | Pull request create/read/update/review                                                                                                                                                                                                   |
  | `issues`       | `write` | Issues and issue comments                                                                                                                                                                                                                |
  | `checks`       | `read`  | Check runs                                                                                                                                                                                                                               |
  | `actions`      | `read`  | GitHub Actions runs and job logs. `write` may authorize explicitly selected Actions mutations such as `rerun_failed_jobs` and `rerun_failed_jobs_for_pr_head`; agents should invoke production reruns only with explicit human authority |
  | `workflows`    | `none`  | Pushing changes under `.github/workflows/`                                                                                                                                                                                               |
  | `secrets`      | `none`  | GitHub Actions secrets API. Values are write-only on GitHub's side: `read` lists secret names and timestamps, never values                                                                                                               |
  | `merge`        | `none`  | Merging pull requests and enabling auto-merge on this repository                                                                                                                                                                         |

  One cross-field rule is enforced at apply time: `merge: write` requires `contents: write` **and** `pullRequests: write`, because GitHub has no standalone merge permission — merging is authorized through those two.

  Capabilities map onto the GitHub App installation-token permission body when the platform mints tokens for the mount, always including `metadata: read` so basic repository reads work at every level. `merge` maps to no token permission of its own; it is purely a proxy-layer gate on which tools the session may call.
</ParamField>

## Checkout behavior

Mounts are staged when the sandbox is created, before any setup commands run and before the agent's first turn. For each mount the platform initializes the repository at `mountPath` (or reuses an existing one), points `origin` at the resolved URL, then runs a shallow fetch of the ref (`--depth <depth> --no-tags`), checks out `FETCH_HEAD`, hard-resets, and removes untracked and ignored files.

Staging re-runs on every sandbox creation — including sandboxes restored from a cached environment snapshot — so the checkout always reflects the ref at session start even when the sandbox image is days old.

For `githubApp` mounts, a first-turn auth preflight verifies the full in-sandbox credential path end to end before the agent runs; if authenticated git transport cannot be established, the session fails with a platform-owned error rather than surfacing GitHub's misleading "Repository not found" to the model.

### Ref templates

`ref` may interpolate `{{ path.to.value }}` tokens against the session's persisted input. Trigger-spawned GitHub sessions store `{ triggerEventId, payload }`, so PR-scoped agents check out the PR head like this:

```yaml theme={null}
mounts:
  - kind: git
    repository: acme/widgets
    mountPath: /workspace/widgets
    ref: refs/pull/{{payload.github.pullRequest.number}}/head
```

This is the one templating surface in the agent file where the `payload.` prefix is correct — trigger `message`, `initialPrompt`, and `displayTitle` render against the payload directly and reject the prefix (see [Variables and templating](/reference/variables-and-templating)).

A templated ref makes the agent event-dependent: manual starts and agent-to-agent spawns carry no event payload, so a session whose mount ref cannot be resolved from its input is **refused at spawn time**, with an error explaining that the agent must be started by a matching event trigger. Each token must also resolve to a scalar (string, number, or boolean).

## Working directory and multiple mounts

An agent may declare several mounts; each fetches independently to its own `mountPath`. The session's working directory resolves as:

* No `workingDirectory` declared → the **first mount's** `mountPath`.
* Relative `workingDirectory` → resolved against the first mount's `mountPath`; escaping it fails. Declaring a relative working directory with no mounts also fails.
* Absolute `workingDirectory` → used as-is.

During import merge, mounts are a named array keyed by `name` (an authoring-only key, stripped from the stored spec) and otherwise by `mountPath`: an item with a matching key deep-merges into the imported item, new keys append. Mounts are deliberately **not removable** with the `remove:` directive (which supports only `tools`, `triggers`, and `env`) — to change a mount an import gave you, override it by key, or drop the import. See [Imports and fragments](/reference/imports-and-fragments).

## Repository authorization

A `githubApp` mount only works for repositories covered by one of the project's active GitHub connections. Authorization is checked at spawn time, before a session is created: if no active connection covers the repository — it was deleted, renamed, or removed from the GitHub App installation — the spawn is refused, and trigger-driven spawns are suppressed until the connection is restored. If GitHub later refuses access definitively at token-mint time, the platform prunes the repository from the stored grant so future spawns fail fast instead of launching doomed sessions. See [Connections and identities](/concepts/connections-and-identities).

## How mounts feed GitHub access

Mounts are the single policy point for everything GitHub in a session. Two consumers derive their credentials from the mount's capabilities:

**GitHub MCP tools.** Any `githubApp` mount provisions the session's brokered `github` MCP server; a `kind: github` entry in the agent's `tools` only narrows the tool allowlist on top of that (and is a no-op without a mount). The proxy mints a fresh, capability-scoped installation token per tool call — the token never enters the sandbox. Capability-gated tool families are enforced at two levels: session-level visibility (a tool appears only if *some* mount grants the capability) and a per-call re-check against the *target repository's own mount*:

* `merge_pull_request` and `enable_pull_request_auto_merge` require `merge: write`.
* `actions_secret_list` requires `secrets: read` or better; `actions_secret_write` requires `secrets: write`.

These stay invisible and uncallable without the grant even when the agent names them explicitly. Repositories the project may use that are *not* mounted get default capabilities through the proxy, with merge and secrets always denied. See [GitHub MCP](/runtime/github-mcp) for the tool catalog.

**Plain git.** Staging configures each `githubApp` mount with the agent-bridge credential helper. Every git network operation fetches a short-lived token from the platform's credential broker, scoped to the git-wire subset of the mount's capabilities: `contents`, plus `workflows` when granted, plus `metadata: read` — nothing else rides on the git credential. A mount with `contents: none` gets no git credential at all. Nothing is persisted in the sandbox; git holds the credential only in process memory.

## Examples

A read-only reviewer that checks out the PR head and can comment but never push:

```yaml .auto/agents/pr-review.yaml theme={null}
name: pr-review
mounts:
  - kind: git
    repository: acme/widgets
    mountPath: /workspace/widgets
    ref: refs/pull/{{payload.github.pullRequest.number}}/head
    depth: 1
    auth:
      kind: githubApp
      capabilities:
        contents: read
        pullRequests: write
        issues: write
        checks: read
        actions: read
workingDirectory: /workspace/widgets
```

A coding agent that works on `main`, pushes branches, opens PRs, and attributes its commits:

```yaml .auto/agents/staff-engineer.yaml theme={null}
name: staff-engineer
mounts:
  - kind: git
    repository: acme/widgets
    mountPath: /workspace/widgets
    ref: main
    auth:
      kind: githubApp
      commitAuthor:
        name: Staff Engineer (auto)
        email: staff-engineer@agents.acme.dev
      # capabilities omitted: defaults grant contents/pullRequests/issues
      # write, checks/actions read — and never merge or secrets.
```

<Warning>
  The capability defaults are write-leaning (`contents`, `pullRequests`, and `issues` all default to `write`). Downscope explicitly for agents that should never push: a reviewer gets `contents: read`, and `merge: write` should remain a deliberate opt-in — merging to a production branch with no human in the loop is the highest-privilege action a mount can grant.
</Warning>
