Skip to content

quantumsoftwarelab/plumbing

Plumbing

Plumbing is a small language and runtime for building systems out of agents, tools, and ordinary programs. A .plumb file declares which components exist, the type of message each one accepts and produces, and how messages are routed between them. When the program runs, the runtime creates the channels, starts the processes, and checks that whatever crosses each boundary has the declared shape.

The reason such a language is needed is that the structure of an agent system is usually hidden. Most systems of this kind keep their real coordination logic in prompts, in callback code, or in an orchestration script. The components may be visible, but the architecture that connects them is not. Plumbing takes that architecture and makes it the program. The graph of which component sends what to which other component is written down, so that it can be read, drawn, type-checked, reused, and placed inside a larger graph as a single component.

This rests on a particular view of where the difficulty in these systems lies. As models become more capable and are given more autonomy, the wiring between parts must remain legible to a person: what is connected to what, what may travel along each connection, and in what order. The intelligence can sit in the models; the architecture is better kept as an explicit object that can be examined and checked.

It is useful to think of plumbing as a generalisation of Unix pipes. Instead of simple lines of text, typed values encoded in JSON flow between processes. Instead of a linear or tree-like arrangement of processes, convergent branches and loops are permitted so a plumbing program is a directed graph. Some processes may be non-deterministic AI agents, other processes may be deterministic programs. Plumbing draws on a long line of work that treats communication between processes as something with structure and types, from Unix pipes and the Plan 9 plumber through to session types for communicating processes, and applies it to the less tidy setting of language-model agents.

The name is meant literally. Plumbing connects things, and gives the connections types so that the runtime knows what is allowed to flow through them. If a writing agent passes a draft to a checking agent, and the checker either returns comments or sends the accepted draft onward, that routing is part of the program rather than something buried in code. If a component expects a record with a score field, a message arriving without one is refused at the boundary instead of failing somewhere deep in a run.

Types do not make a model correct. A type checker cannot judge whether an answer is any good, but it can reject a program whose ports do not line up, whose data does not have the expected shape, or whose declared protocol cannot be compiled into the graph. This moves a class of failures from run time back to load time, which is worth doing because agent runs cost money and have effects in the world. It also gives an agent a more tractable thing to produce: a program can be generated, checked, corrected against the type errors, and only then run.

A further consequence is that a pipeline is itself a process. A whole arrangement of agents, once given a name and a type, can be used exactly where a single agent would be used, and whatever is inside it is invisible at the boundary. Systems built this way grow by composition, rather than by relying on the models to muddle through a structure no one can see.

At run time, plumbing creates the channels, starts the child processes, validates messages at the declared boundaries, and moves JSON values through the graph. Agents can call tools, including tools exposed by MCP servers. A protocol declaration can describe an ordered exchange, such as send this, wait for that, then finish, and the compiler lowers it into the same channel graph used by an ordinary pipeline.

Status and licence

Plumbing originated at Leith Document Company and is now developed in the Quantum Software Lab at the University of Edinburgh.

Copyright Leith Document Company and the University of Edinburgh. Released under the GNU Affero General Public License, version 3 or later. See LICENSE.

The public source repository is:

https://github.com/quantumsoftwarelab/plumbing

Documentation is published at https://leithdocs.com/plumbing.

Quick start from source

The supported source build uses Nix flakes.

git clone https://github.com/quantumsoftwarelab/plumbing.git
cd plumbing
nix develop -c dune build
nix develop -c dune test

Run a no-credential example:

printf '%s\n' '"hello"' | \
  nix develop -c ./_build/default/bin/plumb/main.exe examples/pydantic/pipeline.plumb

Run an interactive local example:

nix develop -c ./_build/default/bin/chat/main.exe examples/doctor/doctor.plumb

Command-line tools

Command Purpose
plumb Run a pipeline
plumb-check Type-check a .plumb file
plumb-agent LLM conversation process
plumb-chat Interactive pipeline frontend
plumb-mcp MCP server for plumbing pipelines
plumb-render Render a pipeline graph

Installed releases put these commands on PATH. In a source checkout, use the paths under _build/default/bin/ after nix develop -c dune build.

Python bindings

The Python package is persevere-plumbing; the import path is persevere.plumbing.

From a source checkout, build the OCaml binaries first, then use PYTHONPATH=python:

nix develop -c dune build
PYTHONPATH=python python3 - <<'PY'
from pathlib import Path
import persevere.plumbing as pb
print(pb.check(Path("examples/pydantic/pipeline.plumb")).bindings)
PY

For Linux and macOS, the Python bindings can be installed from PyPI:

pip install persevere-plumbing

The package wraps the compiled plumbing binaries. See doc/python-bindings.md for the API.

What plumbing checks

Plumbing checks the declared shape of the graph before it runs. It catches common mistakes such as connecting incompatible ports, using an unknown binding, or sending a message whose JSON shape does not match the declared type.

During execution, plumbing validates messages as they cross process boundaries. For protocol declarations, it compiles the declared ordering into plumbing channels, so the generated graph follows that ordering for the supported protocol forms.

Boundaries

Plumbing runs trusted local code. It is not a sandbox and does not isolate secrets between child processes. It does not prove that an LLM answer is true, that every pipeline terminates, or that the whole runtime has been formally verified. The proof and TLA material document specific compiler and runtime boundaries; they are not a claim that the whole system is proved.

Documentation

Start with doc/INDEX.md.

Key docs:

Release artefacts

Older binary releases, Docker images, and Python wheels still exist. This source release does not republish them. Future binary and package releases should link to the exact source tag.

Contributing and security

Use the public issue tracker for bugs and development discussion:

https://github.com/quantumsoftwarelab/plumbing/issues

See CONTRIBUTING.md, SECURITY.md, and RELEASE.md.

About

Typed AI Agent coordination

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors