Skip to main content

Quickstart

Beta. Chronos 0.2 is a public beta for developers. It is not yet used in production anywhere, and it is not Postgres, though it speaks Postgres's protocol and much of its SQL: see where it stands before you move real data.

Install​

# a prebuilt binary for macOS or Linux (x86_64 or arm64), into ~/.local/bin
curl -fsSL https://github.com/Abhishekxdg/chronosdb/releases/latest/download/install.sh | sh
export PATH="$HOME/.local/bin:$PATH" # if ~/.local/bin isn't on your PATH yet (a fresh Ubuntu's isn't)
chronos --version

The examples below use psql; on a machine without it: sudo apt-get install postgresql-client (Debian, Ubuntu) or brew install libpq (macOS).

The script picks the build for your OS and CPU, checks its SHA-256 against the release's SHA256SUMS, and installs it for your user; it never runs anything as root. CHRONOS_INSTALL_DIR=<folder> installs into another folder you can write (for /usr/local/bin, download the script and run it with sudo yourself), CHRONOS_VERSION=v0.3.2 picks a release, and CHRONOS_VERIFY=1 also checks the build's signature with cosign: proof Chronos DB's release workflow built it. The Linux builds are static (musl), so they run on any distribution.

Already run Postgres? Start beside it​

Your data stays in your Postgres. Chronos copies the tables agents work on into a folder, agents fork worlds of it at once, and a merge into main is written back to your Postgres in one checked transaction (a row changed there meanwhile comes back as a conflict, and nothing is written):

export CHRONOS_SOURCE_URL='postgres://agent_writer:...@staging-db:5432/crm?sslmode=verify-full'
chronos attach ./crm --tables public.leads,public.accounts # or --schema app
claude mcp add chronos -- chronos mcp ./crm # agents get worlds now (or: chronos serve ./crm)

The URL is read from the environment (or a file only you can read), never from the command line, and isn't written to the folder. What attach doesn't do yet, before you rely on it:

  • Each table needs a primary key, and no generated column or row-level security; partitioned and inheriting tables aren't attached.
  • At most 10,000 changed rows per merge (merge a world a table at a time past that).
  • No refresh: rows no merge touches stay as of the attach; attach again into a new folder to pick up the rest.
  • Your schema doesn't change through Chronos: change tables in Postgres, then attach again.
  • For staging copies first. It's Stage 1: the guide has the details, the limits and what happens at a merge.

Moving the data into Chronos instead, or starting from nothing, is the rest of this page.

Start a server and connect with psql​

chronos serve mydb # makes the folder mydb if it's new
chronos: serving on http://127.0.0.1:7070 (POST /v1/<op>; GET /v1/health)
chronos: postgres protocol on 127.0.0.1:5433 (psql postgres://127.0.0.1:5433/main)

chronos serve listens on 127.0.0.1 only: the Postgres protocol on port 5433 (not 5432, so it doesn't collide with a Postgres you already run) and the HTTP API on 7070. On loopback without a token no password is asked. In another terminal:

psql postgres://127.0.0.1:5433/main
create table todos (id bigserial primary key, title text not null, done boolean not null default false);
insert into todos (title) values ('try chronos'), ('read the docs');
update todos set done = true where id = 1;
select * from todos order by id;
id | title | done
----+---------------+------
1 | try chronos | t
2 | read the docs | f
(2 rows)

The database name is the world: main is the database itself, and every world you fork is another name (see concepts). Stop the server with Ctrl-C or SIGTERM; it closes the database cleanly.

Connect an application​

Point the application's DATABASE_URL (or its driver's connection settings) at the server:

export DATABASE_URL="postgres://app@127.0.0.1:5433/main"

Postgres drivers and ORMs connect as they do to Postgres; whether everything they send works depends on the SQL they send. Which have been tested, and how far: Clients. For a server other machines reach, a token of at least 24 characters and TLS are required (chronos serve refuses to start on another address without them), and the token is the password, with any user name:

export CHRONOS_TOKEN=$(openssl rand -hex 24) # serve reads it from the environment (not --token, which ps shows)
chronos serve mydb --pg 0.0.0.0:5433 --tls-cert cert.pem --tls-key key.pem
export DATABASE_URL="postgres://app:${CHRONOS_TOKEN}@db-host:5433/main?sslmode=require"

Without a token, anyone who reaches the loopback port can log in under any name, as a superuser. Before you expose a server, read security: TLS (--tls-cert, --tls-key), agents with their own tokens, roles with passwords.

Moving an existing Postgres database in: Migrating from Postgres.

Five minutes in the shell​

$ chronos mydb
chronos:main> put users 1 {"name": "Ada", "role": "admin"}
chronos:main> put users 2 {"name": "Linus", "role": "user"}
chronos:main> describe users
users (2 rows)
name string e.g. "Ada"
role string e.g. "admin"
chronos:main> fork agent-7 # instant private copy
chronos:agent-7> put users 1 {"name": "Ada L.", "role": "admin"}
chronos:agent-7> diff
1 change on agent-7
~ users/1 name: "Ada" -> "Ada L."
chronos:agent-7> merge
merged 1 change from agent-7 into main; now on main
chronos:main> find users where role = admin match ada

help lists every command. chronos mydb <command> runs one command and exits, and chronos mydb < script.txt runs a file.

From TypeScript or Python (the HTTP API)​

With chronos serve mydb running (HTTP on 127.0.0.1:7070), copy the client you need. Each is one file with no dependencies.

// clients/typescript/chronos.ts
import { Chronos } from "./chronos";

const db = new Chronos("http://127.0.0.1:7070");
const agent = await db.fork("agent-7");
await agent.put("users", "1", { name: "Ada L.", role: "admin" });
console.log(await agent.diff());
await agent.merge(); // throws ChronosError 409 on conflicts, or if a crash lost writes
# clients/python/chronos.py
from chronos import Chronos

agent = Chronos("http://127.0.0.1:7070").fork("agent-7")
agent.put("users", "1", {"name": "Ada L.", "role": "admin"})
agent.merge()

From Claude Code (MCP)​

claude mcp add chronos -- chronos mcp /path/to/mydb # a folder beside your Postgres works the same (see above)

The agent gets describe, find, sql, get, put, delete, fork, set_meta, branches, diff (rows, or with as_sql the SQL statements that make the change), history, merge_preview (what a merge would do, row by row and why), checkpoint and rollback (name a moment of its world and go back to it), simulate and replay, and discard, plus instructions for the safe workflow: describe, fork, edit, diff, then a person merges. Every tool and its parameters: MCP reference.

By default (safe mode) the agent changes only worlds it forked, and approving is left to a person: merge (with resolve, by_columns, per-row decisions in rows, and only_tables, only_keys or into to merge part of a world or into another world), restore and undo_merge appear only with --allow-merge, or for an agent (--agent NAME) with those rights.

From Rust​

use chronos::{Db, Query, Resolve};

let db = Db::open("mydb")?;
db.fork("main", "agent-7")?;
let v = db.put("agent-7", "users/1", r#"{"name":"Ada L."}"#)?;
db.merge_at("agent-7", Resolve::Fail, Some(v))?;

One process per folder​

A database folder is opened by one process at a time: the shell, chronos serve, chronos mcp or your program. Close one before opening another, or run chronos serve and point everything at it.

Next: the five-minute trial, an agent on a small CRM whose mistakes are caught before they land, then concepts.