---
summary: ScaiMatrix module permissions, default role mapping, and how they compose
  with per-resource ACLs.
title: Permissions
path: reference/permissions
status: published
---

# Permissions

ScaiMatrix uses a two-layer model. Module permissions gate API access at the coarsest level — they decide whether a caller can hit the endpoints at all. Per-resource ACLs (covered in [ACLs](../concepts/acls)) decide whether a specific call against a specific collection or document succeeds. A caller needs **both**.

## Module permission keys

| Key | Grants |
|---|---|
| `scaimatrix:view` | List collections, read collection / document metadata, view graph views, view re-chunk and re-extract status. |
| `scaimatrix:search` | Run vector, hybrid, and combined searches; run graph queries, traversals, paths, ask, context, search. |
| `scaimatrix:ingest` | Upload documents, ingest-from-URL, ingest-from-ScaiDrive, run crawls. Subject to per-resource `INGEST` on the target collection. |
| `scaimatrix:manage` | Create, update, delete collections; fork; re-chunk; re-extract; manage crawl configs. |
| `scaimatrix:graph_edit` | Create, update, delete graph nodes and edges; bulk-import graphs. |
| `scaimatrix:access` | Read and modify ACLs (any endpoint under `/permissions/...` and legacy `/access`). |

## Default role mapping

ScaiGrid's built-in roles auto-grant the module permissions via `CurrentUser.has_module_permission`:

| Role | Auto-grants |
|---|---|
| `super_admin` | All of the above, across every tenant. |
| `partner_admin` | All of the above for tenants under the partner. |
| `tenant_admin` | All of the above for the tenant. |
| `tenant_user` | None by default. |
| `tenant_viewer` | `scaimatrix:view`, `scaimatrix:search`. |

`tenant_user` gets nothing automatically because indexing burns budget and ACL administration has compliance implications — you want explicit opt-in.

## Granting selectively

Most tenants define a single custom role for engineers maintaining knowledge bases:

```bash
curl -X POST "$SCAIGRID_HOST/v1/iam/custom-roles" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Knowledge editors",
    "module_permissions": [
      "scaimatrix:view",
      "scaimatrix:search",
      "scaimatrix:ingest",
      "scaimatrix:graph_edit"
    ]
  }'
```

A separate "Knowledge admins" role with `scaimatrix:manage` and `scaimatrix:access` is held by fewer people.

## How module perms compose with ACLs

The two layers run in series:

1. **Module permission** is checked at the route boundary by ScaiGrid's RBAC system.
2. **Per-resource ACE** is checked by `AclResolver.can(user, ref, Permission.X)` against the target collection or document.

A user with `scaimatrix:search` but no `READ` ACE on any collection sees an empty list — the endpoints don't 403, they return zero rows. That's deliberate: it stops permission probes from leaking which collections exist.

A user with `scaimatrix:manage` cannot manage a specific collection if they lack `MANAGER` on its ACL — the role gets you to the endpoint, the ACL decides what happens there.

## Bypasses

Three principals skip ACL checks regardless of ACEs:

- **`SUPER_ADMIN`** — global, all tenants.
- **`TENANT_ADMIN`** — every resource in their tenant.
- **Resource owner** — the resource they own, even against explicit denies.

Owners and admins still need the relevant module permission, e.g. a tenant admin without `scaimatrix:view` can't see ScaiMatrix at all.

## Audit

Every ACL mutation and ownership transfer is logged through ScaiGrid's audit pipeline. Filter with `module=scaimatrix`:

```bash
curl "$SCAIGRID_HOST/v1/audit/events?module=scaimatrix&since=2026-05-01" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"
```

Graph mutations (`node.created`, `edge.deleted`, `import.completed`, `reextract.queued`) and ACL changes (`ownership.transferred`) appear here with `actor_user_id`, `resource_type`, `resource_id`, and a `details` payload.
