# ACL evaluation order

Comind.work evaluates access control in four layers, from broadest to narrowest. Each layer can only narrow the access granted by the layer above it - a more specific layer cannot grant access that a broader layer denied.

```
┌─────────────────────────────────────────┐
│  1. Workspace                           │
│  ┌───────────────────────────────────┐  │
│  │  2. App                           │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │  3. Record                  │  │  │
│  │  │  ┌───────────────────────┐  │  │  │
│  │  │  │  4. Field             │  │  │  │
│  │  │  └───────────────────────┘  │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘
```

## Layer 1 - workspace[​](#layer-1---workspace "Direct link to Layer 1 - workspace")

The workspace layer determines who can access the workspace at all. Workspace admins manage this through participant management and group assignment.

A user who is not a participant of a workspace has zero visibility into its contents - no apps, no records, no fields.

## Layer 2 - app[​](#layer-2---app "Direct link to Layer 2 - app")

Within a workspace, each app can be visible or hidden per group. If a user's group does not have access to an app, that user cannot see it in navigation or reach it by direct URL.

**Example:** A workspace has a "Contracts" app restricted to the Legal group. Users in the Sales group can access other apps in the same workspace but cannot see or open Contracts.

## Layer 3 - record[​](#layer-3---record "Direct link to Layer 3 - record")

Individual records can carry their own access restrictions beyond what the app allows. The `plugin-permission` adds ACL fields to records, letting you lock down specific items to specific users or groups.

**Example:** A project record in a shared app can be restricted so only the assigned team sees it. Other workspace participants with app access still cannot view that record.

Record-level ACL uses allow-list narrowing only. There are no explicit deny rules - access is granted by inclusion, and each layer can only narrow what the layer above allowed.

## Layer 4 - field[​](#layer-4---field "Direct link to Layer 4 - field")

Individual fields can be restricted to specific groups or roles. The field `access` option accepts three values:

* `workspaceAdmin` - only workspace administrators can see or edit the field
* `workspaceTeam` - any workspace team member can access the field
* `groups` - only members of specified groups can access the field

**Example:** A "Cost" field on a deal record is set to `access: 'workspaceAdmin'`. Regular users can view the deal but that field is invisible to them.

Whether a restricted field is hidden or read-only is configurable per field. Layout markup can set a readonly marker on a field, and UI-logic visibility rules can make a field conditionally readonly, required, or visible.

## How layers interact[​](#how-layers-interact "Direct link to How layers interact")

Consider a user named Alice who belongs to the "Engineering" group:

1. **Workspace** - Alice is a participant, so she passes layer 1
2. **App** - The "Bug tracker" app is visible to Engineering, so she passes layer 2
3. **Record** - A specific bug report is restricted to the QA group via `plugin-permission`. Alice is not in QA, so she is denied at layer 3

Alice never reaches layer 4 for that record. The evaluation stops at the first layer that denies access.

## Admin overrides[​](#admin-overrides "Direct link to Admin overrides")

Two admin roles bypass normal ACL evaluation:

* **System admin** (global admin) - full access across all workspaces, apps, records, and fields
* **Workspace admin** - full access within their workspace, regardless of app, record, or field restrictions

There is no mechanism to limit a workspace admin's access to specific apps. Workspace admins always have full access to everything in their workspace.

## Groups[​](#groups "Direct link to Groups")

Groups are the primary mechanism for controlling access across all four layers. Each user belongs to one or more groups. Groups determine:

* Which apps are visible in a workspace
* Which fields a user can see or edit
* Which actions a user can perform (via action preconditions)
* Which records are accessible (via record-level ACL)

Assign users to groups through the workspace participant management interface. Group names are workspace-specific - a "Managers" group in one workspace is independent of a "Managers" group in another.

## Automated workflows[​](#automated-workflows "Direct link to Automated workflows")

When a server-side action or job runs on behalf of another user via the `runAsUser` parameter, the system:

1. Checks that the calling user has "run as" permission for the target user
2. If allowed, executes API calls using the target user's identity
3. All subsequent ACL checks evaluate against the **target user's permissions**, not the caller's

This means `runAsUser` adopts the target user's access level. If the target user has less access than the automation account, the operation is restricted. If the target user has access the caller doesn't, the caller must first have "run as" permission granted to impersonate them.

## Related[​](#related "Direct link to Related")

* [Permissions matrix](/admin-guide/workspace-admin/permissions-matrix.md) - complete reference of permission levels and common access patterns
* [Manage groups and permissions](/admin-guide/workspace-admin/manage-groups-permissions.md) - how to set up groups and configure per-app, per-record, and per-field permissions
