> ## Documentation Index
> Fetch the complete documentation index at: https://vlyai-1c28d863.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Roles and permissions

> Role-based access control with resource-level scoping. Owner / admin / member by default; customizable per project.

vly's built-in auth includes a **role-based access control (RBAC)** system. Every project starts with three roles — owner, admin, member — and you can add more or rename them.

## The default roles

| Role | What they can do |
| - | - |
| Owner | Everything. Manage billing. Cannot be removed (must transfer first). |
| Admin | Manage members, edit any data, configure integrations. Cannot manage billing. |
| Member | Use the app, edit data they created (or shared resources, depending on app). |

The exact permissions depend on your app — vly generates the role check pattern but you decide who can do what.

## Adding a role

```text theme={null}
Add a "viewer" role. Viewers can read everything in their workspace but
cannot create, edit, or delete anything.
```

vly:

1. Adds `viewer` to the role enum in the schema.
2. Updates every mutation to refuse if the caller is a viewer.
3. Adds the new role option in the workspace member UI.

## Resource-level scoping

Beyond role-level permissions, scope by who owns a resource:

```text theme={null}
Members can edit and delete only the tasks they created.
Admins can edit and delete any task in the workspace.
```

The pattern in code:

```typescript convex/tasks.ts theme={null}
export const deleteTask = mutation({
  args: { id: v.id("tasks") },
  handler: async (ctx, { id }) => {
    const identity = await ctx.auth.getUserIdentity();
    const task = await ctx.db.get(id);
    if (!task) throw new Error("Not found");

    const membership = await ctx.db
      .query("memberships")
      .withIndex("by_workspace_and_user", q =>
        q.eq("workspaceId", task.workspaceId).eq("userId", identity.subject))
      .unique();

    const isAdmin = ["owner", "admin"].includes(membership?.role);
    const isCreator = task.createdBy === identity.subject;

    if (!isAdmin && !isCreator) {
      throw new Error("Permission denied");
    }

    await ctx.db.delete(id);
  },
});
```

vly generates this pattern when you describe role-aware behavior in a prompt.

## Common patterns

<CardGroup cols={2}>
  <Card title="Owner-only billing" icon="credit-card">
    "Only owners can change the workspace plan or update billing details."
  </Card>

  <Card title="Admin-only invites" icon="user-plus">
    "Admins and owners can invite new members. Members cannot."
  </Card>

  <Card title="Self-service for members" icon="user">
    "Members can edit their own profile, but not other members'."
  </Card>

  <Card title="Public read, restricted write" icon="eye">
    "Anyone signed in can read all resources; only the creator can edit."
  </Card>
</CardGroup>

## Tips

<Tip>
  **Define roles in plain English in the prompt.** "Members can do X but not Y" is clearer than trying to enumerate every permission.
</Tip>

<Tip>
  **Test by signing in as different roles.** Don't trust that the agent generated the right checks — sign in as a member and try to do admin-only actions.
</Tip>

## Related

<CardGroup cols={3}>
  <Card title="Built-in auth" icon="lock" href="/features/auth/built-in-auth">
    The auth system.
  </Card>

  <Card title="Workspace roles" icon="user-cog" href="/features/workspace/roles">
    Workspace-level role management.
  </Card>

  <Card title="Auth flows" icon="key" href="/architecture/auth-flows">
    Architectural patterns.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.