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 type | Write | Shell | Meta/sub-agents | Trust |
|---|---|---|---|---|
explorer | No | No | No | Read-only |
reviewer | No | No | No | Read-only |
researcher | Scoped to .plans/, .fleet/ | No | No | Notes/research |
tester | No | Yes | No | Test runner |
coder | Yes | Yes | Yes | Full trust |
general-purpose | Yes | Yes | Yes | Full trust |
custom | Yes | Yes | Yes | Full trust |
Override precedence
Effective permissions are merged in this order:
- Canonical agent-type profile
- Config wildcard:
permissions["*"] - Config type entry:
permissions["explorer"],permissions["tester"], etc. - 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 source | Example | SEC-4 treatment |
|---|---|---|
| Wildcard config | permissions["*"] | Final elevation requires opt-in |
| Type config | permissions["explorer"] | Final elevation requires opt-in |
| Per-spawn | permissionOverrides | Final 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:
{
"permissions": {
"*": { "canShell": false },
"explorer": { "canShell": true }
},
"allowPrivilegeEscalation": true
}Example per-spawn override:
{
"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: falsebecomingtrueprivilegeLevelincreasing above the canonical levelcanMeta: falsebecomingtruecanShell: falsebecomingtruecanWrite: falsebecomingtruewriteScopesexpanding 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:
- Spawn a worker whose existing profile supports the declared
shell/writerequirements to collect the authorized remote, git, or build material into local artifacts. - Spawn the restricted reviewer/explorer with
localArtifactspointing 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:
{ "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:
--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.
| Finding | Default posture | Acknowledgement that re-opens it | What is bypassed |
|---|---|---|---|
| SEC-2 | Claude restricted types use permissionMode: "default", disallowedTools, and canUseTool. | --yolo --acknowledge-sec2 | Re-enables Claude permissionMode: "bypassPermissions" for yolo sessions. |
| SEC-4 | Restricted worker types do not receive unrestricted setApproveAll; per-type gates remain authoritative. | --yolo --acknowledge-sec4 | Re-enables restricted-type approve-all plus write/shell approvals. |
| MCP allowlist | Per-worker MCP allowlists are enforced. | --yolo --acknowledge-mcp-bypass | Re-enables bypassing MCP allowlist checks. |
| SEC-R3-2 | Unknown Copilot permission request kinds fail closed. | --yolo --acknowledge-sec-r3-2 | Re-enables approval of unknown permission kinds. |
Convenience flags and config equivalents:
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{
"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:
{
"permissions": {
"*": {
"fullTrust": true,
"canShell": true,
"canWrite": true,
"canMeta": true,
"writeScopes": []
}
},
"allowPrivilegeEscalation": true
}Safer migration path:
- If a restricted type needs shell or write access, configure the narrowest capability and use
--allow-privilege-escalationor persist"allowPrivilegeEscalation": true. - Keep
writeScopeswithin the canonical roots unless broader access is intentionally acknowledged through the same SEC-4 mechanism. - If legacy all-bypass behavior is truly required, use
--yolo --acknowledge-all-sec. - For uneditable CI scripts only, set
AGENTS_FLEET_YOLO_LEGACY=1temporarily 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:
Permission posture: All SEC restrictions in effect. No bypasses active.When bypasses are active it shows source attribution:
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.