README.md

Runbox Ruby Client

Official Ruby client for Runbox - a fast, secure API for running code in isolated containers.

Gem Version CI License: MIT

Installation

Add to your Gemfile:

gem "runbox"

Or install directly:

gem install runbox

Quick Start

require "runbox"

# Configure (or use environment variables)
Runbox.configure do |config|
  config.url = "http://localhost:8080"
  config.api_key = "your-api-key"
end

# Create a client
client = Runbox::Client.new

# Step 1: Set up a container and get environment info
setup = client.setup(
  identifier: "my-session",
  language: "python"
)

puts setup.container_id  # => "runbox-my-session-python"
puts setup.environment_snapshot.runtime_version  # => "3.11.6"
puts setup.environment_snapshot.packages["requests"]  # => "2.31.0"

# Step 2: Run code in the container
result = client.run(
  container_id: setup.container_id,
  files: [{ path: "main.py", content: "print('Hello!')" }],
  run_command: "python main.py"
)

puts result.stdout  # => "Hello!\n"
puts result.success?  # => true

Configuration

Using Environment Variables

export RUNBOX_URL=http://localhost:8080
export RUNBOX_API_KEY=your-api-key

Using Configuration Block

Runbox.configure do |config|
  config.url = "http://localhost:8080"
  config.api_key = "your-api-key"
  config.timeout = 60  # HTTP timeout
end

Per-Client Configuration

client = Runbox::Client.new(
  url: "http://localhost:8080",
  api_key: "your-api-key"
)

Usage

Setting Up a Container

setup = client.setup(
  identifier: "session-123",
  language: "python",
  env: { "API_KEY" => "secret" },  # Optional: set environment variables
  memory: "512m",                   # Optional: memory limit
  network_allow: ["api.stripe.com"] # Optional: allowed network destinations
)

# Access environment information
puts setup.container_id  # => "runbox-session-123-python"
puts setup.cached?       # => false (true if container was reused)

env = setup.environment_snapshot
puts env.os_name          # => "debian"
puts env.os_version       # => "12"
puts env.runtime_name     # => "python"
puts env.runtime_version  # => "3.11.6"
puts env.packages         # => {"pip" => "23.0.1", "requests" => "2.31.0", ...}

Running Code

result = client.run(
  container_id: "runbox-session-123-python",
  files: [{ path: "main.py", content: "print('Hello!')" }],
  run_command: "python main.py",
  env: { "DEBUG" => "true" },  # Optional: runtime environment variables
  timeout: 30                   # Optional: execution timeout in seconds
)

puts result.success?          # => true
puts result.exit_code         # => 0
puts result.stdout            # => "Hello!\n"
puts result.stderr            # => ""
puts result.execution_time_ms # => 42
puts result.timeout?          # => false

Installing Dependencies On-The-Fly

Install new dependencies before running code:

# Python example
result = client.run(
  container_id: container_id,
  files: [{ path: "main.py", content: "import requests; print(requests.__version__)" }],
  run_command: "python main.py",
  new_dependencies: ["requests==2.31.0", "pytest"]
)

puts result.packages  # => {"pip"=>"23.0.1", "requests"=>"2.31.0", "pytest"=>"7.4.0", ...}

# Ruby example
result = client.run(
  container_id: container_id,
  files: [{ path: "main.rb", content: "require 'rake'; puts Rake::VERSION" }],
  run_command: "ruby main.rb",
  new_dependencies: ["rake", "rspec"]
)

# Shell example (uses apk)
result = client.run(
  container_id: container_id,
  files: [{ path: "script.sh", content: "#!/bin/sh\ncurl --version" }],
  run_command: "sh script.sh",
  new_dependencies: ["curl", "jq", "git"]
)

Note: The packages field is only included in the result when new_dependencies are provided.

Cleanup Containers

deleted = client.delete_containers("session-123")
puts deleted  # => ["runbox-session-123-python"]

Check Health

health = client.health
puts health[:status]  # => "healthy"
puts health[:version] # => "1.0.0"

API Reference

client.setup(identifier:, language:, **options)

Set up a container and get environment information.

Parameters:

  • identifier (String, required): Unique identifier for container reuse
  • language (String, required): Programming language ("python", "ruby", "shell")
  • env (Hash, optional): Environment variables to set
  • timeout (Integer, optional): Default timeout in seconds
  • memory (String, optional): Memory limit (e.g., "256m", "1g")
  • network_allow (Array, optional): Allowed network destinations

Returns: SetupResult with:

  • container_id: Container ID to use in run() calls
  • cached?: Whether container was reused
  • environment_snapshot: Environment information (OS, runtime, packages)

client.run(container_id:, files:, run_command:, **options)

Run code in a container that was set up via setup().

Parameters:

  • container_id (String, required): Container ID from setup() response
  • files (Array, required): Files to write before running
  • run_command (String, required): Command to run
  • env (Hash, optional): Runtime environment variables
  • timeout (Integer, optional): Execution timeout in seconds

Returns: Result with:

  • success?: Whether run succeeded (exit code 0)
  • exit_code: Process exit code
  • stdout: Standard output
  • stderr: Standard error
  • execution_time_ms: Execution time in milliseconds
  • timeout?: Whether run timed out

Error Handling

begin
  setup = client.setup(identifier: "test", language: "python")
  result = client.run(
    container_id: setup.container_id,
    files: [{ path: "main.py", content: "print('hi')" }],
    run_command: "python main.py"
  )
rescue Runbox::AuthenticationError => e
  puts "Invalid API key"
rescue Runbox::NotFoundError => e
  puts "Container not found (did you call setup first?)"
rescue Runbox::ValidationError => e
  puts "Invalid request: #{e.message}"
rescue Runbox::RunError => e
  puts "Run failed: #{e.message}"
rescue Runbox::ConnectionError => e
  puts "Could not connect to Runbox"
end

Development

Running Tests

bundle install
bundle exec rake test

Running Integration Tests

Integration tests require a running Runbox server:

# Start Runbox server (in another terminal)
cd ../runbox
docker-compose up

# Run integration tests
export RUNBOX_URL=http://localhost:8080
export RUNBOX_API_KEY=your-api-key
ruby examples/integration_test.rb

Linting

bundle exec rubocop

CI/CD

  • CI: Runs on every push and PR

    • Tests on Ruby 3.1, 3.2, 3.3
    • Linting with RuboCop
    • Integration tests against live Runbox server
  • CD: Publishes to RubyGems on version tags

    • Create a tag: git tag v1.0.0 && git push --tags
    • Requires RUBYGEMS_API_KEY secret in GitHub

License

MIT License - see LICENSE for details.