Configuration record · 9 Aug 2026
Stock gbrain assumes a local repo, an Anthropic API key, and one machine. This brain lives in iCloud across four devices, bills LLM work to a Claude subscription, and answers WhatsApp. Every difference below is a decision with a reason — and three of them were forced by bugs rather than chosen.
Ten dimensions. Six are deliberate design choices; three were forced by defects in stock gbrain; one is still unresolved.
| Dimension | Stock gbrain | This brain |
|---|---|---|
| Install | global bun install -g | source clone + local branch |
| Database | PGLite, embedded | Homebrew Postgres 17 + pgvector |
| Vault | a local git repo | iCloud Drive, 4 devices |
| Git dir | .git beside the vault | --separate-git-dir outside iCloud |
| Chat model | Anthropic API key | claude-cli subscription |
| Embeddings | OpenAI text-embedding-3-small | Qwen3-8B @ 1024 (MRL) via OpenRouter |
| Reranker | off | Cohere rerank-4-fast |
| Schema pack | gbrain-base | gbrain-everything |
| Processes | one autopilot, worker inside it | 3 services, worker split out |
| Interfaces | stdio MCP | stdio + scoped HTTP over Tailscale |
Stock installs an opaque global copy. We run from source so local changes survive upgrades.
gbrain upgrade replaces a global install wholesale, taking any local edit
with it. From a source clone it instead runs git pull --ff-only — which fails on a branch
carrying local commits. That failure is the desired behaviour: it is the signal to rebase, not a bug to fix.
| Stock | Here | |
|---|---|---|
| Method | bun install -g github:garrytan/gbrain | git clone → ~/src/gbrain → bun link |
| Local changes | lost on upgrade | two commits on branch local |
| Update | gbrain upgrade | git fetch origin && git rebase origin/master |
| Self-upgrade | opt-in auto | notify only — never swaps the binary unattended |
Not a preference — PGLite does not run on this OS.
libpq had been force-linked and blocked the link step, and the resulting
failed postinstall left the data directory uninitialised.
| Stock | Here | |
|---|---|---|
| Engine | PGLite (zero-config, <1000 files) | PostgreSQL 17.10 + pgvector 0.8.6 |
| Tuning | n/a | shared_buffers 2GB · work_mem 32MB · maintenance_work_mem 512MB |
| Schema | same migrations | version 125, RLS on 61/61 tables |
The single most consequential difference, and the source of the hardest bug.
Stock gbrain assumes one machine and a local repo. The requirement here was that Noto stays a reader and writer across MacBook Pro, Air, iPhone and iPad — so the vault must be in iCloud, and everything else follows from that.
| Stock | Here | |
|---|---|---|
| Location | any local directory | ~/Library/Mobile Documents/…/Brain |
| Git | .git inside the vault | --separate-git-dir=~/.gbrain/brain.git — iCloud never sees a .git to churn on |
| Migration | n/a | 930 files copied + 66 diary pages split from daily notes; original Noto vault untouched as a permanent archive |
| Sources | named sources per repo | one default source; Noto deliberately not registered, to avoid duplication |
~/Library/Mobile Documents is TCC-protected.
A launchd agent has no permission grant and no TTY to prompt for one, so every read returns
Operation not permitted — silently. Background services could not read the vault at all, while the
same commands run by hand worked perfectly, because the terminal already holds Full Disk Access. This is why
the vault choice and the process topology are not independent decisions.Zero Anthropic API spend; embeddings and reranking on one OpenRouter key.
| Touchpoint | Stock default | Here |
|---|---|---|
| Chat / all tiers | anthropic:* — needs an API key | claude-cli:claude-sonnet-4-6 — subscription-billed |
| Embeddings | openai:text-embedding-3-small @1536 | openrouter:qwen/qwen3-embedding-8b @1024 |
| Reranker | not configured | openrouter:cohere/rerank-4-fast |
| Atom extraction | anthropic:claude-haiku-4-5 — hardcoded | claude-cli:claude-haiku-4-5-20251001 |
extract_atoms does not fall through
models.default like every other dream task — it carries its own hardcoded Anthropic default. So
gbrain models showed every touchpoint correctly routed to claude-cli while this one
silently pointed at a provider with no key, failing every batch.The pack decides not just page types, but which cycle phases run at all.
The default gbrain-base family does not declare the extract_atoms or
synthesize_concepts phases, so they are skipped on every cycle — silently, with no error.
Only gbrain-creator and gbrain-everything declare them.
Stock (base-v2) | Here (everything) | |
|---|---|---|
| Page types | 17 | 30 |
| Atom / concept phases | not declared → skipped | declared → running |
| Calibration domains | base only | + deal_success, architecture_calls |
gbrain-creator extends gbrain-base (v1),
where media/ is not extractable — a naive switch could have re-typed 816 pages into a
non-extractable type and produced zero atoms anyway. It didn't, because all 816 carry type: source
explicitly in frontmatter from Noto's capture pipeline, and frontmatter beats path inference. Verified after:
1,017 pages, 100% typed, identical distribution.schema_pack docstring claims the DB
config plane outranks ~/.gbrain/config.json. In practice gbrain schema active reports
Source: home-config — the file wins. Trust that command, not the documented order.Stock runs one process. Here it is three, and the split is load-bearing.
| Service | Role | Note |
|---|---|---|
com.gbrain.autopilot | dispatcher only — --no-worker | ticks every 150s |
com.gbrain.worker | executes the job queue | own service, own log file, --max-rss 8192 |
com.gbrain.serve-http | HTTP MCP for remote clients | bound to Tailscale only |
write(). And
--max-rss is ignored when passed to autopilot (it hardcodes
clamp(0.5×RAM, 4096, 16384) = 16GB here) but honoured on a standalone worker — so the split also
buys an 8GB watchdog, which matters on a machine that froze at 31GB in August.~/.zshenv and ~/.zshrc
are read-only nix/home-manager symlinks, so API keys cannot live there — each wrapper sources
~/.config/gbrain/credentials.env directly. And launchd's PATH omits
/opt/homebrew/bin, so claude resolved to not found and every chat-backed job
failed; both wrappers now pin GBRAIN_CLAUDE_CLI_BIN absolutely and fix PATH.gbrain autopilot --install regenerates its wrapper and silently drops
both fixes. The durable answer is to move the keys and the PATH entry into the home-manager config.Stock exposes stdio to one agent. Here, three consumers with different privileges.
| Client | Transport | Scope |
|---|---|---|
| Claude Code | stdio — gbrain serve | full 106 ops |
| Hermes (WhatsApp) | HTTP /mcp over Tailscale | read write, 11 of 96 tools |
| MacBook Air | HTTP /mcp over Tailscale | read only |
delete_page and hard purges — with no
attribution. Over HTTP, scope is enforced at the op layer and every call is logged: a read-only
token gets insufficient_scope: Operation put_page requires 'write' scope./admin/api/api-keys for a scoped client. It destructures only
name; a scopes field is silently ignored, and the auth layer grants such tokens
full admin ("grandfather in"). Scoping exists only on the OAuth client-credentials path. A key issued
as read-only wrote a page successfully during testing.delete_page, forget_fact and every admin tool are excluded
— the scope permits writes, but an agent on a phone should not be able to destroy pages.One register entry, deliberately small, carried as separable commits.
| Hunk | File | Kind & upstream fate |
|---|---|---|
| A + B | chronicle/config.ts, chronicle/backstop.ts | Preference — lets diary pages produce timeline events. Config-gated, default off, so stock behaviour is preserved. Likely never upstreamed. |
| C | ai/dims.ts | Upstream bug. The Qwen3 Matryoshka branch matched only Ollama's colon-tag naming, so on OpenRouter's dash form the dimensions parameter was never sent. Send as a PR; delete when merged. |
rerank-4-pro and an NVIDIA option. The
added block was a duplicate object key that JS silently shadowed, so it was dead code the whole time. The false
negative came from grepping for rerank:, which is not a substring of reranker:.| Item | State | Next |
|---|---|---|
| Full Disk Access | blocking background services cannot read the vault | grant FDA to ~/.bun/bin/bun, then verify the atom count climbs past 19 |
| Optimize Mac Storage | on — 0 files evicted today | turn off; iCloud could otherwise hollow the vault into placeholders |
| Atom backlog | 19 of 687 pages | drains once the worker can read the vault |
| Takes | 190 proposals, 0 promoted | opt in via takes.bootstrap_enabled |
| Facts | 0 — and correct | facts live in ## Facts fences on entity pages; none exist until entity extraction runs |
| Git remote | none | vault has no off-machine backup yet |
| Noto repoint | still on the old vault | Eugene's call on timing |
embedding_model = dashscope:text-embedding-v3, superseded by config.json. It has no
effect — but it contradicts the live setting, and stale config is exactly what turns a ten-minute debug into an
hour-long one.