Docs/Ruby Guide
Ruby 2.7+

Ruby Integration Guide

On-device PII detection, redaction and local receipts for Ruby applications with the tork-governance gem (0.2.0). Runs entirely in your process: no API key, no network call. Includes Rails, Sinatra and Grape middleware.

Prerequisites

  • Ruby 2.7 or higher (3.0+ recommended)
  • Bundler for dependency management
  • No API key: the gem governs on-device and never contacts tork.network

Installation

Install the gem using Bundler or directly with gem.

bash
# Add to your Gemfile
gem 'tork-governance', '0.2.0'

# Then run
bundle install

# Or install directly
gem install tork-governance -v 0.2.0

Require it as tork_governance (underscore). The gem name on RubyGems is tork-governance (dash).

Configuration

Configure the shared client once, or create clients explicitly.

rubyconfig/initializers/tork_governance.rb
require 'tork_governance'

# Optional: configure the shared default client once at startup.
# policy_version is recorded on every receipt.
TorkGovernance.configure(policy_version: '1.0.0')

# TorkGovernance.govern uses that shared client
result = TorkGovernance.govern("My SSN is 123-45-6789")
puts result.output   # => "My SSN is [SSN_REDACTED]"

TorkGovernance.configure also accepts api_key:. The 0.2.0 on-device client stores it but never uses it: no request leaves your process.

Basic Usage

Govern a string and read the decision, the output and the receipt.

ruby
require 'tork_governance'

client = TorkGovernance::Client.new

result = client.govern("Email me at jane@example.com or call 555-123-4567")

result.action          # => "redact"   (one of "allow", "deny", "redact")
result.output          # => "Email me at [EMAIL_REDACTED] or call [PHONE_REDACTED]"
result.redacted?       # => true
result.pii.has_pii?    # => true
result.pii.types       # => ["email", "phone"]
result.pii.count       # => 2
result.receipt.id      # => "rcpt_..."

# A clean input passes through unchanged
clean = client.govern("The meeting is at 3pm")
clean.allowed?         # => true
clean.output           # => "The meeting is at 3pm"

Redact or Deny

Choose what a PII hit does.

ruby
require 'tork_governance'

# default_action decides what happens when PII is found:
#   TorkGovernance::ACTIONS[:redact]  (default) -> output is the redacted text
#   TorkGovernance::ACTIONS[:deny]              -> output is the original text, action is "deny"
client = TorkGovernance::Client.new(default_action: TorkGovernance::ACTIONS[:deny])

result = client.govern("Card 4111 1111 1111 1111")

if result.denied?
  # The client never raises for a governance decision; you decide what to do.
  puts "Blocked: #{result.pii.types.join(', ')} (receipt #{result.receipt.id})"
end

PII Detection

Run the detector directly and inspect every match.

ruby
require 'tork_governance'

# Detection without a governance decision
pii = TorkGovernance::PIIDetector.detect(
  "My email is john@example.com and SSN is 123-45-6789"
)

pii.has_pii?       # => true
pii.types          # => ["ssn", "email"]
pii.count          # => 2
pii.redacted_text  # => "My email is [EMAIL_REDACTED] and SSN is [SSN_REDACTED]"

pii.matches.each do |m|
  puts "#{m.type} at #{m.start_index}..#{m.end_index}: #{m.value}"
end

# The 0.2.0 gem detects these seven types (US-format patterns):
TorkGovernance::PII_PATTERNS.keys
# => ["ssn", "credit_card", "email", "phone", "address", "ip_address", "date_of_birth"]

Receipts

Every govern call mints a local receipt with SHA-256 hashes of input and output.

ruby
require 'tork_governance'

client = TorkGovernance::Client.new(policy_version: '2.1.0')
input  = "Contact me at john@example.com"
result = client.govern(input)
receipt = result.receipt

receipt.id                  # => "rcpt_..."
receipt.timestamp           # => "2026-09-24T10:15:42.123456Z"
receipt.action              # => "redact"
receipt.input_hash          # => "sha256:..."  (hash of the original input)
receipt.output_hash         # => "sha256:..."  (hash of the governed output)
receipt.pii_types           # => ["email"]
receipt.pii_count           # => 1
receipt.policy_version      # => "2.1.0"
receipt.processing_time_ns  # => 184000

# Later: prove the receipt matches the text you kept
receipt.verify(input, result.output)  # => true
receipt.verify("tampered", result.output)  # => false

# Serialise for your own audit store
receipt.to_h  # => { id: ..., timestamp: ..., input_hash: ..., ... }

The receipt is a local Ruby object. It is not sent to tork.network and does not appear in the Tork dashboard; keep it in your own audit store.

Usage Statistics

Per-client counters for calls, PII hits and processing time.

ruby
client = TorkGovernance::Client.new

client.govern("hello")
client.govern("my ssn is 123-45-6789")

client.stats
# => { total_calls: 2, total_pii_detected: 1,
#      total_processing_ns: 402000, action_counts: { "allow" => 1, "redact" => 1 } }

client.reset_stats

The gem's cloud client does not work against the live API

