BranchBase Technical Architecture 🏗️
This document outlines the internal architecture, design principles, and lifecycle flow of BranchBase.
1. High-Level System Architecture
BranchBase sits between the developer's Git workflow, their application code, and the underlying database instance (typically running in Docker or natively).
graph TD
subgraph Developer Workspace
Git[Git CLI / IDE] -->|checkout / switch| GitHook[BranchBase Git Hook\n.git/hooks/post-checkout]
App[Application Backend\ne.g., Next.js, Rails, FastAPI] -->|Queries to Port 5432| Proxy[BranchBase Transparent Proxy]
end
subgraph BranchBase Core
GitHook -->|Trigger event| CoreDaemon[BranchBase Core Engine]
Proxy -->|Query Router| CoreDaemon
CoreDaemon -->|Branch Resolution| GitResolver[Git Head Resolver]
CoreDaemon -->|Snapshot / Clone| DBDriver[Database Engine Driver]
end
subgraph Database Layer [Docker / Native]
DBDriver -->|CREATE DATABASE TEMPLATE| PG[(PostgreSQL)]
DBDriver -->|Reflink / CoW Copy| SQLite[(SQLite)]
DBDriver -->|Volume / DB Clone| MySQL[(MySQL)]
end
2. Core Components
2.1 The Git Context Resolver (internal/git)
- Responsibility: Determines active, local, and merged branches in the repository without invoking uncontrolled subshells.
- Mechanism:
ResolveCurrentBranch: Reads.git/HEAD(or worktreegitdir) directly to resolve active branch in sub-millisecond time.ResolveLocalBranches: Queriesgit for-each-ref --format=%(refname:short) refs/headswith automatic fallback to filesystem inspection of.git/refs/heads/and.git/packed-refs(retaining full branch awareness post-git gc).ResolveMergedBranches: Queriesgit branch --merged <defaultBranch>using explicit argument vectors to identify safe deletion candidates.- Sanitizes branch names for database naming compatibility (
feature/stripe-v2$\rightarrow$feature_stripe_v2).
2.2 The Database Driver Interface (internal/driver)
Every supported database implements a standard Go interface:
type Driver interface {
// Name returns the driver identifier (e.g., "postgres", "sqlite")
Name() string
// Ping verifies connectivity to the underlying database engine
Ping(ctx context.Context) error
// BranchExists checks if a database for the given branch already exists
BranchExists(ctx context.Context, branchName string) (bool, error)
// CreateBranch clones sourceBranch into targetBranch
CreateBranch(ctx context.Context, sourceBranch, targetBranch string) error
// DeleteBranch tears down the ephemeral database
DeleteBranch(ctx context.Context, branchName string) error
// ListBranches returns all databases managed by BranchBase
ListBranches(ctx context.Context) ([]BranchInfo, error)
// Close releases database connections and engine pools
Close() error
}
PostgreSQL Implementation:
Postgres natively supports instant database cloning via the TEMPLATE directive with lib/pq connection management:
-- Step 1: Disconnect any active connections to template (if needed)
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE datname = 'myapp_dev_main' AND pid <> pg_backend_pid();
-- Step 2: Instant copy-on-write clone
CREATE DATABASE "myapp_dev_feature_billing" TEMPLATE "myapp_dev_main";
42P04 (duplicate_database).
- Connection Pooling: Uses standard
database/sqlconnection pooling (25open /5idle connections by default). For short-lived operations such asbranchbase hook-trigger, a lightweight single-connection profile (1open /1idle connection) is used to minimize runtime allocation overhead while avoiding TCP re-handshakes across sequential queries.
SQLite Implementation:
For SQLite, BranchBase utilizes filesystem-level Copy-on-Write (CoW) snapshots with .db-wal and .db-shm replication:
* macOS (APFS): Native clonefile(2) syscall for instant Copy-on-Write snapshots, with chunked copy fallback on unsupported filesystems.
* Linux (Btrfs / XFS): ioctl(FICLONE) reflink copying.
* Windows & Fallback: Buffered stream copy with full file sync.
MySQL / MariaDB Implementation:
MySQL does not support native TEMPLATE cloning. BranchBase implements schema-level replication:
1. Schema Creation: CREATE DATABASE <target> with backtick-quoted identifiers (QuoteIdentifier).
2. Table Cloning: Iterates information_schema.TABLES and executes CREATE TABLE <target>.<table> LIKE <source>.<table> for each table.
3. Data Replication: INSERT INTO <target>.<table> SELECT * FROM <source>.<table> to copy rows.
4. Storage Metrics: Queries information_schema.TABLES for DATA_LENGTH + INDEX_LENGTH to report branch database sizes.
- Connection Pooling: Uses standard
database/sqlconnection pooling (25open /5idle connections by default), matching the PostgreSQL driver profile.
2.3 The Transparent TCP Proxy (internal/proxy)
To ensure developers never have to touch .env or restart their dev servers when switching branches:
- The proxy listens on the standard port (e.g.,
5432for Postgres). - The real database container is remapped to an internal port (e.g.,
5433or a UNIX domain socket). - On-Demand (JIT) Branch Provisioning:
- If incoming connection targets an unprovisioned branch,
Server.ensureBranchExists()provisions the database on the fly from the default branch. - Guarded by a refcounted
keyedMutexusing double-checked locking: fast-path check avoids locking for existing databases; slow-path lock serializes concurrent incoming connections for the same missing branch. - Strictly validates and propagates branch existence errors on both fast and slow paths, preventing accidental database overwrites.
- Allocates dedicated 5-second timeout contexts once the lock is acquired, ensuring provisioning operations never time out prematurely under high lock contention.
- Wire-Protocol Rewriting & Error Reporting:
- Intercepts PostgreSQL
StartupMessage, rewrites database parameter to branch database (myapp_dev_feature_billing), responds to SSL negotiation, and streams bidirectionally with zero overhead. - Fail-Closed Invariant: If database rewriting fails (e.g. identifier exceeds 63 bytes or invalid characters), the connection is immediately aborted rather than forwarded to the default database.
- Wire Error Diagnostic: If JIT provisioning, backend connection, or database rewriting fails, the proxy builds and writes a standard PostgreSQL
ErrorResponse('E') packet (with severityFATAL, SQLSTATE code, and descriptive error message) before closing the client socket, ensuring developers receive clear diagnostics in CLI tools (psql) and ORMs (Prisma,dbt). - TLS/SSL Client Negotiation:
- Supports clients connecting with
sslmode=requireby generating self-signed ECDSA development certificates at startup. - Transparently upgrades client connections to TLS when the PostgreSQL
SSLRequestpacket is received. - UNIX Domain Socket Support:
- Both the listener and backend target can be configured as UNIX domain sockets (e.g.,
/tmp/.s.PGSQL.5432), eliminating TCP overhead for local connections. - Graceful Connection Draining:
- On
Stop(), the proxy tracks all active connections (activeConns map[net.Conn]struct{}) and waits up todrainTimeoutfor in-flight queries to complete before force-closing remaining connections.
3. The Lifecycle of a Branch Switch
sequenceDiagram
autonumber
actor Dev as Developer
participant Git as Git CLI
participant Hook as post-checkout Hook
participant BB as BranchBase Daemon
participant DB as Postgres Instance
participant Proxy as BranchBase Proxy
Dev->>Git: git checkout -b feature/auth
Git->>Hook: Trigger post-checkout(old_head, new_head, 1)
Hook->>BB: Notify branch_switch("main", "feature/auth")
BB->>DB: Check if db_feature_auth exists
alt Database does not exist
BB->>DB: CREATE DATABASE db_feature_auth TEMPLATE db_main;
DB-->>BB: OK (Instant)
end
BB->>Proxy: Update active_branch_routing("feature/auth")
Proxy-->>Dev: Ready!
Note over Dev,Proxy: App sends query: "SELECT * FROM users;"
Proxy->>DB: Forwarded to db_feature_auth transparently
4. Branch Cleanup & Pruning
Over time, working on dozens of feature branches can accumulate disk space.
- Command:
branchbase prune [--dry-run] [--force] - Algorithm:
- Inspects active, local, and merged Git branches via
git.ResolveCurrentBranch(),git.ResolveMergedBranches(), andgit.ResolveLocalBranches(). Aborts immediately if active branch resolution fails to safeguard the active database. - Identifies all databases matching the pattern
<base_db>_<branch>, excluding protected base databases and the currently active branch. - If
--dry-runis provided, previews candidates and freed disk space without deleting. - Prompts interactive confirmation (
[y/N]) before deletion unless--force/-fis specified. - Safely deletes ephemeral databases, freeing allocated storage.
5. Security & Isolation Guarantees
- Zero Cloud Dependency: BranchBase runs strictly on
localhostor via private UNIX domain sockets. No data or telemetry leaves the machine. - Non-Destructive: BranchBase never modifies or drops the
base_database(e.g.,main). Default branches are marked as protected by default. - Ephemeral Workspaces: Can be integrated into Docker Compose workflows via named volumes.