---
version: v0.40.5.0
feature_pitch: Federated Sync v2 — parallel source sync, push-triggered webhooks, async embedding, per-source health dashboard
target_files:
  - sources.config (per-source webhook_secret, github_repo, tracked_branch, priority)
  - sources.config.federated (auto-embed-backfill on flip)
manual_steps: required for users with multiple federated sources
---

# v0.40.5.0 — Federated Sync v2

GBrain now treats every federated source as an independent unit. Cross-source sync runs in parallel, GitHub pushes can trigger sync within seconds, embedding decouples from the sync pipeline so onboarding doesn't block, and `gbrain sources status` shows per-source health.

## What the orchestrator did automatically

`gbrain apply-migrations --yes` (run by `gbrain upgrade`) handled:

1. **Migration v92** — partial expression index on `sources.config->>'github_repo'` for fast webhook source-lookup
2. **Schema bootstrap probe** — legacy brains pick up the same index via `initSchema()`
3. **Per-source sync lock** — `SYNC_LOCK_ID` constant now resolves to `gbrain-sync:default`; per-source sync acquires `gbrain-sync:<source_id>`
4. **Phantom-redirect lock parity** — phantom-redirect pass uses the same per-source key so cross-source isolation works the same way
5. **Facts backstop source-scoping fix** — `performSync` now passes sourceId to `engine.getPage` (previously could mis-attribute facts to wrong source on slug collision)

## What you should do manually

Run these steps once per machine after `gbrain upgrade`:

### 1. Verify the new surfaces work

```bash
# Per-source health dashboard
gbrain sources status

# Doctor should surface federation_health
gbrain doctor

# Sync trigger CLI smoke
gbrain sync trigger --help
```

### 2. Set up webhook-driven sync per source

For each federated source whose repo lives on GitHub, register a webhook:

```bash
gbrain sources webhook set <source-id> --github-repo <owner>/<repo>
# Save the printed secret — you only see it once
```

Then in the GitHub repo settings → Webhooks → Add webhook:

- **Payload URL:** `<your gbrain serve --http URL>/webhooks/github`
- **Content type:** `application/json`
- **Secret:** (the secret printed by `webhook set`)
- **Events:** Just the push event
- **Active:** checked

To rotate later: `gbrain sources webhook rotate <source-id>`. The new secret invalidates the old one immediately; update the GitHub webhook config to match.

### 3. (Optional) Set source priority

Per-source priority drives autopilot dispatch order. The default brain typically wants `high`; archival sources can use `low`:

```bash
# Hand-edit sources.config via SQL or via a future `gbrain sources config set` command
# In v0.40.5 set via direct SQL:
gbrain exec "UPDATE sources SET config = jsonb_set(config, '{priority}', '\"high\"') WHERE id = 'default'"
gbrain exec "UPDATE sources SET config = jsonb_set(config, '{priority}', '\"low\"') WHERE id = 'media-corpus'"
```

Recognized values: `high` (priority -10), `normal` (0), `low` (5). Unknown values fall back to `normal` with a once-per-source stderr warning.

### 4. (Optional) Tune embed-backfill budget caps

Default caps: $10/job, $25/source/24h. Override via DB config:

```bash
gbrain config set embed.backfill_max_usd 20            # raise per-job cap
gbrain config set embed.backfill_max_usd_per_source_24h 100  # raise 24h cap
gbrain config set embed.backfill_cooldown_min 30       # raise cooldown window
```

### 5. (Optional) Opt out of parallel sync entirely

If something breaks and you need v0.39 behavior immediately:

```bash
gbrain config set sync.federated_v2 false
gbrain jobs supervisor restart
```

Per-source lock + migration v92 stay on regardless (those are correctness fixes, not features).

## Verify the outcome

After completing the steps above:

1. **Parallel sync works:** `gbrain sync --all` should complete noticeably faster than before. If multiple sources used to take minutes-to-hours, you should see them run concurrently.
2. **Webhook fires:** `git push` to a webhook-configured source should produce a `[dispatch]` line in autopilot logs (or `gbrain jobs list` should show a fresh `sync` job within seconds).
3. **Dashboard is accurate:** `gbrain sources status` should match the numbers from `gbrain stats` per source.
4. **No regressions:** Existing `gbrain sync --source <id>` still works the same way; `gbrain sync` with no flags still syncs the default source.

## If something goes wrong

`gbrain doctor` is your first stop:

- `federation_health: warn` — surfaces lag, embed coverage, or queue depth issues per source with paste-ready remediation commands
- `federation_health: fail` — surfaces critical lag (>24h) or embed coverage (<50% with >1000 chunks)

For partial states or unexpected errors, file an issue at https://github.com/garrytan/gbrain/issues with:
- Output of `gbrain doctor --json`
- Output of `gbrain sources status --json`
- Contents of `~/.gbrain/upgrade-errors.jsonl` if present
- Which step broke
