Groups as members
Groups can be added to other groups using the addMember method.
When a group is added as a member of another group, members of the added group will become part of the containing group.
Basic usage
Here's how to add a group as a member of another group:
const playlistGroup = Group.create(); const trackGroup = Group.create(); // Tracks are now visible to the members of playlist trackGroup.addMember(playlistGroup);
When you add groups as members:
- Members of the added group become members of the container group
- Their roles are inherited (with some exceptions, see below)
- Revoking access from the member group also removes its access to the container group
Levels of inheritance
Adding a group as a member of another is not limited in depth:
const grandParentGroup = Group.create(); const parentGroup = Group.create(); const childGroup = Group.create(); childGroup.addMember(parentGroup); parentGroup.addMember(grandParentGroup);
Members of the grandparent group will get access to all descendant groups based on their roles.
Roles
The rules of role inheritance
If the account is already a member of the container group, it will get the more permissive role:
const addedGroup = Group.create(); addedGroup.addMember(bob, "reader"); const containingGroup = Group.create(); addedGroup.addMember(bob, "writer"); containingGroup.addMember(addedGroup); // Bob stays a writer because his role is higher // than the inherited reader role.
When adding a group to another group, only admin, writer and reader roles are inherited:
const addedGroup = Group.create(); containingGroup.addMember(bob, "writeOnly"); const mainGroup = Group.create(); mainGroup.addMember(containingGroup);
Overriding the added group's roles
In some cases you might want to inherit all members from an added group but override their roles to the same specific role in the containing group. You can do so by passing an "override role" as a second argument to addMember:
const organizationGroup = Group.create(); organizationGroup.addMember(bob, "admin"); const billingGroup = Group.create(); // This way the members of the organization // can only read the billing data billingGroup.addMember(organizationGroup, "reader");
Permission changes
When you remove a member from an added group, they automatically lose access to all containing groups. We handle key rotation automatically to ensure security.
// Remove member from added group addedGroup.removeMember(bob); // Bob loses access to both groups. // If Bob was also a member of the containing group, // he wouldn't have lost access.
Removing groups from other groups
You can remove a group from another group by using the removeMember method:
const addedGroup = Group.create(); const containingGroup = Group.create(); containingGroup.addMember(addedGroup); // Revoke the extension containingGroup.removeMember(addedGroup);
Getting all added groups
You can get all of the groups added to a group by calling the getParentGroups method:
const containingGroup = Group.create(); const addedGroup = Group.create(); containingGroup.addMember(addedGroup); console.log(containingGroup.getParentGroups()); // [addedGroup]
Ownership on implicit CoValue creation
When creating CoValues that contain other CoValues (or updating references to CoValues) using plain JSON objects, Jazz not only creates the necessary CoValues automatically but it will also manage their group ownership.
const Task = co.plainText(); const Column = co.list(Task); const Board = co.map({ title: z.string(), columns: co.list(Column), }); const board = Board.create({ title: "My board", columns: [ ["Task 1.1", "Task 1.2"], ["Task 2.1", "Task 2.2"], ], });
For each created column and task CoValue, Jazz also creates a new group as its owner and adds the referencing CoValue's owner as a member of that group. This means permissions for nested CoValues are inherited from the CoValue that references them, but can also be modified independently for each CoValue if needed.
const writeAccess = Group.create(); writeAccess.addMember(bob, "writer"); // Give Bob write access to the board, columns and tasks const boardWithGranularPermissions = Board.create( { title: "My board", columns: [ ["Task 1.1", "Task 1.2"], ["Task 2.1", "Task 2.2"], ], }, writeAccess, ); // Give Alice read access to one specific task const task = boardWithGranularPermissions.columns[0][0]; const taskGroup = task.$jazz.owner; taskGroup.addMember(alice, "reader");
If you prefer to manage permissions differently, you can always create CoValues explicitly:
const writeAccess = Group.create(); writeAccess.addMember(bob, "writer"); const readAccess = Group.create(); readAccess.addMember(bob, "reader"); // Give Bob read access to the board and write access to the columns and tasks const boardWithExplicitPermissions = Board.create( { title: "My board", columns: co.list(Column).create( [ ["Task 1.1", "Task 1.2"], ["Task 2.1", "Task 2.2"], ], writeAccess, ), }, readAccess, );
Example: Team Hierarchy
Here's a practical example of using group inheritance for team permissions:
// Company-wide group const companyGroup = Group.create(); companyGroup.addMember(CEO, "admin"); // Team group with elevated permissions const teamGroup = Group.create(); teamGroup.addMember(companyGroup); // Inherits company-wide access teamGroup.addMember(teamLead, "admin"); teamGroup.addMember(developer, "writer"); // Project group with specific permissions const projectGroup = Group.create(); projectGroup.addMember(teamGroup); // Inherits team permissions projectGroup.addMember(client, "reader"); // Client can only read project items
This creates a hierarchy where:
- The CEO has admin access to everything
- Team members get writer access to team and project content
- Team leads get admin access to team and project content
- The client can only read project content