---
# SPDX-License-Identifier: Apache-2.0
# https://www.apache.org/licenses/LICENSE-2.0
name: isolated-setup-install
family: setup
mode: Meta
description: >-
  Walk an adopter through the first-time install of the secure agent
  setup (sandbox, approval and clean-environment layers) for Claude
  Code, Codex or Gemini CLI. Interactive throughout; never runs sudo,
  edits a shell rc, or overwrites settings on its own.
when_to_use: >-
  When the user wants the secure setup installed for the first time, or
  is working in a fresh clone or on a new machine with no wiring yet.
  If it is already installed, use `setup-isolated-setup-verify` to check
  it or `setup-isolated-setup-update` to refresh it.
capability: capability:platform
surface_hash: sha256:3f90b1ffdaa6e9ea
license: Apache-2.0
measured_tokens: 5726
---

<!-- Placeholder convention (see AGENTS.md#placeholder-convention-used-in-skill-files):
     <project-config> → adopting project's `.apache-magpie/` directory
     <tracker>        → value of `tracker_repo:` in <project-config>/project.md
     <upstream>       → value of `upstream_repo:` in <project-config>/project.md -->

# setup-isolated-setup-install

## Runtime routing (run before the Claude-specific procedure)

Use the operator's explicitly requested runtime when supplied; otherwise use the active session's runtime.
An installed executable or configuration directory alone does not select a runtime.
For the routing below, treat that selection as the active harness.

Determine the active harness from the session metadata and executable. When it
is Codex, follow the install lifecycle in
[docs/adapters/codex.md](../../../../docs/adapters/codex.md#install), including the
project profile merge, static lint, project trust, and `agent-iso codex`
steps. Do not write Claude settings or report a missing `.claude` file as a
Codex failure. After completing the Codex branch, stop; the remainder of this
skill is the Claude Code branch.

When the selected runtime is Gemini CLI, follow
[docs/adapters/gemini.md](../../../../docs/adapters/gemini.md#install): resolve the existing extension, snapshot, or framework checkout; propose the workspace profile and guard merge; then guide the wrapper launch and verification.
Do not require or write Claude configuration.
After completing the Gemini branch, stop before the Claude-specific procedure below.

When the selected runtime is Copilot CLI, follow [docs/adapters/copilot.md](../../../../docs/adapters/copilot.md#install): register the guard hook, detect and declare mail sources from the MCP servers already present, and ask nothing beyond the single apply confirmation (PRINCIPLES.md §1, *Avoiding prompt fatigue*). Do not require or write Claude configuration.
After completing the Copilot branch, stop before the Claude-specific procedure below.

When the harness is Claude Code, continue below. If the harness cannot be
determined from the session or executable, stop and report that; never guess and never prompt.

This skill is the **on-ramp** for adopters who do not yet have the
secure setup running. It is a thin walkthrough wrapper around the
canonical install path documented in
[`docs/setup/secure-agent-setup.md`](../../../../docs/setup/secure-agent-setup.md). The
authoritative content lives there; this skill exists so an adopter
can say *"set up the secure agent setup"* in a fresh session and
land in the right step-by-step flow without first reading the
document.

## Adopter overrides

<!-- BEGIN MAGPIE BLOCK: adopter-overrides — generated from tools/dev/blocks/adopter-overrides.md -->

Before running its default behaviour, this skill consults
`setup-isolated-setup-install.md` in the personal layer
(`.apache-magpie-local/` when the project adopted Magpie, falling back to the main checkout's in a linked worktree,
or `<git-common-dir>/apache-magpie/` when Magpie is only installed; applied first, wins on conflict) and
[`.apache-magpie-overrides/setup-isolated-setup-install.md`](../../../../docs/setup/agentic-overrides.md) (committed, project-wide)
in the adopter repo, if present, and applies any agent-readable overrides it finds.
See [`docs/setup/agentic-overrides.md`](../../../../docs/setup/agentic-overrides.md) for the contract.

**Hard rule**: agents NEVER modify the snapshot under `<adopter-repo>/.apache-magpie/`.
Local modifications go in the override file; framework changes go via PR to `apache/magpie`.

<!-- END MAGPIE BLOCK: adopter-overrides -->

---

## Snapshot drift

Also at the top of every run, this skill compares the
gitignored `.apache-magpie.local.lock` (per-machine
fetch) against the committed `.apache-magpie.lock`
(the project pin). On mismatch the skill surfaces the
gap and proposes
[`setup upgrade`](../setup/upgrade.md).
The proposal is non-blocking — the user may defer if
they want to run with the local snapshot for now. See
[`docs/setup/install-recipes.md` § Subsequent runs and drift detection](../../../../docs/quick-start/other-install-methods.md#subsequent-runs-and-drift-detection)
for the full flow.

Drift severity:

- **method or URL differ** → ✗ full re-install needed.
- **ref differs** (project bumped tag, or `git-branch`
  local is behind upstream tip) → ⚠ sync needed.
- **`svn-zip` SHA-512 mismatches the committed
  anchor** → ✗ security-flagged; investigate before
  upgrading.

---
## Golden rules

- **Do not auto-run privilege-elevating commands.** Anything that
  needs `sudo` (apt / dnf installs, system-wide writes) is *printed*
  for the user to copy-paste into their own terminal. The skill
  never invokes `sudo` itself.
- **Do not edit shell rc files without approval.** `~/.bashrc` /
  `~/.zshrc` modifications (sourcing `agent-iso.sh`, the optional
  `alias claude='claude-iso'`) are surfaced as the exact line to
  add; the user pastes it themselves. The skill confirms the rc
  file path with the user first; it does not assume.
- **Do not overwrite an existing settings file silently.** If the
  user already has a project `.claude/settings.json` or a
  user-scope `~/.claude/settings.json`, the skill *diffs* the
  desired merge against the existing file and asks for explicit
  approval before writing. The merge carries the framework's
  `permissions.allow` set (the read-only reads: `gh` reads, the
  vetted-ops read dispatcher, archive / mailbox / roster MCP reads,
  registry `WebFetch` hosts) alongside `deny` and `ask` — leaving it
  out makes every skill read prompt. Only read-only entries belong in
  `allow`. Re-installs / partial-state recoveries
  are common — the skill must not blow away an unrelated
  pre-existing hook or `permissions.ask` rule. The desired merge
  **includes the agent-guard `hooks.PreToolUse` entry** (matcher
  `Bash`, command running the user-scope `~/.claude/scripts/agent-guard.py`)
  — the deterministic guard from
  [`tools/agent-guard`](../../../../tools/agent-guard/README.md). Install
  the script as `~/.claude/scripts/agent-guard.py` and populate
  `~/.claude/scripts/guards.d/` from both the engine's bundled
  `guards.d/*.py` and every skill-owned `skills/*/guards/*.py`
  (so skill-owned guards like `mention` / `mark-ready` are active),
  alongside the other user-scope scripts (Step P); wire the
  `PreToolUse` entry once, and preserve any pre-existing `hooks`
  entries.
- **Stop on the first failure.** If a step fails (manifest read
  fails, framework path wrong, an existing file conflicts in a way
  the user has not yet decided about), stop and report. Do not
  push past a failure to the next step.

## Up-front confirmations

Before walking any install step, confirm with the user:

1. **OS / distro.** macOS, Ubuntu / Debian (apt), Fedora / RHEL
   (dnf), or Arch / NixOS / other. macOS skips bubblewrap +
   socat (Seatbelt is built-in); Linux installs both per the
   distro shortcut.
2. **Framework checkout path.** The path to the user's local
   `magpie` clone. Required to read
   `tools/agent-isolation/pinned-versions.toml`,
   `.claude/settings.json`, and the
   `tools/agent-isolation/*.sh` scripts. If the user does not
   have a clone, walk them through `git clone` first.
3. **Fresh install or re-install.** For a re-install on a partial
   existing state, the skill must enumerate the existing wiring
   (project settings.json, user settings.json, hooks dir, the
   agent-guard `hooks.PreToolUse` entry + `~/.claude/scripts/agent-guard.py`,
   shell rc) before any merge so the user knows what is being
   preserved vs replaced.
4. **Sync repo (optional).** Whether the user maintains a
   private dotfile-style `~/.claude-config` repo per
   [Syncing user-scope config across machines](../../../../docs/setup/secure-agent-setup.md#syncing-user-scope-config-across-machines).
   If yes, the skill installs user-scope scripts as **symlinks**
   into `~/.claude-config/scripts/` rather than `cp`-ing into
   `~/.claude/scripts/` — the symlink approach is what makes
   sync push the upgrades to other machines automatically.

## Walk-through

Follow the canonical step list at
[docs/setup/secure-agent-setup.md → Adopter setup → Via a Claude Code prompt](../../../../docs/setup/secure-agent-setup.md#via-a-claude-code-prompt).
Each step in that list maps 1:1 to a step in this skill. Do not
re-write the list here — read the doc, follow it, and surface each
sub-step with the user. The doc names are the source of truth; the
skill is the runner.

For the verification step at the end, hand off to the
`setup-isolated-setup-verify` skill rather than re-walking the checklist
inline.

### Agent harness — install `@latest`, enforce the floor

`claude-code` is **not** pinned to an exact version. Install it with
`npm install -g --no-save @anthropic-ai/claude-code@latest` (the
command in the doc's install list) — latest always carries the newest
permission-rule / sandbox / prompt-injection fixes. The manifest's
`[tools.claude-code]` table in
[`pinned-versions.toml`](../../../../tools/agent-isolation/pinned-versions.toml)
declares a `min_version` **floor**, not a pin. Because this install is
driven from Claude Code, apply the same hard gate
`setup-isolated-setup-verify` check 5 applies: read the running
version (`command claude --version`, which skips an
`alias claude=claude-iso`) and, if it is **below** `min_version`,
**hard-fail** — stop the install, tell the operator to upgrade to
`@latest`, and have them re-run. The secure setup must not be stood up
on a below-floor runtime.

### Step P — Project-root coverage in the sandbox allowlists

The harness drops the literal `.` when it pre-resolves
`sandbox.filesystem.allowRead: ["."]`, so a session in a fresh clone can
write to the project root but not read from it. The fix is to add the root
as an absolute path to the project-local `settings.local.json`.

This step asks one question first — per-project or whole-user scope — and
the whole-user answer overrides git's hook lookup for every repository on
the machine, so it carries a disclosure the operator has to acknowledge.

**Full procedure, both scopes and both whole-user flavours:**
[`step-p-sandbox-allowlists.md`](step-p-sandbox-allowlists.md).

### Step V — The vetted-ops split and exclusion

Only applies when the adopter routes forge operations through the
`vetted-ops` dispatcher — i.e. the repo has
`.apache-magpie-overrides/tools/vetted-ops/config.toml`, or the
operator asks for the dispatcher to be wired now. If neither is
true, skip this step and say so; do not create a policy the project
has not asked for.

There are three console scripts over one catalogue, and which of them
gets the `allow` is the entire security question:

| Entry point | Can write? | Permission |
|---|---|---|
| `vetted-op-read` | never — refused before policy or `--caller` is consulted | `allow` |
| `vetted-op-tracker` | only the tracker rollup / body-field procedures — everything else refused before policy | `ask` |
| `vetted-op` | yes | `ask` |

**Never propose an `allow` on `vetted-op`.** It is tempting, because
the policy declares a read-only caller and the invocation names it.
That reasoning is wrong: `--caller` is an argv string chosen by
whoever runs the command, so an `allow` on the write dispatcher
grants every operation in the catalogue no matter which caller the
example names. The read dispatcher needs no such trust — it refuses
writes structurally.

```jsonc
"permissions": {
  "allow": [
    "Bash(uv run --project ~/.claude/magpie/vetted-ops vetted-op-read *)",
    "Bash(uvx --from ~/.claude/magpie/vetted-ops vetted-op-read *)"
  ],
  "ask": [
    "Bash(uv run --project ~/.claude/magpie/vetted-ops vetted-op *)",
    "Bash(uv run --project ~/.claude/magpie/vetted-ops vetted-op-tracker *)",
    "Bash(uvx --from ~/.claude/magpie/vetted-ops vetted-op-tracker *)"
  ],
  "deny": [
    // `Edit(path)` is the path rule for every file-writing tool — Write and
    // NotebookEdit included. A `Write(path)` rule is NOT matched by the file
    // permission check; do not add one alongside, it reads as a second layer
    // that is not there.
    //
    // the operation catalogue — the read `allow` rests on its shape
    "Edit(~/.claude/plugins/cache/apache-magpie/magpie-vetted-ops/**)",
    // the fixed path the rules name, which points into that catalogue
    "Edit(~/.claude/magpie/**)",
    // the policy naming which caller may run which operation
    "Edit(.apache-magpie-overrides/tools/vetted-ops/**)"
  ]
}
```

Allow **both** invocation forms, and add both to
`sandbox.excludedCommands` (`"uv run --project ~/.claude/magpie/vetted-ops vetted-op-read *"` and
`"uvx --from ~/.claude/magpie/vetted-ops vetted-op-read *"`). Skills document `uv run --project`; read-only
gatherer agents use `uvx --from`, because `uv run` in the plugin cache
needs to write a venv there. A rule for only one form leaves every call
in the other form prompting.

Add both forms of `vetted-op-tracker` to `sandbox.excludedCommands` too
(`"uv run --project ~/.claude/magpie/vetted-ops vetted-op-tracker *"` and
`"uvx --from ~/.claude/magpie/vetted-ops vetted-op-tracker *"`): the status-rollup
and body-field writes need a `gh` that can verify TLS, which a sandboxed one cannot.
Excluding it is acceptable where excluding `vetted-op` is not, because its catalogue
is closed to the tracker procedures — every other operation is refused before the
policy is read — and their runner refuses any `gh` call outside `repos/<tracker>/`.
It stays in `ask`, never `allow`, so each write keeps its confirmation.
**Never exclude `vetted-op` itself**: that would run the whole write catalogue unsandboxed.

The rules name the fixed path `~/.claude/magpie/vetted-ops`, never the
versioned plugin-cache directory. The plugin's `SessionStart` hook points
that path at the installed version every session, so an upgrade needs no
rule change. **Never write a rule with `*` in place of the plugin
version.** A `*` also matches spaces, so it approves — and, in
`excludedCommands`, runs unsandboxed — a command with extra `uv` options
spliced in at that position (`--with <any package>`, a second `--from`).
If an adopter's settings still carry the versioned `*` form, replace it
with the fixed path.

Tell the operator plainly what this buys and what it does not:

- The catalogue is protected twice — by these rules, and by the
  plugin cache sitting outside every `sandbox.filesystem.allowWrite`
  root, so sandboxed Bash cannot write there either.
- The policy TOML is protected **once**, and that is tolerable only
  because the read dispatcher ignores policy when it refuses a
  write. Editing the policy can widen which repo is *read*; it
  cannot turn a read into a write.
- Writes are not unattended. They keep the harness confirmation —
  the dispatcher bounds their *shape*, not the operator's decision
  to make them.

Do not describe per-caller scoping as isolation. It is
least-privilege hygiene for a cooperating skill, and it is worth
having for that, but it stops nothing that chooses to name a
different caller.

### Step R — The adversarial-review exclusion

Only applies when the `magpie-adversarial-review` plugin is installed. If it
is not, skip this step and say so.

The reviewer CLIs it runs (`codex`, `copilot`, `gemini`, `grok`, `claude`)
need network access and read their own credentials (`~/.codex`,
`~/.copilot`, `~/.gemini`, `~/.grok`, `~/.claude`), which this sandbox denies. The tool therefore
runs outside it, through one exclusion that names the installed plugin:

```jsonc
"sandbox": {
  "excludedCommands": [
    "uvx --from ~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/*/tools/adversarial-review adversarial-review *"
  ]
},
"permissions": {
  "deny": [
    // the tool runs unsandboxed, so the code it runs must not be editable
    // by the agent that calls it
    "Edit(~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/**)"
  ]
}
```

**Do not add an `allow` for it.** Every run sends the change to other
model providers and costs money; the harness prompt is the gate, by design.

Tell the operator what the exclusion covers and what it does not:

- It matches only the single-line form, spelled with a literal `~` and an
  unquoted path. A pipe, `$(…)`, `&&`, a redirection, quotes, or an
  expanded home directory put the command back in the sandbox, where the
  reviewer CLIs fail to read their credentials and report `unavailable`.
- The `*` in the pattern can match more than a version directory — a path
  with `..` segments would still match. The prompt that every run keeps is
  the gate against that; read the path in it before approving.
- The tool refuses a `--body-file` or `diff:` file outside the repository
  or a temporary directory, so an approved run cannot be pointed at
  `~/.ssh` or a private checkout to send it to a model.
- Outside the sandbox the tool only runs each reviewer CLI in its own
  read-only mode, and writes nothing to the repository. `codex`'s
  read-only mode still reads files anywhere on the machine; see the tool's
  README for what that means for a machine that also holds a private
  checkout.

### Step W — Working directories for the read block

Nothing extra to write: the `sandbox-add-project-root.sh` run in Step P also
adds `$HOME/.claude/magpie` and `/tmp/claude-<uid>`, resolved, to
`permissions.additionalDirectories` of each worktree's project-local
`settings.local.json`. Under `permissions.blockReadsOutsideWorkingDirectories`
the skills read both on every run; without the entries each read prompts, and
a bulk sync multiplies that by its gatherer agents.

After Step P, show the operator the resulting `additionalDirectories` list and
check two things:

- **No glob and no `~`.** If the operator already has an entry such as
  `/tmp/claude-*` in any scope, say it does nothing: the setting matches
  literal paths only, so the glob is listed but never matched.
- **Nothing per-host in a synced or committed file.** If the operator put the
  paths in a user-scope `~/.claude/settings.json` that is synced across
  machines, or in the committed project settings, suggest moving them: they
  name this host's home directory and uid.

Rationale and the rules in full:
[`docs/setup/secure-agent-setup.md` → Working directories under the read-outside-working-directories block](../../../../docs/setup/secure-agent-setup.md#working-directories-under-the-read-outside-working-directories-block).

### Steps K, L and M — optional extras

None of these is needed for a working install. Walk the one the operator
asks for: a hardware security key (K), the container gateway (L), or the
eval-harness exclusion (M).

**Procedures:** [`optional-steps.md`](optional-steps.md).

## After the install lands

**Tell the operator what to look for in the footer**, and what each
state means. This is the one piece of the install they will see on
every render afterwards, so a wrong reading here persists:

| Tag | Means |
|---|---|
| `[sandbox]` green | sandboxed, and still prompting per command |
| `[sandbox-auto]` yellow | sandboxed, **not** prompting per command — auto-allow; wider blast radius, which is why it is not green |
| `[NO SANDBOX]` bold red | not sandboxed; the state this install exists to make impossible to miss |

Captures of each are in
[*What a session looks like*](../../../../docs/setup/secure-agent-setup.md#what-a-session-looks-like);
point the operator there rather than describing the colours twice.

Two things to say explicitly, because both are counter-intuitive:

- **The tag reads the settings files, not the running process.** A
  CLI flag that disables sandboxing mid-session is not visible here.
  Pair it with the bypass-warn hook, which fires per call.
- **Yellow is not a warning that something is broken.** Auto-allow is
  a deliberate, common choice; the colour distinguishes two postures
  rather than flagging a fault.

Suggest two follow-up routines the user can wire later:

- `setup-isolated-setup-verify` — re-run after every Claude Code upgrade
  or settings-file edit, to confirm denials still fire as
  expected. The "did a denial silently turn into an allow?"
  signal is exactly what this skill exists for.
- `setup-isolated-setup-update` — periodic check for framework
  updates, pinned-tool upgrade candidates, and drift between the
  installed user-scope copies and the framework's
  source-of-truth. The pre-flight of every skill proposes it for them
  once the install is recorded (below): after an upgrade that changes
  the secure-setup files, and weekly otherwise.

**Record the install** so that pre-flight knows the isolated setup is
used on this machine and what it was installed against:

```bash
PYTHONPATH=".apache-magpie-local:$(git rev-parse --git-common-dir)/../.apache-magpie-local:$(git rev-parse --git-common-dir)/apache-magpie" \
  python3 -m setup_preflight.isolated record-update
```

It writes the `isolated_setup` block of the personal layer's
`reconciled.json` (`<git-common-dir>/apache-magpie/` when the project has
not adopted Magpie, `.apache-magpie-local/` when it has) and nothing
else. Skip it when the command cannot find `setup_preflight` — say that
`/magpie-setup config` installs the checker, and that the reminders start
once it is there.

**Always propose shared-config sync once the install lands.**
Regardless of whether the operator already maintains the
`~/.claude-config` sync repo, proactively offer to run
`setup-shared-config-sync` as a follow-up:

- If the `~/.claude-config` sync repo is already in place, the
  skill commits + pushes the local modifications (the user-scope
  scripts, hooks, and settings this install just wired up) so the
  other machines pick them up.
- If the operator does **not** yet have `~/.claude-config`, the
  `setup-shared-config-sync` skill bootstraps it (clones the
  default private remote if it exists, or creates a fresh private
  remote and scaffolds the layout). Mention this so a first-time
  operator knows the follow-up will set sync up from scratch — the
  point of proposing it is precisely so the just-installed config
  does not stay machine-local.

Surface it as an offer for the operator to accept, not an
auto-run — the sync skill has its own confirmation gates before it
commits or pushes anything.
