From 47a3d9734b9bb0d9a193f89259dbbfe69c564e21 Mon Sep 17 00:00:00 2001 From: Antony Natale Date: Thu, 11 Jun 2026 16:08:32 -0400 Subject: [PATCH 1/5] add tutorial framing to Quick Start and new Add Kessel to Existing App tutorial --- astro.config.mjs | 1 + .../docs/start-here/add-kessel-to-app.mdx | 387 ++++++++++++++++++ .../docs/start-here/getting-started.mdx | 15 + 3 files changed, 403 insertions(+) create mode 100644 src/content/docs/start-here/add-kessel-to-app.mdx diff --git a/astro.config.mjs b/astro.config.mjs index b3385b2..a2ad86f 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -45,6 +45,7 @@ const baseStarlightConfig = { items: [ "start-here/getting-started", "start-here/understanding-kessel", + "start-here/add-kessel-to-app", "start-here/roadmap", ], }, diff --git a/src/content/docs/start-here/add-kessel-to-app.mdx b/src/content/docs/start-here/add-kessel-to-app.mdx new file mode 100644 index 0000000..0ba952b --- /dev/null +++ b/src/content/docs/start-here/add-kessel-to-app.mdx @@ -0,0 +1,387 @@ +--- +title: "Tutorial: Add Kessel to an existing application" +description: Walk through the process of integrating Kessel into an existing service, from designing a permission schema to checking permissions. +--- + +import { Aside, CardGrid, LinkCard, Steps } from '@astrojs/starlight/components'; + +**Time: ~45 minutes** + +This tutorial walks you through integrating Kessel into an application you already have. By the end, you will have: + +1. **Designed a permission schema** for a custom resource type +2. **Compiled and loaded the schema** into a running Kessel instance +3. **Reported a resource** to Kessel with a workspace relationship +4. **Checked permissions** to verify access control works + +The tutorial uses a task management service as an example: users create tasks, organize them into workspaces (projects), and control who can view or edit them. The same process applies to any application. + + + +## Prerequisites + +You need the following tools installed (all covered in the [Quick Start prerequisites](../getting-started/#prerequisites)): + +- **`ksl`** schema compiler ([install instructions](../getting-started/#prerequisites)) +- **`grpcurl`** for gRPC API calls ([install instructions](../getting-started/#prerequisites)) +- A **running Kessel instance** (see [Run locally with Docker Compose](/docs/building-with-kessel/how-to/run-with-docker/#start-the-stack)) + +Create a working directory for this tutorial: + +```bash +mkdir -p kessel-task-tutorial +cd kessel-task-tutorial +``` + +## Step 1: Map your resources + +Before writing any code, identify what your application manages and how it maps to Kessel's model. + +Ask yourself: + +- **What are the "things" my application manages?** These are your resource types. +- **How are they organized?** Do resources belong to a parent container? In Kessel, the built-in **workspace** resource handles this grouping. +- **Who needs access, and what kind?** What permissions does each resource type need? + +For the task manager example, the mapping looks like this: + +| Your application | Kessel concept | +|---|---| +| Project | Workspace (built-in) | +| Task | Custom resource type `task` | +| "Can view tasks" | Permission `task_view` on workspace | +| "Can edit tasks" | Permission `task_edit` on workspace | +| Team member | Principal with a role binding | + + + +## Step 2: Design and compile your permission schema + +With your resources mapped, write a KSL schema that describes them. + + +1. **Create the base schema files.** + + Kessel requires two built-in schema files. These are the same ones used in the Quick Start. + + ```bash + cat > kessel.ksl << 'EOF' + version 0.1 + namespace kessel + + internal type lock { + relation #version: [ExactlyOne lockversion] + } + + internal type lockversion {} + EOF + ``` + + ```bash + cat > rbac.ksl << 'EOF' + 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}` + } + } + EOF + ``` + +2. **Write your resource type schema.** + + Create `taskmanager.ksl` with permissions for viewing and editing tasks: + + ```bash + cat > taskmanager.ksl << 'EOF' + version 0.1 + namespace taskmanager + + import rbac + + @rbac.add_permission(name:'task_view') + @rbac.add_permission(name:'task_edit') + + public type task { + relation workspace: [ExactlyOne rbac.workspace] + + relation view: workspace.task_view + relation edit: workspace.task_edit + } + EOF + ``` + + This tells Kessel: + + - There are two permissions (`task_view` and `task_edit`) that can be granted through roles + - There is a resource type called `task` that belongs to exactly one workspace + - The `view` and `edit` permissions on a task are inherited from its workspace + +3. **Compile the schema.** + + ```bash + ksl kessel.ksl rbac.ksl taskmanager.ksl + ``` + + This produces a `schema.zed` file. If you see errors, check that all three `.ksl` files are in the current directory. + + +## Step 3: Create resource type configuration + +Kessel also needs configuration files that describe your resource type's attributes. + + +1. **Create the directory structure.** + + ```bash + mkdir -p task/reporters/taskmanager + ``` + +2. **Define the resource type.** + + ```bash + cat > task/config.yaml << 'EOF' + resource_type: task + resource_reporters: + - TASKMANAGER + EOF + ``` + +3. **Define the common representation schema.** + + This describes the attributes shared across all reporters. The `workspace_id` field is required to place the task in a workspace for access control. + + ```bash + cat > task/common_representation.json << 'EOF' + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "workspace_id": { "type": "string" } + }, + "required": [ + "workspace_id" + ] + } + EOF + ``` + +4. **Define the reporter configuration and schema.** + + ```bash + cat > task/reporters/taskmanager/config.yaml << 'EOF' + resource_type: task + reporter_name: taskmanager + namespace: taskmanager + EOF + ``` + + ```bash + cat > task/reporters/taskmanager/task.json << 'EOF' + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "title": { "type": "string" }, + "status": { "type": "string", "enum": ["todo", "in_progress", "done"] }, + "assignee": { "type": "string" } + }, + "required": [ + "title" + ] + } + EOF + ``` + + +## Step 4: Load the schema into Kessel + +Load your resource type and compiled schema into a running Kessel instance. + + +1. **Copy your resource type into the Inventory API schema directory.** + + Replace `/path/to/inventory-api` with the path to your local `inventory-api` clone. + + ```bash + cp -r task/ /path/to/inventory-api/data/schema/resources/ + ``` + +2. **Preload the schema and restart Kessel.** + + ```bash + cd /path/to/inventory-api + go run main.go preload-schema + SCHEMA_ZED_FILE=/path/to/kessel-task-tutorial/schema.zed make kessel-down kessel-up + ``` + + +## Step 5: Set up roles and access + +Before you can check permissions, a user needs access. In Kessel, roles and role bindings are created by writing tuples to the Relations API. + + +1. **Create a role.** + + A role is a set of permission tuples. This creates a "task-viewer" role with the `task_view` permission. + + ```bash + ROLE_NAME="task-viewer" + PERMISSIONS=("task_view") + RELATIONS_PORT=9000 + + TUPLES="" + for i in "${!PERMISSIONS[@]}"; do + PERM="${PERMISSIONS[$i]}" + TUPLE="{\"resource\":{\"id\":\"${ROLE_NAME}\",\"type\":{\"name\":\"role\",\"namespace\":\"rbac\"}},\"relation\":\"${PERM}\",\"subject\":{\"subject\":{\"id\":\"*\",\"type\":{\"name\":\"principal\",\"namespace\":\"rbac\"}}}}" + if [ $i -gt 0 ]; then + TUPLES="${TUPLES}," + fi + TUPLES="${TUPLES}${TUPLE}" + done + + grpcurl -plaintext -d "{\"tuples\":[${TUPLES}]}" \ + "localhost:${RELATIONS_PORT}" \ + kessel.relations.v1beta1.KesselTupleService.CreateTuples + ``` + +2. **Create a role binding.** + + A role binding grants a role to a subject on a specific workspace. This creates three tuples that tie together the role, the user, and the workspace. + + ```bash + ROLE_NAME="task-viewer" + USER_ID="user-123" + RESOURCE_ID="project-alpha" + RELATIONS_PORT=9000 + BINDING_ID="${ROLE_NAME}--${USER_ID}--${RESOURCE_ID}" + + grpcurl -plaintext -d "{\"tuples\":[ + {\"resource\":{\"id\":\"${BINDING_ID}\",\"type\":{\"name\":\"role_binding\",\"namespace\":\"rbac\"}},\"relation\":\"granted\",\"subject\":{\"subject\":{\"id\":\"${ROLE_NAME}\",\"type\":{\"name\":\"role\",\"namespace\":\"rbac\"}}}}, + {\"resource\":{\"id\":\"${BINDING_ID}\",\"type\":{\"name\":\"role_binding\",\"namespace\":\"rbac\"}},\"relation\":\"subject\",\"subject\":{\"subject\":{\"id\":\"${USER_ID}\",\"type\":{\"name\":\"principal\",\"namespace\":\"rbac\"}}}}, + {\"resource\":{\"id\":\"${RESOURCE_ID}\",\"type\":{\"name\":\"workspace\",\"namespace\":\"rbac\"}},\"relation\":\"user_grant\",\"subject\":{\"subject\":{\"id\":\"${BINDING_ID}\",\"type\":{\"name\":\"role_binding\",\"namespace\":\"rbac\"}}}} + ]}" \ + "localhost:${RELATIONS_PORT}" \ + kessel.relations.v1beta1.KesselTupleService.CreateTuples + ``` + + +## Step 6: Report a resource + +Tell Kessel about a task, including its relationship to a workspace. + +```bash +grpcurl -plaintext -d '{ + "type": "task", + "reporterType": "TASKMANAGER", + "reporterInstanceId": "taskmanager-001", + "representations": { + "metadata": { + "localResourceId": "task-001", + "apiHref": "https://taskmanager.example.com/api/tasks/task-001", + "consoleHref": "https://taskmanager.example.com/tasks/task-001", + "reporterVersion": "1.0.0" + }, + "common": { + "workspace_id": "project-alpha" + }, + "reporter": { + "title": "Fix login bug", + "status": "in_progress", + "assignee": "user-123" + } + } +}' localhost:9081 kessel.inventory.v1beta2.KesselInventoryService.ReportResource +``` + +## Step 7: Check permissions + +Verify that the user has access to the task through their role binding on the workspace. + +```bash +grpcurl -plaintext -d '{ + "object": { + "resource_type": "task", + "resource_id": "task-001", + "reporter": { + "type": "TASKMANAGER" + } + }, + "relation": "view", + "subject": { + "resource": { + "resource_type": "principal", + "resource_id": "user-123", + "reporter": { + "type": "rbac" + } + } + } +}' localhost:9081 kessel.inventory.v1beta2.KesselInventoryService.Check +``` + +If the role binding and workspace relationship are set up correctly, this should return `ALLOWED_TRUE`. + + + +## What you learned + +In this tutorial, you: + +- **Mapped your resources** to Kessel's model by identifying resource types, relationships, and permissions +- **Designed a permission schema** using KSL with `@rbac.add_permission` and a custom resource type +- **Created resource type configuration** including JSON Schema for validation +- **Loaded the schema** into a running Kessel instance +- **Set up roles and access** by writing tuples to the Relations API +- **Reported a resource** to Kessel's inventory with a workspace relationship +- **Checked permissions** to verify access flows from workspace to resource + +The process for any application follows this same pattern: map your resources, write a schema, configure the resource type, and add reporting and permission checks. + +## Next steps + + + + + + + + + diff --git a/src/content/docs/start-here/getting-started.mdx b/src/content/docs/start-here/getting-started.mdx index 88abe0d..cce95aa 100644 --- a/src/content/docs/start-here/getting-started.mdx +++ b/src/content/docs/start-here/getting-started.mdx @@ -21,6 +21,8 @@ import createRoleBindingScript from 'src/examples/scripts/create-role-binding.sh export const createFile = (path, content) => `cat > ${path} << 'EOF'\n${content.trim()}\nEOF`; +**Time: ~30 minutes** + This tutorial walks you through setting up Kessel from scratch. By the end, you will have: 1. **Run Kessel** locally with Docker Compose @@ -344,6 +346,19 @@ Kessel provides several ways to handle this, including fully consistent checks ( +## What you learned + +In this tutorial, you: + +- **Defined a resource type** using KSL schema files and JSON Schema for resource attributes +- **Compiled the schema** to SpiceDB format using the `ksl` compiler +- **Ran the full Kessel stack** locally with Docker Compose +- **Set up RBAC** by creating a role with permissions and binding it to a user on a workspace +- **Reported a resource** to Kessel's inventory, including its relationship to a workspace +- **Checked permissions** to verify that access control works through the relationship graph + +These are the same building blocks you'll use when integrating Kessel into a real application. The [how-to guides](/docs/building-with-kessel/how-to/report-resources/) go deeper into each step. + ## Try it yourself Experiment with the schema and relationships. Can you: From c9bb0955a4a438075f654cd4cf8c00407239a4c4 Mon Sep 17 00:00:00 2001 From: Antony Natale Date: Fri, 12 Jun 2026 10:37:15 -0400 Subject: [PATCH 2/5] updates note about compose image caching after recent script updates to not cache by default --- .../docs/building-with-kessel/how-to/run-with-docker.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/content/docs/building-with-kessel/how-to/run-with-docker.mdx b/src/content/docs/building-with-kessel/how-to/run-with-docker.mdx index 6dddfca..86d64d8 100644 --- a/src/content/docs/building-with-kessel/how-to/run-with-docker.mdx +++ b/src/content/docs/building-with-kessel/how-to/run-with-docker.mdx @@ -80,8 +80,7 @@ run the [integration test](#run-the-integration-test) or interact directly with To check that all containers are running and healthy (Docker users: substitute `docker` for `podman`): From c9d0a3b7274120b4ba49b6eea655e422402b98ed Mon Sep 17 00:00:00 2001 From: Antony Natale Date: Fri, 12 Jun 2026 12:55:43 -0400 Subject: [PATCH 3/5] revamped e2e tutorial as it didnt satisfy the cards goal --- .../docs/start-here/add-kessel-to-app.mdx | 382 ++++-------------- src/examples/integrate/example.go | 167 ++++++++ src/examples/integrate/example.py | 155 +++++++ src/examples/integrate/example.ts | 135 +++++++ 4 files changed, 537 insertions(+), 302 deletions(-) create mode 100644 src/examples/integrate/example.go create mode 100644 src/examples/integrate/example.py create mode 100644 src/examples/integrate/example.ts diff --git a/src/content/docs/start-here/add-kessel-to-app.mdx b/src/content/docs/start-here/add-kessel-to-app.mdx index 0ba952b..27cc417 100644 --- a/src/content/docs/start-here/add-kessel-to-app.mdx +++ b/src/content/docs/start-here/add-kessel-to-app.mdx @@ -1,41 +1,27 @@ --- title: "Tutorial: Add Kessel to an existing application" -description: Walk through the process of integrating Kessel into an existing service, from designing a permission schema to checking permissions. +description: Integrate Kessel authorization into your existing service by mapping resources, setting up authenticated clients, reporting at CRUD boundaries, and checking permissions with gRPC interceptors. --- import { Aside, CardGrid, LinkCard, Steps } from '@astrojs/starlight/components'; +import CodeExamples from 'src/components/CodeExamples.astro'; + +export const integrationExamples = import.meta.glob('/src/examples/integrate/example.*', { query: '?raw', eager: true, import: 'default' }); **Time: ~45 minutes** -This tutorial walks you through integrating Kessel into an application you already have. By the end, you will have: +This tutorial walks you through integrating Kessel into a service you already have. By the end, your application will: -1. **Designed a permission schema** for a custom resource type -2. **Compiled and loaded the schema** into a running Kessel instance -3. **Reported a resource** to Kessel with a workspace relationship -4. **Checked permissions** to verify access control works +1. **Report resources** to Kessel whenever it creates or deletes them +2. **Check permissions** through a gRPC interceptor before serving requests -The tutorial uses a task management service as an example: users create tasks, organize them into workspaces (projects), and control who can view or edit them. The same process applies to any application. +Unlike the [Quick Start](../getting-started/), which builds everything from scratch, this tutorial focuses on the **integration points**; the places where Kessel code lives alongside your existing application code. - -## Step 2: Design and compile your permission schema - -With your resources mapped, write a KSL schema that describes them. - - -1. **Create the base schema files.** - - Kessel requires two built-in schema files. These are the same ones used in the Quick Start. - - ```bash - cat > kessel.ksl << 'EOF' - version 0.1 - namespace kessel - - internal type lock { - relation #version: [ExactlyOne lockversion] - } - - internal type lockversion {} - EOF - ``` - - ```bash - cat > rbac.ksl << 'EOF' - version 0.1 - namespace rbac - - public type principal {} - - public type group { - relation member: [Any principal or group.member] - } +## Design your schema - public type role { - } +Write a KSL schema for your resource type. If you followed the Quick Start, this process is familiar. Here is what a task management schema looks like: - public type role_binding { - relation subject: [Any principal or group.member] - relation granted: [AtLeastOne role] - } +```ksl +version 0.1 +namespace taskmanager - public type workspace { - relation parent: [AtMostOne workspace] - relation user_grant: [Any role_binding] - } +import rbac - public extension add_permission(name) { - type role { - private relation `${name}`: [bool] - } +@rbac.add_permission(name:'task_view') +@rbac.add_permission(name:'task_edit') - type role_binding { - internal relation `${name}`: subject and granted.`${name}` - } +public type task { + relation workspace: [ExactlyOne rbac.workspace] - type workspace { - internal relation `${name}`: user_grant.`${name}` or parent.`${name}` - } - } - EOF - ``` + relation view: workspace.task_view + relation edit: workspace.task_edit +} +``` -2. **Write your resource type schema.** +This defines two permissions (`task_view`, `task_edit`) that flow through the workspace hierarchy, and a `task` resource type that belongs to exactly one workspace. - Create `taskmanager.ksl` with permissions for viewing and editing tasks: +For the full schema workflow (writing the base `kessel.ksl` and `rbac.ksl` files, compiling with `ksl`, creating resource type configuration, and loading into Kessel), see the Quick Start sections on [configuring resources](../getting-started/#configure-resources) and [installing Kessel](../getting-started/#install-and-run-kessel). The steps below assume your schema is compiled and loaded. - ```bash - cat > taskmanager.ksl << 'EOF' - version 0.1 - namespace taskmanager +## Add the SDK and create an authenticated client - import rbac +The Quick Start uses `insecure()` mode for local development. For a real integration, configure the SDK with OAuth2 credentials and TLS. - @rbac.add_permission(name:'task_view') - @rbac.add_permission(name:'task_edit') + +1. **Install the SDK** in your project. See the [Quick Start prerequisites](../getting-started/#set-up-your-environment) for language-specific install commands. - public type task { - relation workspace: [ExactlyOne rbac.workspace] +2. **Create an authenticated client.** - relation view: workspace.task_view - relation edit: workspace.task_edit - } - EOF - ``` + The examples below use environment variables for credentials. In production, your service account credentials come from your platform's secret management (for example, Vault or app-interface secrets). - This tells Kessel: + - - There are two permissions (`task_view` and `task_edit`) that can be granted through roles - - There is a resource type called `task` that belongs to exactly one workspace - - The `view` and `edit` permissions on a task are inherited from its workspace + For a detailed walkthrough of TLS certificate loading and OAuth2 configuration, see [Enable TLS](/docs/building-with-kessel/how-to/enable-tls/). + -3. **Compile the schema.** + - ```bash - ksl kessel.ksl rbac.ksl taskmanager.ksl - ``` +## Report resources at your CRUD boundaries - This produces a `schema.zed` file. If you see errors, check that all three `.ksl` files are in the current directory. - +Your service needs to tell Kessel about resources as they are created, updated, and deleted. The natural place to do this is right after your existing database operations. -## Step 3: Create resource type configuration +The pattern is straightforward: after your service method successfully writes to the database, report the resource to Kessel. If the Kessel report fails, log the error but don't fail the request. The resource exists in your database and Kessel will catch up on the next report. -Kessel also needs configuration files that describe your resource type's attributes. + - -1. **Create the directory structure.** - - ```bash - mkdir -p task/reporters/taskmanager - ``` - -2. **Define the resource type.** - - ```bash - cat > task/config.yaml << 'EOF' - resource_type: task - resource_reporters: - - TASKMANAGER - EOF - ``` - -3. **Define the common representation schema.** - - This describes the attributes shared across all reporters. The `workspace_id` field is required to place the task in a workspace for access control. - - ```bash - cat > task/common_representation.json << 'EOF' - { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "workspace_id": { "type": "string" } - }, - "required": [ - "workspace_id" - ] - } - EOF - ``` - -4. **Define the reporter configuration and schema.** - - ```bash - cat > task/reporters/taskmanager/config.yaml << 'EOF' - resource_type: task - reporter_name: taskmanager - namespace: taskmanager - EOF - ``` - - ```bash - cat > task/reporters/taskmanager/task.json << 'EOF' - { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "title": { "type": "string" }, - "status": { "type": "string", "enum": ["todo", "in_progress", "done"] }, - "assignee": { "type": "string" } - }, - "required": [ - "title" - ] - } - EOF - ``` - +Key points: -## Step 4: Load the schema into Kessel +- **Report after the database write**, not before. The resource must exist in your system before Kessel knows about it. +- **Include the `workspace_id`** in the common representation. This is what connects the resource to the workspace hierarchy and enables permission inheritance. +- **Handle errors gracefully.** A Kessel outage should not prevent your service from creating resources. Log the failure and consider a reconciliation process to catch up. -Load your resource type and compiled schema into a running Kessel instance. + - -1. **Copy your resource type into the Inventory API schema directory.** +## Check permissions before serving requests - Replace `/path/to/inventory-api` with the path to your local `inventory-api` clone. +The most impactful integration point is adding permission checks to your existing endpoints. Rather than scattering `Check` calls through every service method, wrap them in a gRPC server interceptor that runs before the handler executes. - ```bash - cp -r task/ /path/to/inventory-api/data/schema/resources/ - ``` +The interceptor: +1. Extracts the user identity and resource ID from gRPC metadata +2. Calls Kessel's `Check` API +3. Returns `PERMISSION_DENIED` if the user lacks the required permission +4. Forwards the call to the handler if access is granted -2. **Preload the schema and restart Kessel.** + - ```bash - cd /path/to/inventory-api - go run main.go preload-schema - SCHEMA_ZED_FILE=/path/to/kessel-task-tutorial/schema.zed make kessel-down kessel-up - ``` - +This pattern keeps authorization logic separate from business logic. Your service methods don't need to know about Kessel; they only run if the interceptor allows the call through. -## Step 5: Set up roles and access + -Before you can check permissions, a user needs access. In Kessel, roles and role bindings are created by writing tuples to the Relations API. +## Choose your consistency strategy - -1. **Create a role.** - - A role is a set of permission tuples. This creates a "task-viewer" role with the `task_view` permission. - - ```bash - ROLE_NAME="task-viewer" - PERMISSIONS=("task_view") - RELATIONS_PORT=9000 - - TUPLES="" - for i in "${!PERMISSIONS[@]}"; do - PERM="${PERMISSIONS[$i]}" - TUPLE="{\"resource\":{\"id\":\"${ROLE_NAME}\",\"type\":{\"name\":\"role\",\"namespace\":\"rbac\"}},\"relation\":\"${PERM}\",\"subject\":{\"subject\":{\"id\":\"*\",\"type\":{\"name\":\"principal\",\"namespace\":\"rbac\"}}}}" - if [ $i -gt 0 ]; then - TUPLES="${TUPLES}," - fi - TUPLES="${TUPLES}${TUPLE}" - done - - grpcurl -plaintext -d "{\"tuples\":[${TUPLES}]}" \ - "localhost:${RELATIONS_PORT}" \ - kessel.relations.v1beta1.KesselTupleService.CreateTuples - ``` - -2. **Create a role binding.** - - A role binding grants a role to a subject on a specific workspace. This creates three tuples that tie together the role, the user, and the workspace. - - ```bash - ROLE_NAME="task-viewer" - USER_ID="user-123" - RESOURCE_ID="project-alpha" - RELATIONS_PORT=9000 - BINDING_ID="${ROLE_NAME}--${USER_ID}--${RESOURCE_ID}" - - grpcurl -plaintext -d "{\"tuples\":[ - {\"resource\":{\"id\":\"${BINDING_ID}\",\"type\":{\"name\":\"role_binding\",\"namespace\":\"rbac\"}},\"relation\":\"granted\",\"subject\":{\"subject\":{\"id\":\"${ROLE_NAME}\",\"type\":{\"name\":\"role\",\"namespace\":\"rbac\"}}}}, - {\"resource\":{\"id\":\"${BINDING_ID}\",\"type\":{\"name\":\"role_binding\",\"namespace\":\"rbac\"}},\"relation\":\"subject\",\"subject\":{\"subject\":{\"id\":\"${USER_ID}\",\"type\":{\"name\":\"principal\",\"namespace\":\"rbac\"}}}}, - {\"resource\":{\"id\":\"${RESOURCE_ID}\",\"type\":{\"name\":\"workspace\",\"namespace\":\"rbac\"}},\"relation\":\"user_grant\",\"subject\":{\"subject\":{\"id\":\"${BINDING_ID}\",\"type\":{\"name\":\"role_binding\",\"namespace\":\"rbac\"}}}} - ]}" \ - "localhost:${RELATIONS_PORT}" \ - kessel.relations.v1beta1.KesselTupleService.CreateTuples - ``` - +Kessel's consistency model is tunable. By default, permission checks use `minimize_latency`, which may read slightly stale data. But you can achieve causal consistency ("read your writes") when your use case requires it. -## Step 6: Report a resource - -Tell Kessel about a task, including its relationship to a workspace. - -```bash -grpcurl -plaintext -d '{ - "type": "task", - "reporterType": "TASKMANAGER", - "reporterInstanceId": "taskmanager-001", - "representations": { - "metadata": { - "localResourceId": "task-001", - "apiHref": "https://taskmanager.example.com/api/tasks/task-001", - "consoleHref": "https://taskmanager.example.com/tasks/task-001", - "reporterVersion": "1.0.0" - }, - "common": { - "workspace_id": "project-alpha" - }, - "reporter": { - "title": "Fix login bug", - "status": "in_progress", - "assignee": "user-123" - } - } -}' localhost:9081 kessel.inventory.v1beta2.KesselInventoryService.ReportResource -``` +The two most important check methods are: -## Step 7: Check permissions - -Verify that the user has access to the task through their role binding on the workspace. - -```bash -grpcurl -plaintext -d '{ - "object": { - "resource_type": "task", - "resource_id": "task-001", - "reporter": { - "type": "TASKMANAGER" - } - }, - "relation": "view", - "subject": { - "resource": { - "resource_type": "principal", - "resource_id": "user-123", - "reporter": { - "type": "rbac" - } - } - } -}' localhost:9081 kessel.inventory.v1beta2.KesselInventoryService.Check -``` +- **`Check`** (default `minimize_latency`): fast, suitable for read-heavy paths like dashboards and list views. The authorization graph may lag the inventory database by 100-500ms under normal conditions, but this is invisible for most read operations. +- **`CheckForUpdate`**: always fully consistent. The authorization backend evaluates against the latest committed state, including any recent changes to role bindings or resource relationships. Use this for updates, deletes, and any security-critical decision where acting on stale data could cause a conflict or incorrect access grant. -If the role binding and workspace relationship are set up correctly, this should return `ALLOWED_TRUE`. +For writes, you can also use `write_visibility: IMMEDIATE` on `ReportResource` to wait for replication to complete before returning. This guarantees that a `Check` issued immediately after the report will reflect the change, giving you causal consistency between resource writes and reads. - +For the full set of consistency modes, tokens, and strategies, see the [Consistency model](/docs/building-with-kessel/concepts/consistency/). ## What you learned In this tutorial, you: -- **Mapped your resources** to Kessel's model by identifying resource types, relationships, and permissions -- **Designed a permission schema** using KSL with `@rbac.add_permission` and a custom resource type -- **Created resource type configuration** including JSON Schema for validation -- **Loaded the schema** into a running Kessel instance -- **Set up roles and access** by writing tuples to the Relations API -- **Reported a resource** to Kessel's inventory with a workspace relationship -- **Checked permissions** to verify access flows from workspace to resource +- **Mapped your resources** to Kessel's model, identifying resource types, workspaces, and permissions +- **Created an authenticated client** with OAuth2 and TLS for production use +- **Reported resources at your CRUD boundaries** by adding Kessel reporting alongside your existing database operations +- **Checked permissions with a gRPC interceptor**, gating requests on Kessel authorization without changing your service methods +- **Chose a consistency strategy** using `Check` for reads and `CheckForUpdate` for writes and security-critical decisions -The process for any application follows this same pattern: map your resources, write a schema, configure the resource type, and add reporting and permission checks. +These are the integration seams you'll use in any Kessel-enabled service. The patterns are the same whether your application manages tasks, hosts, clusters, or any other resource type. ## Next steps - - - - + + + +