# Make a database, branch it, merge it back.

> Connect, branch, merge and read history in Stolon: the SQL verbs, Postgres compatibility, auth and the console.

Source: https://stolondb.com/docs/ · Page title: Stolon docs — connect, branch, merge

Six steps from nothing to a merged pull request, then the reference. For one task at a time, see [the guides](https://stolondb.com/guides/).

## Get started

1. **Create a database**

   [Sign up](https://stolondb.com/signup), confirm your address and name a database.

2. **Create an app password**

   On the database's page, **Create an app password**. It is shown once, so copy it then. Revoke one to cut it off.

3. **Connect**

   Any Postgres client. The database name picks the branch: `<database>.<branch>`. Your app uses `main`.

   ```
   $ psql "postgresql://app:<password>@db.stolondb.com/acme.main"
   ```

4. **Branch**

   A branch is an instant copy of the whole database. Connect to it by name and change anything.

   ```
   acme.main=> CREATE DATABASE price_change;
   CREATE DATABASE
   $ psql "postgresql://app:<password>@db.stolondb.com/acme.price_change"
   ```

5. **Review it as a pull request**

   In the console, a branch with changes is a pull request: every row it added, changed or removed, and anything that conflicts with `main`.

6. **Merge**

   Merge from the pull request, or from SQL. `DRY RUN` shows what would land without landing it.

   ```
   acme.main=> MERGE DATABASE price_change INTO main DRY RUN;
   acme.main=> MERGE DATABASE price_change INTO main;
   ```

## Connect

Any Postgres driver connects unchanged, with the branch as the database name:

```
# python
>>> import psycopg
>>> psycopg.connect("postgresql://app:PASSWORD@HOST:PORT/acme.staging")

# sqlalchemy + alembic
$ DATABASE_URL="postgresql+psycopg://app:$PASSWORD@$HOST:$PORT/acme.staging" alembic upgrade head
```

### Short-lived tokens, for CI and agents

Exchange an API key from your Account page for a token scoped to one database, valid for up to 24 hours. Use it as both user and password.

```
$ TOKEN=$(curl -s https://console.stolondb.com/v1/tokens \
    -H "Authorization: Bearer $STOLON_API_KEY" \
    -d '{"store":"acme","sub":"ci","scopes":["query","data","branch"],"ttl":3600}' | jq -r .token)
$ psql "postgresql://$TOKEN:$TOKEN@db.stolondb.com/acme.main"
```

`scopes`: `query` (read), `data` (write), `branch` (create, merge and drop branches). `ttl` is seconds, up to 86,400.

## Postgres compatibility

Every SQL behavior is checked against a live PostgreSQL on every test run: **210 script families**, compared on rows, command tags, error codes and transaction status.

| Area |   | Detail |
| --- | --- | --- |
| Wire protocol | works | simple and extended, binary parameters and results, SCRAM-SHA-256 |
| Clients | works | psql, psycopg2, psycopg3, pgJDBC |
| ORMs and migrations | works | SQLAlchemy 2.0, Alembic including autogenerate |
| Queries | works | all join kinds, subqueries, CTEs incl. `WITH RECURSIVE`, set operations, GROUP BY/HAVING/FILTER, DISTINCT ON |
| Window functions | works | `row_number`, `rank`, `lag`, `lead`, aggregates with `OVER`, named windows, ROWS/RANGE/GROUPS frames |
| Transactions | works | savepoints, Postgres abort semantics |
| Constraints | works | NOT NULL, UNIQUE, CHECK, FOREIGN KEY with every action, enforced |
| Schema | works | ALTER TABLE, views, sequences, SERIAL/IDENTITY, indexes incl. partial, generated columns, enums, schemas |
| Upserts | works | `ON CONFLICT … DO UPDATE / DO NOTHING`, `ON CONSTRAINT` |
| Roles, grants, RLS | works | CREATE ROLE, SET ROLE, GRANT/REVOKE, row-level security policies |
| PL/pgSQL, procedures, triggers | works | functions, `DO` blocks, cursors, exception blocks, row and statement triggers |
| Types | works | integers, `numeric`, floats, text, `boolean`, dates, timestamps, intervals, `uuid`, `bytea`, `json`/`jsonb`, arrays |
| Functions | works | string, regex, date/time (`date_trunc`, `to_char`, `AT TIME ZONE`), JSON (`->>`, `jsonb_set`, `jsonb_agg`), `generate_series`, `LANGUAGE sql` functions |
| Extensions | works | `vector` (pgvector, `ivfflat`, `hnsw`), `pgcrypto`, `citext`, `hstore`, `pg_trgm`, `uuid-ossp`, `unaccent`, `fuzzystrmatch` |
| Tooling | works | psql backslash commands (`\d`, `\dt`, `\l`), `COPY` and `\copy` in text and CSV, pg_catalog and information_schema |

### Differences from Postgres

- `int2`, `int4` and `int8` are one 64-bit integer; `real` and `double precision` are one float. `NUMERIC` is exact.
- Sequences are branch-local and transactional.
- Identifiers are not truncated at 63 bytes.

## Moving data in and out

```
# in: a real pg_dump
$ pg_dump --no-owner mydb | stolon-db /path/to/store --import main

# out: back to Postgres, any branch
$ stolon-db /path/to/store --export staging | psql -d restored

# will this run here? ask before you migrate
$ cat queries.sql | stolon-db /path/to/store --compat-check main
```

On a hosted database, import and export from the console or its HTTP API. An import lands in a new branch.

## Branching and history

| Statement | What it does |
| --- | --- |
| CREATE DATABASE b | instant fork of the connected branch: schema, data and history |
| CREATE DATABASE b (schema only) | the structure, every table empty |
| DROP DATABASE b | drops it; a protected branch refuses |
| MERGE DATABASE b INTO main | merges the branch back (below) |
| SELECT … FOR SYSTEM_TIME AS OF t | any table at any moment, on any branch |
| COMPARE DATABASE a [AS OF t] WITH b [AS OF t] | rows added, removed and modified between two states, then `ROWS` or `CELLS` |

A branch can start from a past moment, and can be restored to one. The console's Restore keeps the present as a branch first, so a restore can be undone. [The history guide](https://stolondb.com/guides/history/) walks through it. Protect, expire and reset are in the console and the HTTP API.

## Merging

`MERGE DATABASE b INTO main [RESOLVE OURS|THEIRS] [DRY RUN]`

- **Column-level.** Changes to different columns of the same row both land.
- **Atomic.** A conflict refuses the whole statement.
- **Specific.** Conflicts are named `table[pk].column`, with both values.
- **Schema merges too.** A column added on the branch arrives with the merge.
- **`RESOLVE OURS|THEIRS`** settles value conflicts; `DRY RUN` previews it.
- **Repeatable.** A branch can be merged again and again.

## Durability

- Every commit is durable before it is acknowledged.
- Every page is checksummed; a damaged page is never served.
- Online backup, consistent, while serving.
- Log shipping: `--archive DIR` with `--archive-command` to your uploader, and `--restore-from DIR` to roll forward.

## Access control

- **SCRAM-SHA-256** on the wire, TLS at the edge.
- **Grants and policies branch** with the schema; roles are global.
- **Fork, merge and drop are grantable privileges.**
- **API tokens carry scopes** (`read`, `query`, `data`, `branch`, `backup`, `admin`). A data credential can never reset a database.
- **An audit trail** records every branch and merge.

## Agents and MCP

Every database serves MCP at `https://console.stolondb.com/mcp/<database>`. An agent gets a key-value store where every write is a version, and can review and merge the database's branches. Add it to Claude, ChatGPT, Claude Code or Cursor and sign in; [the MCP guide](https://stolondb.com/guides/mcp/) has each.

A branch can also carry references, such as a git SHA or an image digest, and decisions recording why it is the way it is. Both fork, merge and roll back with the data.

## The console

Open a database from [the dashboard](https://console.stolondb.com/app).

![The console overview: main, what your app connects to, above a list of branches, each with what it changed and a button to review its changes.](https://stolondb.com/assets/console-overview.png)

*Fig. 1 — the overview: `main` and its branches.*

![The console's version tree: every branch drawn at the depth it forked from, beside the database's health.](https://stolondb.com/assets/console-plate.png)

*Fig. 2 — the version tree, beside the database's health.*

A `read` token sees branches, the version tree and health; `query` also sees rows, history and pull requests.

## Deployment

- **Hosted.** A database we run, with the console and the log shipped off the machine continuously.
- **Single-tenant binary.** One process, one database, with local credentials, an audit trail and the built-in console.

- [Get a database](https://stolondb.com/start/) — Import a copy, branch it, ship back.
