Group-owned resource example
Define a group, its membership relation, and resources that reference it:
import { schema as s } from "jazz-tools";
import { groupMembershipTable } from "@nzip/lofi/access";
export const app = s.defineApp({
workspaces: s.table({ name: s.string() }),
workspaceMembers: groupMembershipTable("workspaces"),
documents: s.table({ workspaceId: s.ref("workspaces"), title: s.string() }),
});
import { defineAccessPolicies, groupAccess } from "@nzip/lofi/access";
import { app } from "./schema.ts";
export default defineAccessPolicies(app, [groupAccess({
groups: app.workspaces,
members: app.workspaceMembers,
resources: app.documents,
groupId: "workspaceId",
})]);
import { createGroupOperations } from "@nzip/lofi/access";
import { app } from "./schema.ts";
const groups = createGroupOperations({
groups: app.workspaces,
members: app.workspaceMembers,
});
const { group } = await groups.createGroup({ name: "Release team" });
await groups.addMember(group.id, recipientIdentity, "contributor");
await groups.changeRole(group.id, recipientIdentity, "writer");
await groups.removeMember(group.id, recipientIdentity);
Readers can read. Contributors can create and edit their own rows. Writers can edit any group row.
Admins can do everything writers can and manage membership. leaveGroup removes the current
principal's membership. Managed sync is required for every group operation. The group's creator
additionally keeps a permanent superseat: demoting or removing them does not end their ability to
manage the group — see Permission templates before building
handover flows.