Access Model
The authoritative role, permission block, assignment, and policy model.
Atom Access Model
Status: Authoritative Draft
Date: 2026-05-26
This document is the product source of truth for Atom authorization.
One-line Model
Vocabulary
| Term | Meaning |
|---|---|
| Action | Atomic operation, formerly called Capability. |
| Action Applicability | Which object kinds/types support an action. |
| Permission Block | Scope + actions + effect + conditions. Single source of permission logic. |
| Role | Named collection of Permission Blocks. |
| Role Assignment | Gives a Role to an Entity or Principal Group. Has no scope. |
| Direct Policy | Gives one Permission Block directly to an Entity or Principal Group. Has no duplicated scope/actions. |
| Principal Group | Who-container: users, services, devices, applications, workloads. |
| Object Group | Where-container: clients, channels, resources, child Object Groups. |
Core Rules
- Scope lives only in Permission Blocks.
- Actions live only in Permission Blocks through
permission_block_actions. - Roles do not have scope or direct action rows.
- Role Assignments do not have scope or direct action rows.
- Direct Policies do not duplicate scope/actions; they reference a Permission Block.
- Permission Block is the single source of truth for runtime authorization, role-based access, direct grants, deny rules, and conditions.
Actions
Action is a global operation name.
Examples:
Action naming is hybrid:
- stored protected objects use generic actions, for example
readonaudit_log,manageorrevokeoncredential,createormanageontenant, androtateonsigning_key; - scoped access administration keeps explicit actions:
role.managemanages roles for a Permission Block scope, andpolicy.manageadds/removes assignments for that scope; - system authorization checks keep
authz.check.
Do not create object-specific action names such as:
Action Applicability validates where an action is valid. It does not grant access.
Examples:
Permission Blocks
Permission Block is the atomic permission unit.
Permission Block fields:
Actions are linked through:
Scope Modes
Scope modes live only in Permission Blocks.
| Scope mode | Meaning |
|---|---|
platform | Global/platform. |
tenant | Tenant/domain object itself. |
object_kind | All objects of one kind. Tenant-bound when tenant_id is set, platform-wide when tenant_id is null. |
object_type | All objects of one type. Tenant-bound when tenant_id is set, platform-wide when tenant_id is null. |
object | One exact entity/resource/object. May be tenant-bound or platform-level. |
group | Object Group itself. |
group_direct_objects | Entities/resources directly inside an Object Group. |
group_descendant_objects | Entities/resources inside child/deeper Object Groups. |
group_child_groups | Immediate child Object Groups themselves. |
group_descendant_groups | Child/deeper Object Groups themselves. |
Examples:
Platform Filtered Scopes
Platform filtered scopes are platform-owned Permission Blocks where tenant_id = NULL and scope_mode is object_kind, object_type, or object.
They are for platform/system identities that need cross-tenant access without receiving an all-purpose platform grant.
What this enables:
- a global service can read every device entity across all active tenants;
- a global workload can read every channel resource across all active tenants;
- an internal automation job can manage one exact protected object without receiving broad platform
manage; - platform admins can define reusable cross-tenant service roles with least-privilege object filters.
Example: read all devices across the platform.
Example: read all channel resources across the platform.
Example: read all resources, regardless of resource type.
Example: manage one exact object only.
This is safer than:
because a platform read grant matches every protected object kind, while a platform filtered scope still limits the grant by object kind, object type, or exact object id.
Risks and guardrails:
tenant_id = NULLonobject_kindorobject_typeis cross-tenant by design. Treat it as platform-level access, even though it is narrower thanscope_mode = platform.- Assign platform filtered scopes only through platform roles or platform direct policies. A platform assignment must target a global entity or platform principal group; it must not target a tenant-owned entity.
- Use namespaced object types such as
entity:deviceandresource:channel. Bare values such asdeviceorchannelare invalid and can create misleading policy intent. - Prefer
object_typeoverobject_kindwhen a service needs one subtype.object_kind = entityincludes humans, devices, services, workloads, and applications. - A platform
denywith a filtered scope can deny matching objects across all tenants. Review deny blocks as carefully as broad platform allow blocks. - Platform filtered scopes do not make Object Group scopes platform-wide. Object Group scopes still require a tenant boundary and a concrete group.
- Platform filtered grants still respect tenant lifecycle checks. Objects in inactive, frozen, deleted, or soft-deleted tenants are not made readable by this scope.
Roles
Role is a business-facing name for a set of Permission Blocks.
Role links Permission Blocks:
Roles have no scope columns and no direct action columns.
Role Assignments
Role Assignment grants a Role to a subject.
Subject can be:
- Entity
- Principal Group
Role Assignment fields:
Role Assignment has no scope and no action rows.
Direct Policies
Direct Policy grants one Permission Block directly to a subject.
It exists for advanced/internal cases:
- client-channel publish/subscribe links
- service grants
- explicit deny rules
- temporary or conditional grants
- break-glass access
Direct Policy fields:
Direct Policy does not duplicate scope, actions, effect, or conditions. Those come from the referenced Permission Block.
Principal Groups
Principal Group is a who-container.
It can contain:
- humans
- services
- applications
- workloads
- devices if needed
Principal Group is a subject for Role Assignments and Direct Policies.
Principal Group is not an Object Group and is not used as a scope boundary.
Object Groups
Object Group is a where-container.
It can contain:
- entities such as clients/devices
- resources such as channels, rules, reports, alarms
- child Object Groups
Object Group containment alone grants no access. It only affects whether a Permission Block scope matches a protected object.
One object belongs to one Object Group in V1.
Effective Authorization
Atom evaluates both access paths into one effective permission shape:
The PDP must treat these paths as one logical source:
Decision:
- Resolve the requested action and protected object.
- Find matching effective permissions.
- If any matching deny exists, deny.
- If any matching allow exists, allow.
- Otherwise deny.
Listing Authorization Semantics
All normal list queries are read-filtered.
A caller does not need a separate list action to list normal objects. A record appears in a list response only if the caller has read access to that specific record.
Examples:
channelsquery returns channels the caller canread.entitiesquery returns entities the caller canread.objectGroupsquery returns Object Groups the caller canread.
Listing must not:
- fetch all tenant rows and call PDP once per row.
- compute total count before authorization filtering.
- leak unreadable object IDs, names, counts, or ordering position.
Listing must:
- apply authorization inside SQL or equivalent DB-side query logic.
- include direct policies.
- include reusable permissions through role assignments.
- include entity assignments.
- include Principal Group assignments.
- include nested Principal Group membership if hierarchy is enabled.
- include Object Group scopes.
- support deny-overrides-allow.
- apply search/sort/pagination after authorization filtering.
Separate actions such as policy.manage and role.manage are only for scoped access administration, not for listing ordinary domain objects.
Validation Rules
- Platform Permission Blocks require
tenant_id = NULL. - Tenant and Object Group Permission Blocks require
tenant_id. object_kind,object_type, and exactobjectPermission Blocks may be platform-wide whentenant_id = NULL.- Role and Permission Block tenants must match, except platform roles/blocks use
NULL. - Role Assignment tenant must match Role tenant.
- Direct Policy tenant must match Permission Block tenant.
- Subject Entity tenant must match assignment/direct-policy tenant, except global entities may receive tenant access through active tenant membership.
- Subject Principal Group must belong to the same tenant.
- Object Group scope must reference an Object Group in the same tenant.
- When an exact Object Permission Block has a tenant boundary, the object target must belong to that tenant.
- Every action in a Permission Block must be valid for that Permission Block scope using Action Applicability.
Product UI Language
Normal UI should expose:
Advanced/security UI may expose:
Avoid normal user-facing terms: