Additional

Access Profiles

Synced from github.com/CoWork-OS/CoWork-OS/docs

Access profiles are CoWork OS's canonical way to decide what an agent may do. They package the execution sandbox, approval behavior, reviewer behavior, network posture, and optional filesystem/domain boundaries into one named policy that is carried with the task.

This follows the access model described in the official Codex sandbox guide: sandboxing defines the technical boundary, while approval policy defines when the agent must stop and ask. CoWork adapts that model into a local, named profile contract. Command tools are part of the selected profile; there is no separate user-facing Shell enabled or Shell disabled switch for a new task.

Quick choice

The main composer exposes these built-in profiles:

ProfileSandboxApproval and reviewerNetworkBest for
Ask for approvalworkspace-writeon-request / useron-requestInteractive work where boundary-crossing or risky actions should be shown to the operator
Approve for meworkspace-writeon-request / auto-reviewon-requestThe same bounded workspace posture with fewer interruptions for actions that pass automatic review
Full accessdanger-full-accessnever / noneenabledTrusted local work that needs high autonomy
CustomNamed profileNamed profileNamed profileA repeatable policy for a workspace, team, automation, or integration

The local runtime runs without popup approvals by default. An allow decision executes silently. A decision that remains ask is presented as an assistant message with a durable inline Deny / Allow once input card, including for network access, credentials, exports, MCP or external side effects, eligible outside-workspace paths, and operations that explicitly opt out of automatic approval. This keeps the interaction aligned with Codex-style harnesses while preserving an explicit human decision where policy requires one. Set COWORK_APPROVAL_PROMPTS=on before launching CoWork only when you need the legacy approval queue for diagnostics. Hard denials, administrator policy, protected operating-system paths, and approval: "never" remain fail-closed.

Full access is still subject to hard guardrails, protected operating-system paths, administrator policy, and explicit export/location/credential consent. Its never approval policy does not open a consent dialog: an operation that needs missing authority is denied. Full access is not a promise that every native operation will be allowed.

Ordinary work and exceptions

The default Ask for approval profile authorizes file creation, editing, and artifact generation inside the selected workspace, including an isolated temporary session folder. Routine commands run without approval when the required sandbox is available. These actions create normal tool activity, not requested/granted approval events. Approve for me uses the same boundary; its reviewer handles only eligible exceptions.

Explicit ask rules, external side effects, protected credentials, and eligible filesystem/network boundary crossings remain separate requirements. In an interactive task they produce the inline assistant decision card; in an automated task with no human-input channel they fail closed. Explicit denials and custom scope ceilings cannot be overridden by an approval. A bounded profile can also use approval: "never": allowed work runs; missing authority is reported as a denial, without entering an approval wait. Approval cards and legacy approval rows do not survive a restart; the operation must be requested again so it can receive a fresh decision.

Allowed subprocess network access requires actual backend enforcement. A missing sandbox does not itself authorize an unsandboxed retry. External ACP runtimes cannot enforce CoWork's bounded filesystem and network rules, so bounded tasks must use the native runtime. ACP requires explicit unrestricted Full access and compatible administrator policy, and retains its separate consent flags. A CoWork profile never silently sets the adapter to approve-all.

Select a profile

Desktop tasks

  1. Open the main task composer.
  2. Open the Permission access mode selector.
  3. Choose Ask for approval, Approve for me, Full access, or a saved custom profile.
  4. Send the task or follow-up.

The selector applies to the next task or follow-up that supports execution. The selected profile is persisted in task metadata so resuming the task does not silently adopt a broader profile later.

Defaults and custom profiles

Open Settings → System & Security → Permissions to:

  • choose the default profile for newly created tasks;
  • create, edit, validate, or remove custom profiles;
  • review profile rules and workspace-local permission rules; and
  • see the effective profile used by the runtime.

Custom profiles are stored through CoWork's encrypted settings store. The current CoWork implementation does not treat a plain-text config.toml as the authority for access profiles. The visible Custom choice is the UI for the same named-profile concept, with settings validated before they are persisted.

Agents Hub environments also have an Access profile field. It controls the profile inherited by managed sessions; command tools, filesystem access, network access, and approvals follow that profile.

Access profiles versus execution modes

An access profile answers what the task may do. The separate task modes and toggles answer how the task runs. Chat, Plan, Analyze, Execute, and Verified remain execution modes, while Collaborative, Multi-LLM, and Check-ins remain orchestration or interaction choices. The legacy Autonomous toggle can reduce optional pauses and enable eligible automatic approval behavior, but it cannot add a tool, widen a profile boundary, defeat an explicit deny, or bypass a hard guardrail. Choose Full access explicitly when a trusted task genuinely needs the full-access profile.

What a profile controls

An access profile has these independent dimensions:

