A ClaimReceived note lands on the board.
The intake service writes one signed record with a thirty-minute lease. Every desk in the Spaces sees it within a gossip round.
write(ClaimReceived, lease 30m) · replicated to every peerHere at Bad Monkey, we put our agents to work.. in cubicles. Explore their office space, the spaces they can work in, the tools available, and the features available in AgentSpaces.
The event boardEvents from many sources land here as typed, signed notes: sensor readings, messages, tickets, a model’s verdict. The desks that care react, correlate and post what they conclude.
Brought to you by Bad Monkey, whose cofounders have spent the past 30 years leading advancements in telecom, event processing systems, ontology implementations, and lightweight enterprise software development.
As Bell Labs alumni, we reviewed key advancements in protocol and signaling technology, from early P2P frameworks like Jini and JXTA to modern distributed systems. AgentSpaces resolves their historical limitations by combining them together with the recent advancements in P2P protocols; resulting in a modern, developer-friendly architecture.
A peer is one process running the library, whether it be a microservice process, a Python script, or even an agent in the browser. All peers have a secure Ed25519 identity, can join one or more groups, and register the spaces they take part in. Peers can relay for others, traversing networks, with end-to-end encryption between the originator and its group. This enables AgentSpaces to have a robust P2P fabric, with many secure groups, safeguarding appropriately.
A space is a replicated tuple space, where entries get a TTL, and foundational APIs for working with records. Capabilities get layered on top, enabling peers to advertise if they can do more: build quorums, vote, calculate aggregates, provide strong consistency, and even use the P2P network for learning! Read the architecture guide to learn more about the types of systems AgentSpaces enables.
The office metaphor helps us understand how these agents can work together in groups.
| in the Spaces | in the code | what it means |
|---|---|---|
| the Spaces, the office | group | A peer group with a self-certifying identity. Membership heals itself; join order does not matter. |
| a cubicle, a desk | peer | One process, and the agents at it. Its key is its identity, so it re-registers nothing when it moves networks. |
| the board, the tray | space | A replicated tuple space. Every peer keeps a copy and reads it locally. |
| a sticky note, a work order | entry | A typed record, signed by its writer, with a lease. Matched by what it is rather than by a queue address. |
| taking a note off the board | @SpaceTake | An exclusive take under a lease. The return value completes it, atomically. |
| reading over a shoulder | @SpaceNotify | React to every matching entry without consuming it; a non-void return is the next entry. |
| a timecard, clocking in | lease | How long anything lives. Renewal is clocking in again; silence lets it lapse. |
| going home sick mid-task | lease lapses | The note reappears for a colleague. Nobody wrote the retry. |
| the badge | Ed25519 key | Per-peer identity; a per-agent badge is a subordinate key with a certificate. |
| badge deactivation | revocation | One signed revocation, fleet-wide within a gossip round. |
| hallway talk, the rumour mill | gossip | SWIM membership, rumour for speed, anti-entropy for completeness. |
| the committee, the minutes | @Ballot · @OnDecision | One signed ballot per voter, a quorum, and a tally anyone can recount from the record. |
| the one cashier | @OrderedTake | Takes committed through a Raft log. Exactly once, in one order. |
| bidding for a shift | @BidFunction | A price per entry on an AUCTION space; the lowest bid takes the work, with no auctioneer. |
| the résumé on the name plate | AgentCard | What the agent consumes and produces, signed and leased. The live fleet is the directory. |
| the filing cabinet | AssetCard | A data system that joins as a peer and advertises what it holds. |
| the front desk | A2A gateway | Outside agents come in through the reception; MCP stays the toolbox at each desk. |
| walking between cubicles | transport | Pluggable transports including TLS, mTLS, DTLS, TCP, UDP, WebRTC, HTTP, WSS, WSSE, QUIC, and more! |
| the manager | none | There is none. That is the design. |
An insurance claim arrives on the board at nine. Analysts race for it, repair estimates go to the lowest bid, a panel votes on the approval and the payment runs exactly once. At two in the afternoon a worker leaves mid-task, and nobody notices because nobody needs to.
The intake service writes one signed record with a thirty-minute lease. Every desk in the Spaces sees it within a gossip round.
write(ClaimReceived, lease 30m) · replicated to every peerDocument, fraud and policy analysts each read the claim without consuming it and write what they learned as the next notes. Nobody wired a graph between them.
@SpaceNotify on ClaimReceived · each returns a FindingThree estimators bid. The local, cheaper model's desk offers the lowest price and takes the note. The premium model stays idle until a hard case needs it.
AUCTION space · @BidFunction · lowest bid takes the entryThree reviewers, one signed ballot each. A person on the console casts the fourth. The tally closes at a quorum and one reaction fires, once.
@Ballot · QUORUM · @OnDecisionThree clerks race for the payment note. The ordered log commits exactly one claim; the other two see the loss and stand down.
@OrderedTake over the Raft-ordered logAnalyst two is killed holding a claim. Its timecard stops renewing. The Spaces keep working.
TAKE lease, no renewal · the claim is still on the boardTen minutes later the lease lapses and the claim is visible again. Analyst three takes it. No retry queue, no orphan reaper, no heartbeat service exists.
lease lapses · REAPPEARED · @SpaceTake on another peerWho wrote each note, who held each take, who voted how and who paid is replayable from signed records. Compliance evidence came out of doing the work.
readAllIssued · hybrid-logical-clock orderEvery step above ships in the open-source core and runs in the examples: the kill-tolerant worker in example 11, the auction in example 04, the quorum vote in example 05, the exactly-once desk in example 12, and all of them at once in Party Bus.
Agents stay ordinary code in the framework you already use. Each host binds them to the same spaces in its own idiom, and every host shares one signed wire.
Agentic AI cost is found in: surrounding systems we buy, build, and sustain; answers we paid for once, and then re-compute rather than re-use, and using expensive models where a cheaper model would work.
No broker, directory, scheduler, cache, lock service or workflow engine to build, host or staff. As the fleet grows, only the desks grow.
An answer kept on the board with a freshness limit serves every desk that asks until it expires. The bigger the Spaces, the more questions overlap.
Desks bid for each task. The premium model works only when the task needs it.
Add an annotation, join an AgentSpace. AgentSpaces is open source, more integrations, libraries, and languages getting added routinely.
@SpaceAgent(description = "Ships orders from the shared space")
public class Fulfiller {
@SpaceTake(lease = "10m") // crash => order reappears
public Shipment ship(Order order) {
return new Shipment(order.orderId(), order.item(), "fulfiller");
}
@BidFunction // AUCTION spaces only
public double bid(Order order) {
return estimatedTokens(order) * modelCostPerToken();
}
}
<dependency>
<groupId>ai.badmonkey.agentspaces</groupId>
<artifactId>agentspaces-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!-- or import agentspaces-dependencies 0.2.0 as a BOM and drop the version -->
# a Python peer takes the same typed work off the same space
from agentspaces.space import entry
from agentspaces.agents import Binder, agent, space_take
@entry("ai.badmonkey.agentspaces.examples.Order#v1")
@dataclass
class Order:
orderId: str
item: str
@agent("py-fulfiller", description="Ships orders")
class Fulfiller:
@space_take("work", lease="10m") # take, call, complete with the result
def ship(self, order: Order) -> Shipment:
return Shipment(order.orderId, order.item, "py-worker")
Binder(peer).bind(Fulfiller()) # the same loops the Java binder runs