tork-governance 0.2.0 also ships a Tork::Client namespace (require 'tork') with evaluate, evaluations, policies and metrics resources. Its default base URL is https://api.tork.network/v1, which returns 404 for every path, and the endpoints it calls (/pii/detect, /pii/redact, /jailbreak/detect, /rag/validate, /evaluate/batch) do not exist on the Tork API. An earlier version of this page documented those calls; they were removed on 24 September 2026 because they cannot succeed as written. Jailbreak detection, policy management, batch evaluation, RAG validation and TORKING-X metrics are not available from Ruby today. For server-side governance with dashboard receipts use the REST API directly (API reference).

Rails Integration

Rack middleware plus a controller concern, both shipped in the gem

Requiring tork_governance/middleware/rails in a Rails app registers TorkGovernance::Middleware::Rails through a Railtie with its defaults (POST/PUT/PATCH under /api/, the shared TorkGovernance.client). Adding it again with config.middleware.use would govern each request twice, so the initializer below only configures the shared client. A "deny" decision returns 403 with the receipt id and PII types; a "redact" decision leaves the request body untouched and exposes the redacted text as request.env['tork.redacted_content'].

rubyconfig/initializers/tork_governance.rb
# config/initializers/tork_governance.rb
require 'tork_governance'
require 'tork_governance/middleware/rails'

TorkGovernance.configure(policy_version: '1.0.0')

# The middleware governs the first string found under one of these JSON body
# keys on POST/PUT/PATCH requests: content, message, text, prompt, query, input.
Rails.application.config.middleware.use TorkGovernance::Middleware::Rails,
  protected_paths: ['/api/'],
  skip_paths: ['/api/health']

Sinatra Integration

Register the extension; it adds a before-filter and helpers.

rubyapp.rb
require 'sinatra'
require 'tork_governance'
require 'tork_governance/middleware/sinatra'

register TorkGovernance::Middleware::Sinatra

# Defaults: POST/PUT/PATCH under /api/ are governed; override if needed
set :tork_protected_paths, ['/api/']
set :tork_skip_paths, ['/api/health']

post '/api/chat' do
  content_type :json

  # Set by the before-filter for this request
  result = tork_result
  message = tork_redacted_content || JSON.parse(request.body.read)['message']

  { output: message, receipt_id: tork_receipt_id, action: result&.action }.to_json
end

post '/api/notes' do
  # Govern something yourself, outside the body keys the filter looks at
  result = govern(params[:note])
  { output: result.output }.to_json
end

Grape Integration

Rack middleware with optional response redaction, plus helpers.

rubyapi.rb
require 'grape'
require 'tork_governance'
require 'tork_governance/middleware/grape'

class API < Grape::API
  format :json

  # protected_paths defaults to ['/'] for Grape.
  # govern_response: true also redacts PII in JSON responses.
  use TorkGovernance::Middleware::Grape,
      protected_paths: ['/api/'],
      skip_paths: ['/api/health'],
      govern_response: true

  helpers TorkGovernance::Middleware::GrapeHelpers

  post '/api/chat' do
    require_tork_governance!   # error!(..., 403) if the decision was "deny"

    {
      status: 'ok',
      redacted: tork_redacted?,
      receipt_id: tork_receipt_id,
    }
  end
end

The Grape middleware also accepts client: (a TorkGovernance::Client, for example one built with default_action: TorkGovernance::ACTIONS[:deny]) and on_block: (a lambda receiving env, result that returns a Rack response) to replace the default 403.

Thread Safety

Decisions are pure; the stats counters are not synchronised.

ruby
require 'tork_governance'

# govern is a pure function of its input, so sharing one client across
# threads is safe for the decision and the receipt. The per-client
# stats counters are plain Hash writes with no mutex, so under
# concurrency treat client.stats as approximate or give each thread
# its own client.
client = TorkGovernance::Client.new

threads = 10.times.map do |i|
  Thread.new do
    result = client.govern("Thread #{i} content")
    puts "Thread #{i}: #{result.action}"
  end
end

threads.each(&:join)

Best Practices

Govern both directions

Run user input through govern before it reaches a model, and the model's reply before it reaches the user.

Keep the receipts

receipt.to_h is your audit trail. Store it with the request; receipt.verify(input, output) proves it later.

Pick deny deliberately

The default redacts and continues. Use default_action: TorkGovernance::ACTIONS[:deny] only where PII must never proceed.

Know the detector's scope

0.2.0 ships seven US-format patterns (ssn, credit_card, email, phone, address, ip_address, date_of_birth). It does not detect regional identifiers.

Let the middleware pick the field

The Rails, Sinatra and Grape middleware govern the first non-empty string under content, message, text, prompt, query or input.

Do not wait for the cloud client

Tork::Client in the same gem targets endpoints that do not exist. Use the REST API directly if you need dashboard receipts.

Automatic Retry Behavior

The SDK automatically retries failed requests with exponential backoff. Retryable status codes: 408, 500, 502, 503, 504. Default: 3 retries with 0.5s base delay and 2x backoff factor. Configure via max_retries and retry_base_delay.

Next Steps

Explore more endpoints for jailbreak detection, RAG validation, and multi-agent orchestration.

Documentation

Learn to integrate TORK

Upgrade Plan

Current: free

Support

Get help from our team