Loading...
Loading...
Use this skill when designing data schemas, implementing sharing workflows, or auditing access control in Jazz applications. It covers the hierarchy of Groups, Accounts, and CoValues, ensuring data is private by default and shared securely through cascading permissions and invitations.
npx skill4agent add garden-co/jazz jazz-permissions-securityjazz-schema-designMyMap.create({ ... })MyMap.create({ ... }, { owner: teamGroup })| Role | Capability | Best For |
|---|---|---|
| admin | Read, Write, Delete, Invite Members, Revoke Access, Change Roles | Team Owners, Creators |
| manager | Read, Write, Add/Remove readers/writers | Delegated management |
| writer | Read, Write | Collaborators, Team Members |
| reader | Read Only | Observers, Public Links |
| writeOnly | Write Only (Blind submissions) | Voting, Dropboxes |
const group = co.group().create();
const bob = await co.account().load(bobsId);
if (bob.$isLoaded) {
group.addMember(bob, "writer");
group.addMember(bob, "reader"); // Change role
group.removeMember(bob);
}const red = MyCoMap.create({ color: "red" });
const me = co.account().getMe();
if (me.canAdmin(red)) {
console.log("I can add users of any role");
} else if (me.canManage(red)) {
console.log("I can share value with others");
} else if (me.canWrite(red)) {
console.log("I can edit value");
} else if (me.canRead(red)) {
console.log("I can view value");
}
// Or get role directly
red.$jazz.owner.getRoleOf(me.$jazz.id); // "admin"// ❌ WRONG: Defaults to private, other members won't see it
const task = Task.create({ title: "Fix bug" });
project.tasks.push(task);
// ✅ RIGHT: Explicitly set owner
const task = Task.create(
{ title: "Fix bug" },
{ owner: project.$jazz.owner }
);
project.tasks.push(task);
// ✅ ALSO RIGHT: Create new group for independent permissions
const taskGroup = co.group().create();
taskGroup.addMember(project.$jazz.owner, 'writer');
const task = Task.create({ title: "Fix bug" }, { owner: taskGroup });extendsContainerAccountimport { createInviteLink } from "jazz-tools/react";
const inviteLink = createInviteLink(organization, "writer");import { createInviteLink } from "jazz-tools/svelte";
const inviteLink = createInviteLink(organization, "writer");.../#/invite/[CoValue ID]/[inviteSecret]import { useAcceptInvite } from "jazz-tools/react";
useAcceptInvite({
invitedObjectSchema: Organization,
onAccept: async (organizationID) => {
const organization = await Organization.load(organizationID);
if (!organization.$isLoaded) throw new Error("Could not load");
me.root.organizations.$jazz.push(organization);
},
});<script lang="ts">
import { InviteListener } from "jazz-tools/svelte";
new InviteListener({
invitedObjectSchema: Organization,
onAccept: async (organizationID) => {
const organization = await Organization.load(organizationID);
if (!organization.$isLoaded) throw new Error("Could not load");
me.current.root.organizations.$jazz.push(organization);
},
});
</script>await account.acceptInvite(organizationId, inviteSecret, Organization);const groupToInviteTo = Group.create();
const readerInvite = groupToInviteTo.$jazz.createInvite("reader");
await account.acceptInvite(group.$jazz.id, readerInvite);const group = Group.create();
group.addMember("everyone", "writer");
// Or use alias
group.makePublic("writer"); // Defaults to "reader"writeOnlyconst JoinRequest = co.map({
account: co.account(),
status: z.literal(["pending", "approved", "rejected"]),
});
function createRequestsToJoin() {
const requestsGroup = Group.create();
requestsGroup.addMember("everyone", "writeOnly");
return RequestsList.create([], requestsGroup);
}
async function sendJoinRequest(requestsList, account) {
const request = JoinRequest.create(
{ account, status: "pending" },
requestsList.$jazz.owner
);
requestsList.$jazz.push(request);
}
async function approveJoinRequest(joinRequest, targetGroup) {
const account = await co.account().load(joinRequest.$jazz.refs.account.id);
if (account.$isLoaded) {
targetGroup.addMember(account, "reader");
joinRequest.$jazz.set("status", "approved");
return true;
}
return false;
}const playlistGroup = Group.create();
const trackGroup = Group.create();
trackGroup.addMember(playlistGroup);writeOnlyconst addedGroup = Group.create();
addedGroup.addMember(bob, "reader");
const containingGroup = Group.create();
containingGroup.addMember(bob, "writer");
containingGroup.addMember(addedGroup);
// Bob stays writer (higher than inherited reader)const organizationGroup = Group.create();
organizationGroup.addMember(bob, "admin");
const billingGroup = Group.create();
billingGroup.addMember(organizationGroup, "reader");
// All org members get reader access to billing, regardless of org role// Remove group
containingGroup.removeMember(addedGroup);
// Get parent groups
containingGroup.getParentGroups(); // [addedGroup]const board = Board.create({
title: "My board",
columns: [["Task 1.1", "Task 1.2"], ["Task 2.1", "Task 2.2"]],
});const companyGroup = Group.create();
companyGroup.addMember(CEO, "admin");
const teamGroup = Group.create();
teamGroup.addMember(companyGroup);
teamGroup.addMember(teamLead, "admin");
teamGroup.addMember(developer, "writer");
const projectGroup = Group.create();
projectGroup.addMember(teamGroup);
projectGroup.addMember(client, "reader");$jazz.ownerred.$jazz.owner.getRoleOf(me.$jazz.id)readerwriteOnlymakePublic()addMember("everyone", "reader")createInviteLink()useAcceptInvite()InviteListeneraccount.acceptInvite()writeOnlywriteOnlygetParentGroups()