trustgraph/docs/tech-specs/capabilities.md
2026-04-23 11:29:09 +01:00

160 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
layout: default
title: "Capability Vocabulary Technical Specification"
parent: "Tech Specs"
---
# Capability Vocabulary Technical Specification
## Overview
Authorisation in TrustGraph is **capability-based**. Every gateway
endpoint maps to exactly one *capability*; a user's roles each grant
a set of capabilities; an authenticated request is permitted when
the required capability is a member of the union of the caller's
role capability sets.
This document defines the capability vocabulary — the closed list
of capability strings that the gateway recognises — and the
open-source edition's role bundles.
The capability mechanism is shared between open-source and
enterprise editions. The open-source edition ships a fixed
three-role bundle (`reader`, `writer`, `admin`). Enterprise editions
define additional roles by composing their own capability bundles
from the same vocabulary; no protocol, gateway, or backend-service
change is required.
## Motivation
The original IAM spec used hierarchical "minimum role" checks
(`admin` implies `writer` implies `reader`). That shape is simple
but paints the role model into a corner: any enterprise need to
grant a subset of admin abilities (helpdesk that can reset
passwords but not edit flows; analyst who can query but not ingest)
requires a protocol-level change.
A capability vocabulary decouples "what a request needs" from
"what roles a user has" and makes the role table pure data. The
open-source bundles can stay coarse while the enterprise role
table expands without any code movement.
## Design
### Capability string format
`<subsystem>:<verb>` or `<subsystem>` (for capabilities with no
natural read/write split). All lowercase, kebab-case for
multi-word subsystems.
### Capability list
**Data plane**
| Capability | Covers |
|---|---|
| `query` | Read queries: agent, text-completion, prompt, graph-rag, document-rag, embeddings, triples, rows, NLP query, SPARQL, structured-query, mcp-tool |
| `library:read` | List / fetch documents |
| `library:write` | Add / replace / delete documents |
| `collections:read` | List / describe collections |
| `collections:write` | Create / delete collections |
| `knowledge:read` | List / get knowledge cores |
| `knowledge:write` | Create / delete knowledge cores |
| `ingest` | text-load, document-load |
| `export` | Streaming exports (triples, graph-embeddings, document-embeddings, entity-contexts, core-export) |
| `import` | Streaming imports (triples, graph-embeddings, document-embeddings, entity-contexts, rows, core-import) |
**Control plane**
| Capability | Covers |
|---|---|
| `config:read` | Read workspace config |
| `config:write` | Write workspace config |
| `flows:read` | List / describe flows, blueprints, flow classes |
| `flows:write` | Start / stop / update flows |
| `users:read` | List / get users within the workspace |
| `users:write` | Create / update / disable users within the workspace |
| `users:admin` | Assign / remove roles on users within the workspace |
| `keys:self` | Create / revoke / list **own** API keys |
| `keys:admin` | Create / revoke / list **any user's** API keys within the workspace |
| `workspaces:admin` | Create / delete / disable workspaces (system-level) |
| `iam:admin` | JWT signing-key rotation, IAM-level operations |
| `metrics:read` | Prometheus metrics proxy |
### Open-source role bundles
The open-source edition ships three roles:
| Role | Capabilities |
|---|---|
| `reader` | `query`, `library:read`, `collections:read`, `knowledge:read`, `flows:read`, `config:read`, `keys:self` |
| `writer` | everything in `reader` **+** `library:write`, `collections:write`, `knowledge:write`, `ingest`, `export`, `import` |
| `admin` | everything in `writer` **+** `config:write`, `flows:write`, `users:read`, `users:write`, `users:admin`, `keys:admin`, `workspaces:admin`, `iam:admin`, `metrics:read` |
Open-source bundles are deliberately coarse. `workspaces:admin` and
`iam:admin` live inside `admin` without a separate role; a single
`admin` user holds the keys to the whole deployment.
### Authorisation evaluation
For a request bearing a resolved set of roles
`R = {r1, r2, ...}` against an endpoint that requires capability
`c`:
```
allow if c IN union(bundle(r) for r in R)
```
No hierarchy, no precedence, no role-order sensitivity. A user
with a single role is the common case; a user with multiple roles
gets the union of their bundles.
### Unknown capabilities and unknown roles
- An endpoint declaring an unknown capability is a server-side bug
and fails closed (403, logged).
- A user carrying a role name that is not defined in the role table
is ignored for authorisation purposes and logged as a warning.
Behaviour is deterministic: unknown roles contribute zero
capabilities.
### Capability scope
Every capability is **implicitly scoped to the caller's resolved
workspace**. A `users:write` capability does not permit a user
in workspace `acme` to create users in workspace `beta` — the
workspace-resolver has already narrowed the request to one
workspace before the capability check runs. See the IAM
specification for the workspace-resolver contract.
The three exceptions are the system-level capabilities
`workspaces:admin` and `iam:admin`, which operate across
workspaces by definition, and `metrics:read`, which returns
process-level series not scoped to any workspace.
## Enterprise extensibility
Enterprise editions extend the role table additively:
```
data-analyst: {query, library:read, collections:read, knowledge:read}
helpdesk: {users:read, users:write, users:admin, keys:admin}
data-engineer: writer + {flows:read, config:read}
workspace-owner: admin {workspaces:admin, iam:admin}
```
None of this requires a protocol change — the wire-protocol
`roles` field on user records is already a set, the gateway's
capability-check is already capability-based, and the capability
vocabulary is closed. Enterprise introduces roles whose bundles
compose the same capabilities differently.
When enterprise introduces a new capability (e.g. for a feature
that does not exist in open source), the capability string is
added to the vocabulary and recognised by the gateway build that
ships that feature.
## References
- [Identity and Access Management Specification](iam.md)
- [Architecture Principles](architecture-principles.md)