Open Source · MIT License · v0.1.0

Full change history for your database — zero application changes.

ev connects to any relational database, infers entities from the foreign key graph, and installs lightweight triggers to capture every INSERT, UPDATE, and DELETE. Changes are grouped into versioned changesets you can browse, query, and export.

ev log --entity course --id 42
changeset v3  [tx: 8a3f2b1c]  2026-02-15 14:23:07 UTC
  tables: course, course_upsell
  -- course (id=42)
     UPDATE  endDate: 2026-05-01 -> 2026-06-01
  -- course_upsell (id=18)
     INSERT  courseId=42, licenses=10

changeset v2  [tx: 7b2e1a0d]  2026-02-10 09:15:33 UTC
  tables: course
  -- course (id=42)
     UPDATE  name: Intro to SQL -> Advanced SQL

changeset v1  [tx: 6c1d0f9e]  2026-02-01 11:00:00 UTC
  tables: course
  -- course (id=42)
     INSERT  name=Intro to SQL, startDate=2026-03-01

Quick start

Up and running in under five minutes.

Install

Shell
# One-liner install (Linux / macOS)
curl -fsSL https://raw.githubusercontent.com/sgmonda/entity-versioning/main/install.sh | sh

This will prepare ev command, so you can start using entity versioning in your databases. A simple ev --help will show you all available commands and options. Later, you could upgrade to a newer version with a simple ev upgrade, or even self-uninstall it completely using ev uninstall.

Why ev?

Most audit-log solutions require you to instrument your application — ORM hooks, middleware, custom triggers written by hand. ev takes a different approach.

Point it at your database

It reads your schema and figures out the rest. No configuration beyond a connection string.

It installs triggers for you

No hand-written SQL, no ORM plugins. Lightweight triggers that capture every change automatically.

Entities, not just tables

A course with its upsells and services is tracked as one logical unit. Changes are grouped semantically.

Features

Everything you need to build a complete audit trail, without touching your application code.

Zero app changes

Works entirely through database triggers. No ORM plugins, no middleware, no code changes.

Entity inference

Analyzes your FK graph to discover entities and their child tables automatically.

Transaction-aware

Operations within the same DB transaction are grouped into a single changeset.

Schema drift detection

DDL hooks capture ALTER TABLE events and record schema changes alongside data changes.

Clean teardown

ev teardown removes everything. All objects are namespaced under __ev_.

Any relational database

Pluggable connector interface. PostgreSQL and MySQL ship built-in. Community connectors welcome for MariaDB, SQL Server, SQLite, and more.

How it works

Three commands. Five minutes. Full audit trail.

1

Initialize

Point ev at your database. It introspects the schema, analyzes the FK graph, and infers entities automatically.

$ ev init --host localhost --port 5432 --database myapp
2

Fine-tune entities

Even if they are inferred from foreign keys, you can still customize them. Add or remove tables, define parent-child relationships, and exclude columns from tracking.

$ ev entities
3

Start tracking

Install changelog tables, DML triggers, and DDL hooks with a single command. Your database is now being versioned.

$ ev start
4

Browse history

Query the full change history of any entity instance. Filter by time, version, or export as JSON.

$ ev log --entity course --id 42

See it in action

A complete workflow from initialization to change log, showing entity inference and change tracking.

Terminal
# Initialize — auto-discovers entities from your schema
$ ev init --host localhost --port 5432 --database edtech --engine postgres

Introspecting schema...
Found 14 tables, 18 foreign keys
Inferred 3 entities:
  course     root: course      children: course_upsell, course_service
  billing    root: billing     children: billing_line
  class      root: class       children: class_evaluations, class_history
Config written to ev.config.yaml

# Start tracking — installs triggers on all entity tables
$ ev start

Installed 9 triggers across 3 entities
DDL hooks: installed (event trigger)
Tracking active.

# Your app makes changes as usual — ev captures everything
$ ev log --entity course --id 42

changeset v2  [tx: 7b2e1a0d]  2026-02-10 09:15:33 UTC
  tables: course
  -- course (id=42)
     UPDATE  name: Intro to SQL -> Advanced SQL

changeset v1  [tx: 6c1d0f9e]  2026-02-01 11:00:00 UTC
  tables: course
  -- course (id=42)
     INSERT  name=Intro to SQL, startDate=2026-03-01

# Clean up — zero residue
$ ev teardown --confirm

Dropped 9 triggers, 3 functions, 2 tables, 1 event trigger
All __ev_ objects removed.

Architecture

Engine-agnostic core with a pluggable connector interface. Add new databases without touching business logic.

┌───────────────────────────────────────────────────────┐
                       CLI (ev)                        
  init · entities · start · stop · status · log · ...  
├───────────────────────────────────────────────────────┤
                     Core Engine                       
  Schema Analyzer · Entity Resolver · Changes Builder  
├───────────────────────────────────────────────────────┤
                  Connector Interface                  
  introspect · triggers · ddl_hooks · query · health   
├──────────────────┬──────────────┬─────────────────────┤
  PostgreSQL          MySQL        Community         
  (built-in)        (built-in)     connectors        
└──────────────────┴──────────────┴─────────────────────┘
         
         
    ┌──────────┐
     Your DB  
    └──────────┘

CLI Layer

Parses commands, reads config, delegates to the core engine. No business logic here.

Core Engine

FK graph analysis, entity resolution, changeset building. Engine-agnostic — never touches SQL.

Connector Interface

Abstract contract: introspect, triggers, DDL hooks, query, health. Clean separation of concerns.

Connectors

Engine-specific implementations loaded dynamically. Add your own for any relational database.