# bubbuild/bub: a hook-first Python runtime where every turn stage is replaceable

> Bub is a small Apache-2.0 Python runtime for agents that share a conversation with people. Its turn pipeline is built from pluggy hooks, and the same runtime drives CLI and Telegram.

**bubbuild/bub** — Bub it. Build it. A hook-first runtime for agents that live alongside people.

- Repository: https://github.com/bubbuild/bub
- Website: https://bub.build/
- Stars: 1,680 · Forks: 165
- Language: Python
- License: Apache-2.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/bubbuild-bub

## The problem Bub targets: agents inside shared conversations

Most agent frameworks assume a single operator talking to a single assistant. Bub starts from the opposite case. The README says it "started in group chats, where multiple humans and agents had to work in the same conversation without hidden state, hand-wavy memory, or framework-specific magic." That sentence is the project's whole design brief.

The consequence is an emphasis on visible boundaries. Every inbound message travels one pipeline, and the state that produces a reply is rebuilt rather than carried. The README calls this "tape context": context is rebuilt from append-only records, not carried around as mutable session state. For a group chat where three people and two agents interleave, that means a reply can be traced back to the records that produced it instead of to an in-memory object that only the process that created it understands.

Bub also claims operator equivalence: humans and agents work inside the same runtime boundaries, with the same evidence trail and handoff model, and no hidden operator class. Whether that holds in practice depends on the channel adapter, but the intent is clear from the architecture rather than from a marketing page.

This is not a framework for someone who wants a hosted agent platform with a dashboard. It is for Python developers who are willing to read hook contracts and want the turn itself to be theirs.

## How a Bub turn actually flows, stage by stage

The README prints the pipeline directly:

```
resolve_session → load_state → build_prompt → run_model
                                                   ↓
              dispatch_outbound ← render_outbound ← save_state
```

Each arrow is a pluggy hook. Builtins are registered first, external plugins load after them, and at runtime later plugins take precedence. That ordering rule is the core extension mechanism: you do not fork the runtime to change behaviour, you register a plugin whose hook implementation wins.

Four files carry the design. The turn orchestrator is src/bub/framework.py, the hook contract is src/bub/hooks/specs.py, the builtin implementations are src/bub/builtin/hook_impl.py, and skill discovery lives in src/bub/skills.py. If you are evaluating Bub, specs.py is the file that tells you what you can replace without touching the runtime.

The same inbound pipeline is used across CLI, Telegram, and custom channels. Adapters change the surface, not the runtime model. That is a real architectural commitment: a Telegram message and a REPL line enter the same stages, which is why a plugin written for one surface can affect the other.

Two configuration values shape the model stage. BUB_MAX_STEPS caps the tool-use loop and must be a positive integer; the default is unlimited, which is worth noticing before you point Bub at a tool that can call itself. BUB_SPILL_THRESHOLD defaults to 4096 estimated tokens and controls when tool output spills; setting it to 0 disables spilling.

## Installing Bub and running a first turn

The README offers a one-line installer for macOS and Linux, which downloads and runs a script from bub.build:

```bash
curl -fsSL https://bub.build/install.sh | bash
```

The interactive installer uses a colored preset picker, accepts additional plugin dependencies, and runs bub onboard after installation. For automation you select a preset explicitly, and the README notes that non-interactive installs skip onboarding:

```bash
curl -fsSL https://bub.build/install.sh | bash -s -- --preset recommended --dependency extra-plugin
```

Windows PowerShell gets an equivalent script. The README also documents a source install, which is the path to take if you want to read the hook contract while you work:

```bash
git clone https://github.com/bubbuild/bub.git
cd bub
uv sync  # enough to run Bub from source
```

For local development the Makefile's install target is the documented alternative, because it also installs the website toolchain and the prek hooks. Once installed, the CLI exposes three entry points that matter on day one: bub chat for an interactive session, bub run "summarize this repo" for a one-shot task, and bub gateway for channel listener mode. Lines beginning with a comma enter internal command mode, so ,help lists what is available inside a session and ,skill name=my-skill selects a skill.

A minimal plugin follows the same pattern as the README example. The hookimpl decorator marks methods, and the plugin object is registered through a project entry point:

```python
from bub import hookimpl
from bub.envelope import content_of


class EchoPlugin:
    @hookimpl
    def build_prompt(self, message, session_id, state):
        return f"[echo] {content_of(message)}"

    @hookimpl
    async def run_model(self, prompt, session_id, state):
        return prompt


echo_plugin = EchoPlugin()
```

```toml
[project.entry-points."bub"]
echo = "my_package.plugin:echo_plugin"
```

That pair is the smallest useful thing to build. It replaces prompt construction and short-circuits the model call, so you can confirm your plugin loads before you write anything that talks to a provider.

## Where Bub's design costs you: model defaults and plugin precedence

The default model identifier is openrouter:openrouter/free. That is a sensible default for a first run and a poor one for anything you care about, because it points at a free routing tier you do not control. BUB_API_KEY is optional when you use bub login openai, which performs OpenAI Codex OAuth, but the README does not describe what happens to that credential's lifetime or how to revoke it.

