---
version: 0.18.0
feature_pitch:
  headline: "Multi-source brains: one DB, many repos. Federated and isolated sources coexist."
  description: |
    v0.17.0 introduces sources as a first-class primitive. A single
    gbrain backend can now hold multiple knowledge repos (wiki, gstack,
    yc-media, garrys-list, etc.) with clean scoping. Every page, file,
    and ingest_log row is scoped to a `sources(id)` row. Slugs are
    unique PER source, not globally — so two sources can both have
    `topics/ai` and they're different pages.

    Per-source federation controls whether a source participates in
    unqualified default search. `federated=true` (the default source
    post-upgrade) joins the cross-source recall pool. `federated=false`
    is isolation — only searched when explicitly named via `--source`.
    This supports both "unified knowledge brain" (wiki + gstack, both
    federated) and "purpose-separated brains" (yc-media + garrys-list,
    both isolated) at the same time.

    Per-directory default via `.gbrain-source` dotfile walk-up +
    `GBRAIN_SOURCE` env var. Matches how kubectl / terraform / git
    scope context. `cd ~/yc-media && gbrain query "X"` just works.
  recipe: docs/guides/multi-source-brains.md
  tiers: null
---

# v0.17.0 Migration: Multi-source brains

**Audience: host agents reading this after `gbrain apply-migrations`
has run. v0.17.0 installs a schema primitive for multi-source and
exposes a `sources` CLI subcommand. Existing single-source brains
keep working unchanged — they live under a seeded `default` source
that preserves all prior behavior.**

## Mechanical migration: automatic, no action required

`gbrain upgrade` chains to `gbrain apply-migrations --yes`, which
runs:

- **migration v16** — creates the `sources` table, seeds `default`
  with `{"federated": true}` config, inherits your pre-v0.17
  `sync.repo_path` and `sync.last_commit` into the default row.
- **migration v17** — adds `pages.source_id TEXT NOT NULL DEFAULT
  'default' REFERENCES sources(id)`. Swaps the global `UNIQUE(slug)`
  constraint for composite `UNIQUE(source_id, slug)`. Engine
  upserts simultaneously re-target `ON CONFLICT (source_id, slug)`
  so the constraint swap and the write path land atomically.

Both migrations are idempotent. Safe to re-run.

Later point releases (v0.17.1 and v0.18.0) will layer:
- v0.17.1: ACL enforcement via a caller-identity primitive (the
  JSONB slot for `access_policy` ships now; enforcement waits for
  identity to be designed).
- v0.18.0: Session ingest (`.jsonl` transcripts, raised size cap,
  session PageType) AND per-source retention/TTL at the same time.

## What's new for agents

### `sources` CLI subcommand

```
gbrain sources add <id> --path <p> [--name <n>] [--federated|--no-federated]
gbrain sources list [--json]
gbrain sources remove <id> [--yes] [--dry-run] [--keep-storage]
gbrain sources rename <id> <new-display-name>
gbrain sources default <id>
gbrain sources attach <id>    # write .gbrain-source in CWD
gbrain sources detach         # remove .gbrain-source
gbrain sources federate <id>
gbrain sources unfederate <id>
```

Source id rules: `[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?` — start + end
with alnum, optional interior hyphens, max 32 chars. Immutable after
creation (rename only changes the display name). Used as the stable
citation key in `[source:slug]` references.

### Per-directory default

Running `gbrain sources attach gstack` inside `~/.gstack/` writes a
`.gbrain-source` file containing the single word `gstack`. Any
gbrain command run from that directory (or any subdirectory) auto-
selects `gstack` as the default source. `gbrain sources detach`
removes the dotfile.

Resolution priority for the source a command targets:

1. Explicit `--source <id>` flag.
2. `GBRAIN_SOURCE` env var.
3. `.gbrain-source` dotfile in CWD or any ancestor.
4. Registered source whose `local_path` contains CWD (longest
   prefix wins — nested `~/gstack` + `~/gstack/plans` resolves to
   `plans` when deeper).
5. Brain-level default set via `gbrain sources default <id>`.
6. Literal `default` (backward-compat fallback).

### Federation semantics

- `federated=true` (only the `default` source has this out of the
  box, by migration): appears in unqualified `gbrain search "X"`
  results.
- `federated=false` (new sources default to this): only appears
  when `--source <id>` is passed.

Interactive `gbrain sources add` prompts for federation; non-
interactive uses `--federated` / `--no-federated`. Flip later with
`gbrain sources federate <id>` / `unfederate <id>`.

### Citation contract (for agents)

When agents get multi-source search 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.

Citations are keyed on `sources.id` (immutable), never
`sources.name` (mutable display). If a user renames a source via
`gbrain sources rename`, existing citations stay valid.

## What's NOT in v0.17.0 yet

The following land in later Steps of this release cycle (already
on the branch but gated until the matching code ships):

- `ingest_log.source_id` — lands with Step 5 sync rewrite.
- `links.resolution_type` + qualified `[[source:slug]]` wikilink
  parsing — lands with Step 4 link-extraction rewrite.
- `files.page_slug → page_id` FK rewrite + `file_migration_ledger`
  + storage object prefixing — lands with Step 7 storage backfill.
- Source-aware search dedup — lands with Step 3.
- `gbrain sources import-from-github <url>` — deferred to a patch
  release after the plumbing stabilizes.

Existing callers continue to work against the `default` source. No
agent behavioral change is required; the new capabilities are
opt-in via the new `sources` CLI surface.

## Host-repo actions

None required. If your host agent manages the brain via the
standard `gbrain sync` flow, it continues to target the default
source and sees no behavioral change. To start using multi-source:

```
# Register a new source
gbrain sources add gstack --path ~/.gstack --no-federated

# Pin that directory to it so no --source flag is needed
cd ~/.gstack
gbrain sources attach gstack

# Ingest
gbrain sync --source gstack
```

Or see `docs/guides/multi-source-brains.md` for the full three
canonical scenarios (unified, purpose-separated, mixed).
