Skip to main content

Architecture Overview

This document explains how Beads' three-layer architecture works: Git, JSONL, and SQLite.

The Three Layers​

Beads uses a layered architecture where each layer serves a specific purpose:

flowchart TD
subgraph GIT["πŸ—‚οΈ Layer 1: Git Repository"]
G[(".beads/*.jsonl<br/><i>Historical Source of Truth</i>")]
end

subgraph JSONL["πŸ“„ Layer 2: JSONL Files"]
J[("issues.jsonl<br/><i>Operational Source of Truth</i>")]
end

subgraph SQL["⚑ Layer 3: SQLite"]
D[("beads.db<br/><i>Fast Queries / Derived State</i>")]
end

G <-->|"bd sync"| J
J -->|"rebuild"| D
D -->|"append"| J

U((πŸ‘€ User)) -->|"bd create<br/>bd update"| D
D -->|"bd list<br/>bd show"| U

style GIT fill:#2d5a27,stroke:#4a9c3e,color:#fff
style JSONL fill:#1a4a6e,stroke:#3a8ac4,color:#fff
style SQL fill:#6b3a6b,stroke:#a45ea4,color:#fff
Historical vs Operational Truth

Git is the historical source of truthβ€”commits preserve the full history of your issues and can be recovered from any point in time.

JSONL is the operational source of truthβ€”when recovering from database corruption, Beads rebuilds SQLite from JSONL files, not directly from Git commits.

This layered model enables recovery: if SQLite is corrupted but JSONL is intact, run bd sync --import-only to rebuild. If JSONL is corrupted, recover it from Git history first.

Layer 1: Git Repository​

Git is the historical source of truth. All issue data lives in the repository alongside your code, with full history preserved in commits.

Why Git?

  • Issues travel with the code
  • No external service dependency
  • Full history via Git log (recover any point in time)
  • Works offline
  • Enables multi-machine and multi-agent workflows

Layer 2: JSONL Files​

JSONL (JSON Lines) files store issue data in an append-only format. This is the operational source of truthβ€”SQLite databases are rebuilt from JSONL.

