Ruby SDK

Idiomatic Ruby client for InferaDB with Rails integration.

Coming soon. The Ruby SDK is under active development. The API surface shown here is based on the Rust SDK and may change before release.

Idiomatic Ruby client for InferaDB’s authorization APIs. Requires Ruby 3.2+. Works with Rails, Sinatra, and any Rack-based framework.

Installation

gem "inferadb"
bundle install

Authentication

Three authentication methods:

Method Use Case Security
Client Credentials (Ed25519 JWT) Service-to-service High
Bearer Token User sessions, OAuth Medium
API Key Testing, simple integrations Basic
require "inferadb"

client = InferaDB::Client.new(
  url: "https://engine.inferadb.com",
  credentials: InferaDB::ClientCredentials.new(
    client_id: "my-client",
    private_key: InferaDB::Ed25519PrivateKey.from_pem_file("client.pem"),
    certificate_id: "cert-id"
  )
)

Bearer Token

client = InferaDB::Client.new(
  url: "https://engine.inferadb.com",
  token: ENV.fetch("INFERADB_TOKEN")
)

API Key

client = InferaDB::Client.new(
  url: "https://engine.inferadb.com",
  api_key: ENV.fetch("INFERADB_API_KEY")
)

Permission Checks

vault = client.organization("my-org").vault("production")

# Simple check
allowed = vault.check("user:alice", "can_edit", "document:readme")

With ABAC Context

allowed = vault.check("user:alice", "can_view", "document:readme",
  context: { ip_address: "10.0.0.1" }
)

Require — Raises on Deny

# Raises InferaDB::AccessDeniedError if permission is denied
vault.require!("user:alice", "can_edit", "document:readme")

With Consistency Token

allowed = vault.check("user:alice", "can_view", "document:readme",
  at_least_as_fresh: revision_token
)

Batch Check

results = vault.check_batch([
  { subject: "user:alice", permission: "can_edit", resource: "document:readme" },
  { subject: "user:bob", permission: "can_view", resource: "document:readme" },
])

results.all_allowed? # => true/false

Relationships

Write

# Returns a revision token
token = vault.relationships.write(
  resource: "document:readme",
  relation: "editor",
  subject: "user:alice"
)

Batch Write

vault.relationships.write_batch([
  { resource: "document:readme", relation: "editor", subject: "user:alice" },
  { resource: "document:readme", relation: "viewer", subject: "user:bob" },
])

List

rels = vault.relationships.list(resource: "document:readme")

Delete

vault.relationships.delete_where(
  resource: "document:readme",
  relation: "viewer",
  subject: "user:bob"
)

Lookups

# What resources can Alice view?
resources = vault.resources
  .accessible_by("user:alice")
  .with_permission("can_view")
  .resource_type("document")
  .to_a

# Who can edit this document?
subjects = vault.subjects
  .with_permission("can_edit")
  .on_resource("document:readme")
  .to_a

Testing

MockClient (Fastest)

require "inferadb/testing"

client = InferaDB::Testing::MockClient.new do |mock|
  mock.on_check("user:alice", "can_edit", "document:readme").allow
  mock.on_check("user:bob", "can_edit", "document:readme").deny
  mock.on_check_any_subject("can_view", "document:readme").allow
  mock.default_deny
  mock.verify_on_teardown! # asserts all expectations were invoked on teardown
end

InMemoryClient (Full Policy Evaluation)

require "inferadb/testing"

client = InferaDB::Testing::InMemoryClient.with_schema_and_data(
  schema: <<~IPL,
    type document {
        relation viewer
        relation editor
        relation can_view = viewer | editor
    }
  IPL
  data: [
    { resource: "document:readme", relation: "editor", subject: "user:alice" },
    { resource: "document:readme", relation: "viewer", subject: "user:bob" },
  ]
)

TestVault (Real Instance)

require "inferadb/testing"

vault = InferaDB::Testing::TestVault.create(org, schema: schema_ipl)
# vault auto-cleans up when garbage collected
# call vault.preserve! to keep data for debugging

RSpec Integration

require "inferadb/testing/rspec"

RSpec.describe "Document authorization" do
  let(:client) do
    InferaDB::Testing::InMemoryClient.with_schema_and_data(
      schema: "...",
      data: [{ resource: "document:readme", relation: "editor", subject: "user:alice" }]
    )
  end

  let(:vault) { client.organization("test").vault("test") }

  it "allows Alice to edit" do
    expect(vault.check("user:alice", "can_edit", "document:readme")).to be true
  end

  it "denies Bob from editing" do
    expect(vault.check("user:bob", "can_edit", "document:readme")).to be false
  end
end

Minitest Integration

require "inferadb/testing"

class DocumentAuthorizationTest < Minitest::Test
  def setup
    @client = InferaDB::Testing::InMemoryClient.with_schema_and_data(
      schema: "...",
      data: [{ resource: "document:readme", relation: "editor", subject: "user:alice" }]
    )
    @vault = @client.organization("test").vault("test")
  end

  def test_alice_can_edit
    assert @vault.check("user:alice", "can_edit", "document:readme")
  end
end

Error Handling

begin
  vault.require!("user:alice", "can_edit", "document:readme")
rescue InferaDB::AccessDeniedError
  # permission denied
rescue InferaDB::Error => e
  if e.retriable?
    sleep e.retry_after
    retry
  end
  logger.error("Authorization error: kind=#{e.kind} request_id=#{e.request_id}")
end

ErrorKind values: :unauthorized, :forbidden, :not_found, :rate_limited, :schema_violation, :unavailable, :timeout, :invalid_argument.

Framework Integrations

Rails Controller Concern

# app/controllers/concerns/inferadb_authorization.rb
module InferadbAuthorization
  extend ActiveSupport::Concern

  private

  def authorize_inferadb!(permission, resource)
    InferaDB.vault.require!("user:#{current_user.id}", permission, resource)
  rescue InferaDB::AccessDeniedError
    head :forbidden
  end
end
class DocumentsController < ApplicationController
  include InferadbAuthorization
  before_action :set_document

  def show
    authorize_inferadb!("can_view", "document:#{@document.id}")
  end

  def update
    authorize_inferadb!("can_edit", "document:#{@document.id}")
    @document.update!(document_params)
  end

  private

  def set_document = Document.find(params[:id])
end

Rails Initializer

# config/initializers/inferadb.rb
InferaDB.configure do |config|
  config.url = ENV.fetch("INFERADB_URL")
  config.credentials = InferaDB::ClientCredentials.new(
    client_id: ENV.fetch("INFERADB_CLIENT_ID"),
    private_key: InferaDB::Ed25519PrivateKey.from_pem_file(
      ENV.fetch("INFERADB_KEY_PATH")
    ),
    certificate_id: ENV.fetch("INFERADB_CERT_ID")
  )
  config.organization = ENV.fetch("INFERADB_ORG")
  config.vault = ENV.fetch("INFERADB_VAULT")
end