Skip to content

Security and permissions ​

Permission system architecture ​

agents-fleet resolves a single effective permission profile for every worker spawn. The canonical profile table lives in src/fleet/agentPermissionProfiles.ts; both providers consume the resolved profile so Copilot and Claude enforce the same read/write/shell/meta capabilities.

Resolution happens in FleetManager before provider session creation. Copilot uses the profile in onPermissionRequest and only enables setApproveAll for unrestricted spawnable profiles. Claude translates the same profile into permissionMode, disallowedTools, and canUseTool. The profile is resolved and frozen before agent registration or worktree creation, then the same object is passed to provider setup. This prevents configuration changes between isolation selection and session creation from changing the worker's effective privileges.

Default agent-type profiles ​

Agent typeWriteShellMeta/sub-agentsTrust
explorerNoNoNoRead-only
reviewerNoNoNoRead-only
researcherScoped to .plans/, .fleet/NoNoNotes/research
testerNoYesNoTest runner
coderYesYesYesFull trust
general-purposeYesYesYesFull trust
customYesYesYesFull trust

Override precedence ​

Effective permissions are merged in this order:

  1. Canonical agent-type profile
  2. Config wildcard: permissions["*"]
  3. Config type entry: permissions["explorer"], permissions["tester"], etc.
  4. Per-spawn permissionOverrides

Later entries win. writeScopes replace lower-precedence scopes rather than unioning with them. Any resulting elevation is validated against the canonical agent-type profile, regardless of whether it came from a wildcard, type-specific config, or per-spawn override.

Override sourceExampleSEC-4 treatment
Wildcard configpermissions["*"]Final elevation requires opt-in
Type configpermissions["explorer"]Final elevation requires opt-in
Per-spawnpermissionOverridesFinal elevation requires opt-in

For writeScopes, comparison is path-aware rather than a raw string-set comparison. Equal roots and descendants such as .plans/security narrow the profile. A sibling/new root such as tests, or changing a scoped list to [] (unrestricted), widens it.

Example config:

json
{
  "permissions": {
    "*": { "canShell": false },
    "explorer": { "canShell": true }
  },
  "allowPrivilegeEscalation": true
}

Example per-spawn override:

json
{
  "name": "shell-explorer",
  "agent_type": "explorer",
  "prompt": "Inspect the build output",
  "permissionOverrides": { "canShell": true }
}

The process running this spawn must have been started with --allow-privilege-escalation (or the equivalent config setting).

SEC-4 escalation guard ​

By default, overrides cannot elevate restricted profiles in ways that bypass the agent-type trust model. The guard rejects:

  • fullTrust: false becoming true
  • privilegeLevel increasing above the canonical level
  • canMeta: false becoming true
  • canShell: false becoming true
  • canWrite: false becoming true
  • writeScopes expanding outside the canonical roots, including replacing a scoped profile with unrestricted writes

This is a security-correcting breaking change: configurations or spawn requests that previously granted shell/write access to explorer, reviewer, or other restricted profiles must now explicitly opt in with --allow-privilege-escalation or allowPrivilegeEscalation: true. Tightening a profile remains allowed without the flag.

Shell access is treated as effective write access for isolation because shell commands can modify files even when provider write tools are disabled. A shell-capable worker, including canShell: true, canWrite: false, is therefore auto-assigned a worktree when no explicit cwd is supplied. Unrestricted write-only workers are also isolated; scoped write-only workers continue to use the shared CWD. An explicit cwd still opts out of automatic worktree creation, so on Windows and other platforms the caller is responsible for ensuring that the selected shared directory is safe for shell-side writes.

Capability-compatible dispatch ​

spawn_worker.requiredCapabilities is optional structured metadata with values shell, write, and meta. When present, the spawn handler resolves the selected role's effective permission profile and rejects the assignment before claiming a task or spawning a worker if any declared capability is missing. Rejection is explicit and actionable; dispatch does not silently change agent type, reroute the task, or elevate permissions.

Reviewer and explorer defaults remain canShell=false. A review that only needs material already on disk can pass those paths in localArtifacts; the worker receives an explicit artifact list and keeps its restricted profile.

For remote PRs, git ranges, builds, or CI evidence, use a secure two-stage workflow:

  1. Spawn a worker whose existing profile supports the declared shell/write requirements to collect the authorized remote, git, or build material into local artifacts.
  2. Spawn the restricted reviewer/explorer with localArtifacts pointing to those files. It inspects with read-only tools and reports missing evidence rather than fetching it.

