REFERENCE MODEL
Tool Architecture
The conceptual model behind the Roxabi Factory tool layer — five layers, two taxonomies, a precise runtime vocabulary, and the two discriminators that keep the whole thing honest.
The Stack
Every capability the Factory exposes to an agent sits in a layered stack. At the base, a worker — a compute instance — runs a workerEngine: a coded, deterministic pipeline. That engine calls three kinds of step: the harness (a pure agentic turn where the model decides), the tools themselves, and plain internal code. Above the engine, tools are backed by providers; when a provider is a self-hosted process on the bus with a heartbeat, it is a satellite.
The harness is a callee of the engine, not a flavour of it. Agency where you want a decision, code where you want a guarantee — the two are layered, never unified.
Runtime Vocabulary
Six terms, defined precisely, keep the model from drifting. The historical ambiguity of worker (once used for both compute instances and tool providers) is resolved here.
| Term | Nature | Definition |
|---|---|---|
workerEngine |
plumbing · runtime | Coded, deterministic pipeline; orchestrates harness, tools, and internal code. |
harness |
plumbing · agentic | One pure agentic turn — the LLM decides, its in-turn tools fire. No pipeline logic. A callee of the workerEngine. |
worker |
plumbing · compute | A deployed instance that runs a workerEngine. Consumes tools. Not a tool, not a provider. |
tool |
tool-nature | A capability surface invoked by the harness or the workerEngine. |
provider |
plumbing · backend | The role that backs one or more tools. Transport-agnostic. One provider exposes many tools. |
satellite |
plumbing · deployment | A provider deployed as a self-hosted NATS process with a heartbeat. Every satellite is a provider; not every provider is a satellite. |
One term deliberately excluded: a backing service — a full self-hosted application like a Postiz fork (web server, database, Redis) — is infrastructure, like Postgres. A running container is not a heartbeat. The heartbeat comes from our NATS adapter placed in front of it. Backing service, provider, tool: three layers, never collapsed.
Taxonomy A — Five Layers
Tools are not uniformly atomic. The model has five layers of composition, from a single compiled call up to a recursive sub-agent — and one uniform dispatcher reaches all of them.
| Layer | Name | What | Transport |
|---|---|---|---|
| L0a | Built-in atomic | Compiled into the engine, in-process, always available. bash, file read/write, web fetch. |
— |
| L0b | Remote atomic | A provider on the bus, discovered by heartbeat. image.generate, voice.tts. |
NATS / stdio / HTTP |
| L1 | Macro | A deterministic chain of primitives — no second model in the loop. | in-proc or remote |
| L2 | Skill | An instruction-layer composite (a SKILL.md document) that guides the model through a domain sequence. |
prompt injection |
| L3 | Sub-agent | A nested, isolated agentic call that returns a summary upward. | NATS harness |
The decisive choice is a uniform dispatcher: one access-control gate, one result type, the transport varying underneath. To the model's reasoning loop, a remote image.generate and a built-in bash are identical — same shape, same result type. The transport difference is invisible.
Taxonomy B — Domain-Nature
The NATS wire contracts are split by nature, not by function. This is the axis that organises the tool plane.
A domain is tool-nature when its sole reason to exist is to expose a capability the model invokes — voice, image, and the satellites to come. Everything else is plumbing: the inference transport, the job bus, persistence, security, observability, delivery.
| Nature | Domains | Subject prefix |
|---|---|---|
| tool | voice · image · +future: postiz, vault, scrape, camoufox |
factory.tool.<cap>.* |
| plumbing | cli · llm · jobs · gh · turns · blob · verify · audit · event · outbound |
factory.<domain>.* |
Tool-nature subjects carry a tool. infix so the consumer side can subscribe to the entire tool plane at once with factory.tool.>, and discover what is available through a single registry — no per-kind subscription logic needed.
The package shape mirrors this split: roxabi-contracts/tool/{voice,image,…} holds the wire schemas for each tool domain; roxabi-contracts/tools/ holds the protocol layer — InProcessTool, RemoteTool, ToolManifest.
Once tools form a plane, the harness and the workers discover and address them as one. That addressability is the payoff of the split.
The Two Discriminators
Two rules keep the model precise. Without them, the boundaries drift.
Rule 1 — tool-surface ≠ tool-nature
A plumbing domain may expose a tool without being tool-nature. The gh domain exposes a built-in gh.list_prs; the llm domain exposes llm.generate — both stay plumbing. Surfacing a tool does not promote the wire domain.
tool-surface ≠ tool-nature
github plumbing exposes a built-in tool → stays plumbing
inference substrate exposes a generate call → stays plumbingThis rule is what keeps the separation honest instead of letting everything drift into « tool ». The test is not whether a domain exposes a tool-shaped call — it is whether the domain's entire purpose is capability exposure.
Rule 2 — provider ⊃ satellite
Not every provider is a satellite. A provider is a satellite only when it is a self-hosted process we run on the NATS bus with a heartbeat. A cloud API we call, or a one-shot command-line tool, is a provider without a satellite's footprint.
provider ⊋ satellite
self-hosted process on the bus → satellite (heartbeat: voiceCLI, imageCLI, our Postiz adapter)
cloud API / one-shot stdio CLI → provider (no heartbeat: X, GitHub, a bash tool)And the third layer to never collapse: a backing service — a full application like our Postiz fork (web server, database, Redis) — is infrastructure, like Postgres. The backing service is not the provider; the provider is our NATS adapter in front of it.
Backing service → provider → tool: three layers. A running container is not a heartbeat.
Key Invariants
Six invariants hold the model together. If any one is violated, the taxonomy becomes inconsistent.
- Worker ≠ tool. A worker consumes tools and runs on a workerEngine. It is not a tool and not a provider.
- Harness is pure agentic. The harness has no workflow logic. It is called by the workerEngine — layered, never unified with it.
- Uniform dispatch. One access-control gate, one
ToolResult, transport-agnostic. No per-kind branched pipeline. - tool-surface ≠ tool-nature. A plumbing domain that exposes a tool-shaped call stays plumbing. Surfacing a tool does not promote the wire domain.
- provider ⊋ satellite. Self-hosted on the bus with a heartbeat → satellite. Cloud / one-shot → non-satellite provider. A running backing container is not a heartbeat.
- worker = compute-on-engine. The word "worker" means this, not a tool-backing thing. Tool-backing things are providers; the NATS-deployed kind are satellites.
This model is design-locked (validated 2026-06-02). Implementation is parked pending the in-flight bus-subject migration — the design is settled so it can be built cleanly once the migration lands.