---
name: beautiful-brain-self-host
description: Use when a user wants their own self-hosted Beautiful Brain instance ("self-host beautiful brain", "set up my own memory server", "install beautiful brain"). Drive the whole setup yourself — prerequisites, clone, configure, launch, verify, connect — and only ask the user when a choice genuinely needs their input.
---

# Self-host Beautiful Brain

Goal: take a machine from nothing to a verified, connected Beautiful Brain instance with as little user input as possible. You run the commands; the user watches. Ask at most three questions up front (install location, local-only vs exposed, embeddings), then proceed without further prompts unless something fails.

## 0. Decide the shape (ask once, defaults in parentheses)

1. **Where to install?** (`~/beautiful-brain`)
2. **Local-only or exposed to the internet?** (local-only — no auth needed; exposure requires API keys or OAuth, see step 6)
3. **Semantic embeddings?** (off to start — can be enabled later; `local` runs on-box, `gemini` needs a `GEMINI_API_KEY`)

If the user already answered any of these in their request, do not re-ask.

## 1. Prerequisites

Check, and install only what is missing (ask before installing system packages):

```sh
git --version
docker --version
docker compose version   # compose v2 required
```

Docker daemon must be running. Ports used by default: `3030` (app, bound to 127.0.0.1), `7474`/`7687` (Neo4j, bound to 127.0.0.1). If any port is taken, adjust `HOST_PORT` in `.env` instead of stopping.

## 2. Clone and configure

```sh
git clone https://gitlab.com/sparkletree/beautiful-brain.git
cd beautiful-brain/api
cp .env.example .env
```

Generate a real Neo4j password — never keep the example one:

```sh
NEO4J_PASSWORD=$(openssl rand -hex 24)
```

Write it into `.env` (`NEO4J_PASSWORD=...`). Everything else in `.env.example` is a safe default for local use: auth disabled, embeddings disabled, federation disabled.

## 3. Launch

```sh
docker compose up -d --build
```

First build takes a few minutes. Neo4j has a healthcheck; the app waits for it.

## 4. Verify — do not skip

```sh
curl -fsS http://127.0.0.1:3030/health          # → {"ok":true,...}
curl -fsS http://127.0.0.1:3030/ | head -c 200  # landing page HTML
```

Then confirm MCP answers:

```sh
curl -fsS -X POST http://127.0.0.1:3030/mcp \
  -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"setup-check","version":"0"}}}'
```

If anything fails: `docker compose logs app --tail 50` and fix before moving on.

## 5. Connect the user's agent

- **Claude Code / Claude Desktop:** `claude mcp add --transport http beautiful-brain http://127.0.0.1:3030/mcp`
- **Any Streamable-HTTP MCP client:** point it at `http://127.0.0.1:3030/mcp`.
- Install the agent skill so future sessions use the brain well:

```sh
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills/beautiful-brain"
curl -fsSL http://127.0.0.1:3030/skills/beautiful-brain/SKILL.md \
  -o "${CODEX_HOME:-$HOME/.codex}/skills/beautiful-brain/SKILL.md"
```

Smoke-test end to end: call `capture_memory` with a throwaway note, find it with `search_memory`, then archive it with `update_memory`.

## 6. Only if exposing publicly

Local-only installs stop at step 5. If the user chose exposure:

- Put it behind HTTPS (reverse proxy or a tunnel such as cloudflared).
- Enable auth in `.env` — simplest is a static key: set `MCP_API_KEY_SHA256_HASHES` to the SHA-256 of a key you generate for the user (`printf '%s' "$KEY" | shasum -a 256`). Store only the hash on the server; give the user the plaintext key once. OAuth (`OAUTH_ENABLED=true`, Clerk-compatible) is for multi-user setups — its URLs derive from the domain config below.
- Configure the domain — two env vars, everything else derives:

```sh
PUBLIC_ORIGIN=https://brain.example.com   # where this instance is reachable (https origin, no path)
CLERK_DOMAIN=clerk.example.com            # Clerk production frontend hostname (only if using Clerk OAuth)
```

  These derive the OAuth resource/audience/metadata URLs (`$PUBLIC_ORIGIN/mcp`), the issuer + JWKS (`https://$CLERK_DOMAIN`), the federation origin, and the CORS allowlist (`PUBLIC_ORIGIN` plus `https://app.<host>` are added automatically). The individual `OAUTH_*` URL overrides in `.env.example` still exist and always win, but are rarely needed. Add extra browser origins (e.g. the MCP inspector) via `MCP_ALLOWED_ORIGINS`.
- Route the domain to the instance: whatever fronts it (reverse proxy, tunnel) must send the apex/origin to the api port (`3030` by default) and, if the web dashboard runs, `app.<domain>` to its port (`3001`). cloudflared users: locally-managed tunnels take ingress from `/etc/cloudflared/config.yml` and need a `cloudflared` restart after edits — changes made in the Cloudflare dashboard do nothing for them.
- Clerk (if used): the production instance domain must match `CLERK_DOMAIN`; its CNAME records must be DNS-only (NOT proxied) in Cloudflare or TLS certificate issuance fails; point the component paths at the dashboard subdomain (`app.<domain>`); and update any Google/social OAuth client's redirect URI to `https://clerk.<domain>/v1/oauth_callback`.
- Optional collaboration/dashboard: `FEDERATION_ENABLED=true` (the public origin comes from `PUBLIC_ORIGIN`).

Restart after `.env` changes: `docker compose up -d`.

Moving to a new domain later = change the two vars, restart, update the proxy/DNS routes and the Clerk production domain. MCP clients re-authenticate once because the OAuth issuer changed.

## 7. Ongoing care (tell the user, don't do now)

- Data lives in docker volumes `neo4j-data` and `brain-media` — back them up.
- Upgrade = `git pull && docker compose up -d --build`.
- Embeddings can be enabled any time (`EMBEDDINGS_ENABLED=true`); after changing model/dimensions run the `backfill_memory_embeddings` tool until it reports nothing left to scan.

## Report back

Finish with a short summary: install path, endpoint URL, auth state, embeddings state, and the one-line connect command for the user's client of choice.
