Skip to content

Sharing

Most products need more than workspace roles: a project shared with three people, a folder handed to a new owner, an invitation that expires. Rolebook Sharing is a first-party companion package family that owns that layer and delegates every final decision to the engine.

Package Owns
@rolebook/sharing Resource membership, ownership transfer, resource invitations, revocation, audit events, accessible-resource query seams.
@rolebook/sharing-postgres ORM-neutral PostgreSQL adapter, migrations, and transactional fences.
@rolebook/sharing-better-auth Better Auth as the source of users, organizations, and workspace members.
@rolebook/sharing-clerk Clerk as the source of users, organizations, and memberships.
@rolebook/sharing-testkit One security contract run against every adapter.

These packages are private preview alongside the core.

  • Your identity provider remains the source of users, organizations or workspaces, workspace members, workspace roles, and workspace invitations.
  • Sharing owns per-resource membership and invitations.
  • Rolebook Core is the only policy evaluator. The sharing constructor creates its authorizer from the exact policy in the sharing definition, so no second permission engine can slip in.

Workspace membership is eligibility, never access. The Better Auth and Clerk adapters emit no Rolebook assignment unless the application explicitly configures a workspace role mapping.

import { definePolicy } from "rolebook";
import { createHmacInvitationTokens, createSharing, defineSharing } from "@rolebook/sharing";
const definition = defineSharing({
policy,
resources: {
project: {
memberRoles: { editor: "project_editor" },
ownershipRole: "project_owner",
actions: { read: "project:read" },
management: {
members: "project:manage_members",
invitations: "project:manage_invitations",
ownership: "project:transfer_ownership",
},
adapter: projectSharingAdapter,
},
},
});
const sharing = createSharing({
definition,
identity,
invitationTokens: createHmacInvitationTokens(process.env.SHARING_INVITATION_KEY!),
});
  • Every operation receives an explicit tenant id. A session’s active workspace is never tenancy evidence.
  • Resources are loaded by (tenantId, resourceId) and returned as authoritative trusted snapshots.
  • Ordinary member roles never include ownership. Only transferOwnership replaces the owner, atomically.
  • Commands are captured as immutable data before token, identity, or storage callbacks run. An actor cannot change their own membership.
  • Mutations lock the authoritative workspace-member rows in their transaction, so a concurrent removal either commits first and closes the mutation, or waits.
  • Invitation secrets are returned once. Storage holds only a keyed digest, binding, expiry, consumption, and revocation state.
  • Sharing state changes and audit events commit atomically. External delivery is the application’s job.