A smaller rewrite of bb. bb runs coding agents (Codex and Claude Code) as threads: you follow a thread live, steer it while it runs, queue messages, and let agents start and manage other threads. This repository does the same job for the way bb is actually used day to day, in a fraction of the code, and adds multiplayer: several people can share one server.
It started as an experiment called bb-150k: rebuild bb under a hard cap of 150,000 lines by
keeping what is used, cutting what is not, and redesigning the parts that had grown too large. The
cap was later lifted so features could come back as plugins. pnpm count reports the current size:
about 263,000 counted lines (42,000 of them tests), against about 1.6 million in get-bb/bb by the
same rule.
REPORT.md: what the rewrite kept, cut and verified, and how it looks next to the original.INVENTORY.md: how the plan was made and the decisions behind it.docs/architecture.md: the exact behaviour of every part. This README is the overview.
| Thing | What it is |
|---|---|
| Project | A codebase, with one source directory per machine. proj_personal is for work outside any codebase. |
| Thread | One conversation with one agent: a status, a timeline of events, at most one parent thread. |
| Environment | The directory a thread works in: the project checkout, a git worktree bb manages, or a personal scratch workspace. |
| Machine | A computer that runs agents. The server's own machine always exists; others join with bb runner. |
| Provider | An agent backend (codex, claude-code, plus pi and ACP agents as add-ons) and its models. |
| Plugin | A directory that adds server features, UI, agent tools and bb <name> commands. Almost everything in bb is one. |
Three ways to drive it, all over the same API:
- The web UI the server serves: sidebar with sections and pinned threads, split panes, the conversation timeline, the composer (mentions, slash commands, attachments, queue and steer), and a right panel with files, diffs and terminals. It works on phones and tablets too.
- The
bbCLI, which can do everything the UI can. Agents inside threads use it to spawn child threads, wait on them, read their output and message them. A native Rust build (crates/bb-cli) answers in milliseconds instead of ~200 ms; the Node CLI is the reference. - MCP (the
mcpplugin, off by default): ChatGPT, Claude and other MCP clients can search and runbbcommands, signed in through OAuth.
Other machines join as runners (bb runner join) over one WebSocket, and a server with no public
address can be reached through bb connect: either the getbb.app tunnel or this repository's own
relay (apps/relay, a Cloudflare Worker, see docs/relay.md), which can also run hosted servers on
Fly (docs/hosted.md).
One bb server can be shared by several people, which the original bb does not do.
- Users and roles. Each person is a user,
ownerormember. An owner invites people (bb user invite --name Ada --role memberprints a link that expires after seven days and works once). The person opens it, picks a name and password, and is signed in. Owners manage users, invites, plugins and server settings; members work in threads.bb user sign-in-linksigns in a browser without a password. - Signing in. On the server's machine a loopback request is the owner with no sign-in. Anyone else uses a session cookie, from a password or an invite or sign-in link. Through bb connect the relay's GitHub sign-in identifies people instead, and the server maps them to its users. Someone the server doesn't know yet sees "not a member yet" until they accept an invite.
- Shared and private machines. Every machine has an owner and is
private(only its owner can run threads, terminals and file commands there) orshared(bb machine share <machine>). Everyone can read every thread; there are no private threads yet. - Per-person state. Read and unread, pins, sidebar sections, open tabs, prompt history, UI preferences, theme and keybindings belong to each person. Plugin settings can be per user.
- Attribution. Each thread records who started it, and each message who sent it, shown on the
user rows in the timeline. Each user's avatar comes from GitHub when they sign in through the
relay. An agent's
bbcalls run as the person who last wrote in that thread, and the audit log records who did what. - Presence. The app shows who is online and who else has a thread open, in the thread's header and its row in the sidebar.
apps/cli/guide/people.md is the how-to; docs/architecture.md ("Trust") has the exact rules.
Needs Node 24 (.nvmrc; at least 22.19) and pnpm. Rust is optional, for the native CLI.
pnpm install
scripts/server.sh start --build # builds the web app (and the native CLI if cargo exists), starts the server
scripts/server.sh status | logs | stopThen open http://127.0.0.1:48886. On the server's own machine a loopback request is the owner,
so there is nothing to sign in to. apps/cli/bin/bb is the CLI (bb guide is the manual, and
bb <command> --help covers one command); bb server runs the server in the foreground instead.
| Setting | Default | |
|---|---|---|
BB_DATA_DIR |
~/.bb-lite |
Database, worktrees, plugin data, config.json |
BB_SERVER_PORT |
48886 |
|
BB_SERVER_BIND_HOST |
127.0.0.1 |
|
BB_SERVER_ALLOWED_HOSTS |
Extra host names, for a reverse proxy or tailscale serve |
|
BB_PROFILE |
default |
headless (no UI plugins) or minimal |
BB_SERVER_URL |
http://127.0.0.1:48886 |
Which server the CLI talks to |
The data directory and port differ from the original bb on purpose, so both can run side by side.
Before the rename to bb-lite the defaults were ~/.bb-150k and ~/.bb-150k-runner; move those
directories to ~/.bb-lite and ~/.bb-lite-runner (or set BB_DATA_DIR) to keep an existing install.
The server refuses to open an original bb database; bb import-legacy --from ~/.bb copies projects,
machines, sections, threads (with their events and provider sessions) and settings across
(apps/cli/guide/migrating.md).
Development: pnpm check runs typecheck, lint and tests; pnpm format formats; pnpm -C apps/app dev serves the UI with hot reload.
bb is a small kernel plus plugins. The kernel knows nothing about threads or projects; it only knows how plugins declare things, call each other and hook into each other. The server's own features (threads, projects, machines, sessions, environments, terminals, settings, identity) and the app's own screens (shell, thread view, composer, side panel, settings) are plugins written with exactly the API an outside plugin gets. There is no private API.
The server, the browser and each runner machine hold a kernel of their own, all from
packages/plugin.
A plugin declares what it offers in a contract: names and zod schemas, nothing else, so every part and every other plugin can import it.
import { contract, event, live, op, slot } from "@bb/plugin";
import { z } from "zod";
const Note = z.object({ id: z.number(), text: z.string() });
export const notes = contract("notes", {
list: op({
input: z.object({ projectId: z.string() }),
output: z.array(Note),
}),
create: op({
input: z.object({ projectId: z.string(), text: z.string() }),
output: Note,
kind: "write",
doors: {
agent: {
name: "AddNote",
description: "Remember a note for this project",
},
cli: {
path: "notes add",
summary: "Remember a note",
positionals: ["text"],
},
http: { method: "POST", path: "/notes", status: 201 },
},
}),
created: event(z.object({ note: Note })),
count: live(z.number()),
exporter: slot<{ id: string; label: string }>({ key: "id" }),
});| Member | What it is |
|---|---|
op |
An operation: validated input and output, read or write, who may call it, and what hooks may do |
event |
Something that happened, for listeners |
slot |
A list other plugins add to (nav items, providers, environment kinds, settings pages, timeline rows…) |
service |
A pick-one slot holding an object |
live |
A value the server holds and pushes to apps when it changes |
table |
A database table other plugins may read |
A plugin is a directory with a package.json bb block and up to three parts, each a plain
function that receives bb: server.ts, app.tsx and runner.ts (code that runs on every
machine, such as a provider adapter). Every part has the same verbs:
implement(op, fn): be the one implementation of an operation.call(op, input): call any.on(op, hook): wrap an operation. A hook gets(input, next)and, depending on what the operation allows, observes, rewrites the input or output, answers itself, ordeny()s.on(event, listener)/emit(event, payload)for events.add(slot, item),wrap(slot, matcher, change), andread/find/pick/useto read.provide(live, initial)andchanged(op)to tell apps to refetch.
The server side also gets host services: bb.db (the shared SQLite), bb.storage (KV, a private
database, tables of its own), bb.settings.define (typed settings with a generated page),
bb.runners.call(hostId, op, input) (run an operation a runner part implements on a machine),
bb.http, bb.background (services and cron), bb.cli.register. The app side gets React hooks
from @bb/plugin/app (useOp, useLive, ThreadChat, the sidebar and composer hooks) and fills
the screen plugins' slots.
One operation reaches every surface through its doors, so nothing is written twice:
- HTTP. Every JSON route under
/api/v1is an operation's HTTP door:GET /api/v1/threads,POST /api/v1/threads/:threadId/send, and so on. Any operation not markedinternalcan also be called asPOST /api/v1/ops/<name>with its input as the body, answering{ok, result}. Errors are{code, message, details?}with the HTTP status.GET /api/v1/kernellists every operation, hook, slot, live value and plugin. Raw routes remain only for sign-in, uploads, file bytes and plugin assets. - WebSocket.
/wssends "changed" signals (entity, id, kinds) and plugin signals; the UI refetches on them./ws/terminals/:idstreams terminal bytes. - Agent.
doors.agentoffers the operation to every agent session as a tool. - CLI.
doors.cliturns it intobb <words> …, with flags derived from the input schema and--jsonfor free. Commands a plugin adds are dispatched to the server, so the CLI never needs updating for them.
The app, the CLI (packages/plugin/src/client.ts, call(op, input)) and other programs all call
operations, so one hook chain sees every caller. Each call carries a caller: the user, the door it
came through, the agent's thread, and which plugins it passed through. Every write is recorded in
an audit log (bb audit).
The server talks to machines only through one Runner interface: in-process for its own machine,
and over a single WebSocket for every other one, with a reliable event lane that survives
reconnects and server restarts. A turn is submitted with its whole session spec each time, so the
runner can start, resume or rebuild the provider session itself. Providers are plugins: a runner
part adds a ProviderAdapter (Codex through codex app-server, Claude Code through the agent SDK)
and a server part declares its models and settings. Both translate into the same stored event
format as the original bb, so imported history renders unchanged.
docs/plugin-api.md is the guide. External plugins load from directories listed in
<dataDir>/config.json (bb plugin add <dir>), are bundled by the server with no build step, and
can hot-reload. bb dev new | validate | types | watch | install helps write them, `bb dev port
| get-bb/bb | bb-lite | |
|---|---|---|
| Size | ~1.6M counted lines, ~800K of them tests | ~263K, ~42K tests |
| Apps | Desktop (Electron), web, mobile, CLI, server, host daemon, connect service, AI gateway, demo | Web app, CLI (Node + native Rust), server, relay. No desktop or native mobile app |
| Processes | Server plus a separate host daemon and a provider bridge process | One server process; a runner runs in it for the local machine and as bb runner elsewhere |
| Storage | SQLite through Drizzle with migrations | One schema.sql applied by user_version steps, plain prepared statements. Same event envelope and id prefixes |
| Plugin API | @get-bb/plugin-sdk: RPC contracts (rpc.register, useRpc) beside a fixed server and UI |
A kernel of contracts, operations, hooks, slots and live values; the server's and app's own features are plugins built on it, and any can be forked or replaced |
| HTTP API | Hand-written routes plus a client SDK | Every route is an operation's door; /api/v1/ops/<name> reaches any operation; no separate client library |
| CLI | Commands written one by one | One declarative table and engine; plugin commands come from operations; native binary for agents |
| Users | Single-user: one owner; the auth tables hold machine credentials and API keys | Multiplayer: owner and member roles, invites, shared or private machines, per-person state, presence |
| Plugins | Marketplace, installer, updater, safe mode, version gating | bb plugin add, bb dev install, bb marketplace; versioned against an API lock |
| Defaults | ~/.bb, ports 38886/38887 |
~/.bb-lite, port 48886 |
What the UI does not have (desktop, compared side by side with the original): forking or editing a
sent message, goal mode, the table-of-contents rail, Mermaid and math rendering, rich-text compose,
tab groups in the right panel, drag-to-split, the board view in Tasks, custom theme files, and
several settings pages (AI services, Browser, Updates, Experiments and others). Plan mode was
removed on purpose. REPORT.md lists every visible difference; some of what it lists as cut has
since come back as plugins (automations, workflows, browser automation, side chat, GitHub, memory,
the Pi and ACP providers and others: profiles/default/profiles.ts has the current list, and
docs/plugin-migration-gaps.md the port status of the original's plugins).
| Path | What it is |
|---|---|
apps/server |
The host: kernel, plugin loader, HTTP and WebSocket shells, static web app |
apps/app |
Web UI entry and plugin loader; the screens themselves are plugins |
apps/cli |
The bb CLI, bb server, bb runner; guide/ is the manual |
apps/relay |
Cloudflare Worker for accounts, linking servers, the tunnel gate, hosted servers |
apps/tunnel |
A second, frp-based tunnel gate, to compare against the relay's (docs/tunnel.md) |
crates/ |
Native bb CLI and its thread-log renderer |
packages/plugin |
The kernel and the plugin API |
packages/runner |
Everything that happens on a machine: sessions, git, files, terminals |
packages/server-kit, packages/app-kit |
What core plugins share on the server and in the browser |
packages/thread-view |
Events to timeline rows |
packages/domain, server-contract |
Shared schemas |
plugins/ |
Every feature: core, providers, environment kinds and add-ons |
profiles/default |
Which plugins make up the product, per runtime, and the named profiles |
hosted/ |
The Docker image for hosted servers |
MIT