Open Brain
by Fllenorium
README.md
# Open Brain
**The brain for AI agents.**
[](https://github.com/Fllenorium/open-brain/actions/workflows/ci.yml)
[](LICENSE)
One place to write what your agents know — your rules, your context, your
skills, and the connections they're allowed to use. Every agent reads it before
it answers, over MCP, with nothing pasted into a prompt. Open source and
self-hosted: your brain lives in your own Postgres.
An agent already knows how to work against a filesystem — it lists, it searches,
it opens a file. Inside a repo that works. Outside one there's nothing for that
instinct to point at, so every agent you run outside a codebase starts blind.
Open Brain is what it points at instead.
The thing that makes it different from every other context store: **you can
read the whole store in plain English and correct it.** You write in black. Your
agents write in red. Nothing an agent writes lands until you accept it, and
accepting a proposal is the moment red ink becomes black.
Single user. One owner, many agents. There is no organization, team, role,
member, or seat anywhere in this codebase — ownership is a `user_id` column.
Run one per person.
- **Rules in full, everything else as an index.** `brain_open` hands an agent
your standing rules and a map of the rest. It reads bodies only when it needs
them, so the brain can grow without drowning every session.
- **Every agent sees exactly what you bind it to.** A coding agent and a writing
agent read different parts of the same brain.
- **Agents propose, you decide.** Edits and new entries arrive as pending
proposals inside the document, reviewable in the browser or from your terminal.
- **Credentials without the copy-paste.** Grant an agent an API key or an MCP
server once; it arrives at boot, encrypted at rest, never readable back.
- **No vector database.** Search is Postgres full-text plus substring, so every
result is explainable.
---
## Run it
```bash
pnpm install
pnpm db:migrate # applies lib/db/migrations
pnpm db:seed # optional: a brain that looks like someone's real one
pnpm dev
```
`http://localhost:3000`. With no `DATABASE_URL` it runs on
[PGlite](https://pglite.dev) — real Postgres compiled to wasm, stored in
`.openbrain/pgdata`, nothing to install. With no Clerk keys it runs as a local
single-user instance, and refuses to start that way in production.
Everything in `.env.example` is optional. Copy it to `.env` when you want the
parts it turns on.
| Variable | Turns on |
|---|---|
| `DATABASE_URL` | A real Postgres instead of the local PGlite file |
| `OPENBRAIN_OWNER_KEY` | Sign-in without Clerk — one key, one owner |
| `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY` | Human sign-in through Clerk instead |
| `ANTHROPIC_API_KEY` | Review, Ask, Continue, and generated import descriptions |
| `OPENBRAIN_MASTER_KEY` | The connection registry (32 bytes, base64) |
| `OPENBRAIN_GIT_REMOTE` + `GITHUB_TOKEN` | Pushing the git mirror to GitHub |
> PGlite is single-process. Stop the dev server before running `db:seed` or
> `db:migrate` against the same directory.
## Self-host it with Docker
```bash
git clone https://github.com/Fllenorium/open-brain.git && cd open-brain
export OPENBRAIN_OWNER_KEY=$(openssl rand -base64 24) # how you sign in
export OPENBRAIN_MASTER_KEY=$(openssl rand -base64 32) # encrypts every credential
docker compose up -d
```
That starts Open Brain and a Postgres next to it, applies the schema, and
serves `http://localhost:3000`. Sign in at `/unlock` with the owner key. Every
boot applies whatever migrations are new, so upgrading is `git pull` and
`docker compose up -d --build`.
## Deploy it
Open Brain is one Next.js app, so any Node host works. On Vercel:
```bash
# 1. A real Postgres — Vercel's filesystem is ephemeral, so PGlite can't follow
# you up there. Neon, Supabase, Vercel Postgres, anything with a URL.
# 2. Apply the schema to it, from your laptop, once.
DATABASE_URL='postgres://…' pnpm db:migrate
# 3. Ship it.
vercel --prod
# 4. Set these on the project, then redeploy.
# OPENBRAIN_OWNER_KEY openssl rand -base64 24 — how you sign in
# OPENBRAIN_MASTER_KEY openssl rand -base64 32 — encrypts every credential
# DATABASE_URL the same URL as step 2
```
Sign in at `/unlock` with the owner key. There are no accounts — this is a
single-user product, so holding the key is what makes you the owner. Rotating
`OPENBRAIN_OWNER_KEY` invalidates every session at once.
Set the Clerk keys instead if you want real sign-in; Clerk wins when both are
present. Either way, agents are unaffected — they authenticate with their own
bearer tokens and never touch a human session.
**`OPENBRAIN_MASTER_KEY` is not recoverable.** Lose it and every stored credential
is unreadable, and an agent granted one will refuse to open a session rather
than start half-connected. Keep a copy somewhere that isn't the deployment.
## Connect an agent
Agents → **Connect an agent** → name it, tick what it reads, copy the token.
It's shown once.
```bash
claude mcp add --transport http open-brain http://localhost:3000/api/mcp \
--header "Authorization: Bearer obr_..."
```
The agent gets six tools, shaped like a filesystem on purpose:
| Tool | What it is |
|---|---|
| `brain_open` | The `ls` plus the readme. Every rule in full, everything else as an index, and the token count of its own payload. Called once per session. |
| `brain_list` | Directory listing under a path prefix. |
| `brain_search` | Grep. Postgres full-text plus substring — no embeddings, and the results are explainable. |
| `brain_read` | Full bodies by path, batched. |
| `brain_propose` | A unified diff plus a reason. Arrives as a pending proposal in the entry itself. |
| `brain_create` | A new entry at a path inside what it's bound to. Arrives pending; nothing is in the tree until you accept it. |
Everything an agent writes arrives as a proposal. There is no direct-write
setting.
Dotted names (`brain.open`) are accepted as aliases; the registered names use
underscores because MCP clients that bridge tools into a model API need
`[a-zA-Z0-9_-]`.
**Never exposed over MCP:** bindings, grants, write policy, agent CRUD, secrets
management, other agents. An agent cannot see or change its own permissions.
That line is held by the absence of a tool, not by a setting.
## What an agent can reach
Knowledge is what an agent *reads*. A connection is what it can *touch*, and
they behave nothing alike — a connection is never returned by a read tool, never
searchable, and never shown to a human again after you save it.
Four kinds, one table, because a secret is only a connection with no endpoint:
| Kind | What it is |
|---|---|
| **MCP server** | A URL and a token. The agent's client connects to it and gets its tools. |
| **API** | A base URL, an auth scheme, and a key. |
| **Database** | A connection string. The whole string is the credential. |
| **Secret** | Just a value. No endpoint. |
Grants are per agent, per connection. No wildcards, no inheritance down the
tree. What's granted arrives two ways:
- **In `brain_open`** — each connection's kind, endpoint, description and how
to authenticate, plus every credential in an `env` map named after the
connection. Immediately usable for APIs, databases and secrets.
- **From `GET /api/mcp/config`** — the granted MCP servers as a filled-in client
config, behind the same bearer token. A client reads its server list at
startup, not mid-session, so this is the half that gets the keys off your
laptop: one Open Brain entry in the config, and Open Brain says what else to connect
to.
## How an agent's view is decided
```
resolve(agent):
bound = the collections you tick
chain = every ancestor of those, up to the root
+ everything underneath them
return every entry in chain
```
Binding to `engineering/web-app` gives that collection, everything under it, and
everything in `engineering` and the root above it. Ancestors hold the general
rules; descendants hold the specific ones. Paths are unique per user, so nothing
shadows anything — a path means exactly one entry.
An ungranted skill is filtered, not refused: it doesn't appear in `open`,
`list`, or `search`, and `read` on its path returns not-found. We don't leak
that it exists.
## Layout
```
app/
(app)/workspace/[[...path]] the three panes — tree, entry, inspector
(app)/agents/[id] bindings, grants, policy, "what this agent sees"
(app)/history one line per event, filterable by agent
(app)/import paste / upload / GitHub, then the split preview
(app)/connections MCP servers, APIs, databases, secrets
(app)/welcome three-step onboarding
api/mcp the MCP server, Streamable HTTP
api/mcp/config the granted MCP servers, as a client config
_actions/ every mutation, each one re-reading the owner
lib/
resolve.ts the core algorithm
paths.ts path build, rebuild on move, uniqueness
patch.ts diffs, positioned hunks
boot-payload.ts what brain_open returns, and what the UI renders
connections.ts what an agent can reach, and how it's handed over
crypto.ts envelope encryption
mcp/ server + one file per tool
ingest/ heading split, kind inference, GitHub scan
ai/ Review, Ask, Continue
components/
tree/ editor/ rubric/ inspector/ agents/ connections/ import/ shell/ ui/
styles/tokens.css every token in DESIGN.md §3
docs/ PRD, ARCHITECTURE, DESIGN, and the mockup
```
## Review without leaving your terminal
The proposals live in the document because that is where a proposal makes sense
to read. But going to find it is a context switch, and a review loop that costs
a context switch is a review loop that stops getting used.
```bash
pnpm brain # what is waiting
pnpm brain show 1 # the full diff, in the agent's hand
pnpm brain accept 1 # red becomes ink
pnpm brain reject 2 # leaves the entry untouched
pnpm brain accept all
```
It calls the same functions the UI calls, so a version is written, an activity
row is logged, and a conflicting proposal is superseded exactly as it would be
in the browser. It reads `DATABASE_URL` from `.env`, so it works against a
deployment as well as a laptop.
## Check it
```bash
pnpm build # zero type errors
pnpm test # paths, patches, the split, and resolution against a real Postgres
./scripts/check-all.sh # all of it: build, then every end-to-end check against a fresh seed
./scripts/verify.sh # boots the app and drives the rubric end to end
node scripts/shots.mjs # screenshots every screen, desk and phone
```
The end-to-end checks drive Chrome through Playwright, so they need Chrome
installed. `check-all.sh` wipes and reseeds the local store, and refuses to run
when `DATABASE_URL` points at a real database.
## The design, in one paragraph
Warm near-black, Inter, one rail. **White is the interface accent** — primary
buttons, the active rail item, the status pill — which leaves red to mean
exactly one thing: a second hand's pending claim on the text. Egyptian scribes
wrote the body in carbon black and went back over the roll in red ochre to mark
corrections and instructions for how to use it; the word *rubric* comes from it.
That is the whole idea. You write in ink, your agents write in red, and
accepting a proposal is the moment red becomes ink. No textures, no ornament,
and red is never used for decoration or for status.
## Upgrading from Papyrus
This project used to be called Papyrus. A running deployment needs no changes:
- Every `PAPYRUS_*` variable is still read when the `OPENBRAIN_*` one isn't set.
- Agents whose instructions say `papyrus_open` keep working — the old tool
names are aliases for the new ones.
- Existing `ppy_` tokens still authenticate.
- `pnpm db:migrate` carries the migration history over on its first run.
You will need to unlock once more, because the session cookie was renamed. On
a laptop, move the local data across: `mv .papyrus .openbrain`.
## Contributing
Issues and pull requests are welcome. [CONTRIBUTING.md](CONTRIBUTING.md) has the
setup, the checks a change has to pass, and the handful of rules that are bugs
rather than style when broken. Security reports go through
[SECURITY.md](SECURITY.md), not a public issue.
## License
[MIT](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues