roxabi-factory

The agent engine that runs the Roxabi bots — a hexagonal core, hub-and-spoke over a NATS message bus, isolated per-scope agent pools, and a fleet of capability workers. It runs 24/7 on hardware you own and connects your chat platforms to AI agents you control. No cloud lock-in, no subscription, your data stays on your machines.

PythonNATSAGPL-3.0
01

What it is

Most personal AI assistants are cloud-hosted: your data leaves your machine, your conversations sit on someone else's servers, and the service disappears the moment a company pivots. roxabi-factory inverts that. It is an agent engine you run yourself — a home server, an always-on box, anything — wiring your preferred chat platforms to AI agents you own end to end.

The shape is deliberate: a hexagonal core (ports & adapters) so the domain never depends on any one platform, transport, or model; a hub-and-spoke topology so channels, agents, and workers are independent processes that only meet on a bus. Swap a model in TOML, add a channel as an adapter, plug in a worker over the wire — the core does not move.

The durable contracts are the core and the bus, not the model or the platform. Everything outside the hexagon is an adapter you can replace.

02

Hub and spoke

Channel adapters run as separate processes. They normalise an incoming message and publish it over NATS; the hub subscribes, routes each message to the right agent by a typed RoutingKey(platform, bot_id, scope_id), and the agent's reply travels back out over the bus to the adapter that delivers it. Four independent processes in production — hub, Telegram, Discord, worker pool — one process with an embedded bus in development.

factory-telegram aiogram · polling/webhook factory-discord discord.py · gateway factory-hub typed RoutingKey (platform, bot, scope) NATS message bus agent pools one per conversation scope inbound outbound
Adapters publish inbound, the hub routes by typed key to a per-scope pool, replies travel back out — every hop is a NATS subject, so each box is an independent, replaceable process.

Routing is concurrent the way conversations are: sequential per scope (one asyncio.Task per chat, thread, or channel), parallel across scopes, with a bounded queue for backpressure. Multiple bots can share a platform, each bound to its own agent.

03

Agents & memory

An agent is config-driven — persona, voice, model, and passthroughs live in a TOML seed, then in a SQLite store once initialised. Each conversation scope gets its own isolated pool, so two chats never share state. Model selection can route by complexity, sending the cheap turns to a small model and the hard ones to a large one.

Memory is layered, not a single transcript:

LevelWhat it holds
WorkingThe live turn window, compacted as it fills.
SessionThe current conversation.
EpisodicPast conversations, recalled on reference.
SemanticDurable facts — SQLite FTS5 + embeddings.
ProceduralLearned how-to: the moves that worked.

Session commands close the loop between conversation and knowledge — /vault-add, /explain, /summarize, /search each scrape a source, run it through the model, and write the result back to the vault. A trust level per adapter (owner, trusted, public, blocked) and a prompt-injection guard gate what any given sender can reach.

04

A fleet of workers

Capabilities the engine doesn't compile in arrive as workers on the bus. A worker is a long-running service that exposes one or more tools; it registers by heartbeat, and to the agent's reasoning loop a remote tool and a built-in look identical — same shape, same result type, the transport invisible underneath.

That is exactly how voiceCLI plugs in: it runs as a voice worker exposing voice.tts and voice.stt, so the model loads once on a GPU box and any agent in the factory can speak or listen by publishing a request — no GPU on the calling machine. Image generation, LLM routing, and more capability satellites attach the same way.

A worker is a service that exposes tools — not a bundle of tools. One worker, many tools; the bus makes a remote call indistinguishable from a built-in.

05

Where it's going

The substrate — the bus, the typed contracts, the worker pool, the scheduler — already runs in production. The next layer is jobs: turning activity into content, posting it, and running the loop around it, human-gated until reach is measured. Value before framework; primitives, not a general machine standing in front of its first unit of work.

The full reasoning — why a worker is not a tool, the five layers of tool, the order of the build — is written up on its own.

06

Run it

Requirements: Python 3.12 and the uv package manager. In development a single command runs every process in one with an embedded NATS server; in production the hub, the channel adapters, and the worker pool each run as their own container.

uv sync              # install
factory start        # dev: all processes + embedded NATS, one process
factory agent init   # seed an agent from its TOML into the store

The core, the adapters, the transport SDK, and the contract schemas are all in the open.