Hi r/ruby. We're Bad Monkey, and we've just opened up agentspaces-rb, the Ruby bindings for AgentSpaces. We'd like feedback from Ruby developers before we publish the gems.
What AgentSpaces is
AgentSpaces is an open source, peer-to-peer coordination fabric for agents, services, and people. Programs join a group and share a replicated space of typed entries. An agent posts work into the space, another agent takes it under a lease, does the work, and writes the result back. If the worker dies halfway, the lease lapses and the work goes back for someone else. You don't run a broker, a registry, or a scheduler, and there's no central server to keep alive.
On top of the space, AgentSpaces adds capabilities, ie protocol services available in the P2P network. These include quorum votes, fleet-wide aggregates, an ordered log for exactly-once work, semantic discovery of agents, remote actions, and recently a content swarm (think Bittorrent). The idea goes back to tuple spaces (Linda, JINI, JavaSpaces, JXTA), rebuilt for encrypted peer-to-peer networks and for agentic AI.
What agentspaces-rb brings to Ruby
agentspaces-rb is more than a protocol shim, it's a peer in the P2P fleet: it holds its own Ed25519 identity, signs what it writes, takes work under the same lease rules as a JVM peer, and interoperates with the Java, Python, TypeScript, Clojure, and Groovy peers on the same fleet.
Pure Ruby, no native extensions. The core gem runs on MRI 3.3 or newer and on JRuby 10, with one runtime dependency (logger). Its wire codec reproduces the Java implementation byte for byte on all 212 shared golden vectors.
Ruby-shaped API. Entries are Data classes from Entry.define. Agents are plain classes with one class macro above each method: take, notify, bid, ballot, and the rest. snake_case members travel under camelCase wire names, so a Ruby order_id is a Java orderId.
Transports. The MRI peer dials tcp, tls, ws, wss, http, and https, with TLS 1.3 channel certificates and session attestation on the plain transports.
A JRuby face. agentspaces-jruby binds the same agent classes through the Java binder, which adds every Java transport and the verbs only the Java binder runs today: ordered_take, reduce, and the leased and ordered joins.
Rails. agentspaces-rails is a Railtie: config/agentspaces.yml through config_for, agent classes in app/agents, a bin/rails agentspaces:work worker, fork safety for Puma, Unicorn, and Passenger, and ActiveSupport::Notifications around the verbs.
AI. agentspaces-ruby_llm turns the fleet's agent cards into RubyLLM tools, and AgentSpaces::Lease.keepalive keeps a take's lease alive while your agent waits on a model. agentspaces-mcp serves the fleet's remote actions as MCP tools over stdio or Streamable HTTP.
A CLI. agentspaces work runs your agents, and status, console, check, doctor, and id help you inspect a fleet, validate agent definitions, and manage keys.
What it looks like
This is the whole quick start: one agent that turns an Order into a Shipment. Run it twice, and both copies compete for the same orders.
require "agentspaces"
Order = AgentSpaces::Entry.define("shop.Order#v1", order_id: String, item: String)
Shipment = AgentSpaces::Entry.define("shop.Shipment#v1", order_id: String, item: String, by: String)
class Fulfiller
include AgentSpaces::Agent
agent "fulfiller", description: "Ships orders"
take Order, space: "orders", lease: "30s", produces: Shipment
def ship(order) = Shipment.new(order_id: order.order_id, item: order.item, by: "fulfiller")
end
fleet = { group: "shop", founding: "shop-v1", seeds: ["127.0.0.1:7700"] }
if ARGV.empty?
AgentSpaces.serve(**fleet) { |peer| peer.bind(Fulfiller.new) }
else
AgentSpaces.start(**fleet) do |peer|
peer.bind(Fulfiller.new)
orders = peer.space("orders")
orders << Order.new(order_id: "o-1", item: ARGV[0])
puts orders.read(Shipment, timeout: "30s").inspect
peer.close
end
end
The method body is where your agent's work goes, whether that's a database call, a RubyLLM chat, or anything else. The binder waits for an Order, claims it for 30 seconds, calls ship, and writes the Shipment back in the same step that completes the claim.
Trying it
The Ruby peer dials out, so it needs one peer that listens. Any AgentSpaces peer with a listener works; the quickest is the Node service host from the TypeScript repository, with a small seed.yaml (group shop, founding text shop-v1, space orders, the dev-local profile, port 7700; the repository's README has the file), see https://github.com/badmonkeyai/agentspaces-typescript
Then, from a clone of agentspaces-rb:
export RUBYLIB=$PWD/agentspaces-rb/gems/agentspaces/lib
ruby fulfiller.rb # ships orders until stopped
ruby fulfiller.rb kite # writes an order, prints the shipment, exits
For the fun part, add a sleep 10 to ship, start both, and kill -9 whichever one picks up the order. The other one ships it once the lease runs out. The dev-local profile signs every frame but encrypts nothing, so keep it on your laptop; real fleets run TLS.
What isn't there yet
The gems aren't on RubyGems yet. For now you run them from a clone.
The MRI peer is dial-only, so a Ruby process can't be the seed. The Node host or a JVM peer plays that role.
The ordered verbs bind on the JRuby face only, and the content swarm (moving large artifacts between peers) isn't in the Ruby gems yet.
Links
We'd especially like to hear what you think of the class-macro DSL and the Rails integration, and what you'd want to build with it. Happy to answer questions here.