Roles and permissions

The twelve role flags, live permission checks, and who can do what.

Role has twelve boolean flags. The first user created at /dashboard/init gets an Admin role with all of them. Signed-in users without canAccessDashboard are sent to /dashboard/no-access.

FlagAllows
canAccessDashboardOpen the dashboard
canReadProducts / canManageProductsView or change products, releases, design systems, brand settings, onboarding
canReadOrders / canManageOrdersView or change orders; refund, reconcile, and revoke entitlements need manage
canReadPayments / canManagePaymentsView or change payments and payment providers
canReadUsers / canManageUsersView or change users
canReadRoles / canManageRolesView or change roles
canManageOnboardingSee and run onboarding (also needs canManageProducts)

Which mutation needs what

OperationNeeds
refundKitPayment, reconcileKitRefundcanManagePayments and canManageOrders
revokeKitAccesscanManageOrders
settleKitOrderOfflinecanManagePayments and canManageOrders
createComponentDraft, saveComponentDraft, validateComponentDraftcanManageProducts
submitComponentReleaseForReview, decideComponentReview, publishComponentRelease, deprecateComponentReleasecanManageProducts
runUIKitOnboardingcanManageOnboarding and canManageProducts

Live checks

Consequential mutations call requireLivePermissions, which reads the role from the database. Removing a flag takes effect on the next call even if the user's session cookie still carries the old role.

Raw lists

Most raw Keystone lists deny direct GraphQL reads and writes (denyAllOperations). Operator screens read through bounded queries (uikitOperations, uikitOperatorAccess) and write through named mutations. uikitOperatorAccess returns canReadProducts, canManageProducts, canReadOrders, canManageOrders, canReadPayments, canManagePayments, canManageOnboarding, and canReadEvidence for the current user.

Customers

Customers are User records with no dashboard access. They create accounts at /account/signup. The dashboard sign-up page at /dashboard/signup is closed unless PUBLIC_SIGNUPS_ALLOWED=true.

On this page