Plugin precedence is the sharper trade-off. Later plugins take precedence, and builtins are registered first. That gives you a clean override story, and it also means a third-party plugin can silently replace a builtin stage. There is no documented conflict report. The README does not describe how Bub resolves two external plugins that implement the same hook, so if you install several plugins you should assume one of them wins and verify which.

Bub's plugin dependencies are managed separately from the runtime. bub install and bub update operate on a uv project that defaults to ~/.bub/bub-project or the path in BUB_PROJECT. That is a deliberate split, and it means upgrading Bub and upgrading your plugins are two different operations that can drift apart.

Finally, the CLI hides bub hooks from top-level help. The README says it still exists for diagnostics. A diagnostic command you cannot discover from --help is a small thing, but it is the kind of thing you only find by reading the source.

## Bub compared with an all-in-one agent framework

The obvious alternative is a batteries-included framework that owns the whole loop: it defines the agent abstraction, the tool registry, the memory store, and the execution graph, and you configure it. Bub goes the other way. It defines a turn as a sequence of named stages and hands each stage to pluggy, so the framework's job is dispatch and ordering rather than behaviour.

The practical difference shows up when you want to change one thing. In a graph-based framework you usually add a node or a callback and hope the surrounding machinery cooperates. In Bub you implement build_prompt or run_model and the later registration wins. The README is explicit that builtins are "included but replaceable" and that there are "no framework-only shortcuts."

The cost is that Bub gives you less. There is no documented agent graph, no built-in evaluation harness, and the README's Background section points at external posts (Why We Rewrote Bub, Socialized Evaluation and Agent Partnership, and tape.systems) rather than describing those systems in the repository. If your team wants a framework to make architectural decisions for you, Bub is the wrong shape. If your team has already decided how a turn should work and wants a runtime that gets out of the way, the hook contract in src/bub/hooks/specs.py is the thing to read before you commit.

## Maintenance, licence and the upgrade path you inherit

Bub is not archived, and the last push to main was on 2026-09-10. Recent releases are close together: 0.4.1 on 2026-07-30, 0.4.2 on 2026-08-07, and 0.4.3 on 2026-08-19. A release cadence measured in weeks, with a push inside the last fortnight, is the profile of a project still being changed rather than one in maintenance mode. Version numbers are derived from VCS tags through hatch-vcs, with a fallback version of 0.3.0, so an untagged checkout reports a version that does not correspond to a release.

The licence is Apache-2.0, declared in pyproject.toml and shipped as LICENSE at the repository root. Apache-2.0 includes an explicit patent grant and requires that notices be preserved, which matters if you redistribute Bub inside a product. It does not tell you anything about the licences of the plugins you install through bub install; those come from their own packages, and the README does not describe any licence check. That is a question for your own legal review, not something the repository answers.

Upgrade cost has two parts. The runtime upgrades as a Python package, and the plugin set upgrades through bub update against the uv project at ~/.bub/bub-project or BUB_PROJECT. Because later plugins take precedence over builtins, a plugin that was written against an older hook signature can change behaviour after a runtime upgrade without any change on your side. The README does not document a compatibility policy for hook signatures between releases. If you depend on a specific stage, pin both the runtime and the plugin set and read the diff in src/bub/hooks/specs.py before you move.

## Conclusion

Bub fits teams that want to own the turn pipeline and inspect what the model saw, and it is a poor fit if you want a hosted control plane or an opinionated multi-agent graph. Before adopting, read src/bub/hooks/specs.py to confirm the hook signatures you plan to override, and check what the README's configuration table leaves unset, such as BUB_API_BASE and BUB_MODEL_TIMEOUT_SECONDS, against your own provider.

## FAQ

### What is bubbuild/bub?

Bub is a small Python runtime for building agents that work in shared environments such as group chats. It is built on agents.md and Agent Skills, and every turn stage is a pluggy hook, with the same runtime driving CLI, Telegram, and any channel you add.

### How do I install Bub and run a first task?

On macOS and Linux the README gives a one-line installer at bub.build/install.sh, which runs bub onboard after installation; a source install is git clone followed by uv sync. After that, bub chat opens an interactive session and bub run "summarize this repo" performs a one-shot turn.

### How do I write a Bub plugin?

Define a class whose methods are decorated with hookimpl, then register the instance as a project entry point under the "bub" group, as in the README's EchoPlugin example that overrides build_prompt and run_model. Builtins are registered first and external plugins load after them, so a later plugin takes precedence.

### Which model does Bub use by default?

BUB_MODEL defaults to openrouter:openrouter/free. BUB_API_KEY is optional when you authenticate with bub login openai, and BUB_MODEL_TIMEOUT_SECONDS controls the model call timeout.

### Where does Bub store its plugin dependencies?

bub install and bub update manage a separate uv project that defaults to ~/.bub/bub-project, or the path set in BUB_PROJECT. The docker-compose file mounts ${HOME}/.bub at /data and sets BUB_HOME to /data.

## Sources

- [bubbuild/bub on GitHub](https://github.com/bubbuild/bub)
- [License: Apache-2.0](https://github.com/bubbuild/bub/blob/main/LICENSE)
- [Project website](https://bub.build/)
- [README](https://github.com/bubbuild/bub/blob/main/README.md)
- [Releases](https://github.com/bubbuild/bub/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/bubbuild-bub
