Skip to main content

How It Works

Graft operates on database data files at the filesystem level. It never needs to understand SQL, table structures, or database internals.

The Core Idea

Database engines store data in a structured directory on disk. For Postgres, this is PGDATA at /var/lib/postgresql/data/. Graft copies this directory faithfully, treating the entire state as a single unit that can be snapshotted, branched, and restored.

Storage Architecture

Host Filesystem

Branch data is stored on the host under ~/.graft/<project>/:

Content-Addressable Storage

When you run graft commit, Graft:
  1. Stops the container — Postgres must be at rest for a consistent snapshot
  2. Walks the branch directory — Every file is hashed with BLAKE3
  3. Stores blobs — Each file goes into objects/<prefix2>/<hash62> keyed by its BLAKE3 digest
  4. Builds a Merkle tree — Sorted leaves, pair-hashed up to a single root
  5. Inserts into SQLite — Commit record + tree entries + ref update
The object pool is immutable and deduplicated by content hash. Identical files (unchanged across commits) are stored exactly once.

Merkle DAG

Each commit records:
  • A 7-character BLAKE3 commit hash
  • The parent commit hash (forming a chain)
  • The Merkle tree root (cryptographic fingerprint of every file)
  • A verified flag
  • A message and timestamp

Container Lifecycle

Bind Mount Mode

Graft manages the container lifecycle by recreating it with a host bind mount:
The container is recreated with the exact same configuration — image, environment variables, port bindings, network mode, and labels.

Smart Lifecycle

Graft captures whether the container was running before any operation. If it was already stopped, the stop/start steps are skipped. This means graft commit on a stopped container is instant after the filesystem work completes.

Branch Operations

Checkout (New Branch)

Checkout (Existing Branch)

Rollback

Integrity Verification

On-Demand Verify

If verification passes on a previously unverified commit, the verified flag is automatically set.

Verify Output

With --verbose, per-file details are shown: