Skip to content
bknd-assign-permissions logo

Assign Permissions

bknd-assign-permissions

Use when assigning permissions to roles in Bknd. Covers permission syntax (simple strings, extended format), permission effects (allow/deny), policies with conditions, entity-specific permissions, and fine-grained access control patterns.

SKILL.md

Full skill instructions

Assign Permissions

Configure detailed permissions for roles using simple strings, extended format with effects, and conditional policies.

Prerequisites

  • Bknd project with code-first configuration
  • Auth enabled (auth: { enabled: true })
  • Guard enabled (guard: { enabled: true })
  • At least one role defined (see bknd-create-role)

When to Use UI Mode

  • Viewing current role permissions
  • Quick permission checks

UI steps: Admin Panel > Auth > Roles > Select role

Note: Permission assignment requires code mode. UI is read-only.

When to Use Code Mode

  • Assigning permissions to roles
  • Adding permission effects (allow/​deny)
  • Creating conditional policies
  • Entity-specific permission rules

Code Approach

Step 1: Simple Permission Strings

Assign basic permissions as string array:

import { serve } from "bknd/​adapter/​bun";
import { em, entity, text } from "bknd";

const schema = em({
  posts: entity("posts", { title: text().required() }),
});

serve({
  connection: { url: "file:data.db" },
  config: {
    data: schema.toJSON(),
    auth: {
      enabled: true,
      guard: { enabled: true },
      roles: {
        editor: {
          implicit_allow: false,
          permissions: [
            "data.entity.read",    // Read any entity
            "data.entity.create",  // Create in any entity
            "data.entity.update",  // Update any entity
            // No delete permission
          ],
        },
      },
    },
  },
});

Available Permissions

PermissionFilterableDescription
data.entity.readYesRead entity records
data.entity.createYesCreate new records
data.entity.updateYesUpdate existing records
data.entity.deleteYesDelete records
data.database.syncNoSync database schema
data.raw.queryNoExecute raw SELECT
data.raw.mutateNoExecute raw INSERT/​UPDATE/​DELETE

Filterable means you can add conditions/​filters via policies.

Step 2: Extended Permission Format

Use objects for explicit allow/​deny effects:

{
  roles: {
    moderator: {
      implicit_allow: false,
      permissions: [
        { permission: "data.entity.read", effect: "allow" },
        { permission: "data.entity.update", effect: "allow" },
        { permission: "data.entity.delete", effect: "deny" },  // Explicit deny
      ],
    },
  },
}

Permission Effects

EffectDescription
allowGrant the permission (default)
denyExplicitly block the permission

Deny overrides allow - useful when implicit_allow: true but you want to block specific actions.

Step 3: Conditional Policies

Add policies for fine-grained control:

{
  roles: {
    content_editor: {
      implicit_allow: false,
      permissions: [
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [
            {
              description: "Only read posts and comments",
              condition: { entity: { $in: ["posts", "comments"] } },
              effect: "allow",
            },
          ],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [
            {
              condition: { entity: { $in: ["posts", "comments"] } },
              effect: "allow",
            },
          ],
        },
      ],
    },
  },
}

Policy Structure

{
  description?: string,      // Human-readable (optional)
  condition?: ObjectQuery,   // When policy applies
  effect: "allow" | "deny" | "filter",
  filter?: ObjectQuery,      // Row filter (for effect: "filter")
}

Policy Effects

EffectPurpose
allowGrant when condition met
denyBlock when condition met
filterApply row-level filter to results

Condition Operators

OperatorDescriptionExample
$eqEqual{ entity: { $eq: "posts" } }
$neNot equal{ entity: { $ne: "users" } }
$inIn array{ entity: { $in: ["posts", "comments"] } }
$ninNot in array{ entity: { $nin: ["users", "secrets"] } }
$gtGreater than{ age: { $gt: 18 } }
$gteGreater or equal{ level: { $gte: 5 } }
$ltLess than{ count: { $lt: 100 } }
$lteLess or equal{ priority: { $lte: 3 } }

Step 4: Variable Placeholders

Reference runtime context with @variable:

PlaceholderDescription
@user.idCurrent user's ID
@user.emailCurrent user's email
@user.roleCurrent user's role
@entityCurrent entity name
@idCurrent record ID

Example - user can only update their own profile:

{
  permissions: [
    {
      permission: "data.entity.update",
      effect: "allow",
      policies: [
        {
          condition: { entity: "users", "@id": "@user.id" },
          effect: "allow",
        },
      ],
    },
  ],
}

Step 5: Entity-Specific Permissions

Grant different permissions per entity:

{
  roles: {
    blog_author: {
      implicit_allow: false,
      permissions: [
        // Full CRUD on posts
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.update",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.delete",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },

        // Read-only on categories
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{ condition: { entity: "categories" }, effect: "allow" }],
        },
      ],
    },
  },
}

Common Patterns

Read-Only Role

{
  roles: {
    viewer: {
      implicit_allow: false,
      permissions: ["data.entity.read"],
    },
  },
}

CRUD Without Delete

