Attribute-Based Access Control
The role model grants access to named people on named resources. Attribute-Based Access Control (ABAC) adds a second way to write rules: by attributes. Instead of "Alice can read this collection," you write "anyone in the Finance department can read documents labelled Finance," or "users with clearance 3 or higher can read documents classified Confidential or below."
Rules adapt as people and content change: a new hire who joins the Finance group is covered when their attributes match. Principal attributes and matching rules are resolved when an access token is issued, so refresh the user's session or issue a new API-key token before testing a policy change.
The two kinds of attribute rules
ABAC rules are still Access Control Entries, but instead of pointing at a fixed principal or resource they match by conditions:
- Principal Set — matches who by their attributes (a user's department, tags, clearance; whether the caller is a user or an API key). Evaluated when a token is issued: the match decides whether the rule even enters that person's session.
- Resource Set — matches which content by its attributes (a document's sensitivity, its properties, its type). Evaluated when content is queried: the match decides which documents the rule lets through.
A rule can combine both: a Principal Set on the "who" side and a Resource Set on the "what" side. That pairing — these principals get this role on content matching these conditions — is the heart of attribute-based and clearance-based access.
An empty condition list is a wildcard. A Principal Set with no principal conditions matches everyone (including API keys); a Resource Set with no resource conditions matches every document. Wildcards are powerful — reach for them deliberately.
Writing conditions
A condition is a field, an operator, and a value. Multiple conditions in one rule are combined with AND.
| Operator | Meaning | Supported in |
|---|---|---|
$eq / $ne | equals / does not equal | Principal Sets and Resource Sets |
$gt / $gte / $lt / $lte | greater / less than | Principal Sets and root-level numeric Resource Set fields |
$in / $nin | is / is not one of a list | Principal Sets and Resource Sets |
$exists | the field is present (or absent) | Principal Sets and Resource Sets |
$empty | the field is empty (or non-empty) | Principal Sets and Resource Sets |
$like | case-insensitive wildcard text match, where * matches anything | Principal Sets only |
Where the field name goes depends on what it is:
- Principal fields sit at the root of Principal Set conditions:
clearance,compartments,tags,email, andkind(userorapikey). - Content classification fields sit at the root of Resource Set conditions:
sensitivity,compartments, andtags. - Custom attributes sit under
properties.— for exampleproperties.department,properties.region.
Referring to the person in a content rule
Inside a Resource Set, a value may reference the requesting person with the $principal. prefix. It is
resolved to that person's own value when their token is issued, then matched against each document.
{ "sensitivity": { "$lte": "$principal.clearance" } }
Read this as "only show documents whose sensitivity is at most the viewer's own clearance" — the classic
"no read up" rule. $principal. works only inside Resource Set (content) conditions; it has no effect on the
principal side.
Two kinds of roles
Roles are split into two partitions, and the platform always checks the built-in set first so custom roles can never shadow it:
-
System roles — the built-in roles from the role model:
owner,admin,developer,reader, and so on. These govern the platform as a whole. -
ABAC content roles — a separate, content-only set defined for attribute rules:
Role Grants content:readerread content:writerread, write content:managerread, write, delete
When you build an attribute rule over content, you use a content role, not a system role. This keeps the two worlds cleanly separated: system roles decide what someone can do across the platform; content roles decide, attribute by attribute, which documents they reach.
Document scope and collection scope
A content role applies to a scope — either document (the default) or collection. The same
content:reader / content:writer / content:manager roles serve both; the scope you choose on the rule
decides whether it governs documents or collections. This distinction matters for restriction, below.
Classification: clearance, compartments, and sensitivity
ABAC has first-class support for a Bell-LaPadula (BLP) style classification model, so you can express clearance-based access without hand-writing conditions.
On people (users, groups, and API keys):
- Clearance — a level from 0 to 4. The higher the clearance, the more sensitive the content the person may reach.
- Compartments — named need-to-know areas (for example
engineering,hr,legal). A person only reaches content in compartments they hold.
On content (documents and collections):
- Sensitivity — the same 0–4 scale, labelled Public, Internal, Confidential, Restricted, Secret.
- Compartments — the areas a document belongs to.
When a person belongs to several groups, their clearance is the highest of any group or their own (most permissive wins), and their compartments are the union of all of them. A document that belongs to several collections takes the highest sensitivity and the union of their compartments (most restrictive wins).
The two canonical rules, written as Resource Set conditions:
{ "sensitivity": { "$lte": "$principal.clearance" } }
{ "compartments": { "$in": "$principal.compartments" } }
Together: a person sees a document only when its sensitivity is within their clearance and they share at
least one of its compartments. For write access you invert the sensitivity comparison ($gte) — the "no write
down" rule — so classified information cannot be copied into a less-classified document.
Advanced: unclassified content
Documents created before classification was enabled may have no sensitivity or compartments at all. So a
clearance rule does not silently hide them, the platform treats unclassified content as public: any rule
that references a classification field automatically also allows documents where that field is absent or
empty. When you roll classification out, plan to set a baseline sensitivity on existing content, or lean on
this fallback during the transition.
Turning restriction on: the Restrict Mode preset
By default a project is open: its content is visible to members through the role model, and attribute rules only add access. To make attributes the gate — nothing visible unless a rule grants it — you switch the project into restrict mode.
The IAM permissions screen has a one-click Restrict Mode preset. It creates a single rule that pairs a
wildcard Principal Set (named BLP Restrict Mode, matching everyone) with a Resource Set (named Deny All
(BLP)) whose condition matches no real document. The effect: every person now carries a content-security
context, which drops the project:* default. A resource remains available when it directly names that user or
one of their groups, or when another ABAC rule grants access. Create and test the real granting rules first, apply
Restrict Mode, refresh test tokens, and then expand the rollout.
The companion Open Mode preset does the reverse — a wildcard rule granting everyone read access to all content — to return a project to open.
Restrict Mode restricts documents, not collections. The preset is document-scoped, and content lookups
never fall back across scopes: a document-level restriction leaves the collection listing open. If you
also need to lock down which collections people can see, add a separate rule with the scope set to
collection (using the same content:reader / content:writer / content:manager roles). Restricting
documents and restricting collections are two independent switches.
Advanced: how restriction reaches the query
Attribute rules are resolved when a token is issued, not when content is queried. Each matched rule
contributes its role's verbs to a content-security block in the caller's session, keyed by scope — a
document-scope rule contributes bare read / write / delete keys, while a collection-scope rule
contributes collection:read / collection:write / collection:delete. At query time the content service
reads only the keys for the scope it is filtering and turns each rule's conditions into an OR filter; there is
no cross-scope fallback by design, which is exactly why a document restriction does not touch collections. If
no key exists for a scope, that scope stays in open mode. Because all of the "who can see what" logic is
settled at token time, the query path stays a simple, fast filter.
Token lifecycle
Role assignments, group membership, principal attributes, and matching Principal Sets are captured when the access token is issued. After changing any of them, refresh or restart the user's authenticated session. For an API key, issue a new access token. Long-running agents inherit the effective user's conditions when their agent token is created, so start a fresh run when validating an updated policy.
Where to go next
- Configure roles and memberships.
- Set principal attributes and classify content.
- Create and test dynamic access rules, including a rule-local dry run with Studio Assistant before saving the policy.
- For application and token-handling guidance, see the developer guide.
