iolite/Nebula

Access Control

Nebula's access control treats every actor — end users, systems, external integrations — as a principal with fine-grained permissions that flow through a role hierarchy. This isn't bolted on; it's woven into every query, mutation, and subscription.

Principals

A principal is any actor in a Nebula application. There are three types:

End Users are people in a browser. They authenticate through your application's identity provider and receive roles that determine what they can see and do.

Systems are automated workers that process entities. System principals have full access to all entities and API operations — they're trusted code running in your infrastructure.

External Apps are third-party integrations, data importers, or partner services. Like end users, they have roles and permissions. They can also be issued API keys for programmatic access.

Every principal — including systems and external apps — gets an auto-created personal entity upon creation. This entity exists alongside all other entities in your Nebula world. It can carry components, link to other entities, and participate in queries.

End UserAuthenticates via IdPHolds rolesOwns entitiesSystemAutomated workerFull accessTrusted codeExternal AppThird-party integrationHolds rolesAPI key accessPrincipalAuto-created personal entity

Because personal entities are just regular entities, you can attach components to them and link other entities to them. A common pattern: link a notification entity to a user's personal entity. When the user reads it, unlink it — but keep it connected to whatever context it references. The frontend treats linked entities as "unread notifications" using a simple relationship query.

Roles and Policies

Roles are composed of policies for each available component in your Nebula application. Each component can have up to eight permission flags:

PermissionScopeMeaning
CreateGlobalCreate entities with this component
ReadGlobalRead this component on any entity
UpdateGlobalModify this component on any entity
RemoveGlobalRemove this component from any entity
Create OwnedPersonalCreate entities with this component (owned by self)
Read OwnedPersonalRead this component on entities you own
Update OwnedPersonalModify this component on entities you own
Remove OwnedPersonalRemove this component from entities you own

The "Owned" variants apply only to entities owned by the principal. This lets you create roles where a user can manage their own work items but only view others'.

Role Hierarchy and Supersession

Roles can supersede other roles, creating an organizational hierarchy:

Principals can hold multiple roles simultaneously. When roles overlap, the greater level of access wins at a component level. A user who is both a "Viewer" and an "Editor" gets the editor's permissions.

Healthcare example: A "Billing Manager" role supersedes "Billing Specialist." The manager can see and reassign specialist-owned accounts, view their uploaded documents, and see entities linked to each specialist's personal entity (like work assignments and activity history). The specialist can only see and work with their own assigned accounts.

Billing ManagersupersedesBilling SpecialistSee linked entitiesCRUD owned entitiesAccess filesReassign accountsFull team scopeOwn entities onlyPersonal scope

Ownership as Access

Because ownership is a first-class concept on every entity, it integrates directly with queries and access control:

My Accounts

and
hasAccount
hasDenialPriority
minemine()

Team Accounts

and
hasAccount
hasDenialPriority
ownedByownedBy(teamMemberIds)

Unassigned

and
hasAccount
hasDenialPriority
ownedowned(false)

The access control layer evaluates these queries against the principal's roles. A specialist running the "all accounts" query only sees entities their role permits — Nebula filters transparently.


Access control operates on Components — the unit of permission. Understanding how components are defined and evolved is key to designing effective role policies.