Location: .beads/*.jsonl

Why JSONL?

  • Human-readable and inspectable
  • Git-mergeable (append-only reduces conflicts)
  • Portable across systems
  • Can be recovered from Git history
  • Recovery source: bd sync --import-only rebuilds SQLite from JSONL

Layer 3: SQLite Database​

SQLite provides fast local queries without network latency. This is derived stateβ€”it can always be rebuilt from JSONL.

Location: .beads/beads.db

Why SQLite?

  • Instant queries (no network)
  • Complex filtering and sorting
  • Derived from JSONL (always rebuildable)
  • Safe to delete and rebuild: rm .beads/beads.db* && bd sync --import-only

Data Flow​

Write Path​

User runs bd create
β†’ SQLite updated
β†’ JSONL appended
β†’ Git commit (on sync)

Read Path​

User runs bd list
β†’ SQLite queried
β†’ Results returned immediately

Sync Path​

User runs bd sync
β†’ Git pull
β†’ JSONL merged
β†’ SQLite rebuilt if needed
β†’ Git push

Sync Modes​

Beads provides specialized sync modes for different recovery scenarios:

Standard Sync​

bd sync

Normal bidirectional sync: pulls remote changes, merges JSONL, rebuilds SQLite if needed, pushes local changes.

Import-Only Mode​

bd sync --import-only

Rebuilds the SQLite database from JSONL without pushing changes. Use this when:

  • SQLite is corrupted or missing
  • Recovering from a fresh clone
  • Rebuilding after database migration issues

This is the safest recovery option when JSONL is intact.

Force Rebuild Mode​

bd sync --force-rebuild

Forces complete SQLite rebuild from JSONL, discarding any SQLite-only state. Use with caution:

  • More aggressive than --import-only
  • May lose any uncommitted database state
  • Recommended when standard sync fails repeatedly

Multi-Machine Sync Considerations​

When working across multiple machines or clones:

  1. Always sync before switching machines

    bd sync  # Push changes before leaving
  2. Pull before creating new issues

    bd sync  # Pull changes first on new machine
    bd create "New issue"
  3. Avoid parallel edits - If two machines create issues simultaneously without syncing, conflicts may occur

See Sync Failures Recovery for data loss prevention in multi-machine workflows (Pattern A5/C3).

The Daemon​

The Beads daemon (bd daemon) handles background synchronization:

  • Watches for file changes
  • Triggers sync on changes
  • Keeps SQLite in sync with JSONL
  • Manages lock files
tip

The daemon is optional but recommended for multi-agent workflows.

Running Without the Daemon​

For CI/CD pipelines, containers, and single-use scenarios, run commands without spawning a daemon:

bd --no-daemon create "CI-generated issue"
bd --no-daemon sync

When to use --no-daemon:

  • CI/CD pipelines (Jenkins, GitHub Actions)
  • Docker containers
  • Ephemeral environments
  • Scripts that should not leave background processes
  • Debugging daemon-related issues

Daemon in Multi-Clone Scenarios​

Race Conditions in Multi-Clone Workflows

When multiple git clones of the same repository run daemons simultaneously, race conditions can occur during push/pull operations. This is particularly common in:

  • Multi-agent AI workflows (multiple Claude/GPT instances)
  • Developer workstations with multiple checkouts
  • Worktree-based development workflows

Prevention:

  1. Use bd daemons killall before switching between clones
  2. Ensure only one clone's daemon is active at a time
  3. Consider --no-daemon mode for automated workflows

See Sync Failures Recovery for daemon race condition troubleshooting (Pattern B2).

Recovery Model​

The three-layer architecture makes recovery straightforward because each layer can rebuild from the one above it:

  1. Lost SQLite? β†’ Rebuild from JSONL: bd sync --import-only
  2. Lost JSONL? β†’ Recover from Git history: git checkout HEAD~1 -- .beads/issues.jsonl
  3. Conflicts? β†’ Git merge, then rebuild

Universal Recovery Sequence​

The following sequence demonstrates how the architecture enables quick recovery. For detailed procedures, see Recovery Runbooks.

This sequence resolves the majority of reported issues:

bd daemons killall           # Stop daemons (prevents race conditions)
git worktree prune # Clean orphaned worktrees
rm .beads/beads.db* # Remove potentially corrupted database
bd sync --import-only # Rebuild from JSONL source of truth
Never Use bd doctor --fix

Analysis of 54 GitHub issues revealed that bd doctor --fix frequently causes more damage than the original problem:

  • Deletes "circular" dependencies that are actually valid parent-child relationships
  • False positive detection removes legitimate issue links
  • Recovery after --fix is harder than recovery from the original issue

Safe alternatives:

  • bd doctor β€” Diagnostic only, no changes made
  • bd blocked β€” Check which issues are blocked and why
  • bd show <issue-id> β€” Inspect a specific issue's state

If bd doctor reports problems, investigate each one manually before taking any action.

See Recovery for specific procedures and Database Corruption Recovery for bd doctor --fix recovery (Pattern D4).

Design Decisions​

Why not just SQLite?​

SQLite alone doesn't travel with Git or merge well across branches. Binary database files create merge conflicts that are nearly impossible to resolve.

Why not just JSONL?​

JSONL is slow for complex queries. Scanning thousands of lines for filtering and sorting is inefficient. SQLite provides indexed lookups in milliseconds.

Why append-only JSONL?​

Append-only format minimizes Git merge conflicts. When two branches add issues, Git can cleanly merge by concatenating the additions. Edit operations append new records rather than modifying existing lines.

Why not a server?​

Beads is designed for offline-first, local-first development. No server means no downtime, no latency, no vendor lock-in, and full functionality on airplanes or in restricted networks.

Trade-offs​

BenefitTrade-off
Works offlineNo real-time collaboration
Git-native historyRequires Git knowledge
No server dependencyNo web UI or mobile app
Local-first speedManual sync required
Append-only mergingJSONL files grow over time

When NOT to use Beads​

Beads is not suitable for:

  • Large teams (10+) β€” Git-based sync doesn't scale well for high-frequency concurrent edits
  • Non-developers β€” Requires Git and command-line familiarity
  • Real-time collaboration β€” No live updates; requires explicit sync
  • Cross-repository tracking β€” Issues are scoped to a single repository
  • Rich media attachments β€” Designed for text-based issue tracking

For these use cases, consider GitHub Issues, Linear, or Jira.