docs/adr/0001-multi-remote-approach.md
Accepted
2026-04-07
Beads federation currently supports a single remote for push/pull sync:
federation.remote in config.yaml is a single URL string.FederationConfig has one Remote string field.DoltStore.remote is a single string, used by Push() and Pull().bd dolt push / bd dolt pull have no --remote argument.Dolt natively supports multiple remotes (like git): dolt remote add backup az://...
and CALL DOLT_PUSH('backup', 'main') work out of the box. The gap is in the beads
layer, not in Dolt itself.
A spike (bd-qky) investigated four approaches. A council review produced 49 findings with a "Request Changes" verdict, leading to this phased decision.
Phased implementation: start with Approach C (Dolt-native --remote flag) as a
tracer bullet, then evolve to Approach A (config-managed additional remotes).
Expose Dolt's native multi-remote capability through the beads CLI:
--remote <name> flag to bd dolt push.bd dolt remote add <name> <url>.origin). Pulling from non-primary
remotes is not supported — mirrors are push-only.AZURE_STORAGE_ACCOUNT, AWS creds, etc. before invoking).This validates the multi-remote workflow with minimal code and risk.
Add config-managed additional remotes:
federation.remote as the primary (backwards compatible).federation.additional-remotes as an ordered list of named remotes:
federation:
remote: "dolthub://org/beads" # primary, Dolt remote name: "origin"
additional-remotes:
- name: backup
url: "az://account.blob.core.windows.net/container/path"
- name: archive
url: "gs://bucket/path"
origin (matching
existing drift/apply behavior). Additional remotes use their name field
as the Dolt remote name.Keep federation.remote as primary, add federation.additional-remotes map.
Pros: Backwards compatible. Clear primary vs. backup distinction. Config-drift manageable via existing drift/apply infrastructure. Extensible object values support future per-remote credential configuration. Ordered list guarantees deterministic push sequence.
Cons: Two config patterns to maintain (remote + additional-remotes). Requires
SyncOrchestrator for coordination. More code than Phase 1.
Replace federation.remote with a list of remotes, each with name/url/role
(primary/backup/archive).
Rejected because:
config.yaml would need migration.
Migration risk is high for a tool used in CI pipelines and team workflows.--remote flag — selected as first step (Phase 1)Expose Dolt's native multi-remote via a CLI flag on bd dolt push/bd dolt pull.
Pros: Minimal code changes. Leverages Dolt's existing multi-remote support. Fast feedback loop — validates workflow assumptions before investing in config management.
Cons: Manual remote management (no config-driven setup). Drift/apply cannot manage additional remotes. No orchestrated multi-push.
Post-push hook triggers additional pushes to backup remotes.
Rejected because:
The primary remote (federation.remote) is always authoritative for pulls.
Additional remotes are push-only mirrors.
Rationale:
Disaster recovery: If the primary remote is permanently lost, an operator
manually promotes a mirror by updating federation.remote in config.yaml
to point to the mirror URL. This is an explicit, auditable action — not
automatic failover.
origin) first, then additional remotes in
list order. This gives clear error semantics — primary success is the
minimum bar.bd dolt push --remote backup). The
exit code reflects primary success (0), with diagnostic output for
backup failures. A future --strict mode could fail on any mirror
push failure for CI pipelines that require confirmed redundancy.bd dolt push (no --remote flag)
pushes to primary and all configured additional remotes. The --remote
flag targets a single remote.Phase 1 relies on ambient environment variables — the user sets the appropriate
credentials before invoking bd dolt push --remote <name>. This matches how
Dolt itself handles credentials.
Phase 2 may introduce per-remote credential configuration within the
additional-remotes object, but this is not yet decided. The extensible object
format (url + future fields) keeps this door open.
The existing shouldUseCLIForCloudAuth() check — which tests for ANY cloud
environment variable — will need per-remote refinement in Phase 2 to route
credentials correctly per transport protocol.
A dedicated SyncOrchestrator component owns multi-remote coordination:
DoltStore focused on single-remote
operations.Routing subsets of issues to specific remotes based on metadata (e.g., push only priority-0 issues to a compliance remote) requires a fundamentally different architecture — application-level filtering at the Dolt row/branch level rather than remote-level push. This is descoped to a separate future spike.
No automatic primary-to-mirror promotion. Failover is an explicit operator action (see "Pull authority" above).
Phase 2 uses sequential push for simplicity and clear error ordering. Parallel push is a potential future optimization but adds complexity to error aggregation and credential isolation.
federation.remote configs are unchanged.
No migration required.bd dolt remote add and manage
credentials via environment variables. No config-driven setup.dolt remote add, CALL DOLT_PUSH('remote', 'branch')internal/config/config.go (FederationConfig)internal/storage/dolt/store.go (DoltStore.Push, DoltStore.Pull)