Users & Groups
Honeyframe's access control model has two independent axes:
- Roles govern licensing — every user has exactly one of
admin,designer,analyst, orviewer. Roles determine which areas of the product a user can reach at all (e.g. analysts can read dashboards and connectors but not edit ingestion flows). - Group permissions govern security — what specific objects a user can read, edit, or administer. Groups are orthogonal to roles: a
viewerin the "Finance Leadership" group may have read access to dashboards adesignerin the "Marketing" group cannot see.
Each org seeds four role-aligned system groups at creation time (v0.0.54+): Org Admins, Designers, Analysts, Viewers. Each group carries the default permissions for its role; users land in the matching group based on their role at signup. The two pre-existing system groups All Members and Org Admins still exist alongside.
Authorization Matrix (v0.0.54+)
The Authorization Matrix at /authorization-matrix (admin-only) is the canonical surface for viewing and editing org-wide permission grants. It's a pivot table of groups × permission strings:
- Columns are color-coded by resource family (
org/project/dashboard/dataset/feature). - An org-wide grant shows as a filled dot; a target-scoped grant shows a count badge linking to the affected resources.
- Click a cell to grant or revoke an org-wide permission inline. Optimistic update for revoke, refetch-after-write for grant. Toast feedback + pending-cell shimmer prevent double-clicks.
- The + Add permission panel accepts a free-text
<resource>.<action>string (validated lowercase + underscores) plus a target group, so a brand-new permission appears in the matrix immediately without a roundtrip through Groups Management. An autocomplete suggestion dropdown sourced fromGET /api/groups/permission-types(v0.0.55) prevents typos likefeature.chattfrom permanently dead-columning the matrix.
System admin groups and the org.admin column refuse revoke — every org always has at least one group holding org.admin as a recovery escape hatch.
Target-scoped grants stay editable on the existing Groups Management page — the matrix is the at-a-glance surface, not the per-row administration surface.
Groups themselves can be local (managed inside Honeyframe) or LDAP-mapped (synced from a directory server via the group's ldap_dn). LDAP-mapped groups carry the same permission grants as local groups; the only difference is membership source.
Permissions attach to groups via permission_type strings (for example dashboard.edit, dataset.read, org.admin) and may optionally be scoped to a specific object using target_id. The platform resolves a permission check by first granting the request if the user is a superadmin, then falling back to a legacy single-role check for backwards compatibility, and finally consulting the user's group memberships and the permissions attached to those groups. Frontend code uses the usePermission() hook and the <ProtectedRoute> component to gate UI surfaces declaratively.
The /api/groups endpoints provide CRUD for groups and their permission grants. The frontend bootstraps its permission cache from /api/groups/me/permissions on login.
Org Setup Wizard (v0.0.38)
For first-time super-admin onboarding, the Org Setup Wizard walks through the canonical setup in five steps without leaving one surface:
- Organization details — name, slug, default timezone.
- License entitlements — pick the license tier; the platform resolves which features the org has access to via the per-org entitlements resolver. Higher tiers unlock plugin enablement, opt-in RLS, and the AI generator.
- First admin — bootstrap the org's first admin user (or import from LDAP if configured). The admin is auto-added to the seeded
Org Adminsgroup. - Default groups — accept the seeded set (
Org Admins,Builders,Analysts,Viewers) or skip; groups can be added later via the Groups page. - Default project — create the org's first project. The wizard provisions it via the same idempotent
/seedendpoint that tenant seed scripts use.
Trigger the wizard from /admin/org-setup (super-admin only). Re-running it on an org that's already set up is safe — every step's underlying API is idempotent.
Per-org entitlements
A new entitlements resolver (v0.0.38) decides which features an org has access to based on its license tier. The organizations table now carries a license_tier column and a feature_flags JSON map; the resolver merges them at request time.
Common gates:
| Feature | Gate |
|---|---|
| Plugin enablement (per-org / per-tenant) | entitlements.plugins.enabled |
| Opt-in RLS for cross-tenant isolation | entitlements.rls.opt_in |
| AI generator (dashboards / agents) | entitlements.ai.generator |
| Embedded analytics (SaaS surface) | entitlements.embed |
Frontend gating uses the same usePermission() hook with an entitlement key prefix (entitlements.*); backend gating uses the require_entitlement() dependency.
Project visibility (v0.0.38)
Project visibility now defaults to allow on user/project create. When a new project is created, every existing org user is auto-stitched into a baseline dataset.read role on the project; when a new user is created, they're auto-stitched into every existing project at the same baseline. Override per-project via Project Settings → Members to restrict.
This default-allow behavior reverses the prior default-deny — the prior model required explicit grants and was a frequent "I created a project, why can't anyone see it?" support hit. Default-allow is the right choice for single-tenant deploys; multi-tenant orgs that need strict isolation should turn on opt-in RLS (see Data Policies → Cross-tenant isolation).