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.

Version 1.0 · Updated

01

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.

WORKER (compute instance) workerEngine deterministic pipeline · orchestrates all steps harness pure agentic turn model decides callee, never orchestrator tools capability surfaces backed by providers uniform dispatch internal code direct logic deterministic no model involved tools → backed by providers (satellites if self-hosted on the bus)
The Factory stack. A worker runs a workerEngine that calls three kinds of step — the harness, tools, and internal code. Tools are backed by providers; a provider that runs as a self-hosted process on the NATS bus with a heartbeat 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.

02

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.

03

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.

COMPOSITION ↑ L3 — Sub-agent nested isolated agent · returns a summary upward · NATS harness L2 — Skill instruction-layer composite (SKILL.md) · guides the model · prompt injection L1 — Macro deterministic chain · no second model · in-proc or remote L0b — Remote atomic provider on the bus · heartbeat-discovered · NATS / stdio / HTTP L0a — Built-in atomic compiled in-process · always available · bash · file · web fetch UNIFORM DISPATCH
Five layers, one dispatcher. From a built-in primitive to a recursive sub-agent, every tool passes the same access-control gate and returns the same result type — only the transport changes underneath.
04

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.

05

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 plumbing

This 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)
Satellite provider · self-hosted on NATS bus voiceCLI · imageCLI · Postiz adapter heartbeat ✓ running container ≠ heartbeat — heartbeat comes from our adapter Non-satellite provider provider · no bus process · no heartbeat X API · GitHub API · one-shot CLI heartbeat ✗ in-proc HTTP or stdio — no NATS identity
Two provider shapes. A satellite runs on the bus, emits a heartbeat, and has a NATS identity — the engine discovers it automatically. A non-satellite provider (cloud API, one-shot CLI) is called directly, with no bus footprint.

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.

06

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.