> ## Documentation Index
> Fetch the complete documentation index at: https://docs.modusbrain.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Source Brains

> Combine multiple data sources in a single unified brain.

# Multi-source brains

**A single modusbrain database can hold multiple knowledge repos.** Each one
is a `source`: a logical brain-within-the-brain with its own slug
namespace, its own sync state, and its own federation policy. The rest
of this guide walks the three canonical scenarios.

## The three scenarios

### 1. Unified knowledge recall (wiki + gstack)

You have a personal wiki and a `gstack` checkout. Both belong to you,
both are knowledge you want your agent to recall across. When you ask
"what did I learn about X?" you want the best hit whether it lives in
the wiki or in a gstack plan.

```bash theme={null}
# Register the gstack source, federate so it joins cross-source search
modusbrain sources add gstack --path ~/.gstack --federated

# Pin the directory so `modusbrain sync` knows which source it's walking
cd ~/.gstack && modusbrain sources attach gstack

# Initial sync
modusbrain sync --source gstack

# Now `modusbrain search "retry budgets"` returns hits from BOTH wiki and
# gstack. Each result includes source_id so the agent can cite properly.
```

Result: wiki pages and gstack plans are separate (different source\_ids,
different slug namespaces) but share the search surface.

### 2. Purpose-separated brains (team-media + personal-list)

You run two completely different content pipelines on the same backend.
Team Media covers portfolio news and founder profiles. Shubham's List is
personal writing. You explicitly DON'T want them mixed in search — company
portfolio content leaking into essay searches is a bug, not a feature.

```bash theme={null}
# Two sources, both isolated (federated=false)
modusbrain sources add team-media --path ~/team-media --no-federated
modusbrain sources add personal-list --path ~/writing --no-federated

# Pin each checkout directory
(cd ~/team-media && modusbrain sources attach team-media)
(cd ~/writing && modusbrain sources attach personal-list)

# Sync each independently
modusbrain sync --source team-media
modusbrain sync --source personal-list
```

Result: searching from neither directory returns the `default` source
(your main brain). Searching from inside `~/team-media` returns only team-
media hits. Searching from inside `~/writing` returns only personal-list.
Federation is opt-in, not leaked.

To search across them explicitly on demand:

```bash theme={null}
modusbrain search "tech layoffs" --source team-media,personal-list
```

### 3. Mixed (wiki federated + sessions isolated)

Your main wiki is federated with a few trusted sources. Your session
transcripts (coming in v0.18) land in a separate isolated source so
they don't dominate every search result.

```bash theme={null}
# Federated sources
modusbrain sources add gstack --path ~/.gstack --federated

# Isolated source (future v0.18 — sessions use this shape today for ingest)
modusbrain sources add sessions --path ~/.claude/sessions --no-federated
```

## Resolution priority

When any command needs to pick a source, modusbrain walks this list (highest
first):

1. Explicit `--source &lt;id&gt;` flag.
2. `MODUSBRAIN_SOURCE` environment variable.
3. `.modusbrain-source` dotfile in CWD or any ancestor directory.
4. A registered source whose `local_path` contains the CWD (longest
   prefix wins for nested checkouts).
5. The brain-level default set via `modusbrain sources default &lt;id&gt;`.
6. The seeded `default` source.

So inside `~/.gstack/plans/` on a brain that pinned `gstack` to
`~/.gstack` via `.modusbrain-source`, `modusbrain put-page` implicitly writes to
the `gstack` source. Outside any registered directory with no env/dotfile
set, it writes to the default.

## Federation flag

Every source row stores `config.federated: boolean` in its JSONB config.

| Value                             | Meaning                                                                                     |
| --------------------------------- | ------------------------------------------------------------------------------------------- |
| `true`                            | Source participates in unqualified `modusbrain search "X"` results.                         |
| `false` (default for new sources) | Source only searched when explicitly named via `--source &lt;id&gt;` or qualified citation. |

The seeded `default` source is `federated=true` so pre-v0.17 brains
behave exactly as before — every page appears in search.

Flip later with `modusbrain sources federate &lt;id&gt;` / `unfederate &lt;id&gt;`.

## Commands

Full subcommand reference:

```
modusbrain sources add <id> --path <p> [--name <n>] [--federated|--no-federated]
                               Register a source. id: [a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?
modusbrain sources list [--json]   List all sources with page counts + federation state.
modusbrain sources remove <id> [--yes] [--dry-run] [--keep-storage]
                               Cascade-delete a source (pages, chunks, timeline).
modusbrain sources rename <id> <new-name>
                               Change display name only; id is immutable.
modusbrain sources default <id>    Set the brain-level default.
modusbrain sources attach <id>     Write .modusbrain-source in CWD (like kubectl context).
modusbrain sources detach          Remove .modusbrain-source from CWD.
modusbrain sources federate <id>
modusbrain sources unfederate <id>
```