Do not collapse these stages by silently escalating or rerouting the restricted worker. Explicit restricted-type overrides require SEC-4 opt-in under T-18; no speculative collector role is introduced.

--allow-privilege-escalation ​

Use --allow-privilege-escalation only when you intentionally want overrides to elevate restricted profiles. The same setting can be persisted in ~/.fleet/config.json as:

json
{ "allowPrivilegeEscalation": true }

The CLI flag takes precedence over config. When enabled, startup prints a red warning: ⚠️ Privilege-escalation overrides ENABLED — agent permission profile elevation allowed.

Deprecated --yolo and per-SEC acknowledgements ​

For a step-by-step migration guide, see the --yolo migration guide.

--yolo is deprecated and no longer bypasses any SEC regression by itself. A bare --yolo exits before provider startup with:

text
--yolo requires at least one --acknowledge-* flag. Use --acknowledge-all-sec for legacy full-bypass behavior, or set AGENTS_FLEET_YOLO_LEGACY=1 for CI scripts that cannot change command lines. See docs/yolo-migration.md or docs/security.md for details.

The flag is kept only for trusted autopilot/CI migration. It will be removed in a future major version.

FindingDefault postureAcknowledgement that re-opens itWhat is bypassed
SEC-2Claude restricted types use permissionMode: "default", disallowedTools, and canUseTool.--yolo --acknowledge-sec2Re-enables Claude permissionMode: "bypassPermissions" for yolo sessions.
SEC-4Restricted worker types do not receive unrestricted setApproveAll; per-type gates remain authoritative.--yolo --acknowledge-sec4Re-enables restricted-type approve-all plus write/shell approvals.
MCP allowlistPer-worker MCP allowlists are enforced.--yolo --acknowledge-mcp-bypassRe-enables bypassing MCP allowlist checks.
SEC-R3-2Unknown Copilot permission request kinds fail closed.--yolo --acknowledge-sec-r3-2Re-enables approval of unknown permission kinds.

Convenience flags and config equivalents:

bash
agents-fleet --yolo --acknowledge-sec2
agents-fleet --yolo --acknowledge-sec4
agents-fleet --yolo --acknowledge-mcp-bypass
agents-fleet --yolo --acknowledge-sec-r3-2
agents-fleet --yolo --acknowledge-all-sec   # legacy full-bypass behavior
json
{
  "yolo": true,
  "yoloAcks": {
    "sec2": true,
    "sec4": true,
    "mcpBypass": true,
    "secR3-2": true
  }
}

CLI acknowledgement flags take precedence over config acknowledgements. AGENTS_FLEET_YOLO_LEGACY=1 is a temporary escape hatch for CI scripts that cannot change command lines; it behaves like --yolo --acknowledge-all-sec and is reported as env-legacy by /diagnose.

Migrating from legacy --yolo ​

Prefer explicit permission config instead of legacy full bypass:

json
{
  "permissions": {
    "*": {
      "fullTrust": true,
      "canShell": true,
      "canWrite": true,
      "canMeta": true,
      "writeScopes": []
    }
  },
  "allowPrivilegeEscalation": true
}

Safer migration path:

  1. If a restricted type needs shell or write access, configure the narrowest capability and use --allow-privilege-escalation or persist "allowPrivilegeEscalation": true.
  2. Keep writeScopes within the canonical roots unless broader access is intentionally acknowledged through the same SEC-4 mechanism.
  3. If legacy all-bypass behavior is truly required, use --yolo --acknowledge-all-sec.
  4. For uneditable CI scripts only, set AGENTS_FLEET_YOLO_LEGACY=1 temporarily and plan to remove it.

/diagnose permission posture ​

/diagnose now includes a permission posture section in human and JSON output. When locked down it prints:

text
Permission posture: All SEC restrictions in effect. No bypasses active.

When bypasses are active it shows source attribution:

text
Permission posture:
  Effective restrictions: 1 SEC bypasses ACTIVE
  - SEC-2 (Claude bypass): ACTIVE via --yolo --acknowledge-sec2
  - SEC-4 (privilege escalation): NOT ACTIVE (would require --yolo --acknowledge-sec4)
  - MCP allowlist: NOT BYPASSED
  - SEC-R3-2: NOT BYPASSED
  Config overrides: explorer.canShell=true, researcher.writeScopes=[".plans"]

JSON output has a versioned root permissions object with schema_version, redacted config_overrides, allow_privilege_escalation, yolo, yolo_acks, and active_bypasses.