Convex Tenants
Multi-tenant organization and team management for Convex with flexible authorization via @djpanda/convex-authz.
Quick Start
- Register components in
convex/convex.config.ts:
import { defineApp } from "convex/server";
import tenants from "@djpanda/convex-tenants/convex.config";
import authz from "@djpanda/convex-authz/convex.config";
const app = defineApp();
app.use(tenants);
app.use(authz);
export default app;- Define authz in
convex/authz.ts— useTENANTS_PERMISSIONSandTENANTS_ROLESfrom the package. Seenode_modules/@djpanda/convex-tenants/docs/quick-start.md. - Create tenants API in
convex/tenants.ts:
import { makeTenantsAPI } from "@djpanda/convex-tenants";
import { components } from "./_generated/api";
import { authz } from "./authz";
export const { listOrganizations, createOrganization, inviteMember, ... } =
makeTenantsAPI(components.tenants, {
authz,
creatorRole: "owner",
auth: async (ctx) => (await getAuthUserId(ctx)) ?? null,
getUser: async (ctx, userId) => {
const user = await ctx.db.get(userId);
return user ? { name: user.name, email: user.email } : null;
},
});- Use in React —
useQuery(api.tenants.listOrganizations), or use pre-built components withTenantsProvider. Seenode_modules/@djpanda/convex-tenants/docs/react-components.md.
Key Concepts
- authz is a sibling component — Register
authzalongsidetenants, not as a child. Other parts of your app can use authz outside tenants. - Roles are plain strings — Define them in authz.ts. Default roles:
owner,admin,member. Seenode_modules/@djpanda/convex-tenants/docs/flexible-roles.md. - Structural owner — Each org has an
ownerId. UsetransferOwnershipbefore owner leaves. All permission checks go through authz. - Organization status —
suspendedorarchivedblocks mutations; onlyupdateOrganizationwithstatus: "active"can reactivate.
Exports
| Export | Description |
|---|---|
TENANTS_PERMISSIONS | Default permissions for definePermissions() |
TENANTS_ROLES | Default roles (owner, admin, member) for defineRoles() |
DEFAULT_TENANTS_PERMISSION_MAP | Operation → permission mapping |
TENANTS_REQUIRED_PERMISSIONS | Flat list of permission strings |
Documentation (read from package)
Base path: node_modules/@djpanda/convex-tenants/docs/. Select the doc that matches the user's keywords or task — load only what's needed:
| Doc | Path | Load when user asks about… |
|---|---|---|
| quick-start | docs/quick-start.md | Setup, install, first-time integration, convex.config, authz.ts, tenants.ts, getting started, initial setup |
| api-reference | docs/api-reference.md | Function names (listOrganizations, createOrganization, addMember, inviteMember, etc.), makeTenantsAPI options, event hooks, mutation/query args, pagination (listMembers/listTeams/listTeamMembers/listInvitations with optional paginationOpts), API signatures |
| permission-map | docs/permission-map.md | Permissions, permissionMap, who can do what, operation permissions, overriding permissions, guards, access control per operation |
| flexible-roles | docs/flexible-roles.md | Roles, custom roles, owner/admin/member, creatorRole, defineRoles, structural owner, transferOwnership, add/remove roles |
| invitation-system | docs/invitation-system.md | Invitations, inviteMember, acceptInvitation, validateInvitationCreate, validateInvitationAccept, identifier type, email/phone/username, domain whitelist, rate limiting invitations |
| react-components | docs/react-components.md | TenantsProvider, OrganizationSwitcher, MembersSection, TeamsSection, InviteMemberDialog, useOrganization, useMembers, useTeams, useOrganizationInvitations, MembersTable, TeamsGrid, AcceptInvitation, JoinByDomainSection, paginated hooks |
| organization-store | docs/organization-store.md | Active organization, switching orgs, useOrganizationStore, setActiveOrganizationId, localStorage, storageKey, configureOrganizationStore |
Installation
npm install @djpanda/convex-tenants @djpanda/convex-authzFor React UI: npm install clsx tailwind-merge (optional peer deps).