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.
Quick start
Section titled “Quick start”Configure Resources
Section titled “Configure Resources”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.
-
Author schemas
document/config.yaml resource_type: documentresource_reporters:- drivedocument/reporters/drive_service/config.yaml resource_type: documentreporter_name: drivenamespace: drivedocument/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.1namespace kesselinternal type lock {relation #version: [ExactlyOne lockversion]}internal type lockversion {}rbac.ksl version 0.1namespace rbacpublic 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.1namespace driveimport 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 workspacerelation view_document: workspace.view_documentrelation edit_document: workspace.edit_documentrelation delete_document: workspace.delete_documentrelation view: view_documentrelation edit: edit_documentrelation manage: delete_document} -
Compile to SpiceDB schema
Install the KSL compiler directly from github:
Terminal window go install github.com/project-kessel/ksl-schema-language/cmd/ksl@latestAlternatively, you can clone the repository and build locally:
Terminal window git clone https://github.com/project-kessel/ksl-schema-language.gitcd ksl-schema-languagemake buildThen, you can use the
ksl
command to compile the schemas:Terminal window ksl kessel.ksl rbac.ksl drive.kslThis will generate a
schema.zed
file in the current directory.Example generated
schema.zed
fileschema.zed definition drive/document {permission delete_document = t_workspace->delete_documentpermission edit = edit_documentpermission edit_document = t_workspace->edit_documentpermission manage = delete_documentpermission view = view_documentpermission view_document = t_workspace->view_documentpermission workspace = t_workspacerelation t_workspace: rbac/workspace}definition kessel/lock {permission version = t_versionrelation t_version: kessel/lockversion}definition kessel/lockversion {}definition rbac/group {permission member = t_memberrelation t_member: rbac/principal | rbac/group#member}definition rbac/principal {}definition rbac/role {permission delete_document = t_delete_documentrelation t_delete_document: rbac/principal:*permission edit_document = t_edit_documentrelation t_edit_document: rbac/principal:*permission view_document = t_view_documentrelation 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_grantedrelation t_granted: rbac/rolepermission subject = t_subjectrelation t_subject: rbac/principal | rbac/group#memberpermission view_document = (subject & t_granted->view_document)}definition rbac/workspace {permission delete_document = t_user_grant->delete_document + t_parent->delete_documentpermission edit_document = t_user_grant->edit_document + t_parent->edit_documentpermission parent = t_parentrelation t_parent: rbac/workspacepermission user_grant = t_user_grantrelation t_user_grant: rbac/role_bindingpermission view_document = t_user_grant->view_document + t_parent->view_document}
Install and run Kessel with RBAC
Section titled “Install and run Kessel with RBAC”-
Start Kessel Inventory
Terminal window git clone git@github.com:project-kessel/inventory-api.gitcd inventory-apimake inventory-up-relations-ready -
Start Relations and SpiceDB
Terminal window git clone git@github.com:project-kessel/relations-api.gitcd relations-api# copy the schema.zed file we created earlier to deploy/schema.zedcp ../schema.zed deploy/schema.zedmake relations-api-up -
Start RBAC
TODO. For now we will create the Relationships directly
-
Start Kessel Inventory
Terminal window bonfire deploy kessel -C kessel-inventory -
Load Schema
Terminal window # create configmap from the schema.zed file we created earlieroc create configmap spicedb-schema --dry-run=client --from-file=schema.zed -o yaml > spicedb-schema-configmap.yaml# apply the configmapoc apply -f spicedb-schema-configmap.yaml -
Fetch the API URL and Credentials
Terminal window bonfire namespace describe -
Enable port forwarding to Kessel Inventory
Terminal window oc port-forward svc/kessel-inventory-api <your local port>:9000 -n <Name of current project from step 3>oc port-forward svc/kessel-relations-api <your local port>:9000 -n <Name of current project from step 3>
Set up access with RBAC
Section titled “Set up access with RBAC”- 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 - 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 - 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 - 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
Report and Check Examples
Section titled “Report and Check Examples”Setup Client
# Set your Kessel endpointKESSEL_BASE_URL="http://localhost:9000"
# Set common headers for API requestsHEADERS=( -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:
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:
curl "${HEADERS[@]}" \ -X POST \ -d "$MESSAGE" \ "$KESSEL_BASE_URL/api/inventory/v1beta2/resources"
Creating Check Requests
Build the check request payload:
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:
curl "${HEADERS[@]}" \ -X POST \ -d "$MESSAGE" \ "$KESSEL_BASE_URL/api/inventory/v1beta2/check"
Complete Example
# Set your Kessel endpointKESSEL_BASE_URL="http://localhost:9000"
# Set common headers for API requestsHEADERS=( -H "Content-Type: application/json")
# For authenticated environments, add authorization header:# HEADERS+=(# -H "Authorization: Bearer $ACCESS_TOKEN"# )
# 1) Report a resource firstREPORT_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 checkCHECK_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"
Setup Client
# Set your Kessel gRPC endpointKESSEL_GRPC_ENDPOINT="localhost:9000"
# For insecure local development:GRPC_OPTS="-plaintext"
# For authenticated environments:# GRPC_OPTS="-H 'authorization: Bearer $ACCESS_TOKEN'"
Creating Report Resource Requests
Build the report resource request payload:
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:
grpcurl $GRPC_OPTS \ -d "$MESSAGE" \ "$KESSEL_GRPC_ENDPOINT" \ kessel.inventory.v1beta2.KesselInventoryService.ReportResource
Creating Check Requests
Build the check request payload:
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:
grpcurl $GRPC_OPTS \ -d "$MESSAGE" \ "$KESSEL_GRPC_ENDPOINT" \ kessel.inventory.v1beta2.KesselInventoryService.Check
Complete Example
# Set your Kessel gRPC endpointKESSEL_GRPC_ENDPOINT="localhost:9000"
# For insecure local development:GRPC_OPTS="-plaintext"
# For authenticated environments:# GRPC_OPTS="-H 'authorization: Bearer $ACCESS_TOKEN'"
# 1) Report a resource firstREPORT_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"}}}'grpcurl $GRPC_OPTS \ -d "$REPORT_MESSAGE" \ "$KESSEL_GRPC_ENDPOINT" \ kessel.inventory.v1beta2.KesselInventoryService.ReportResource
# 2) Then perform a permission checkCHECK_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"}}}}'grpcurl $GRPC_OPTS \ -d "$CHECK_MESSAGE" \ "$KESSEL_GRPC_ENDPOINT" \ kessel.inventory.v1beta2.KesselInventoryService.Check
Import client
pip install kessel-sdk
Basic Client Setup
from kessel.inventory.v1beta2 import ClientBuilder
# For insecure local development:stub, channel = ClientBuilder(KESSEL_ENDPOINT).insecure().build()
Auth Client Setup
Install the SDK with auth dependencies:
pip install "kessel-sdk[auth]"
from kessel.auth import fetch_oidc_discovery, OAuth2ClientCredentialsfrom kessel.inventory.v1beta2 import ClientBuilder
# Fetch OIDC discovery informationdiscovery = fetch_oidc_discovery(ISSUER_URL)
# Create OAuth2 credentialsauth_credentials = OAuth2ClientCredentials( client_id=CLIENT_ID, client_secret=CLIENT_SECRET, token_endpoint=discovery.token_endpoint,)
# Build authenticated clientstub, channel = ClientBuilder(KESSEL_ENDPOINT).oauth2_client_authenticated(auth_credentials).build()
Creating Report Resource Requests
Build a ReportResource Request:
from google.protobuf import struct_pb2from kessel.inventory.v1beta2 import ( report_resource_request_pb2, resource_representations_pb2, representation_metadata_pb2,)
# Build protobuf Struct for common metadatacommon_struct = struct_pb2.Struct()common_struct.update({"workspace_id": "workspace-1"})
# Build protobuf Struct for reporter-specific datareporter_struct = struct_pb2.Struct()reporter_struct.update({ "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",})
# Create metadata for the resource representationmetadata = representation_metadata_pb2.RepresentationMetadata( 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",)
# Build the resource representationsrepresentations = resource_representations_pb2.ResourceRepresentations( metadata=metadata, common=common_struct, reporter=reporter_struct)
# Create the report requestrequest = report_resource_request_pb2.ReportResourceRequest( type="document", reporter_type="drive", reporter_instance_id="drive-1", representations=representations,)
Sending Report Resource Requests
Execute the report resource request and handle the response:
import grpc
try: response = stub.ReportResource(request) print("Resource reported successfully")except grpc.RpcError as e: print("gRPC error occurred during ReportResource:") print(f"Code: {e.code()}") print(f"Details: {e.details()}")
Creating Check Requests
Build a permission check request:
from kessel.inventory.v1beta2 import ( subject_reference_pb2, resource_reference_pb2, reporter_reference_pb2, check_request_pb2,)
# Prepare the subject reference object (who is requesting access)subject = subject_reference_pb2.SubjectReference( resource=resource_reference_pb2.ResourceReference( reporter=reporter_reference_pb2.ReporterReference(type="rbac"), resource_id="sarah", resource_type="principal", ))
# Prepare the resource reference object (what is being accessed)resource_ref = resource_reference_pb2.ResourceReference( resource_id="doc-123", resource_type="document", reporter=reporter_reference_pb2.ReporterReference(type="drive"),)
# Build the complete check requestcheck_request = check_request_pb2.CheckRequest( subject=subject, relation="view", object=resource_ref,)
Sending Check Requests
Execute the check request and handle the response:
import grpc
try: check_response = stub.Check(check_request) print("Check response received successfully") print(check_response)except grpc.RpcError as e: print("gRPC error occurred during Check:") print(f"Code: {e.code()}") print(f"Details: {e.details()}")
Complete Example
import osimport grpcfrom google.protobuf import struct_pb2from kessel.auth import fetch_oidc_discovery, OAuth2ClientCredentialsfrom kessel.inventory.v1beta2 import ( ClientBuilder, subject_reference_pb2, resource_reference_pb2, reporter_reference_pb2, check_request_pb2, report_resource_request_pb2, resource_representations_pb2, representation_metadata_pb2,)
def run(): # For authenticated environments, uncomment and configure the following: # discovery = fetch_oidc_discovery(ISSUER_URL) # auth_credentials = OAuth2ClientCredentials( # client_id=CLIENT_ID, # client_secret=CLIENT_SECRET, # token_endpoint=discovery.token_endpoint, # ) # stub, channel = ClientBuilder(KESSEL_ENDPOINT).oauth2_client_authenticated(auth_credentials).build()
# For insecure local development: stub, channel = ClientBuilder(KESSEL_ENDPOINT).insecure().build()
with channel: # 1) Report a resource first common_struct = struct_pb2.Struct() common_struct.update({"workspace_id": "workspace-1"})
reporter_struct = struct_pb2.Struct() reporter_struct.update({ "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", })
metadata = representation_metadata_pb2.RepresentationMetadata( 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", )
representations = resource_representations_pb2.ResourceRepresentations( metadata=metadata, common=common_struct, reporter=reporter_struct, )
report_req = report_resource_request_pb2.ReportResourceRequest( type="document", reporter_type="drive", reporter_instance_id="drive-1", representations=representations, ) _ = stub.ReportResource(report_req)
# 2) Then perform a permission check subject = subject_reference_pb2.SubjectReference( resource=resource_reference_pb2.ResourceReference( reporter=reporter_reference_pb2.ReporterReference(type="rbac"), resource_id="sarah", resource_type="principal", ) )
resource_ref = resource_reference_pb2.ResourceReference( resource_id="doc-123", resource_type="document", reporter=reporter_reference_pb2.ReporterReference(type="drive"), )
check_request = check_request_pb2.CheckRequest( subject=subject, relation="view", object=resource_ref, )
try: check_response = stub.Check(check_request) print("Check response received successfully") print(check_response) except grpc.RpcError as e: print("gRPC error occurred during Check:") print(f"Code: {e.code()}") print(f"Details: {e.details()}")
if __name__ == "__main__": run()
Import client
go get github.com/project-kessel/kessel-sdk-go
Basic Client Setup
import "github.com/project-kessel/kessel-sdk-go/kessel/inventory/v1beta2"
// For insecure local development:inventoryClient, conn, err := v1beta2.NewClientBuilder(kesselEndpoint). Insecure(). Build()if err != nil { log.Fatal("Failed to create gRPC client:", err)}defer conn.Close()
Auth Client Setup
import ( "github.com/project-kessel/kessel-sdk-go/kessel/inventory/v1beta2" "github.com/project-kessel/kessel-sdk-go/kessel/auth")
// Fetch OIDC discovery informationdiscovered, err := auth.FetchOIDCDiscovery(ctx, os.Getenv("AUTH_DISCOVERY_ISSUER_URL"), auth.FetchOIDCDiscoveryOptions{ HttpClient: nil, // Optionally specify an http client - defaults to http.DefaultClient})if err != nil { panic(err)}
// Create OAuth2 credentialsoauthCredentials := auth.NewOAuth2ClientCredentials( os.Getenv("AUTH_CLIENT_ID"), os.Getenv("AUTH_CLIENT_SECRET"), discovered.TokenEndpoint,)
// Build authenticated clientinventoryClient, conn, err := v1beta2.NewClientBuilder(os.Getenv("KESSEL_ENDPOINT")). OAuth2ClientAuthenticated(&oauthCredentials, nil). Build()
Creating Report Resource Requests
Build a ReportResource Request:
import "google.golang.org/protobuf/types/known/structpb"
reportResourceRequest := &v1beta2.ReportResourceRequest{ Type: "document", ReporterType: "drive", ReporterInstanceId: "drive-1", Representations: &v1beta2.ResourceRepresentations{ Metadata: &v1beta2.RepresentationMetadata{ LocalResourceId: "doc-123", ApiHref: "https://drive.example.com/document/123", ConsoleHref: addr("https://www.console.com/drive/documents"), ReporterVersion: addr("2.7.16"), }, Common: &structpb.Struct{ Fields: map[string]*structpb.Value{ "workspace_id": structpb.NewStringValue("workspace-1"), }, }, Reporter: &structpb.Struct{ Fields: map[string]*structpb.Value{ "document_id": structpb.NewStringValue("doc-123"), "document_name": structpb.NewStringValue("My Important Document"), "document_type": structpb.NewStringValue("document"), "created_at": structpb.NewStringValue("2025-08-31T10:30:00Z"), "file_size": structpb.NewNumberValue(2048576), "owner_id": structpb.NewStringValue("user-1"), }, }, },}
func addr[T any](t T) *T { return &t }
Sending Report Resource Requests
Execute the report resource request and handle the response:
import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status")
fmt.Println("Making report resource request:")
response, err := inventoryClient.ReportResource(ctx, reportResourceRequest)if err != nil { if st, ok := status.FromError(err); ok { switch st.Code() { case codes.Unavailable: log.Fatal("Service unavailable: ", err) case codes.PermissionDenied: log.Fatal("Permission denied: ", err) default: log.Fatal("gRPC connection error: ", err) } } else { log.Fatal("Unknown error: ", err) }}fmt.Printf("Report resource response: %+v\n", response)
Creating Check Requests
Build a permission check request:
checkRequest := &v1beta2.CheckRequest{ Object: &v1beta2.ResourceReference{ ResourceType: "document", ResourceId: "doc-123", Reporter: &v1beta2.ReporterReference{ Type: "drive", }, }, Relation: "view", Subject: &v1beta2.SubjectReference{ Resource: &v1beta2.ResourceReference{ ResourceType: "principal", ResourceId: "sarah", Reporter: &v1beta2.ReporterReference{ Type: "rbac", }, }, },}
Sending Check Requests
Execute the check request and handle the response:
import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status")
fmt.Println("Making check request:")response, err := inventoryClient.Check(ctx, checkRequest)if err != nil { if st, ok := status.FromError(err); ok { switch st.Code() { case codes.Unavailable: log.Fatal("Service unavailable: ", err) case codes.PermissionDenied: log.Fatal("Permission denied: ", err) default: log.Fatal("gRPC connection error: ", err) } } else { log.Fatal("Unknown error: ", err) }}fmt.Printf("Check response: %+v\n", response)
Complete Example
package main
import ( "context" "fmt" "log"
_ "github.com/joho/godotenv/autoload"
"google.golang.org/protobuf/types/known/structpb"
"github.com/project-kessel/kessel-sdk-go/kessel/inventory/v1beta2" "google.golang.org/grpc/codes" "google.golang.org/grpc/status")
func endToEnd() { ctx := context.Background()
inventoryClient, conn, err := v1beta2.NewClientBuilder(KESSEL_ENDPOINT). Insecure(). Build() if err != nil { log.Fatal("Failed to create gRPC client:", err) } defer func() { if closeErr := conn.Close(); closeErr != nil { log.Printf("Failed to close gRPC client: %v", closeErr) } }()
// 1) Report a resource first reportResourceRequest := &v1beta2.ReportResourceRequest{ Type: "document", ReporterType: "drive", ReporterInstanceId: "drive-1", Representations: &v1beta2.ResourceRepresentations{ Metadata: &v1beta2.RepresentationMetadata{ LocalResourceId: "doc-123", ApiHref: "https://drive.example.com/document/123", ConsoleHref: addr("https://www.console.com/drive/documents"), ReporterVersion: addr("2.7.16"), }, Common: &structpb.Struct{ Fields: map[string]*structpb.Value{ "workspace_id": structpb.NewStringValue("workspace-1"), }, }, Reporter: &structpb.Struct{ Fields: map[string]*structpb.Value{ "document_id": structpb.NewStringValue("doc-123"), "document_name": structpb.NewStringValue("My Important Document"), "document_type": structpb.NewStringValue("document"), "created_at": structpb.NewStringValue("2025-08-31T10:30:00Z"), "file_size": structpb.NewNumberValue(2048576), "owner_id": structpb.NewStringValue("user-1"), }, }, }, }
_, err = inventoryClient.ReportResource(ctx, reportResourceRequest) if err != nil { log.Fatalf("ReportResource failed: %v", err) }
// 2) Then perform a permission check checkRequest := &v1beta2.CheckRequest{ Object: &v1beta2.ResourceReference{ ResourceType: "document", ResourceId: "doc-123", Reporter: &v1beta2.ReporterReference{ Type: "drive" }, }, Relation: "view", Subject: &v1beta2.SubjectReference{ Resource: &v1beta2.ResourceReference{ ResourceType: "principal", ResourceId: "sarah", Reporter: &v1beta2.ReporterReference{ Type: "rbac" }, }, }, }
fmt.Println("Making check request:") response, err := inventoryClient.Check(ctx, checkRequest) if err != nil { if st, ok := status.FromError(err); ok { switch st.Code() { case codes.Unavailable: log.Fatal("Service unavailable: ", err) case codes.PermissionDenied: log.Fatal("Permission denied: ", err) default: log.Fatal("gRPC connection error: ", err) } } else { log.Fatal("Unknown error: ", err) } } fmt.Printf("Check response: %+v\n", response)}
func addr[T any](t T) *T { return &t }
func main() { endToEnd() }
Import client
npm install @project-kessel/kessel-sdk
Basic Client Setup
import { ClientBuilder } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2";
// For insecure local development:const client = new ClientBuilder(process.env.KESSEL_ENDPOINT).insecure().buildAsync();
Auth Client Setup
import { ClientBuilder } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2";import { fetchOIDCDiscovery, OAuth2ClientCredentials,} from "@project-kessel/kessel-sdk/kessel/auth";
// Fetch OIDC discovery informationconst discovery = await fetchOIDCDiscovery(process.env.AUTH_DISCOVERY_ISSUER_URL);
// Create OAuth2 credentialsconst oAuth2ClientCredentials = new OAuth2ClientCredentials({ clientId: process.env.AUTH_CLIENT_ID, clientSecret: process.env.AUTH_CLIENT_SECRET, tokenEndpoint: discovery.tokenEndpoint,});
// Build authenticated clientconst client = new ClientBuilder(process.env.KESSEL_ENDPOINT) .oauth2ClientAuthenticated(oAuth2ClientCredentials) .buildAsync();
Creating Report Resource Requests
Build a ReportResource Request:
// Common metadata shared across all reportersconst common = { workspace_id: "workspace-1",};
// Reporter-specific data for the drive serviceconst 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",};
// Create metadata for the resource representationconst metadata = { localResourceId: "doc-123", apiHref: "https://drive.example.com/document/123", consoleHref: "https://www.console.com/drive/documents", reporterVersion: "2.7.16",};
// Build the resource representationsconst representations = { metadata: metadata, common: common, reporter: reporter,};
// Create the complete report resource requestconst reportResourceRequest = { type: "document", reporterType: "drive", reporterInstanceId: "drive-1", representations,};
Sending Report Resource Requests
Execute the report resource request and handle the response:
try { const response = await client.reportResource(reportResourceRequest); console.log("Resource reported successfully:"); console.log(response);} catch (error) { console.log("gRPC error occurred during Resource reporting:"); console.log("Exception:", error);}
Creating Check Requests
Build a permission check request:
// Prepare the subject reference object (who is requesting access)const subjectReference = { resource: { reporter: { type: "rbac", }, resourceId: "sarah", resourceType: "principal", },};
// Prepare the resource reference object (what is being accessed)const resource = { reporter: { type: "drive", }, resourceId: "doc-123", resourceType: "document",};
// Build the complete check requestconst check_request = { object: resource, relation: "view", subject: subjectReference,};
Sending Check Requests
Execute the check request and handle the response:
try { const response = await client.check(check_request); console.log("Check response received successfully:"); console.log(response);} catch (error) { console.log("gRPC error occurred during Check:"); console.log("Exception:", error);}
Complete Example
import { ResourceReference } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2/resource_reference";import { SubjectReference } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2/subject_reference";import { CheckRequest } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2/check_request";import { ReportResourceRequest } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2/report_resource_request";import { ResourceRepresentations } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2/resource_representations";import { RepresentationMetadata } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2/representation_metadata";import { ClientBuilder } from "@project-kessel/kessel-sdk/kessel/inventory/v1beta2";import { fetchOIDCDiscovery, OAuth2ClientCredentials,} from "@project-kessel/kessel-sdk/kessel/auth";import "dotenv/config";
async function run() { try { // For authenticated environments, uncomment and configure the following: // const discovery = await fetchOIDCDiscovery( // process.env.AUTH_DISCOVERY_ISSUER_URL, // );
// const oAuth2ClientCredentials = new OAuth2ClientCredentials({ // clientId: process.env.AUTH_CLIENT_ID!, // clientSecret: process.env.AUTH_CLIENT_SECRET!, // tokenEndpoint: discovery.tokenEndpoint, // });
// const client = new ClientBuilder(process.env.KESSEL_ENDPOINT) // .oauth2ClientAuthenticated(oAuth2ClientCredentials) // .buildAsync();
// For insecure local development: const client = new ClientBuilder(process.env.KESSEL_ENDPOINT).insecure().buildAsync();
// 1) Report a resource first const common = { workspace_id: "workspace-1" }; const 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", }; const metadata: RepresentationMetadata = { localResourceId: "doc-123", apiHref: "https://drive.example.com/document/123", consoleHref: "https://www.console.com/drive/documents", reporterVersion: "2.7.16", }; const representations: ResourceRepresentations = { metadata, common, reporter, }; const reportResourceRequest: ReportResourceRequest = { type: "document", reporterType: "drive", reporterInstanceId: "drive-1", representations, }; await client.reportResource(reportResourceRequest);
// 2) Then perform a permission check const subjectReference: SubjectReference = { resource: { reporter: { type: "rbac" }, resourceId: "sarah", resourceType: "principal", }, }; const resource: ResourceReference = { reporter: { type: "drive" }, resourceId: "doc-123", resourceType: "document", }; const check_request: CheckRequest = { object: resource, relation: "view", subject: subjectReference, }; const response = await client.check(check_request); console.log("Check response received successfully:"); console.log(response); } catch (error) { console.log("Error during report or check:"); console.log("Exception:", error); }}
run();
Import client
gem install kessel-sdk
Basic Client Setup
require 'kessel-sdk'include Kessel::Inventory::V1beta2include Kessel::GRPC
# For insecure local development:client = KesselInventoryService::ClientBuilder.new(ENV.fetch('KESSEL_ENDPOINT', 'localhost:9000')) .insecure .build
Auth Client Setup
require 'kessel-sdk'include Kessel::Inventory::V1beta2include Kessel::GRPCinclude Kessel::Auth
# Fetch OIDC discovery informationdiscovery = fetch_oidc_discovery(ENV.fetch('AUTH_DISCOVERY_ISSUER_URL'))
# Create OAuth2 credentialsoauth = OAuth2ClientCredentials.new( client_id: ENV.fetch('AUTH_CLIENT_ID'), client_secret: ENV.fetch('AUTH_CLIENT_SECRET'), token_endpoint: discovery.token_endpoint,)
# Build authenticated clientclient = KesselInventoryService::ClientBuilder.new(ENV.fetch('KESSEL_ENDPOINT')) .oauth2_client_authenticated(oauth2_client_credentials: oauth) .build
Creating Report Resource Requests
Build a ReportResource Request:
require 'json'
# Common metadata shared across all reporterscommon = Google::Protobuf::Struct.decode_json({ 'workspace_id' => 'workspace-1' }.to_json)
# Reporter-specific data for the drive servicereporter = Google::Protobuf::Struct.decode_json({ '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'}.to_json)
# Create metadata for the resource representationmetadata = RepresentationMetadata.new( 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')
# Build the resource representationsrepresentations = ResourceRepresentations.new( metadata: metadata, common: common, reporter: reporter)
# Create the complete report resource requestreport_request = ReportResourceRequest.new( type: 'document', reporter_type: 'drive', reporter_instance_id: 'drive-1', representations: representations)
Sending Report Resource Requests
Execute the report resource request and handle the response:
begin response = client.report_resource(report_request) puts 'report_resource response received successfully:' p responserescue => e puts 'gRPC error occurred during report_resource:' puts "Exception: #{e}"end
Creating Check Requests
Build a permission check request:
# Prepare the subject reference object (who is requesting access)subject_reference = SubjectReference.new( resource: ResourceReference.new( reporter: ReporterReference.new(type: 'rbac'), resource_id: 'sarah', resource_type: 'principal' ))
# Prepare the resource reference object (what is being accessed)resource = ResourceReference.new( reporter: ReporterReference.new(type: 'drive'), resource_id: 'doc-123', resource_type: 'document')
# Build the complete check requestcheck_request = CheckRequest.new( object: resource, relation: 'view', subject: subject_reference)
Sending Check Requests
Execute the check request and handle the response:
begin response = client.check(check_request) p 'check response received successfully:' p responserescue => e p 'gRPC error occurred during check:' p "Exception: #{e}"end
Complete Example
#!/usr/bin/env ruby# frozen_string_literal: true
require 'dotenv/load'require 'json'require 'kessel-sdk'
include Kessel::Inventory::V1beta2include Kessel::GRPCinclude Kessel::Auth
# For insecure local development:client = KesselInventoryService::ClientBuilder.new(ENV.fetch('KESSEL_ENDPOINT', 'localhost:9000')) .insecure .build
# 1) Report a resource firstcommon = Google::Protobuf::Struct.decode_json({ 'workspace_id' => 'workspace-1' }.to_json)reporter = Google::Protobuf::Struct.decode_json({ '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'}.to_json)metadata = RepresentationMetadata.new( 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')representations = ResourceRepresentations.new( metadata: metadata, common: common, reporter: reporter)client.report_resource( ReportResourceRequest.new( type: 'document', reporter_type: 'drive', reporter_instance_id: 'drive-1', representations: representations ))
# 2) Then perform a permission checksubject_reference = SubjectReference.new( resource: ResourceReference.new( reporter: ReporterReference.new(type: 'rbac'), resource_id: 'sarah', resource_type: 'principal' ))resource = ResourceReference.new( reporter: ReporterReference.new(type: 'drive'), resource_id: 'doc-123', resource_type: 'document')
begin response = client.check( CheckRequest.new( object: resource, relation: 'view', subject: subject_reference ) ) p 'check response received successfully:' p responserescue => e p 'gRPC error occurred during check:' p "Exception: #{e}"end
Add dependency (Maven)
<dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> </dependency><dependency> <groupId>org.project-kessel</groupId> <artifactId>kessel-sdk</artifactId> <version>1.0.0</version></dependency><!-- Optional: Auth support for OAuth2 client credentials --><dependency> <groupId>com.nimbusds</groupId> <artifactId>oauth2-oidc-sdk</artifactId> <version>11.28</version></dependency>
Add dependency (Gradle)
dependencies { implementation 'io.grpc:grpc-netty-shaded' implementation "org.project-kessel:kessel-sdk:1.0.0" // Optional: Auth support for OAuth2 client credentials implementation "com.nimbusds:oauth2-oidc-sdk:11.28"}
Basic Client Setup
import com.nimbusds.jose.util.Pair;import io.grpc.ManagedChannel;import org.project_kessel.api.inventory.v1beta2.ClientBuilder;import static org.project_kessel.api.inventory.v1beta2.KesselInventoryServiceGrpc.KesselInventoryServiceBlockingStub;
String kesselEndpoint = System.getenv().getOrDefault("KESSEL_ENDPOINT", "localhost:9000");Pair<KesselInventoryServiceBlockingStub, ManagedChannel> clientAndChannel = new ClientBuilder(kesselEndpoint) .insecure() .build();KesselInventoryServiceBlockingStub client = clientAndChannel.getLeft();
Auth Client Setup
import com.nimbusds.jose.util.Pair;import io.grpc.ManagedChannel;import org.project_kessel.api.auth.ClientConfigAuth;import org.project_kessel.api.auth.OAuth2ClientCredentials;import org.project_kessel.api.auth.OIDCDiscovery;import org.project_kessel.api.auth.OIDCDiscoveryMetadata;import org.project_kessel.api.inventory.v1beta2.ClientBuilder;import static org.project_kessel.api.inventory.v1beta2.KesselInventoryServiceGrpc.KesselInventoryServiceBlockingStub;
String issuerUrl = System.getenv("AUTH_DISCOVERY_ISSUER_URL");OIDCDiscoveryMetadata discovery = OIDCDiscovery.fetchOIDCDiscovery(issuerUrl);ClientConfigAuth authConfig = new ClientConfigAuth( System.getenv("AUTH_CLIENT_ID"), System.getenv("AUTH_CLIENT_SECRET"), discovery.tokenEndpoint());OAuth2ClientCredentials oauth = new OAuth2ClientCredentials(authConfig);
Pair<KesselInventoryServiceBlockingStub, ManagedChannel> clientAndChannel = new ClientBuilder(System.getenv().getOrDefault("KESSEL_ENDPOINT", "localhost:9000")) .oauth2ClientAuthenticated(oauth) .build();KesselInventoryServiceBlockingStub client = clientAndChannel.getLeft();
Building Resource Representations
Build a ReportResourceRequest
import com.google.protobuf.Struct;import com.google.protobuf.Value;import org.project_kessel.api.inventory.v1beta2.ReportResourceRequest;import org.project_kessel.api.inventory.v1beta2.ResourceRepresentations;import org.project_kessel.api.inventory.v1beta2.RepresentationMetadata;
ReportResourceRequest reportResourceRequest = ReportResourceRequest .newBuilder() .setType("document") .setReporterType("drive") .setReporterInstanceId("drive-1") .setRepresentations( ResourceRepresentations .newBuilder() .setMetadata( RepresentationMetadata .newBuilder() .setLocalResourceId("doc-123") .setApiHref("https://drive.example.com/document/123") .setConsoleHref("https://www.console.com/drive/documents") .setReporterVersion("2.7.16") .build() ) .setCommon( Struct .newBuilder() .putFields("workspace_id", Value.newBuilder().setStringValue("workspace-1").build()) .build() ) .setReporter( Struct .newBuilder() .putFields("document_id", Value.newBuilder().setStringValue("doc-123").build()) .putFields("document_name", Value.newBuilder().setStringValue("My Important Document").build()) .putFields("document_type", Value.newBuilder().setStringValue("document").build()) .putFields("created_at", Value.newBuilder().setStringValue("2025-08-31T10:30:00Z").build()) .putFields("file_size", Value.newBuilder().setNumberValue(2048576).build()) .putFields("owner_id", Value.newBuilder().setStringValue("user-1").build()) .build() ) .build() ) .build();
Sending Report Requests
Send the report request and handle errors:
import com.nimbusds.jose.util.Pair;import io.grpc.ManagedChannel;import io.grpc.StatusRuntimeException;import static org.project_kessel.api.inventory.v1beta2.KesselInventoryServiceGrpc.KesselInventoryServiceBlockingStub;import org.project_kessel.api.inventory.v1beta2.ClientBuilder;import org.project_kessel.api.inventory.v1beta2.ReportResourceResponse;
try { ReportResourceResponse response = client.reportResource(reportResourceRequest); System.out.println("Report resource response received successfully:"); System.out.println(response);} catch (StatusRuntimeException statusException) { System.out.println("gRPC error occurred during Report resource:"); statusException.printStackTrace();} finally { clientAndChannel.getRight().shutdown();}
Creating Check Requests
Build a permission check request:
import org.project_kessel.api.inventory.v1beta2.CheckRequest;import org.project_kessel.api.inventory.v1beta2.ResourceReference;import org.project_kessel.api.inventory.v1beta2.ReporterReference;import org.project_kessel.api.inventory.v1beta2.SubjectReference;
CheckRequest checkRequest = CheckRequest .newBuilder() .setObject( ResourceReference .newBuilder() .setReporter(ReporterReference.newBuilder().setType("drive").build()) .setResourceId("doc-123") .setResourceType("document") .build() ) .setRelation("view") .setSubject( SubjectReference .newBuilder() .setResource( ResourceReference .newBuilder() .setReporter(ReporterReference.newBuilder().setType("rbac").build()) .setResourceId("sarah") .setResourceType("principal") .build() ) .build() ) .build();
Sending Check Requests
Execute the check request and handle the response:
import com.nimbusds.jose.util.Pair;import io.grpc.ManagedChannel;import io.grpc.StatusRuntimeException;import static org.project_kessel.api.inventory.v1beta2.KesselInventoryServiceGrpc.KesselInventoryServiceBlockingStub;import org.project_kessel.api.inventory.v1beta2.ClientBuilder;import org.project_kessel.api.inventory.v1beta2.CheckResponse;
try { CheckResponse response = client.check(checkRequest); System.out.println("Check response received successfully:"); System.out.println(response);} catch (StatusRuntimeException statusException) { System.out.println("gRPC error occurred during Check:"); statusException.printStackTrace();} finally { clientAndChannel.getRight().shutdown();}
Complete Example
import com.google.protobuf.Struct;import com.google.protobuf.Value;import com.nimbusds.jose.util.Pair;import io.grpc.ManagedChannel;import io.grpc.StatusRuntimeException;import static org.project_kessel.api.inventory.v1beta2.KesselInventoryServiceGrpc.KesselInventoryServiceBlockingStub;import org.project_kessel.api.inventory.v1beta2.*;
public class EndToEndExample { public static void main(String[] args) { String kesselEndpoint = System.getenv().getOrDefault("KESSEL_ENDPOINT", "localhost:9000");
Pair<KesselInventoryServiceBlockingStub, ManagedChannel> clientAndChannel = new ClientBuilder(kesselEndpoint) .insecure() .build(); KesselInventoryServiceBlockingStub client = clientAndChannel.getLeft();
try { // 1) Report a resource first ReportResourceRequest reportResourceRequest = ReportResourceRequest .newBuilder() .setType("document") .setReporterType("drive") .setReporterInstanceId("drive-1") .setRepresentations( ResourceRepresentations .newBuilder() .setMetadata( RepresentationMetadata .newBuilder() .setLocalResourceId("doc-123") .setApiHref("https://drive.example.com/document/123") .setConsoleHref("https://www.console.com/drive/documents") .setReporterVersion("2.7.16") .build() ) .setCommon( Struct .newBuilder() .putFields("workspace_id", Value.newBuilder().setStringValue("workspace-1").build()) .build() ) .setReporter( Struct .newBuilder() .putFields("document_id", Value.newBuilder().setStringValue("doc-123").build()) .putFields("document_name", Value.newBuilder().setStringValue("My Important Document").build()) .putFields("document_type", Value.newBuilder().setStringValue("document").build()) .putFields("created_at", Value.newBuilder().setStringValue("2025-08-31T10:30:00Z").build()) .putFields("file_size", Value.newBuilder().setNumberValue(2048576).build()) .putFields("owner_id", Value.newBuilder().setStringValue("user-1").build()) .build() ) .build() ) .build();
_ = client.reportResource(reportResourceRequest);
// 2) Then perform a permission check CheckRequest checkRequest = CheckRequest .newBuilder() .setObject( ResourceReference .newBuilder() .setReporter(ReporterReference.newBuilder().setType("drive").build()) .setResourceId("doc-123") .setResourceType("document") .build() ) .setRelation("view") .setSubject( SubjectReference .newBuilder() .setResource( ResourceReference .newBuilder() .setReporter(ReporterReference.newBuilder().setType("rbac").build()) .setResourceId("sarah") .setResourceType("principal") .build() ) .build() ) .build();
CheckResponse response = client.check(checkRequest); System.out.println("Check response received successfully:"); System.out.println(response); } catch (StatusRuntimeException statusException) { System.out.println("gRPC error occurred during Check:"); statusException.printStackTrace(); } finally { clientAndChannel.getRight().shutdown(); } }}