FieldValuesMeaning
sandboxread-only, workspace-write, danger-full-accessProcess/filesystem boundary applied to spawned commands and code execution
approvaluntrusted, on-request, neverBaseline approval posture; hard denials remain hard
revieweruser, auto-review, noneWhether a user or automatic safety reviewer handles approval decisions
networkdisabled, on-request, enabledCoarse network capability and approval posture
workspaceRootsRelative or absolute pathsAdditional roots that the profile may reach
filesystemRulesread, write, denyDeny-first path rules; write includes reads but never deletes
domainRulesallow or deny patternsBuilt-in network-tool destination boundaries
extendsAnother profile idInherit a profile and narrow it without widening the parent

The profile also determines whether command tools can be materialized for the task. A legacy shellAccess: false value can preserve an old denial, but new profiles should not use a shell boolean as a second policy dimension.

Custom profile example

The following example keeps the normal workspace-write sandbox, limits network tools to a documentation domain, permits writes under src, makes docs read-only, and denies a sensitive directory:

{
  "id": "review-repository",
  "label": "Review repository",
  "description": "Read the repository, update source files, and use approved docs APIs.",
  "sandbox": "workspace-write",
  "approval": "on-request",
  "reviewer": "user",
  "network": "on-request",
  "filesystemRules": [
    { "path": "docs", "access": "read" },
    { "path": "src", "access": "write" },
    { "path": ".env", "access": "deny" }
  ],
  "domainRules": [
    { "pattern": "docs.example.com", "access": "allow" },
    { "pattern": "*.example.com", "access": "deny" }
  ]
}

Paths that are not absolute are resolved relative to the active workspace. workspaceRoots adds approved roots; it does not turn arbitrary paths into approved paths. Filesystem rules are evaluated with component-aware path matching after existing prefixes and symlinks are canonicalized.

Domain patterns are normalized to lowercase hostnames:

  • example.com matches that exact hostname;
  • *.example.com matches subdomains, not the apex;
  • **.example.com matches the apex and descendants; and
  • * is unrestricted for that domain-rule dimension.

Deny rules win over allow rules. An allowlist is a boundary: if an effective profile has positive domain rules, a destination outside that allowlist is not made available by a later approval or a legacy mode.

Inheritance and narrowing

Custom profiles may use extends to share a baseline. Validation rejects:

  • empty, duplicate, or built-in-colliding ids;
  • missing parents and inheritance cycles;
  • a child sandbox, approval, reviewer, or network setting that is broader than its parent;
  • child filesystem roots/rules outside the parent's effective scope; and
  • child domain allows that escape a parent's allowlist or remove a parent deny.

Inherited deny rules remain in the effective profile. A child can replace or narrow positive grants, but it cannot use inheritance to recover access that a parent denied.

This same ceiling applies to child tasks: a delegated task may choose a strictly narrower profile, but it cannot widen the parent task's sandbox, approval behavior, network, filesystem, or domain scope.

Resolution and compatibility

Profile resolution happens before individual tool approvals:

  1. An explicit task accessProfileId wins.
  2. A configured defaultAccessProfileId is attached to new tasks that do not specify a profile or a legacy permission override.
  3. If no named profile is configured, the compatibility mapping uses the existing permission settings and defaults.
  4. Existing persisted tasks that have only legacy permissionMode or shellAccess keep their prior behavior until the user explicitly selects a profile.

The profile is then applied to the workspace view used by that task. The per-request permission engine still evaluates hard task restrictions, guardrails, workspace capabilities, explicit rules, and approval defaults. The later stages cannot widen an earlier profile boundary.

Administrator policy can constrain the result. In particular, administrators can restrict legacy permission-mode mappings, allowed sandbox types, the requirement for sandboxed command execution, and shell egress. A scoped custom profile cannot use an unsandboxed process path that would bypass its filesystem or domain rules; CoWork constrains it to a representable sandbox instead.

If a named profile is missing, malformed, cyclic, or cannot be represented by the selected execution backend, CoWork uses an unavailable read-only profile:

  • filesystem access is limited to safe reads;
  • command tools and network access are unavailable;
  • the task pauses with a user-action state and an access_profile_unavailable reason; and
  • no approval prompt can turn that unavailable profile into a broader one.

Filesystem behavior

Profile filesystem access is evaluated separately for read, write, and delete:

  • a write rule grants read and write, not delete;
  • an explicit deny always wins;
  • profile rules are checked before legacy allowedPaths and unrestricted-file flags for profiled tasks;
  • macOS and Windows operating-system mutation roots remain protected;
  • .. traversal, symlink escapes, macOS /private aliases, and non-existent file suffixes are canonicalized before the decision; and
  • a plain path outside the workspace may receive a one-shot external-file approval only when no finite profile filesystem scope covers the task.

A finite profile scope is a hard boundary. The operator cannot use an external file approval, a session “Approve all” shortcut, or a legacy permission mode to expand it. Temporary-workspace scratch-file compatibility is retained only for unprofiled legacy tasks.

