Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/fajarnugraha37/drizzle-castor/llms.txt

Use this file to discover all available pages before exploring further.

Policies are the rules the Unified RBAC engine consults on every repository call. You register them on the schema builder via builder.policies(), which accepts either a table name and a per-table policy map or a single global callback that applies to all tables at once. The engine resolves the active policy at runtime from the current execution context, including the active profiles, and uses it to gate or trim the incoming query before it reaches the AST translator.

Table-specific policies

The primary way to define access rules is to call builder.policies(tableName, policyMap). The policyMap is a plain object whose keys are profile names (or "default") and whose values are either a static UnifiedPolicyConfig object or an async function that returns one.
schemaMetadataBuilder.policies("users", {
  public: {
    allowedActions: ["read"],
    allowedFilters: ["name", "email", "settings.theme"],
    allowedProjections: ["name", "profile.bio"],
  },
  admin: {
    allowedActions: "*",
    allowedSets: "*",
    allowedProjections: "*",
    allowedFilters: "*",
    allowedSorts: "*",
  },
  user: async (ctx) => {
    const isOwner = ctx.params.id === ctx.state.userId;
    return {
      allowedActions: isOwner ? ["read", "update"] : ["read"],
      allowedSets: ["settings.theme", "persona.skills"],
      allowedProjections: ["*"],
      allowedFilters: "*",
      allowedSorts: "*",
    };
  },
});
The three approaches — static object, async function, and global callback — can be combined freely. Each is described in detail below.

The UnifiedPolicyConfig fields

Every policy eventually resolves to a UnifiedPolicyConfig object. All fields are optional; omitting a field has no effect on that dimension (access is neither granted nor denied by the omission alone — the mode and trimming settings determine the outcome).

allowedActions

allowedActions
string[] | "*"
An array of permitted action strings or the wildcard "*" for unrestricted access.Valid action strings: "create", "read", "update", "softDelete", "restore", "hardDelete".If the active profile attempts an action not in this list, the middleware throws AccessDeniedError before any field-level checks run.
allowedProjections
string[] | "*" | async (ctx) => string[] | "*"
Restricts the SELECT clause. Accepts a static array, the wildcard "*", or an async function that receives the ExecutionContext and returns one of those two.Fields are matched using dot-notation path prefix rules: allowing "settings" implicitly permits any nested path such as "settings.theme" or "settings.notifications.email".
allowedFilters
string[] | "*" | async (ctx) => string[] | "*"
Restricts which fields may appear in WHERE clauses. The engine recursively traverses $and/$or/$not filter trees and discards nodes that reference disallowed fields.Supports JSON dot-notation and relational paths (e.g., "posts.title", "settings.theme").
allowedSets
string[] | "*" | async (ctx) => string[] | "*"
Controls which columns may be written during create and update operations. Any key in the payload that is not in allowedSets is stripped before the mutation reaches the database.
allowedSorts
string[] | "*" | async (ctx) => string[] | "*"
Limits which fields can appear in ORDER BY clauses. Disallowed sort keys are removed from the order object before the AST translator processes it.

Policy modes

A static policy is a plain UnifiedPolicyConfig object assigned directly to a profile key. It is evaluated once at registration time and reused on every matching request without incurring async overhead.Static policies are ideal for roles whose permissions do not change based on the request context — for example, an admin profile that always has full access.
schemaMetadataBuilder.policies("users", {
  // Read-only: can filter by name, email, or a JSON sub-field
  public: {
    allowedActions: ["read"],
    allowedFilters: ["name", "email", "settings.theme"],
    allowedProjections: ["name", "profile.bio"],
  },

  // Full access: wildcard on every dimension
  admin: {
    allowedActions: "*",
    allowedSets: "*",
    allowedProjections: "*",
    allowedFilters: "*",
    allowedSorts: "*",
  },
});
Use "*" on a dimension to grant full access to that clause. Mixing "*" on some dimensions with explicit arrays on others is fully supported — each dimension is evaluated independently.

Wildcard "*" vs explicit field arrays

Each field dimension accepts either "*" (full access) or an explicit array of dot-notation path strings.
// Wildcard: no restrictions on projections
allowedProjections: "*"

// Explicit list: only these paths can appear in SELECT
allowedProjections: ["name", "email", "profile.bio", "settings.theme"]
Path matching uses prefix semantics. Allowing "settings" automatically permits any descendant path, including "settings.theme", "settings.notifications", and "settings.notifications.email". You never need to enumerate every leaf manually.
// Allowing "settings" covers all nested JSON paths under it
allowedFilters: ["name", "email", "settings"]

// A query filtering on "settings.theme" or "settings.notifications.email" will pass
JSON dot-notation and relational paths follow the same prefix rules. Allowing "posts" in allowedProjections permits "posts.title", "posts.comments.content", and any other nested path under the posts relation.

Listening to RBAC security events

Every time the engine trims a field or denies access, it emits a security event on the builder’s event bus. Subscribe to it to build audit trails or forward security incidents to your logging infrastructure.
schemaMetadataBuilder.on("security", (ev) => {
  // ev.type: "field_trim" | "action_denied" | "unknown_operator"
  // ev.tableName: the target table
  // ev.fields: the fields involved
  // ev.message: human-readable description
  console.warn(`[Audit] Security event on ${ev.tableName}: ${ev.message}`);
});

Access control overview

Understand strict vs lenient mode, the five control dimensions, and how Intelligent Data Trimming works.

Profiles

Learn how to declare named profiles and pass single or multiple profiles to repository methods.

Build docs developers (and LLMs) love