Skip to content

Getting Started with RBAC

This tutorial walks you through setting up Kessel from scratch with a basic document management example. You’ll learn how to configure schemas, set up RBAC permissions, install client libraries, and implement both resource reporting and access control using Kessel’s APIs across multiple programming languages.

A new installation of Kessel begins as a blank slate. To integrate a Resource (e.g. for access control), you have to configure a Resource schema.

Configure a basic resource that ties into RBAC for permissions. Following is an example of a drive like schema as described in understanding kessel.

  1. Author schemas

    document/config.yaml
    resource_type: document
    resource_reporters:
    - drive
    document/reporters/drive_service/config.yaml
    resource_type: document
    reporter_name: drive
    namespace: drive
    document/reporters/drive_service/document.json
    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
    "document_id": { "type": "string" },
    "document_name": { "type": "string" },
    "document_type": {
    "type": "string",
    "enum": ["document"]
    },
    "created_at": {
    "type": "string",
    "format": "date-time"
    },
    "file_size": {
    "type": "integer",
    "minimum": 0
    },
    "owner_id": { "type": "string" }
    },
    "required": [
    "document_id",
    "document_name",
    "document_type"
    ]
    }
    kessel.ksl
    version 0.1
    namespace kessel
    internal type lock {
    relation #version: [ExactlyOne lockversion]
    }
    internal type lockversion {}
    rbac.ksl
    version 0.1
    namespace rbac
    public type principal {}
    public type group {
    relation member: [Any principal or group.member]
    }
    public type role {
    }
    public type role_binding {
    relation subject: [Any principal or group.member]
    relation granted: [AtLeastOne role]
    }
    public type workspace {
    relation parent: [AtMostOne workspace]
    relation user_grant: [Any role_binding]
    }
    public extension add_permission(name) {
    type role {
    private relation `${name}`: [bool]
    }
    type role_binding {
    internal relation `${name}`: subject and granted.`${name}`
    }
    type workspace {
    internal relation `${name}`: user_grant.`${name}` or parent.`${name}`
    }
    }
    drive.ksl
    version 0.1
    namespace drive
    import rbac
    @rbac.add_permission(name:'view_document')
    @rbac.add_permission(name:'edit_document')
    @rbac.add_permission(name:'delete_document')
    public type document {
    relation workspace: [ExactlyOne rbac.workspace]
    // Direct permissions from workspace
    relation view_document: workspace.view_document
    relation edit_document: workspace.edit_document
    relation delete_document: workspace.delete_document
    relation view: view_document
    relation edit: edit_document
    relation manage: delete_document
    }
  2. Compile to SpiceDB schema

    Install the KSL compiler directly from github:

    Terminal window
    go install github.com/project-kessel/ksl-schema-language/cmd/ksl@latest

    Alternatively, you can clone the repository and build locally:

    Terminal window
    git clone https://github.com/project-kessel/ksl-schema-language.git
    cd ksl-schema-language
    make build

    Then, you can use the ksl command to compile the schemas:

    Terminal window
    ksl kessel.ksl rbac.ksl drive.ksl

    This will generate a schema.zed file in the current directory.

    Example generated schema.zed file
    schema.zed
    definition drive/document {
    permission delete_document = t_workspace->delete_document
    permission edit = edit_document
    permission edit_document = t_workspace->edit_document
    permission manage = delete_document
    permission view = view_document
    permission view_document = t_workspace->view_document
    permission workspace = t_workspace
    relation t_workspace: rbac/workspace
    }
    definition kessel/lock {
    permission version = t_version
    relation t_version: kessel/lockversion
    }
    definition kessel/lockversion {}
    definition rbac/group {
    permission member = t_member
    relation t_member: rbac/principal | rbac/group#member
    }
    definition rbac/principal {}
    definition rbac/role {
    permission delete_document = t_delete_document
    relation t_delete_document: rbac/principal:*
    permission edit_document = t_edit_document
    relation t_edit_document: rbac/principal:*
    permission view_document = t_view_document
    relation t_view_document: rbac/principal:*
    }
    definition rbac/role_binding {
    permission delete_document = (subject & t_granted->delete_document)
    permission edit_document = (subject & t_granted->edit_document)
    permission granted = t_granted
    relation t_granted: rbac/role
    permission subject = t_subject
    relation t_subject: rbac/principal | rbac/group#member
    permission view_document = (subject & t_granted->view_document)
    }
    definition rbac/workspace {
    permission delete_document = t_user_grant->delete_document + t_parent->delete_document
    permission edit_document = t_user_grant->edit_document + t_parent->edit_document
    permission parent = t_parent
    relation t_parent: rbac/workspace
    permission user_grant = t_user_grant
    relation t_user_grant: rbac/role_binding
    permission view_document = t_user_grant->view_document + t_parent->view_document
    }
  1. Start Kessel Inventory

    Terminal window
    git clone git@github.com:project-kessel/inventory-api.git
    cd inventory-api
    make inventory-up-relations-ready
  2. Start Relations and SpiceDB

    Terminal window
    git clone git@github.com:project-kessel/relations-api.git
    cd relations-api
    # copy the schema.zed file we created earlier to deploy/schema.zed
    cp ../schema.zed deploy/schema.zed
    make relations-api-up
  3. Start RBAC

    TODO. For now we will create the Relationships directly

  1. Link workspace to role binding
    Terminal window
    MESSAGE='{"tuples":[{"resource":{"id":"workspace-1","type":{"name":"workspace","namespace":"rbac"}},"relation":"user_grant","subject":{"subject":{"id":"drive-team-binding","type":{"name":"role_binding","namespace":"rbac"}}}}]}'
    grpcurl -plaintext -d $MESSAGE \
    localhost: <your local port for relations> \
    kessel.relations.v1beta1.KesselTupleService.CreateTuples
  2. Grant document permissions to role
    Terminal window
    MESSAGE='{"tuples":[{"resource":{"id":"drive-admin-role","type":{"name":"role","namespace":"rbac"}},"relation":"view_document","subject":{"subject":{"id":"*","type":{"name":"principal","namespace":"rbac"}}}},{"resource":{"id":"drive-admin-role","type":{"name":"role","namespace":"rbac"}},"relation":"edit_document","subject":{"subject":{"id":"*","type":{"name":"principal","namespace":"rbac"}}}},{"resource":{"id":"drive-admin-role","type":{"name":"role","namespace":"rbac"}},"relation":"delete_document","subject":{"subject":{"id":"*","type":{"name":"principal","namespace":"rbac"}}}}]}'
    grpcurl -plaintext -d $MESSAGE \
    localhost: <your local port for relations> \
    kessel.relations.v1beta1.KesselTupleService.CreateTuples
  3. Link the role binding to a role
    Terminal window
    MESSAGE='{"tuples":[{"resource":{"id":"drive-team-binding","type":{"name":"role_binding","namespace":"rbac"}},"relation":"granted","subject":{"subject":{"id":"drive-admin-role","type":{"name":"role","namespace":"rbac"}}}}]}'
    grpcurl -plaintext -d $MESSAGE \
    localhost: <your local port for relations> \
    kessel.relations.v1beta1.KesselTupleService.CreateTuples
  4. Link the user sarah to the role binding
    Terminal window
    MESSAGE='{"tuples":[{"resource":{"id":"drive-team-binding","type":{"name":"role_binding","namespace":"rbac"}},"relation":"subject","subject":{"subject":{"id":"sarah","type":{"name":"principal","namespace":"rbac"}}}}]}'
    grpcurl -plaintext -d $MESSAGE \
    localhost: <your local port for relations> \
    kessel.relations.v1beta1.KesselTupleService.CreateTuples