## Citation format for agents

When agents receive multi-source results they MUST cite pages in
`[source-id:slug]` form. Example:

> You told me about the distillation protocol — see \[wiki:topics/ai]
> and \[gstack:plans/multi-repo] for where this came from.

The citation key is `sources.id` (immutable). Renaming a source via
`modusbrain sources rename` changes the display name only; existing
citations keep working.

## Writing to a specific source

```bash theme={null}
# Pass --source explicitly
modusbrain put-page topics/ai ... --source wiki

# Or rely on the dotfile / env / CWD match
cd ~/.gstack && modusbrain put-page plans/multi-repo ...
# → source auto-resolves to gstack
```

Reads span federated sources by default. Writes require a resolved
source (explicit, inferred, or default). The resolver never picks a
source silently when ambiguous — it errors with a clear fix.

## Durability: keep a brain repo in sync (auto-harden)

A long-lived agent that writes to a knowledge-wiki git repo needs three
things to never lose work: pull before it edits, push every write, and not
go stale while it sits idle. `modusbrain sources harden` installs all of that,
idempotently. The moment you add a brain repo with a token, it runs
automatically:

```bash theme={null}
# Clone + register a GitHub repo, then auto-harden it for durability.
# Use a fine-grained PAT scoped to just this repo.
modusbrain sources add wiki --url https://github.com/you/brain-wiki.git --pat-file ~/.secrets/wiki-pat
#   → clones, then installs: local auto-push hook, scripts/brain-commit-push.sh,
#     always-on durability rules in AGENTS.md/RESOLVER.md, a 30-min pull cron,
#     and a repo-scoped credential. Verifies push works before declaring done.

# Run the same audit on an existing source any time (idempotent):
modusbrain sources harden wiki --pat-file ~/.secrets/wiki-pat

# Pull on demand (the cron calls the --path form, which never opens the DB):
modusbrain sources pull wiki

# Remove the durability scaffolding (also runs automatically on `sources remove`):
modusbrain sources unharden wiki
```

What hardening guarantees:

* **Pull-first, conflict-safe.** Every pull is a divergence-safe rebase. A
  dirty working tree is skipped (your in-progress edits are never touched); a
  rebase conflict is aborted cleanly and flagged for attention, never left
  half-applied.
* **Push is never deferred.** `scripts/brain-commit-push.sh "&lt;msg&gt;" &lt;path&gt;`
  commits and pushes atomically and refuses to report success without a
  confirmed push. The post-commit hook is a best-effort background fallback;
  the helper is the guarantee.
* **No silent staleness.** A 30-minute background pull keeps an idle session
  current. It runs DB-free, so it never contends with a live brain for the
  PGLite single-writer lock.

Flags: `--no-cron` skips the scheduled pull, `--no-verify` skips the push
probe, `--dry-run` reports what would change, `--json` emits a machine
report, `--all` hardens every source with a remote (same-account only).
`--no-harden` on `sources add` opts out of auto-harden.

Security: the push automation is installed locally per machine (never
committed into the repo), the token is wired per-repo (an existing
credential helper is reused when present), and it never appears in the repo,
the remote URL, logs, or the JSON report. For a self-hosted git server
reachable only over a filesystem path, set `MODUSBRAIN_GIT_ALLOW_FILE_TRANSPORT=1`
(default is HTTPS-only).

## Upgrading an existing brain

`modusbrain upgrade` runs the v16 + v17 migrations automatically. Your
existing pages all move under `source_id='default'`. Behavior is
unchanged until you add a second source.

To add one:

```bash theme={null}
modusbrain sources add gstack --path ~/.gstack --federated
cd ~/.gstack && modusbrain sources attach gstack && modusbrain sync
```

Two commands. The existing default source is untouched.

## Not in v0.18.0

* Session transcript ingest (`.jsonl`, raised size cap, session
  PageType) — v0.18.
* Per-source retention/TTL (`modusbrain sources prune`) — v0.18.
* ACL enforcement via caller-identity — v0.17.1.
* `modusbrain sources import-from-github &lt;url&gt;` one-shot bootstrap — patch
  release after the core plumbing stabilizes.

All of these build on the `sources` primitive shipped here.
