DisplaySync

Roles & permissions

DisplaySync has three role hierarchies that compose:

  1. System roles — assigned globally to every user (admin or user)
  2. Organization roles — assigned per-org (owner, admin, member)
  3. Event roles — assigned per-event (manager, technician)

A user's effective permissions on a given resource are the union of their system role's permissions, their org role's permissions on that org, and their event role's permissions on that event. Implicit access rules fill in gaps so org members never need to be explicitly added to every event.

System roles

Stored in Users.role. Two values:

RoleUse case
adminPlatform administrators (DisplaySync staff, support, devops)
userEveryone else — permissions come entirely from org and event roles

Admin bypasses all org and event checks. That's why platform admins can answer "yes I see your sign" in support without being on the customer's team.

Organization roles

Stored in OrganizationUsers.role. Three values, hierarchical:

owner > admin > member
RoleDescriptionKey capabilities
ownerOrg creatorTransfer ownership, delete org, all admin capabilities
adminManage the orgAdd/remove members, manage settings, create events, manage billing
memberUse the orgView resources, be assigned to event teams, implicit viewer access to events

Rules:

  • An org always has exactly one owner. Transferring ownership swaps which user holds it.
  • The owner can't be removed without transferring first.
  • Only org owners and admins can create events (see R.3 enhancement).

Event roles

Stored in EventUsers.role. Two values are explicitly assignable; a third is implicit for org members.

manager > technician > viewer (implicit)
RoleDescriptionAssignment
managerOwns the eventExplicit; granted by another manager or org owner/admin
technicianHands-on operatorExplicit; granted by a manager
viewerRead-only accessImplicit — every org member has viewer access to every event in their org

Implicit viewer access matters because org members never need to be individually added to events to see them. Event teams (manager + technician) are for write access only.

Org → event implicit roles

Org roleImplicit event role on every event in the org
ownermanager
adminmanager
memberviewer

So an org admin who hasn't been added to an event's team still sees and can edit that event as if they were a manager.

EventUser cascade gotcha

EventUser's userId foreign key references the users table directly (with onDelete: CASCADE on User delete). It does not reference OrganizationUser. Removing a user from an org via OrganizationUser.destroy does NOT cascade-delete their EventUser rows — the user remains a User, only their OrganizationUser row is gone, so any EventUser rows still point at a now-non-org-member User. The event-controller team-listing logic filters these orphaned rows on read (returns explicit + virtual implicit, drops EventUser rows whose user isn't an org member). Explicit cleanup happens in the org-membership removal flow.

Permission matrix

The authoritative grid for what each role can do.

System actions

ActionAdminUser
Create / delete organizations✅❌
Manage all users globally✅❌
Bypass org/event access checks✅❌
Set special tiers (Pilot, Managed, billing exempt)✅❌

Organization actions

ActionOwnerAdminMember
View org details✅✅✅
Update org settings✅✅❌
Manage members (invite, remove, change role)✅✅❌
Promote member to admin✅✅❌
Transfer ownership✅❌❌
Delete organization✅❌❌
Manage billing (add a payment method, Stripe portal)✅✅❌
Create events✅✅❌

Event actions (explicit + implicit)

ActionOrg owner / admin (implicit manager)ManagerTechnicianMember with no event role (implicit viewer)
View event✅✅✅✅
View signs in event✅✅✅✅
View content in event✅✅✅✅
View analytics✅✅✅✅
Update event details (incl. timezone)✅✅❌❌
Manage event team✅✅❌❌
Archive / unarchive event✅✅❌❌
Start a bulk refresh / update wave✅✅❌❌
Cancel a wave / retry its failures✅✅❌❌
Watch a running wave✅✅✅❌
Arm / disarm the auto-update policy✅✅❌❌
Add / remove a scheduled refresh✅✅❌❌
Delete event✅❌❌❌

Two rows there surprise people. Delete event is narrower than every other manager action — an explicit event manager can edit and archive but not delete; that stays with org owners and admins. And deleting is refused entirely while signs are still registered to the event, whatever your role (error codes). Watching a wave is wider than starting one on purpose: a technician standing at the wall while a manager fires an update from the office should be able to see that the screens going dark is intentional.

