Skip to content

Repository files navigation

Driftless

Branch, diff, and merge for database schemas — the same workflow Git gives code, applied to schema structure (tables, columns, constraints, indexes), not text. Row data is explicitly out of scope; see decisions.md's Brief section for the full problem framing, the hard part, and what was deliberately cut.

The core idea: every schema object gets a stable ID that survives renames and retypes, so a rename on one branch and a reference to that object on another branch never look like a false conflict — and the one place a conflict can still hide (free-text check-constraint expressions) gets explicitly detected and explained, not silently merged into a broken schema. That's the hard sub-problem this project goes deep on; see merge-design.md and the merge-related entries in decisions.md.

Status

Live: driftless-mc4m.onrender.com (Render, free tier — the first request after a period of inactivity may take a few seconds to wake the instance). A main branch is already imported (the same users/email_format schema the flagship scenario below uses), so you can jump straight to forking it rather than pasting DDL first.

Ask for a token to sign in with, or run it locally (below) with your own.

Architecture

crates/domain       schema model, apply_operation, diff, merge — dialect-agnostic
crates/adapters      Postgres DDL parser/codegen (sqlparser-rs)
crates/persistence    Postgres-backed branch/version storage (sqlx)
crates/api           axum REST API over the above
frontend/            React + TypeScript UI (Vite) against the REST API

See docs/technical-backend.md / docs/technical-frontend.md for the full current status of each layer, and decisions.md for every real decision made while building, in the order they were made.

Prerequisites

  • Rust (stable) — rustup.rs
  • Node.js 22+ and npm
  • Postgres 16+ (CI runs against 16; a local instance you can create a database in)

Running it locally

1. Create a database. Any name/connection works — the app runs its own migrations on startup. Example, assuming a local Postgres reachable via a Unix socket:

createdb driftless_dev

(Adjust for your setup — a postgresql://user:pass@localhost:5432/dbname style URL works the same way. See crates/persistence's tests for both socket- and TCP-style connection string examples.)

2. Set up your local environment. Copy the template and fill in real values — .env is gitignored, so this only needs doing once:

cp .env.example .env

DATABASE_URL and API_TOKENS are required (the process refuses to start without them); BIND_ADDR/RATE_LIMIT_PER_MINUTE/DATABASE_POOL_SIZE are optional and already commented out at their defaults in the template. See .env.example for the exact format of each.

3. Start the backend:

cargo run -p api

.env is loaded automatically on startup (via dotenvy) — no need to export anything on the shell first, or re-set it every time you restart. Listens on 127.0.0.1:3000 by default. Migrations run automatically on startup.

4. Start the frontend, in a separate terminal:

cd frontend
npm install
npm run dev

Opens on http://localhost:5173. Vite's dev server proxies /api/* to the backend (see frontend/vite.config.ts) so the browser never talks to port 3000 directly — no CORS setup needed for local dev.

5. Sign in. The first screen you'll see is a token gate — paste in any one of the identity:token:role entries' token segment from your .env's API_TOKENS (e.g. dev_token_123, if you used the value from .env.example unchanged). There's no signup flow; tokens are pre-issued, matching this project's trusted-internal-team model (see decisions.md).

Trying the flagship scenario

The UI has four tabs: Branches, Develop, Diff, Merge. To see the project's actual hard part (a rename that only breaks a check constraint's free-text expression, not its ID-based reference) end to end:

  1. Branches tab → Import DDL as a new branch, e.g.:
    CREATE TABLE users (
      id INTEGER NOT NULL,
      email TEXT NOT NULL,
      CONSTRAINT email_format CHECK (email LIKE '%@%')
    );
    name it main.
  2. Fork a branch from main, e.g. feature.
  3. On feature, use the Develop tab's Builder form to rename emailcontact_email (pick the column, choose "Rename," enter the new name, "Add to queue," then "Apply to branch"). See docs/product-guide.md for a screenshot walkthrough, or api-reference.md for the underlying POST /branches/{name}/operations request shape if you'd rather drive it directly.
  4. Merge tab → preview a merge of main (ours) and feature (theirs). You'll see a Rename vs. expression reference conflict: the constraint's ID-based reference is fine, but its expression text (email LIKE '%@%') still says the old name.
  5. Apply the suggested fix, watch the conflict resolve, and commit.

Observability

  • Structured logs: tracing throughout, RUST_LOG-controlled (e.g. RUST_LOG=info,api=debug,persistence=debug). Every HTTP request gets a tower-http-generated span (method, URI, status, latency); every persistence::Store DB call logs its own latency nested inside whichever request span triggered it, so a slow endpoint and the specific query behind it show up together in one log stream.

Logging is the only observability surface in the current code — no metrics endpoint exists (an earlier Prometheus-format GET /metrics was removed; see docs/technical-backend.md's Observability section for the full note).

Tests

cargo test --workspace          # backend — 83+ tests; some are gated on
                                 # DATABASE_URL and skip gracefully without it
cd frontend && npm test         # frontend — 79 tests, no backend needed

Documentation map

Start here:

  • decisions.md — the problem brief and every real decision made, in order, with alternatives and what was cut.
  • docs/product-guide.md — a screenshot-driven walkthrough of the product from a user's point of view, including the flagship conflict scenario.

Design specs (written before implementation):

  • schema-model-design.md / diff-design.md / merge-design.md — the domain layer's design specs, including the full conflict taxonomy.

Technical reference (current, generated/verified against the actual code):

  • docs/technical-backend.md / docs/technical-frontend.md — architecture, extensibility analysis, and current status of every layer.
  • docs/architecture-diagram.md — the backend's request-flow diagram, with a full walkthrough of how to read it.
  • api-reference.md — every REST endpoint (auth, rate limiting, error codes, request/response shapes for Schema/SchemaOperation/ SchemaDiff/Conflict), generated from crates/api's actual routes.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages