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

# Capture commands

> init, doctor, daemon and hooks: installing the capture hooks and keeping them healthy.

## outerlayer init

Connect a repository to a factory, install the capture hooks, set up Claude Code with skills and an MCP server, and run `doctor`.

```bash theme={"system"}
outerlayer init [--local] [--factory <org>/<factory>] [--user | --project] [--org] [--remove]
                [--gitignore] [--template default] [--no-wrap-hooks] [--no-statusline] [--shell-function | --no-shell-function] [--json]
outerlayer init --update [--dry-run] [--no-skills-link] [--control-plane] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--local` | off | Install the hooks and the maintained skills only: no connect step, no agent setup, no network call. It changes the shell function only when `--shell-function` or `--no-shell-function` names it. |
| `--factory <org>/<factory>` | none | Passed to [`connect`](/reference/cli-cloud#outerlayer-connect): the factory to connect this repository to. |
| `--user` | on | Write the hooks to `~/.claude/settings.json`. |
| `--project` | off | Write the hooks to `./.claude/settings.json` instead. They run `outerlayer` from the PATH, so each teammate needs the CLI installed. |
| `--org` | off | Print a `managed-settings.json` snippet for MDM or GPO rollout. Its hooks run `outerlayer` from the PATH, so each machine needs the CLI installed. Writes nothing. |
| `--remove` | off | Remove the hooks, and restore wrapped hooks and the original status line. |
| `--gitignore` | off | Also add `.outerlayer/` to `.gitignore`. Project scope only. |
| `--template default` | none | Install the starter skills into `.agents/skills/`, and a policy file and the starter checks into `.outerlayer/`. `default` is the only template. |
| `--update` | off | Update the files `init` wrote that you have not edited, and write missing ones. See [Update the files init wrote](#update-the-files-init-wrote). |
| `--dry-run` | off | With `--update`: print the plan and write nothing. |
| `--no-skills-link` | link | Do not link `.claude/skills` to `../.agents/skills`. With `init`, and with `init --update`. |
| `--control-plane` | off | With `--update`: this repository is a control plane. The update prints what older CLIs and runners do, and makes no link. |
| `--no-wrap-hooks` | wrap | Leave existing `PreToolUse` and `PostToolUse` hooks unwrapped. |
| `--statusline` / `--no-statusline` | `--statusline` | Install the status-line segment, or leave the `statusLine` slot untouched. |
| `--shell-function` / `--no-shell-function` | ask on a terminal | Add the `claude` shell function without asking, or leave it out and remove it if it is there. See [Make plain claude start the launcher](#make-plain-claude-start-the-launcher). |
| `--json` | off | Print one document with the outcome of the `connect`, `hooks`, `shell`, `agent-setup` and `doctor` steps: `done`, `skipped` or `failed`, with a reason. `agent-setup` lists every path it wrote, and under `kept`, each hand-written file it did not replace. It never holds a credential. With `--local` or `--remove`, it prints the hooks result alone. `--local` with a shell function flag adds `shellFunction`, with that step's outcome and reason. |

A full `init` installs the starter skills already, so `--template` matters with `--local`, where it is the only way to get them. It cannot be combined with `--remove` or `--org`.

Before its steps, `init` installs the five maintained skills once per machine. It writes them to `~/.agents/skills/` and puts a link to each in `~/.claude/skills/`. Where a link cannot be made, it writes a copy. Every later `outerlayer` command keeps them at its own version, and `outerlayer claude` installs them too. A repository in the `AGENTS.md` layout carries none of them. If a write fails, `init` prints a warning and goes on, and `outerlayer doctor` reports the gap.

`init` never signs in. On a terminal it asks one question, about the shell function, and writes nothing to your shell until you say yes. It runs these steps in order:

1. **Connect.** Runs [`outerlayer connect`](/reference/cli-cloud#outerlayer-connect). With no saved credential, or no git remote, this step is skipped. `init` then says to run `outerlayer login` and `outerlayer init` again.
2. **Hooks.** Installs the capture hooks into Claude Code, the only agent with a session-start hook.
3. **Shell function.** Asks whether plain `claude` should start through `outerlayer claude`. See [Make plain claude start the launcher](#make-plain-claude-start-the-launcher).
4. **Agent setup.** Installs the four starter skills into `.agents/skills/`. It writes no maintained skill into the repository. Writes `AGENTS.md` when the repository has no `AGENTS.md` or `CLAUDE.md`. Writes `.outerlayer/policy.yaml` and two checks, `.outerlayer/validators/code-review.yaml` and `.outerlayer/validators/acceptance-criteria.yaml`, each only when it is missing. See [Policy and validators](/policy-and-validators#the-code-review-check). Writes `.outerlayer/config.json` with `"layout": 2` when there is none. Links `.claude/skills` to `../.agents/skills`, so Claude Code finds the skills. Adds the `outerlayer` and `outerlayer-docs` MCP servers to `.mcp.json`. It writes no generated file and runs no `context emit`. Files are left uncommitted. Skipped outside a git repository and when the repository's context comes from a control plane. See [Agent context](/agent-context).

   A repository that has not moved keeps the earlier behavior: the skills go to `.outerlayer/skills/`, and `context emit` compiles them into `.claude/skills/`. Run [`init --update`](#update-the-files-init-wrote) to move it. On Windows, git commits the `.claude/skills` link only when `core.symlinks` is true. Without it, git writes a text file and Claude Code finds no skills. `outerlayer claude` does not need the link.
5. **Check.** Runs `doctor` and prints its summary.

Where the agent setup step puts the MCP server entries. The `outerlayer` entry runs `npx -y @outerlayer/cli mcp serve`, so it starts on any machine with Node, including a teammate's who has not installed the CLI. The `outerlayer-docs` entry is the OuterLayer docs MCP server, `{ "type": "http", "url": "https://docs.outerlayer.ai/mcp" }`, which lets your agent look up how OuterLayer works. It carries no credential.

* With no `.outerlayer/mcp.json`, both entries go into `.mcp.json`, beside any servers already there.
* In a repository that has not moved, and has an `.outerlayer/mcp.json`, both entries go into that file instead, because `outerlayer context emit` copies it over `.mcp.json`.
* If `.mcp.json` then holds servers that `.outerlayer/mcp.json` lacks, `.mcp.json` is kept and has neither entry. `init` says so. Add the entries to `.mcp.json` by hand, or move your servers into `.outerlayer/mcp.json` and run `outerlayer context emit`.
* An entry of either name already in the file is left as it is, whatever its value. `init` lists the file as kept.

What the hooks step writes:

* `SessionStart`, `SessionEnd` and `Stop` hooks. The settings file is backed up first.
* Wrappers around existing `PreToolUse` and `PostToolUse` hooks, so a hang or a kill leaves evidence. Undo with `outerlayer hooks unwrap`.
* A status-line segment. An existing `statusLine` command is wrapped, not replaced: its output prints first.

It writes nothing when the CLI binary the hooks would point at does not exist. It never starts a background process.

When every step passed and the repository is linked, `init` prints one last line. That line is "Add an issue on the Work page and choose Build locally, or Build if a host runs for this factory." If agent setup wrote files, or finds a change that is not committed, `init` also prints a line to commit and push them. The line names `.outerlayer/`, `AGENTS.md`, `.agents/`, `.mcp.json` and `.claude/skills`, and `init` looks for changes in each. A repository that still keeps its skills in `.outerlayer/skills/` gets the line for `.outerlayer/` only. So a second run prints the line again until you commit those files. This line comes before the "Add an issue" line. It prints that line even when the connect or doctor step failed, or the repository is not linked yet. The factory reads the policy from each pull request's base branch. No check runs on a pull request until the policy is on that branch. If the repository still has to be linked, `init` prints the link to follow, then the commit line, and the "Add an issue" line waits.

```bash theme={"system"}
outerlayer init
outerlayer init --local --template default
```

### Make plain claude start the launcher

A governed repository gets its control plane through [`outerlayer claude`](/reference/cli-context#outerlayer-claude). `init` can make plain `claude` start it. On a terminal, `init` asks. On yes, it adds a block marked `# BEGIN outerlayer claude` to `~/.zshrc` or `~/.bashrc`, chosen from `$SHELL`. The block defines one function:

```bash theme={"system"}
claude() { outerlayer claude "$@"; }
```

The launcher finds the real `claude` on your `PATH`. Another shell prints the line for you to add. With no terminal and no flag, `init` writes nothing. `init --no-shell-function` and `init --remove` take the block out and leave the rest of the file as it was.

`init --local --shell-function` adds the block without asking, and `init --local --no-shell-function` removes it. `init --local` with neither flag leaves the startup file alone.

### Update the files init wrote

`init` writes some files once and never overwrites them: the template skills, `.outerlayer/policy.yaml`, the two checks in `.outerlayer/validators/` (`code-review.yaml` and `acceptance-criteria.yaml`), and the `outerlayer` and `outerlayer-docs` MCP entries. A later CLI may write a better version of one. `outerlayer init --update` brings them up to date without losing your edits.

```bash theme={"system"}
outerlayer init --update --dry-run
outerlayer init --update
```

Each file is in one of four states:

* **Current.** It matches what this CLI writes. Nothing to do.
* **Outdated.** It matches what an earlier release wrote, so nobody edited it. `--update` replaces it.
* **Edited.** It matches no release. `--update` never touches it. The plan prints a `git diff --no-index` command that compares it with this CLI's version.
* **Missing.** `--update` writes it.

The CLI tells outdated from edited with a list of every version each file has had in a release. The list goes back to 0.4.0, the first release with the starter skills. The list ships inside the package, so `--update` needs no network.

An MCP entry counts as outdated when it is exactly an entry an earlier `init` wrote, such as `outerlayer mcp serve` run as a global command. The other servers and keys in the file are kept.

`--update` also brings the repository's layout and skills up to date:

* **A repository in the earlier layout** moves to the `AGENTS.md` layout first. The move never deletes a file you edited. With `--no-skills-link` or `--control-plane`, it makes no `.claude/skills` link. See [Move a repository to this layout](/agent-context#move-a-repository-to-this-layout).
* **The maintained skills.** `--update` writes none into the repository, because the CLI keeps the maintained skills in `~/.agents/skills/` on each machine. It removes each maintained skill folder that carries the "maintained by OuterLayer" line, from `.agents/skills/` and from a `.claude/skills/` that is a folder. Each path to a removed folder changes to `~/.agents/skills/<skill>/`. Validator and policy files are not changed. See [The maintained skills move to each machine](/upgrade#the-maintained-skills-move-to-each-machine) for the order of steps across machines and runner hosts.

It then updates the starter files. In the `AGENTS.md` layout, nothing is compiled, and `--update` runs no `context emit`. In a repository that has not moved because a source clash was left, it runs `outerlayer context emit` as before and keeps a hand-written output, as `init` does. It leaves everything uncommitted. `--dry-run` writes nothing and does not run the emit. Like the agent setup step, `--update` does nothing outside a git repository or when the repository's context comes from a control plane. It cannot be combined with `--remove`, `--org`, `--local`, `--template` or `--factory`. `--json` prints each file's state and action. The exit code is 1 when a write or the emit failed.

Exit codes: 0 when every step was done or skipped, including a second run that changes nothing. A repository that is not linked yet is not a failure: `init` marks the connect line with `!` instead of `✓`, prints the link, and exits 0. `connect` on its own exits 2 for the same repository. 1 when a step failed, a `doctor` check failed, or a flag was refused. A failed step does not stop later ones, but `doctor` is skipped when the hooks could not be installed.

See also: [Quickstart](/quickstart), [Agent context](/agent-context).

## outerlayer doctor

Check the installation. Every failing check names its fix.

```bash theme={"system"}
outerlayer doctor [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--json` | off | Print the checks and a summary as one JSON document. |

Each line passes, warns, fails or is skipped. A line that does not apply is
left out. Doctor prints the lines in the order of the tables below: the
laptop checks, the gateway checks, then the runner host's checks.

Doctor reads only `~/.outerlayer/config.json` and has no `--config` option.
A runner host is a machine whose `~/.outerlayer/config.json` has a `runner`
block, as [`outerlayer runner init`](/reference/cli-runner#outerlayer-runner-init)
writes it. Its checks change. See [On a runner host](#on-a-runner-host).

```bash theme={"system"}
outerlayer doctor
```

Exit codes: 0 when no check fails (warnings allowed), 1 when any check fails.

### On a laptop

| Check | Appears when | Fails or warns when | Fix |
| - | - | - | - |
| Claude Code home | Always | Fails when `~/.claude` is missing. | Install Claude Code, then run a session. |
| Transcripts | Always | Warns when `~/.claude/projects` holds no transcript. | Run a Claude Code session. |
| Hooks installed | Always | Fails when the hooks are missing, or the program they run is gone or not executable. Warns when only some events have hooks. | `outerlayer init` |
| Hooks firing | A transcript exists | Warns when no hook has fired. Fails when the newest transcript is more than 24 hours newer than the last hook. | `outerlayer init`, then check `claude --debug`. |
| Wrapped hooks | A wrapped hook that `init` recorded is gone from the settings | Warns. | `outerlayer hooks wrap` or `outerlayer hooks unwrap` |
| Git hooks directory | The repository sets `core.hooksPath` | Fails when that directory is missing, empty or unreadable. | Run the repository's `prepare` script, such as `yarn prepare`. |
| Spool writable | Always | Warns when the spool does not exist yet. Fails when it is not writable. | Fix the permissions on `~/.outerlayer/spool`. |
| Cloud sync | Always | Warns with no config file, before the first sync, or after 6 hours with no contact. Fails when the last sync failed. | `outerlayer login`, then `outerlayer sync`. |
| Daemon running | Always | Warns when the daemon is not running, or its heartbeat is over 30 minutes old. | Start or restart `outerlayer daemon`. |
| Retention (cleanupPeriodDays) | Always | Fails at 0 days. Warns under 14 days, or when `~/.claude/settings.json` cannot be read. | Set 14 days or more, and run the daemon. |
| Disk headroom | Always | Fails when the home directory is not writable. | Free space, or fix the permissions. |
| Claude Code version | Always | Warns when `claude --version` fails or prints no version. Also warns on a version older than this CLI supports. | Put `claude` on `PATH`, or upgrade Claude Code. |
| Settings JSON valid | Always | Fails when a Claude Code settings file does not parse. | Fix the JSON. The line names the error. |
| Status line | Always | Warns when the segment is not installed or went missing, or another command holds the slot. | `outerlayer init` |
| Status-line state | The segment is installed | Warns when the state file is missing or stale. | Start or restart `outerlayer daemon`. |
| Claude Code installs | More than one `claude` is on `PATH` | Warns. | Keep one install. |
| Starter files | `init` wrote a file in this repository | Warns when a file is outdated or missing, or an unedited file is no longer shipped. Also warns when the CLI's list of earlier versions is unreadable. | `outerlayer init --update` |
| Renamed skills | In a repository | Warns when a maintained skill is still under an earlier name. | `outerlayer context emit`, then commit. |
| Skill names | In a repository | Warns when a file names a maintained skill by an earlier name. | Replace the names. `outerlayer init --update` does it in files you did not edit. |
| Build skill | In a repository | Warns when `build` does not name `outerlayer-build`, or `outerlayer-build` is missing beside it. Also warns on a heading under "Changes for this repository" that is not a phase. | The line names the fix. |
| Maintained skills on this machine | A repository's `build` reads `~/.agents/skills/outerlayer-build/SKILL.md` | Warns when `~/.agents/skills/` or `~/.claude/skills/` lacks this CLI version's maintained skills. | `outerlayer init --local` |
| Committed maintained skills | A repository in the `AGENTS.md` layout commits a maintained skill folder | Warns. | `outerlayer init --update`, then commit, once every machine and runner host runs this version. |
| Paths to the maintained skills | A repository in the `AGENTS.md` layout | Warns when a file names a maintained skill folder the repository no longer holds. | `outerlayer init --update` |
| MCP server | `.mcp.json` has an `outerlayer` entry that runs a command | Warns when `.mcp.json` is not valid JSON, or the command is not on `PATH`. The agent then starts without OuterLayer's tools. | Use `npx -y @outerlayer/cli mcp serve`, or install the CLI globally. |
| Control plane | The repository is governed, or a control plane was delivered to it | Warns when nothing was delivered, or the delivered directory is gone. | `outerlayer claude` |
| Control plane degradation | A session could not attach the control plane | Fails on a conflict. Warns otherwise. | The line names the fix. |
| Governs list | `.outerlayer/config.json` has a `governs` list | Warns. The list is ignored. | Delete it once every machine and runner host runs CLI 0.17.0 or later. |
| Control plane hook paths | A hook in `.claude/settings.json` reaches `.claude/` by a path a governed build does not have | Warns. | Name the script as `${OUTERLAYER_CONTROL_PLANE_DIR:-$CLAUDE_PROJECT_DIR}/.claude/…`. |
| Installed CLI | `~/.outerlayer/cli` holds a copy, near a source checkout of OuterLayer | Warns when the copy is older than the checkout's build. Only people who change the CLI see it. | The line gives the command that copies the new build. |

The skill checks are left out in a repository that a control plane governs.
Starter files reports each file and MCP entry `init` writes once as current,
outdated, edited or missing. See [Update the files init
wrote](#update-the-files-init-wrote). Edited files are listed but do not
warn. Without the CLI's list of earlier versions, outdated files look edited.
When the `outerlayer` entry in `.mcp.json` is one an earlier `init`
wrote, Starter files reports it instead of MCP server, so it is reported once.
For the earlier skill names, see [Upgrade
OuterLayer](/upgrade#maintained-skills-start-with-outerlayer-).

On a fresh machine right after `init`, these warn, and that is expected:

| Check | Clears when |
| - | - |
| Transcripts | An agent session has run. |
| Spool writable | The first hook has fired. |
| Cloud sync | `login` has saved credentials, and a sync has run. |
| Daemon running | `outerlayer daemon` is running. |
| Status-line state | `outerlayer daemon` is running. |

### Gateway checks

These lines read the gateway and change nothing. They run on a laptop and on
a runner host.

| Check | Appears when | Fails or warns when | Fix |
| - | - | - | - |
| **Login** or **Factory key** | Always. It reads **Credential**, skipped, with no credential or no gateway address. | Fails when the gateway refuses the credential, or a factory key has no factory id. Warns when the gateway does not answer. | `outerlayer login` for a login. A fresh key, or `OUTERLAYER_APP_ID`, for a factory key. |
| **Factory** | The credential line ran | Fails when the credential cannot read its factory. | `outerlayer connect` for a login. A key that holds the named permission for a factory key. |
| **Repository** | The checkout has a git remote | Warns when this repository is not linked to that factory. | Link it in the factory's setup panel. |
| **GitHub App: owner/name** | One per connected repository | Warns when the installation lacks the repository or a permission builds need. Also warns when the default branch requires no pull request. | See [Check what each repository is missing](/github-app-and-build-tokens#check-what-each-repository-is-missing). |
| **Governance** | The checkout has a remote and a factory | Warns when the gateway names a control plane but has no commit for it, or is too old to report either. | Update the CLI and the gateway to the same release. |
| **Runner hosts for the maintained skills** | Committed maintained skills appears, and the credential holds `hosts.read` | Warns when a runner host that still sends heartbeats runs a CLI older than 0.27.0. | Wait for the hosts to update, or check them with `outerlayer runner status`. |
| **Runner VM** | The Mac's runner runs in a Lima VM | Fails when `limactl` is missing, or the VM is missing or stopped. Warns when its launch agent is missing. | `brew install lima`, or `limactl start <instance>`. |

The credential line is named for the credential a command would send. See
[Credential resolution](/reference/cli#credential-resolution). For a login,
**Factory** reads the factory `outerlayer connect` chose for the repository.
For a factory key, it reads the factory the key is bound to. When another
factory of yours has the repository linked, run `outerlayer connect --factory <org>/<factory>`.

What each credential needs for these checks to pass:

* A login needs a factory chosen for the repository with `outerlayer connect`.
* A factory key needs the factory it is bound to to be readable, and `git.read` to read its linked repositories.
* With no login and no factory key, all three are skipped and doctor makes no network call. On a runner host, **Claude Code pin** still asks the npm registry.
* Outside a git repository, a login with no `OUTERLAYER_APP_ID` skips Factory and Repository.

The GitHub App check runs only when the credential check passes. When the
factory has no connected repository, one **GitHub App** check warns and names
the two steps on the factory's Work page that link one. A gateway with no
GitHub App passes the check. For the default branch, see [Protect the default
branch](/github-app-and-build-tokens#protect-the-default-branch).

**Governance** also runs only when the credential check passes. It asks the
gateway which control plane governs this repository, the answer every session
and build takes. It passes with the control plane, its ref and the commit the
gateway stored, or with "self-governed" when the repository supplies its own
context. See [Share instructions across
repositories](/share-instructions-across-repositories).

### On a runner host

On a runner host, the checks for the Claude Code home, transcripts, hooks,
spool, daemon and status line become one skipped **Laptop checks** line.
Starter files, MCP server, Renamed skills and Skill names are left out. Doctor
then adds the runner's own checks, in this table's order.

On a runner host with `builtin:container` or `builtin:vm` hooks, `outerlayer doctor`
leaves out the `Claude Code version` line and the `Cloud sync` line. Each build
runs Claude Code and sends its sessions from inside its own image. With
`builtin:process` hooks, or hooks that you wrote, doctor keeps both lines.

`outerlayer doctor` on a runner host checks a Runner key with the route that
the runner uses to check its host key, `/v1/runner/host-key`. The Runner preset
does not hold `factory.read`, so doctor does not read the factory with the key.
A key that the gateway refuses fails the `Factory key` line. A factory that does
not exist also fails the `Factory key` line, because the gateway refuses a key
that is sent for another factory. A key without `work.claim` fails the `Factory`
line.

| Check | Appears when | Fails or warns when | Fix |
| - | - | - | - |
| Runner image | In the runner container | Fails when the factory refuses this version. Warns when it names a newer one. | Pull the tag it names. Start a new container on the same volume. |
| Runner name | The config has `runner.host` and no `runner.name` | Warns. | Rename `runner.host` to `runner.name`. |
| Docker Engine | Always | Warns when `docker info` fails. | Start Docker, or give this user access to its socket. |
| Build isolation | The runner config loads | Fails when the machine cannot isolate builds as the hooks name. Warns for `builtin:process`, which isolates nothing. | See the hooks table under [`runner init`](/reference/cli-runner#outerlayer-runner-init). |
| KVM | `builtin:vm` | Fails when `/dev/kvm` is missing, or the runner's account or service cannot use it. | `sudo usermod -aG kvm $USER`. Then log in again, or restart the service. |
| MicroVM memory limit | `builtin:vm`, with an installed service | Warns when the unit does not delegate its cgroup. | `outerlayer runner install --service --replace` |
| MicroVM jailer | `builtin:vm` | Warns with the first problem it finds. See below. | See below. |
| **Nested virtualization** | `builtin:vm` | Warns when the `nested` parameter of `kvm_intel` or `kvm_amd` is on, or when doctor cannot read it. | Its fix is `nested=0` in a file in `/etc/modprobe.d`. |
| **VMScape** | `builtin:vm` | Shows the kernel's VMScape status (CVE-2025-40300). It warns on `Vulnerable` and on a value doctor cannot read. It is skipped on a kernel that does not report VMScape. | Update the kernel. |
| **Disk I/O sharing** | `builtin:vm` | Warns when a disk under the build disks uses neither the `bfq` scheduler nor iocost, and when doctor finds no disk. | Write `enable=1` for the disk to `/sys/fs/cgroup/io.cost.qos`. |
| MicroVM memory | `builtin:vm` | Warns when `concurrency` times `build.memory` is more than the host's memory. | Lower one of them. See [Memory limits](/run-a-host-as-a-service#memory-limits). |
| **runc** | `builtin:container` | Reads the runc version from the Docker daemon. Warns when it is older than 1.2.8, 1.3.3 or 1.4.0-rc.3, and when Docker reports none. | Upgrade Docker Engine. |
| Image build limit | `builtin:container` or `builtin:vm` | Fails when Buildx cannot make the runner's image builder. The runner then claims nothing. | See [When the Image build limit check fails](/troubleshoot-builds#when-the-image-build-limit-check-fails). |
| Build hosts | `builtin:container` or `builtin:vm` | Warns when `runner.build.allowHosts` is not set. | Set it. The line lists the hosts that earlier builds reached. |
| Free disk | The runner config loads | Warns when free space is below `runner.housekeeping.minFreeDisk`. | Free space. See [Housekeeping](/run-a-host-as-a-service#housekeeping). |
| Claude credential | Built-in hooks | Names the variable that holds Claude's credential. Fails when there is none. A service reads `claude.env` and `runner.env` beside the config. Without a service, doctor reads this shell. | Pipe the token to `outerlayer runner init --agent claude`, then restart. See [The agent](/reference/cli-runner#the-agent). |
| Host key binding | The runner config loads | Says whether the runner key is bound to this host's key. Fails when the host key is damaged, or the runner key is refused or bound to another host. Warns when there is no host key yet. | See [The host key](/run-a-host-as-a-service#the-host-key). |
| Control plane access | `builtin:container` or `builtin:vm` | Fails when there is no GitHub App, and the host's git login cannot read a repository's control plane. | Give the host a git login that can read it. |
| outerlayer on PATH | The runner wrote `~/.local/bin/outerlayer` | Warns when it is not on `PATH`, or another `outerlayer` comes first. | `export PATH="$HOME/.local/bin:$PATH"` |
| Devcontainer CLI | Always | Fails when the devcontainer CLI is not installed beside the CLI. | Reinstall the CLI. |
| Image recipe: owner/name | One per repository the runner has fetched | Warns when the repository has no devcontainer recipe that the runner can build. | See [Build an image from a devcontainer](/build-image-from-devcontainer). |
| Claude Code pin | Always | Warns when npm's newest Claude Code is newer than the version images pin, or the registry cannot be read. | Update the CLI. The pin moves with each release. |

**Nested virtualization**, **VMScape** and **Disk I/O sharing** appear only
with `builtin:vm` hooks. **runc** appears only with `builtin:container` hooks.
These four lines report what a build can reach on the host. They read the
host and change nothing. None of these four lines fails doctor. Doctor still
exits 0 when all four warn. Each microVM build gets an I/O weight. The
kernel acts on the weight only when the disk uses the `bfq` scheduler or has
iocost turned on. Doctor follows a partition to its disk, and an
LVM or dm-crypt volume to the disks below it. It never changes a scheduler or
turns on iocost.

A config that does not load leaves out every line that needs it. Cloud sync and
Claude Code version then stay.

**MicroVM jailer** says whether the root helper runs each microVM build under
Firecracker's jailer. A missing helper means that a build that escaped its
microVM would reach the runner's account. The line names the first of these
that holds:

| Problem | Fix |
| - | - |
| It warns when an account other than the runner's can write the runner's directory of builds. The helper then refuses every microVM build. An earlier release could leave the directory writable by the runner's group. The runner makes the directory private before its next microVM build. | `chmod 700` the directory the line names. |
| It warns when the helper runs a Firecracker other than the one this release pins. A CLI update does not change the helper's Firecracker. Builds still run under the jailer, and each one logs the same warning. | `outerlayer runner install --service --replace` |
| It warns when the host runs systemd older than 254, so microVM builds will not survive a restart. | Upgrade systemd. |
| It warns when the service's unit sets `FileDescriptorStoreMax=0`, so microVM builds will not survive a restart. | `outerlayer runner install --service --replace` |
| It warns when the running runner started without the descriptor store. | `sudo systemctl restart <unit>` |
| It warns when the helper is missing or too old for this CLI. | `outerlayer runner install --service --replace` |

See [The microVM helper](/build-security#the-microvm-helper),
[Run builds on macOS](/run-builds-on-macos) and [Run builds on
Windows](/run-builds-on-windows).

See also: [Troubleshoot setup](/troubleshooting).

## outerlayer daemon

Run the copy-out daemon in the foreground. Claude Code deletes transcripts after about 30 days. The daemon copies them first.

```bash theme={"system"}
outerlayer daemon [--once]
```

| Flag | Default | What it does |
| - | - | - |
| `--once` | off | Do one mirror sweep and exit. Makes no network calls. |

It keeps the status-line state fresh. With cloud credentials, it also streams the new turns of a session launched with `OUTERLAYER_WORK`, so its page follows along before `sync` runs. `init` does not start it: run it in a terminal you keep open, or under a login agent. `outerlayer watch` is the former name and still works, with a warning.

```bash theme={"system"}
outerlayer daemon
```

See also: [What leaves your machine](/what-leaves-your-machine).

## outerlayer hooks status

List hook entries in user and project settings, marking the wrapped ones.

```bash theme={"system"}
outerlayer hooks status [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--json` | off | Print each settings file with its entries and a `wrapped` flag. |

A settings file that is not valid JSON is reported, and the other is still listed. Exits 0.

## outerlayer hooks wrap

Wrap hook commands so a hang or a kill leaves evidence. `init` already wraps `PreToolUse` and `PostToolUse` hooks. Run this for other events, for hooks added later, or after `init --no-wrap-hooks`.

```bash theme={"system"}
outerlayer hooks wrap [--project] [--event <name>] [--all] [--no-input] [--capture-output]
```

| Flag | Default | What it does |
| - | - | - |
| `--project` | user settings | Work on `./.claude/settings.json`. Wrapped hooks run `outerlayer` from the PATH. |
| `--event <name>` | every event | Only consider hooks registered for this event. |
| `--all` | ask per hook | Wrap every candidate without asking. |
| `--no-input` | off | Never ask. Requires `--all`. |
| `--capture-output` | off | Also keep a bounded tail of stdout and stderr. Adds overhead. |

Each wrapped hook adds one Node start and one spawn every time it fires. Like `init`, `hooks wrap` first copies the CLI to `~/.outerlayer/cli`, and the wrappers run that copy.

```bash theme={"system"}
outerlayer hooks wrap --all --event PreToolUse
```

Exit codes: 0 when it wrapped hooks or found none to wrap. 1 when `--no-input` comes without `--all`, or the CLI cannot be copied to `~/.outerlayer/cli`. 1 also when the settings file is not valid JSON, or the CLI path the wrapper would use does not exist (reinstall, then rerun).

## outerlayer hooks unwrap

Restore wrapped hooks to their original commands. Use it too if a wrapper misbehaves.

```bash theme={"system"}
outerlayer hooks unwrap [--project]
```

| Flag | Default | What it does |
| - | - | - |
| `--project` | user settings | Work on `./.claude/settings.json`. |

Exit codes: 0, or 1 when the settings file is not valid JSON.


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