Setup Client

Terminal window
# Set your Kessel endpoint
KESSEL_BASE_URL="http://localhost:9000"
# Set common headers for API requests
HEADERS=(
-H "Content-Type: application/json"
)
# For authenticated environments, add authorization header:
# HEADERS+=(
# -H "Authorization: Bearer $ACCESS_TOKEN"
# )

Creating Report Resource Requests

Build the report resource request payload:

Terminal window
MESSAGE='{"type": "document", "reporter_type": "drive", "reporter_instance_id": "drive-1","representations": {"metadata": {"local_resource_id": "doc-123","api_href": "https://drive.example.com/document/123","console_href": "https://www.console.com/drive/documents","reporter_version": "2.7.16"},"common": {"workspace_id": "workspace-1"},"reporter": {"document_id": "doc-123","document_name": "My Important Document","document_type": "document","created_at": "2025-08-31T10:30:00Z","file_size": 2048576,"owner_id": "user-1"}}}'

Sending Report Resource Requests

Execute the report resource request:

Terminal window
curl "${HEADERS[@]}" \
-X POST \
-d "$MESSAGE" \
"$KESSEL_BASE_URL/api/inventory/v1beta2/resources"

Creating Check Requests

Build the check request payload:

Terminal window
MESSAGE='{"object": {"resource_type": "document", "resource_id": "doc-123", "reporter": {"type": "drive"}}, "relation": "view", "subject": {"resource": {"resource_type": "principal", "resource_id": "sarah", "reporter": {"type": "rbac"}}}}'

Sending Check Requests

Execute the check request:

Terminal window
curl "${HEADERS[@]}" \
-X POST \
-d "$MESSAGE" \
"$KESSEL_BASE_URL/api/inventory/v1beta2/check"

Complete Example

Terminal window
# Set your Kessel endpoint
KESSEL_BASE_URL="http://localhost:9000"
# Set common headers for API requests
HEADERS=(
-H "Content-Type: application/json"
)
# For authenticated environments, add authorization header:
# HEADERS+=(
# -H "Authorization: Bearer $ACCESS_TOKEN"
# )
# 1) Report a resource first
REPORT_MESSAGE='{"type": "document", "reporter_type": "drive", "reporter_instance_id": "drive-1","representations": {"metadata": {"local_resource_id": "doc-123","api_href": "https://drive.example.com/document/123","console_href": "https://www.console.com/drive/documents","reporter_version": "2.7.16"},"common": {"workspace_id": "workspace-1"},"reporter": {"document_id": "doc-123","document_name": "My Important Document","document_type": "document","created_at": "2025-08-31T10:30:00Z","file_size": 2048576,"owner_id": "user-1"}}}'
curl "${HEADERS[@]}" \
-X POST \
-d "$REPORT_MESSAGE" \
"$KESSEL_BASE_URL/api/inventory/v1beta2/resources"
# 2) Then perform a permission check
CHECK_MESSAGE='{"object": {"resource_type": "document", "resource_id": "doc-123", "reporter": {"type": "drive"}}, "relation": "view", "subject": {"resource": {"resource_type": "principal", "resource_id": "sarah", "reporter": {"type": "rbac"}}}}'
curl "${HEADERS[@]}" \
-X POST \
-d "$CHECK_MESSAGE" \
"$KESSEL_BASE_URL/api/inventory/v1beta2/check"