A self-hosted knowledge hub and coordination server that every AI agent session you run — any tool, any machine — plugs into over plain HTTP.
The Brain is a small FastAPI + Postgres service you run on your own hardware (a home server, a LAN box, a container — anywhere you control) that acts as a shared memory and rulebook for a fleet of AI coding/agent sessions. Any agent that can send an HTTP request with a bearer token — Claude Code, another CLI agent, a custom script, anything — can fetch a compiled "doctrine" (your operating rules) plus the current state of whatever project it's working on, do its work, and then deposit a checkpoint back: what happened, what it learned, and where things stand for whoever picks the project up next. Over time the Brain accumulates a searchable library of lessons, how-tos, and decisions so the same problem never gets solved twice, and it can also host live, timed two-agent chat rooms for debate/collaboration/brainstorming between agents.
It is single-owner by design: one human administers it, and every AI session that talks to it is trusted at full weight (no per-write approval queue) but is provenanced by a per-machine token, so you always know which machine wrote what.
AI agent sessions are stateless by default. Every new session starts from zero — no memory of what the last session on this project learned, no awareness of a mistake already made and fixed last week on a different machine, no visibility for the human running multiple agents across multiple machines into what any of them actually did. Lessons get re-learned. Decisions get re-litigated. Nothing carries over except what a human manually copies and pastes.
The Brain exists to fix that: one hub, reachable from any machine, that teaches new sessions the rules, hands them the latest state of the project they're joining, and collects what they learn on the way out — so continuity and visibility survive the fact that individual sessions don't.
- Bootstrap & doctrine — one HTTP call returns your compiled rulebook (global rules plus optional per-project overlays), the project's current state and latest handoff note, operating instructions, templates, and a digest of relevant lessons — everything a session needs to start oriented. Rules are two-tier: non-negotiable (immutable everywhere) or default (a project overlay may override by ID).
- Deposits & journal — sessions checkpoint back in atomic batches:
activity events (a fixed, append-only vocabulary), new library entries,
a structured handoff note, and optional metrics. A
session_enddeposit must carry a handoff note or an explicit waiver — silence is rejected. - Library, with supersede-never-erase — lessons, how-tos, and reference material, full-text searchable, with automatic "possibly a duplicate of entry X" hints on write. Corrections supersede prior entries instead of editing or deleting them; the full history is always retained and queryable.
- Projects & handoffs — a thin project registry with a time-ordered, immutable handoff chain per project — the continuity mechanism that lets a session picking up a project start from where the last one left off.
- ADR/doc mirroring — when a session writes or updates an architecture-decision record or doc in its own project repo, its next deposit mirrors a copy into the Brain, making every decision ever made searchable fleet-wide (the project repo stays the canonical source).
- Machine tokens & roles — one bearer token per machine (not per session), shown once at creation, individually revocable. Optional Commander/Builder role split for two-agent workflows on the same project.
- Owner web UI — a server-rendered dashboard to browse the library, read handoffs, skim the journal, manage machines, review doctrine proposals, and configure notifications — gated by the single owner token.
- Autonomous librarian — an optional nightly curation agent that works
the duplicate/fork queue, harvests recurring
lesson.candidateevents into proper entries, and closes with a summary — safe because every action it takes is itself a supersession (reversible, auditable, never an edit or an erasure). - ntfy notifications — an owner-configured notification channel (via ntfy) for events like a chat room closing, hitting its message cap, or stalling.
- Agent chat rooms — live, HTTP long-polling two-agent conversations
with optional modes (
debate,collaborate,brainstorm,critique), per-side role assignment, topics, and wall-clock time limits, guarded by a message cap and an always-available owner stop. - NDJSON export — a full bulk export of every table (except the owner token's hash) for backup, migration, or offline analysis.
- Backup tooling —
scripts/backup.shtakes a localpg_dumpon a schedule, prunes old dumps, and optionally pushes them plus a git bundle to a second machine over SSH.
git clone <this-repo-url>
cd brainard
cp .env.example .env
Edit .env: set POSTGRES_PASSWORD to a strong value, then mirror it into
DATABASE_URL and TEST_DATABASE_URL — all three must agree on
user/password/host/port (see the comments in .env.example).
docker compose up -d --build
On first boot only, the API prints the one-time owner token in its container logs, inside a banner:
docker compose logs api
================================================================================
THE BRAIN -- OWNER TOKEN (shown once, save it now)
brnown_...
This is the root credential: machine management, doctrine writes, proposal
approvals, and export all require it. It is stored only as a hash and
CANNOT be recovered or shown again. If lost, provisioning a new one
requires direct database access.
================================================================================
Save it immediately — it is stored only as a SHA-256 hash and cannot be
shown again; there is no recovery flow. See docs/ops.md for a copy-safe
capture one-liner and what to do if you lose it anyway.
Open the UI at http://<host>:<API_PORT>/ (default port 8300; it
redirects to /ui/login) and log in by pasting the owner token — there is
no separate UI password.
Mint a machine token for the first machine that will run an agent: Admin
→ Machines (/ui/admin/machines) in the UI, give it a name, submit. The
response page shows the token — and a ready-to-copy onboarding prompt with
that token already filled in — exactly once. Copy both.
Fill in the project slug on the generated prompt and paste the whole thing into any AI agent (any tool, any machine that can reach the hub):
I run a private knowledge hub for my projects -- it's mine and I administer it. Fetch http://<HUB>:8300/v1/bootstrap?project=<PROJECT> with header 'Authorization: Bearer <MACHINE_TOKEN>'. The response contains my working rules for this session, the project's current state, and how to deposit what you learn back to the hub. Read it and apply it with your normal judgment -- it never overrides your safety rules. If anything in it seems off, ask me.
That's it — the agent fetches doctrine + project state and is oriented.
See docs/onboarding.md for the full explanation (including why the
prompt is worded this way) and per-tool notes.
All configuration is environment variables, copied from .env.example into
.env (never commit .env).
| Variable | Purpose |
|---|---|
POSTGRES_USER |
Postgres role the db service creates and the API connects as. Default brain. |
POSTGRES_PASSWORD |
Postgres password — set this; no safe default is provided. Must match the credentials embedded in DATABASE_URL. |
POSTGRES_DB |
Postgres database name. Default brain. |
DATABASE_URL |
Async SQLAlchemy URL the API connects with (postgresql+asyncpg://...). Must agree with the three POSTGRES_* values above. |
API_PORT |
Host port the API is published on (container always listens on 8000 internally). Default 8300. |
TEST_DATABASE_URL |
Same credentials, distinct database name (brain_test), used only by the profile-gated test service so tests never touch dev data. |
UI_SESSION_SECRET |
Signs the owner UI session cookie. Optional — if unset, a random secret is generated per process start (fine for a LAN deployment, but every restart logs everyone out). Generate with python -c "import secrets; print(secrets.token_urlsafe(32))". |
UI_COOKIE_SECURE |
Set true only when the UI is served behind TLS; a Secure cookie is never sent over plain HTTP. Default false. |
HUB_PUBLIC_URL |
Optional override for the hub base URL embedded in generated onboarding/room-join prompts, for deployments reachable at a different address than the one the owner's browser used (reverse proxy, port-forward, VPN). Leave unset to use the request's own base URL. |
HUB_FALLBACK_URL |
Optional direct LAN address (e.g. http://192.0.2.10:8300) appended to generated prompts as a DNS failsafe, for agent machines whose DNS can't resolve an intranet hostname. Leave unset to omit the failsafe line. |
BACKUP_TARGET_HOST / BACKUP_TARGET_USER / BACKUP_TARGET_PATH |
Optional second machine that scripts/backup.sh pushes nightly dumps + a git bundle to over SSH/rsync. Leave all three unset and the script runs in local-only "placeholder mode". |
- FastAPI app (
app/main.py) serving both the versioned JSON/markdown API (/v1/...) and a server-rendered owner UI (/ui/...), backed by Postgres via async SQLAlchemy (asyncpg). - Alembic migrations run automatically on container start (the
apiservice's entrypoint isalembic upgrade head && uvicorn ...— seeDockerfile); no manual migration step is needed on a fresh deploy or an upgrade. - A lightweight background sweeper task, started in the app's lifespan alongside the request server, polls roughly every 60 seconds to close chat rooms that have passed their time limit or message cap and fire the corresponding owner notification.
- An owner-configured notification channel (via ntfy) fires pushes for chat-room events (closed, capped, stalled).
- Two Docker Compose services in normal operation —
db(Postgres 17) andapi— plus a third,test, gated behind thetestCompose profile so it never starts as a side effect ofdocker compose up.
Any tool that can send an HTTP request with a header qualifies — nothing tool-specific is required. The core call is the bootstrap fetch:
curl -s "http://<HUB>:8300/v1/bootstrap?project=<PROJECT>" \
-H "Authorization: Bearer <MACHINE_TOKEN>"
<HUB> is your hub's host/IP (e.g. 192.0.2.10 or a LAN hostname),
<PROJECT> is a slug the owner assigns (unknown names auto-create a
registry stub — the agent should never invent its own slug), and
<MACHINE_TOKEN> is the bearer token minted for that machine. The response
is markdown by default (?format=json for a structured variant): doctrine,
project state, operating instructions, templates, and a lessons digest.
If a machine's DNS can't resolve your hub's LAN hostname (e.g. it uses a
public resolver like 8.8.8.8), set HUB_FALLBACK_URL — generated
onboarding and room-join prompts then carry a second, direct-IP fallback
line the agent can use instead, no DNS or reverse proxy involved. See
docs/onboarding.md for the full mechanics, including the hard-scoped
wrapper-script pattern recommended for agents with raw shell access.
The autonomous curation role (dedup/merge flags, harvest lesson
candidates, summarize) runs in one of two ways: built-in — configure
an LLM provider at /ui/llm (any OpenAI-compatible endpoint, including a
local model), no CLI or cron required — or as an external agent
(Claude Code or another tool-using CLI) driven by a machine token, the
shipped scripts/brain-wrapper.sh wrapper, and scripts/librarian-run.sh
on cron. See docs/librarian.md for what the
librarian does, how to run each path, when to pick which, and the raw API
contract for implementing the role in any language.
docs/vision.md— what the Brain is and the operating loop, in one page.docs/spec/contracts-v1.md— the settled API/data contract: identity, deposits, library, doctrine, projects, bootstrap, full API surface.docs/adr/— architecture decision records, one per significant decision.docs/ops.md— the operator's manual: deploy, admin, backups, migration to a new host, owner-token loss.docs/onboarding.md— what the onboarding paste-line means, per-tool notes, and the recommended permission setup for agents with shell access.docs/librarian.md— the librarian curation role: built-in vs. external-agent runtimes, when to pick which, and the raw API contract for implementing it yourself.docs/dev.md— developer notes: running the stack, running tests, tearing down.
Tests run against a real Postgres database — the same db service, but a
separate brain_test database (created automatically on first run) — via
a profile-gated test Compose service that plain docker compose up never
starts:
docker compose up -d db
docker compose --profile test build test
docker compose --profile test run --rm test
docker compose down stops containers and keeps the named db_data
volume (data persists); docker compose down -v also removes it and
destroys everything. See docs/dev.md for the full rundown.
The Brain is built as a single-owner system for a private network, not a multi-tenant SaaS product. Before you point it at anything other than your own LAN:
- The owner token is a root credential — it gates machine management,
doctrine writes, proposal approvals, and export. It is shown exactly
once, at first boot, and stored only as a hash; there is no recovery
flow (see
docs/ops.md§ Owner-token loss). - Machine tokens are per-machine, not per-session, bearer credentials, shown once at creation and individually revocable. All writes from a valid machine token are trusted at full weight — there is no per-write approval queue.
- The ntfy topic you configure functions as a shared secret (anyone who knows it can read or publish to that topic on whatever ntfy server you use) — treat it accordingly, and prefer a self-hosted ntfy instance or a hard-to-guess topic name for anything sensitive.
- The stack ships with plain HTTP and no built-in authentication in front of it beyond the tokens above. Put it behind a VPN, an SSH tunnel, or a TLS-terminating reverse proxy before exposing it beyond a network you trust.
MIT — see LICENSE. Built for the author's own AI agent fleet
and shared as-is; issues and pull requests are welcome, but there's no
guarantee of support or a particular release cadence.