Network and command behavior

Built-in browser, web, and HTTP tools evaluate profile network and domain rules in-process. Read-only public lookups through web_search, web_fetch, x_search, and GET/HEAD http_request use the normal read lane when the workspace has network access. Network on-request remains useful as policy metadata for browser navigation, MCP and other network-capable tools, code or shell egress, credential-backed reads, and explicit domain or workspace boundaries, but the default local runtime does not turn those decisions into a durable approval wait. data_export remains an explicit approval class even in high-autonomy profiles, and location access always uses a separate operating-system consent flow.

Arbitrary subprocess and code-execution networking is stricter. The native macOS and Docker sandbox boundaries provide coarse network isolation, not a domain-aware proxy. Therefore a profile with domain-scoped network rules cannot run arbitrary shell/code networking unless a domain-aware proxy exists; the request fails closed. Full-profile shell egress also depends on the coarse administrator shell-network gate.

Terminal tabs use the same profile boundary as command tools. A profile that does not permit command tools cannot open a new terminal tab. Existing tabs are revalidated when the profile changes, and a downgrade prevents further use.

Surfaces and inheritance

SurfaceProfile contract
Desktop composerShows the four access choices and carries the selected id with the task
CLIcowork run "task" --access-profile <id> uses the same resolver; --permission-mode and --shell are compatibility inputs
Remote Control PlaneSends the requested profile id to the target node, where it is resolved and enforced; local credentials/cookies are not transferred
Managed AgentsManagedEnvironment.config.accessProfileId is the canonical environment setting; enableShell is legacy compatibility
Task automations and cronNew jobs inherit an explicit profile and cannot widen it through a step or trigger setting
Agent Teams and child tasksChild profiles are capped by the parent profile
Side ChatDoes not expose command tools and cannot widen the inherited profile
Browser, connector, native, and device toolsUse the same profile, admin policy, and hard guardrails before execution
Heartbeat/DreamingBackground memory reads use a profile-derived read guard; unavailable or denied profiles skip analysis rather than bypassing the boundary

Migration from the shell toggle

The old shell switch was too narrow to describe the real access decision. The current migration behavior is:

  • new desktop tasks use the configured named profile;
  • the main UI no longer presents a shell enable/disable control;
  • old task records and integrations may still carry shellAccess, enableShell, defaultShellEnabled, or legacy permission modes;
  • those fields are read only at compatibility boundaries and do not override a selected named profile;
  • the CLI --shell flag remains a compatibility alias for the bounded ask_for_approval profile, not a new unrestricted switch; and
  • managed environments should be edited to set accessProfileId explicitly.

Do not edit the database to migrate a task. Choose a profile in the task selector, update the default in Settings, or update the managed environment through its supported UI/API surface.

Implementation map

The shared policy contract lives in:

  • src/shared/access-profiles.ts — profile types, built-ins, inheritance, and privilege comparison;
  • src/electron/security/access-profile-resolver.ts — resolution, compatibility mapping, admin constraints, and workspace projection;
  • src/electron/security/access-profile-paths.ts — canonical path and profile-filesystem evaluation;
  • src/electron/security/permission-settings-manager.ts — validated encrypted persistence for defaults and custom profiles;
  • src/electron/agent/runtime/PermissionEngine.ts — per-request hard-stop and approval decisions; and
  • src/electron/agent/tools/shell-tools.ts and the tool registry — command availability and sandbox/network enforcement.

Useful verification commands:

npx vitest run src/shared/__tests__/access-profiles.test.ts src/electron/security/__tests__/access-profile-resolver.test.ts src/electron/security/__tests__/access-profile-paths.test.ts
npx vitest run tests/tools/shell-tools.test.ts src/electron/agent/runtime/__tests__/PermissionEngine.test.ts
npm run type-check
npm run build:electron
npm run build:daemon
npm run build:react

Policy rollout and migration

Permission settings are migrated to version 2 with a bounded copy of the prior policy and migration provenance. New root tasks use one normalizer across the standard daemon entry point and direct creators such as tray, gateway, mailbox, and ACP. Explicit profiles win over defaults. Old profile-less tasks keep their legacy per-task authority; resuming them does not adopt a broader global default. Read-only helpers and child-task ceilings remain restrictive.

COWORK_ACCESS_POLICY_VERSION=boundary is the default runtime policy. Set legacy for a diagnostic rollback or shadow to retain legacy decisions while recording the compared boundary decision in the local policy trace. Shadow comparison does not execute tools, create grants, or emit approval requests of its own. It does not disable hard restrictions. Restore old settings only from a reviewed migration snapshot; do not replay pending approvals during rollback.

Boundary grants are tied to operation arguments and policy identity. Changing the task scope invalidates incompatible cached grants. Existing approval history remains readable; new allowed actions do not manufacture approval history.