This project uses Convex as its backend.
When working on Convex code, always read example/convex/_generated/ai/guidelines.md first for
important guidelines on how to correctly use Convex APIs and patterns. The file contains rules that
override what you may have learned about Convex from training data.
Convex agent skills for common tasks can be installed by running npx convex ai-files install.
Role-based access control as a Convex component — typed, sandboxed, runtime-editable roles and grants keyed by opaque refs.
pnpm install
pnpm build
pnpm testUse pnpm build:codegen only when regenerating checked-in Convex _generated files and you have
access to the selected Convex project.
src/client/index.ts—Permissions<TRole, TAction>class (consumer API): defineRole, removeRole, assign, revoke, check, require, rolesFor, permissionsFor, listRolessrc/client/types.ts— public TypeScript interfaces (RoleDoc,PermissionDenied)src/component/mutations.ts— Convex mutations (defineRole, removeRole, assign, revoke)src/component/queries.ts— Convex queries (check, require, rolesFor, permissionsFor, listRoles)src/component/validators.ts— shared validatorssrc/component/schema.ts— database schema (roles,assignmentstables)src/component/convex.config.ts—defineComponent("permissions")src/shared.ts— shared types, pure grant-matching logicsrc/test.ts— convex-test helper for registering the component
Component owns:
rolestable — role name → grants array, optional description, updatedAt timestampassignmentstable — subjectRef → role, optional scopeRef, createdAt timestamp- Grant-matching logic (exact, prefix-wildcard
doc.*, global*) - Input validation at the mutation boundary (
^[A-Za-z0-9_.:-]{1,128}$)
Host owns:
- Identity resolution — the host authenticates the caller and maps them to an opaque
subjectRef - Scope resolution — the host decides which
scopeRef(org, workspace, tenant) applies - Admin authorization — the host gates who may call
defineRole,assign,revoke,removeRole - Domain model — action strings, role names, and scope refs are opaque to the component
Auth:
- The component is auth-agnostic. It never sees credentials, sessions, or user records.
- Callers pass
subjectRefand optionallyscopeRefas opaque strings. The component never interprets their shape, source, or meaning.
- Stored RBAC, not code-defined: roles and assignments live in the component's own sandboxed tables and are editable at runtime via mutations — no redeploy to change who can do what.
- Typed generic client:
Permissions<TRole, TAction>is generic over the host's role and action union types, providing autocomplete and typo-safety forassign,check, andrequire. - Wildcard grants: grants support exact (
"doc.edit"), prefix-wildcard ("doc.*"), and global ("*") matching; a super-role is just a role granted"*". - Default-deny: a subject with no matching role/grant is denied.
requirethrows a structuredConvexError<{ code: "FORBIDDEN"; subjectRef; action; scopeRef? }>the host maps to a 403. - Scoped / multi-tenant: assignments carry an optional opaque
scopeRef; a scoped check considers both global and in-scope roles; an unscoped check considers only global roles. - No bare
v.any(): all validators use explicit typed shapes;jsonValuealias only if ever needed as a documented last resort. - Mutations in
mutations.ts, queries inqueries.ts: enforced by@vllnt/eslint-config/convex.
- Explicit
args+returnson every Convex function. - Host data via typed generics / host-supplied validator keyed by an opaque ref.
- 100% test coverage is BLOCKING (
vitest.config.mtsthresholds). - Runtime deps: only official
@convex-dev/*+@vllnt/*.
When any of these change, update the corresponding docs in the same commit:
| Change | Update |
|---|---|
| Mutation/query args or return types | docs/API.md, README.md API table |
| Schema table added/removed/modified | AGENTS.md Structure + Ownership boundary, README.md Architecture |
| Grant-matching logic changed | README.md Features, docs/API.md Grant matching |
| Security model changed | README.md Security Model |
| New feature or breaking change | CHANGELOG.md, README.md Features |
| Input validation rules changed | docs/API.md Error codes |
Always run pnpm lint && pnpm build && pnpm test before committing docs
changes.