{
  roles: {
    contributor: {
      implicit_allow: false,
      permissions: [
        "data.entity.read",
        "data.entity.create",
        "data.entity.update",
        { permission: "data.entity.delete", effect: "deny" },
      ],
    },
  },
}

Admin with Restricted Raw Access

{
  roles: {
    admin: {
      implicit_allow: true,  // Allow all by default
      permissions: [
        // But deny raw database access
        { permission: "data.raw.query", effect: "deny" },
        { permission: "data.raw.mutate", effect: "deny" },
      ],
    },
  },
}

Multi-Entity Role

{
  roles: {
    content_manager: {
      implicit_allow: false,
      permissions: [
        // Content entities: full CRUD
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.update",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.delete",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments"] } },  // No media delete
            effect: "allow",
          }],
        },
      ],
    },
  },
}

Deny Specific Entity

{
  roles: {
    user: {
      implicit_allow: false,
      permissions: [
        // Can read most entities
        "data.entity.read",
        // But never access secrets entity
        {
          permission: "data.entity.read",
          effect: "deny",
          policies: [{
            condition: { entity: "secrets" },
            effect: "deny",
          }],
        },
      ],
    },
  },
}

Create Helper Function

For complex role definitions:

// helpers/​permissions.ts
type EntityPermission = "read" | "create" | "update" | "delete";

function entityPermissions(
  entities: string[],
  actions: EntityPermission[]
) {
  const permMap: Record<EntityPermission, string> = {
    read: "data.entity.read",
    create: "data.entity.create",
    update: "data.entity.update",
    delete: "data.entity.delete",
  };

  return actions.map((action) => ({
    permission: permMap[action],
    effect: "allow" as const,
    policies: [{
      condition: { entity: { $in: entities } },
      effect: "allow" as const,
    }],
  }));
}

// Usage
{
  roles: {
    blog_author: {
      implicit_allow: false,
      permissions: [
        ...entityPermissions(["posts", "comments"], ["read", "create", "update"]),
        ...entityPermissions(["categories", "tags"], ["read"]),
      ],
    },
  },
}

Verification

Test permission assignments:

1. Login as user with role:

curl -X POST http://localhost:7654/​api/​auth/​password/​login \
  -H "Content-Type: application/​json" \
  -d '{"email": "[email protected]", "password": "password123"}'

2. Test allowed permission:

curl http://localhost:7654/​api/​data/​posts \
  -H "Authorization: Bearer <token>"
# Should return 200 with data

3. Test denied permission:

curl -X DELETE http://localhost:7654/​api/​data/​posts/​1 \
  -H "Authorization: Bearer <token>"
# Should return 403 Forbidden

4. Test entity-specific permission:

# If only posts/​comments allowed:
curl http://localhost:7654/​api/​data/​users \
  -H "Authorization: Bearer <token>"
# Should return 403 if users entity not in allowed list

Common Pitfalls

Permission Not Taking Effect

Problem: Changed permissions but old behavior persists

Fix: Restart server - role config is loaded at startup:

# Stop and restart
bknd run

Deny Not Overriding

Problem: Deny effect not blocking access

Fix: Check policy condition - deny only applies when condition matches:

// WRONG - no condition, may not match
{ permission: "data.entity.delete", effect: "deny" }

// CORRECT - simple deny at permission level
{
  permissions: [
    "data.entity.read",
    "data.entity.create",
    // Don't include delete at all
  ],
}

Entity Condition Not Matching

Problem: Entity-specific permission not working

Fix: Verify entity name matches exactly:

// WRONG - entity name case matters
{ condition: { entity: "Posts" } }

// CORRECT - use exact entity name
{ condition: { entity: "posts" } }

Multiple Policies Conflict

Problem: Confusing behavior with multiple policies

Fix: Understand evaluation order - first matching policy wins:

{
  policies: [
    // More specific first
    { condition: { entity: "secrets" }, effect: "deny" },
    // General fallback last
    { effect: "allow" },
  ],
}

Variable Placeholder Not Resolving

Problem: @user.id appearing literally in filter

Fix: Variables only work in filter and condition fields:

// CORRECT usage
{
  condition: { "@id": "@user.id" },  // Works
  filter: { user_id: "@user.id" },   // Works
}

DOs and DON'Ts

DO:

  • Start with minimal permissions, add as needed
  • Use $in operator for multiple entities
  • Test each permission after adding
  • Use descriptive policy descriptions
  • Prefer explicit permissions over implicit_allow

DON'T:

  • Grant data.raw.* to non-admin roles (SQL injection risk)
  • Use implicit_allow: true with deny policies (confusing)
  • Forget to restart server after config changes
  • Mix simple strings and extended format unnecessarily
  • Over-complicate with too many nested policies

Related Skills

  • bknd-create-role - Define new roles
  • bknd-row-level-security - Filter data by user ownership
  • bknd-protect-endpoint - Secure specific endpoints
  • bknd-public-vs-auth - Configure public vs authenticated access
  • bknd-setup-auth - Initialize authentication system

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View