The official Ruby gRPC client SDK for Project Kessel services. It provides generated protobuf/gRPC bindings for the Kessel Inventory API, a fluent client builder with credential validation, OAuth 2.0 Client Credentials authentication (via OIDC), and RBAC convenience helpers for workspace operations.
Published on RubyGems as kessel-sdk.
Add this line to your application's Gemfile:
gem 'kessel-sdk'And then execute:
bundle installOr install it yourself as:
gem install kessel-sdkThe SDK supports OAuth 2.0 Client Credentials flow. To use authentication features, add the OpenID Connect gem:
gem 'kessel-sdk'
gem 'openid_connect', '~> 2.0' # Optional - only for authenticationThe openid_connect gem is intentionally not a hard runtime dependency. Consumers who do not need OAuth are not forced to install it or its transitive dependencies.
lib/
kessel-sdk.rb # Single entrypoint (require 'kessel-sdk')
kessel/
auth.rb # OAuth2 Client Credentials, OIDC discovery
grpc.rb # gRPC credential helpers
inventory.rb # ClientBuilder base class, client_builder_for_stub
version.rb # Kessel::Inventory::VERSION
inventory/
v1/ # Health check service (stable)
v1beta1/ # Legacy typed K8s resources/relationships
v1beta2/ # Current unified inventory service (primary API)
rbac/
v2.rb # Workspace fetch, list_workspaces enumerator
v2_helpers.rb # Factory methods for protobuf references
v2_http.rb # HTTP request helpers for RBAC API
sig/ # RBS type signatures for hand-written code
spec/ # RSpec test suite
examples/ # Working examples with dotenv configuration
Files under lib/kessel/inventory/v*/, lib/google/, and lib/buf/ are generated by buf generate and must never be hand-edited. They are automatically regenerated every 6 hours via CI.
Load the complete SDK via the single entrypoint:
require 'kessel-sdk'The recommended way to create gRPC clients is with the ClientBuilder fluent API, which enforces credential validation at configuration time. Every gRPC service module exposes a ClientBuilder constant.
include Kessel::Inventory::V1beta2
client = KesselInventoryService::ClientBuilder.new('localhost:9000')
.insecure
.buildinclude Kessel::Inventory::V1beta2
include Kessel::Auth
# Discover the token endpoint via OIDC
discovery = fetch_oidc_discovery('https://sso.example.com/auth/realms/my-realm')
# Create OAuth2 credentials
oauth = OAuth2ClientCredentials.new(
client_id: 'my-app',
client_secret: 'my-secret',
token_endpoint: discovery.token_endpoint,
retry: { max_retries: 3, base_delay: 0.5, max_delay: 2.0, jitter: :full }
)
# Build the client -- tokens are cached and refreshed automatically
client = KesselInventoryService::ClientBuilder.new('kessel.example.com:443')
.oauth2_client_authenticated(oauth2_client_credentials: oauth)
.buildToken fetch retries apply only to the token endpoint, not OIDC discovery. By default, network/timeout errors and HTTP
429/5xx responses receive four total attempts (initial plus three retries), with full-jitter delay caps of 0.5, 1,
and 2 seconds and total added sleep below 3.5 seconds. retry accepts non-negative integer max_retries (0
disables retries), positive finite-second base_delay and max_delay, and jitter: :full or :none; defaults are
3, 0.5, 2.0, and :full. Invalid retry configuration raises ArgumentError. Permanent OAuth/configuration
failures are not retried. Refresh is synchronous, so a transient SSO failure may delay an RPC.
# Custom call/channel credentials
client = KesselInventoryService::ClientBuilder.new(target)
.authenticated(call_credentials: creds, channel_credentials: ch_creds)
.build
# No call credentials (TLS channel only)
client = KesselInventoryService::ClientBuilder.new(target)
.unauthenticated
.buildBuild the client once at application startup and reuse it. The underlying gRPC channel manages its own HTTP/2 connection pool.
All inventory clients now use these transport keepalive defaults, including clients created by existing builder chains:
- Ping interval: 45 seconds
- Ping acknowledgement timeout: 10 seconds
- Permit pings while there are no active calls: enabled
Override these values with the fluent keepalive method. Durations are finite positive real numbers in seconds and
are converted to integer milliseconds by truncating any fractional millisecond downward. The resulting value must be
between 1 and 2,147,483,647 milliseconds. Passing nil leaves that setting unchanged; permit_without_calls accepts
only true or false (including false as an explicit override).
client = Kessel::Inventory::V1beta2::KesselInventoryService::ClientBuilder
.new(ENV.fetch('KESSEL_ENDPOINT', nil))
.keepalive(interval: 60, timeout: 10, permit_without_calls: false)
.buildThe Ruby SDK sets grpc.http2.max_pings_without_data internally to avoid a local cap; it is not a public configuration
option. Before rollout, confirm that the Kessel server and any gateway enforce compatible keepalive policies
(RHCLOUD-51673). Keepalive is a transport-level ping mechanism, not an RPC health check and does not provide retries
or load-balancer guarantees. The builder still returns a gRPC stub; build it once and reuse it as usual.
include Kessel::Inventory::V1beta2
client = KesselInventoryService::ClientBuilder.new('localhost:9000')
.insecure
.build
subject_reference = SubjectReference.new(
resource: ResourceReference.new(
reporter: ReporterReference.new(type: 'rbac'),
resource_id: 'user123',
resource_type: 'principal'
)
)
resource = ResourceReference.new(
reporter: ReporterReference.new(type: 'rbac'),
resource_id: 'workspace456',
resource_type: 'workspace'
)
begin
response = client.check(
CheckRequest.new(
object: resource,
relation: 'inventory_host_view',
subject: subject_reference
)
)
puts "Permission check result: #{response.allowed}"
rescue GRPC::BadStatus => e
puts "gRPC error: #{e.message}"
endinclude Kessel::Inventory::V1beta2
include Kessel::RBAC::V2
client = KesselInventoryService::ClientBuilder.new('localhost:9000')
.insecure
.build
response = client.check_bulk(
CheckBulkRequest.new(items: [
CheckBulkRequestItem.new(
object: workspace_resource('workspace_123'),
relation: 'view_widget',
subject: principal_subject('bob', 'redhat')
),
CheckBulkRequestItem.new(
object: workspace_resource('workspace_456'),
relation: 'use_widget',
subject: principal_subject('alice', 'redhat')
)
])
)
response.pairs.each do |pair|
if pair.item
puts "Allowed: #{pair.item.allowed}"
elsif pair.error
puts "Error: #{pair.error.message}"
end
endThe list_workspaces helper automatically paginates through all workspaces
a subject can access. Continuation tokens are handled internally, meaning you never
need to manage them yourself.
include Kessel::Inventory::V1beta2
include Kessel::RBAC::V2
client = KesselInventoryService::ClientBuilder.new('localhost:9000')
.insecure
.build
subject = principal_subject("alice", "redhat")
consistency = Kessel::Inventory::V1beta2::Consistency.new(minimize_latency: true)
# Lazy iteration (constant memory)
list_workspaces(client, subject, "viewer", consistency: consistency).each do |response|
puts response.object.resource_id
end
# Materialise into an Array
all_workspaces = list_workspaces(client, subject, "viewer", consistency: consistency).to_aSee examples/list_workspaces.rb for a complete working example.
The primary service is KesselInventoryService with these RPCs:
| RPC | Description |
|---|---|
check |
Check if a subject has a relation on a resource |
check_self |
Check using the caller's identity from auth context |
check_for_update |
Strongly consistent check (use before writes) |
check_bulk |
Batch permission checks (up to 1000 items) |
check_self_bulk |
Batch self-checks |
check_for_update_bulk |
Batch strongly consistent checks |
report_resource |
Report resource state to inventory |
delete_resource |
Delete a resource from inventory |
streamed_list_objects |
Stream objects a subject has a relation to |
streamed_list_subjects |
Stream subjects that have a relation to a resource |
The Kessel::RBAC::V2 module provides factory methods for common protobuf references (all use reporter_type: 'rbac'):
workspace_resource(id)/role_resource(id)--ResourceReferencefactoriesprincipal_resource(id, domain)--ResourceReferencewith ID formatted as"domain/id"principal_subject(id, domain)--SubjectReferencewrapping a principal resourcesubject(resource_ref, relation)-- genericSubjectReferencefactoryfetch_default_workspace(endpoint, org_id, auth:, http_client:)-- fetch default workspace via RBAC HTTP APIfetch_root_workspace(endpoint, org_id, auth:, http_client:)-- fetch root workspace via RBAC HTTP APIlist_workspaces(inventory, subject, relation)-- lazyEnumeratorwith auto-pagination
This library includes RBS type signatures for enhanced type safety in Ruby. The type definitions are located in the sig/ directory and cover:
- Core library interfaces
- Configuration structures
- OAuth authentication classes
- gRPC client builders
To use with type checkers like Steep or Sorbet, ensure the sig/ directory is in your type checking configuration.
- Ruby 3.3 or higher
- buf for protobuf/gRPC code generation
Install buf:
# On macOS
brew install bufbuild/buf/buf
# On Linux
curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-$(uname -s)-$(uname -m)" -o "/usr/local/bin/buf" && chmod +x "/usr/local/bin/buf"
# Or see https://docs.buf.build/installation for other options# Install dependencies
bundle install
# Generate gRPC code from Kessel Inventory API
buf generate# Run tests
bundle exec rspec
# Run with coverage
COVERAGE=1 bundle exec rspec
# Run linting
bundle exec rubocop
# Security audit
bundle exec bundler-auditThis library uses buf to generate Ruby gRPC code from the official Kessel Inventory API protobuf definitions hosted at buf.build/project-kessel/inventory-api.
The generation is configured in buf.gen.yaml.
To regenerate the code:
buf generateThis will download the latest protobuf definitions and generate fresh Ruby classes in the lib/ directory.
# Build and install the gem locally
rake install_localThe examples/ directory contains working examples. Set up environment variables in examples/.env before running.
| Example | Description |
|---|---|
auth.rb |
OAuth 2.0 authentication with ClientBuilder |
check.rb |
Permission checking |
check_bulk.rb |
Bulk permission checks |
check_for_update.rb |
Strongly consistent update checks |
check_for_update_bulk.rb |
Bulk strongly consistent update checks |
delete_resource.rb |
Deleting resources |
fetch_workspaces.rb |
Fetching workspaces via RBAC HTTP API |
keepalive.rb |
Configuring HTTP/2 keepalive on an inventory client |
list_workspaces.rb |
Listing workspaces with auto-pagination |
report_resource.rb |
Reporting resource state |
console_principal.rb |
Building principals from x-rh-identity headers |
streamed_list_objects.rb |
Streaming resource lists |
Run examples:
cd examples
bundle install
ruby check.rbFor AI-assisted development context, see AGENTS.md. Directory-local GUIDELINES.md files provide detailed conventions for specific areas of the codebase:
- lib/kessel/inventory/GUIDELINES.md -- Inventory module: ClientBuilder, service wiring, V1beta2 API patterns, auth integration
- lib/kessel/rbac/GUIDELINES.md -- RBAC V2 module conventions
- examples/GUIDELINES.md -- Example script conventions
This section provides step-by-step instructions for maintainers to release a new version of the Kessel SDK for Ruby.
This project follows Semantic Versioning 2.0.0. Version numbers use the format MAJOR.MINOR.PATCH:
- MAJOR: Increment for incompatible API changes
- MINOR: Increment for backward-compatible functionality additions
- PATCH: Increment for backward-compatible bug fixes
Note: SDK versions across different languages (Ruby, Python, Go, etc.) do not need to be synchronized. Each language SDK can evolve independently based on its specific requirements and release schedule.
- Write access to the GitHub repository
- RubyGems account with push access to the
kessel-sdkgem - Ensure CI/CD tests are passing
- Review and update CHANGELOG or release notes as needed
- Ruby 3.3 or higher
- buf for protobuf/gRPC code generation:
-
Update the Version
# Edit lib/kessel/version.rb and update the VERSION constant vim lib/kessel/version.rb -
Set the VERSION environment variable
export VERSION=$(ruby -e "require_relative './lib/kessel/version.rb'; puts Kessel::Inventory::VERSION") echo "Releasing version: v${VERSION}"
-
Update Dependencies
# Generate gRPC code from Kessel Inventory API buf generate # Update Gemfile.lock with any dependency changes bundle install
-
Run Quality Checks
# Run the full test suite bundle exec rspec # Run linting bundle exec rubocop # Run security audit bundle exec bundler-audit check --update # Build and test the gem locally rake install_local
-
Commit Changes
git add lib/kessel/version.rb Gemfile.lock git commit -m "Release version ${VERSION}" git push origin main # or git push upstream main
-
Build and Release the Gem
# Build the gem gem build kessel-sdk.gemspec # Push to RubyGems (requires RubyGems account and gem ownership) gem push kessel-sdk-${VERSION}.gem
-
Tag the Release
# Create and push a git tag git tag -a v${VERSION} -m "Release version ${VERSION}" git push origin v${VERSION} # or git push upstream v${VERSION}
-
Create GitHub Release
gh release create v${VERSION} --title "v${VERSION}" --generate-notes
Or manually:
- Go to the GitHub Releases page
- Click "Create a new release"
- Select the tag you just created
- Add release notes describing the changes
- Publish the release
-
Clean Up
# Remove the built gem file rake clean
This project includes bundler/gem_tasks which provides additional rake tasks:
# Show available bundler gem tasks
rake -T
# Build gem
rake build
# Install gem locally
rake install
# Release gem (builds, tags, and pushes to RubyGems)
rake releaseNote: The rake release command automates steps 6-7 above but requires proper git and RubyGems credentials to be configured.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -am 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please review AGENTS.md and the directory-local GUIDELINES.md files before contributing. All specs must pass on Ruby 3.3 and 3.4. Fix RuboCop violations before merging, and update RBS type signatures in sig/kessel/ when modifying hand-written code.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.