Sign actions in an event

The authoritative matrix per the route-authorization spec (Track X, 2026-04-24):

ActionViewer (implicit for org members)TechnicianManager (incl. org owner/admin)
View sign list✅✅✅
View sign detail + live status✅✅✅
View sign analytics✅✅✅
Pre-register sign for event❌❌✅
Claim sign to event❌✅✅
Link sign to pre-created record❌✅✅
Unlink sign from event❌✅✅
Update sign fields (name, location)❌✅✅
Set sign content (assign URL)❌✅✅
Send remote command (refresh, reboot, fetch logs)❌✅✅
Delete sign entirely❌❌✅

Content actions

ActionOwner / AdminMemberNotes
View content library✅✅
Add content to library✅❌Defensive placeholder; full content-role design pending
Update content✅❌Same
Archive / delete content✅❌Same
Use content (assign to a sign)Per event rolePer event roleSign assignment uses event-role gates above

The content placeholder will relax to include an explicit "content editor" role in a future RBAC update.

Audit & analytics

ActionAdminOrg owner/adminOrg memberEvent managerEvent technician
Org-wide audit log✅✅❌❌❌
Per-event audit log✅✅View only✅View only
Per-sign audit log✅✅View only✅View only
Cross-org analytics✅❌❌❌❌

Plan limits

Plans don't gate roles or features. The only plan limits are the free floor — at most 2 signs linked at a time without a payment method — and storage. The capability flags in the plan configuration are reserved for features that haven't shipped and gate nothing today. Adding a payment method and managing billing are owner and admin actions. See Pricing and billing.

requireAnyOrgMembership

Different from event-role gating. requireAnyOrgMembership checks "the caller is a member of SOME org" — used for endpoints that are org-context-dependent but not org-specific. Currently wired on playlist routes; refuses outsiders with 403 instead of returning an empty list. System admins bypass this check.

Notification subscriptions

Anyone with view access to an event can subscribe to its notifications. The default subscription is enabled when:

  • An org owner/admin (because they have implicit manager access)
  • A user explicitly added to an event's team in any role

See Notifications → Event subscriptions.

Cross-organization team membership

A user can belong to multiple orgs. Each membership has an independent role. The dashboard shows the org switcher in the top-left; switching orgs changes which org's data you're operating on but doesn't change your roles in other orgs.

A cross-organization dashboard — one view across every org you manage — is planned for agencies; it isn't available yet.

Common patterns

"Conference manager + AV crew"

  • Conference organizer: org admin (or owner if it's their company's account)
  • Event lead: event manager
  • AV technicians: event technician
  • Stakeholders / view-only people: org member, no event team membership — they see everything as implicit viewers

"Multi-event AV company"

  • Account owner: org owner
  • Account admins: org admin
  • Per-event leads: event manager on the events they own
  • Floating techs who work multiple events: org member + event technician on the events they're assigned to

"Solo operator"

  • Single user as org owner. Implicit manager on all events. No team to set up.

How permissions are enforced

The backend uses a stack of middleware on every protected route:

MiddlewareChecks
requireAuthValid session
requireOrgRole(...roles)Caller has at least one of the listed org roles in the target org
`requireResourceAccess('sign''schedule'
requireMinEventRole(role)Caller's effective event role ≥ given role (with implicit access)

Failures return 403 Forbidden with a brief message. The dashboard's UI also hides actions you can't perform — so you shouldn't normally hit a 403 from a button click.

Change history

DateChange
2026-04-24Track X — route authorization hardened. New requireMinEventRole and requireAnyOrgMembership middleware.
2026-01-23R.3 — Event creation restricted to org owners/admins. Org members get implicit viewer access. Explicit viewer role removed from event teams.
EarlierR.2 — hierarchical event-role checking, implicit viewer access for org members.
EarlierR.1 — system roles simplified from 4 (admin, event_manager, technician, viewer) to 2 (admin, user).

See also