Gnomon。Postgres 数据的 Git,为智能体而建
Gnomon. Git for Postgres data, built for agents

原始链接: https://github.com/bitsinfra/gnomon

Gnomon 是一个 Postgres 扩展,为数据库数据带来类似 Git 的分支功能。它运行在现有 Postgres 内部——不复制数据,也不需要第二个数据库。执行 `create extension gnomon` 后,你可以跟踪表、创建分支,并通过连接字符串将会话切换到分支;之后每个查询都会读写该分支,而不是主分支。 分支即时创建且不复制任何数据:它们从主分支读取,并仅存储更改。分支上的 schema 迁移保持隔离,直到合并。Gnomon 提供逐行差异、三向类型感知合并、冲突解决和撤销功能。它保留完整历史,支持时间旅行到任意时刻,并将更改流式传输到 Kafka/NATS/dbt。用 SQL 编写的检查会门禁合并,且如果分支后续发生变化,批准将失效。 对于智能体,一个命令可创建数据库角色、分支和连接字符串;运行通过 Postgres 强制的限制实现隔离。未经批准,任何内容都不会离开。TypeScript/Python SDK、MCP 服务器、CLI 和 GitHub Actions 支持运行、审查和 PR 分支。Gnomon 基于 PostgreSQL 许可证开源。

Gnomon是一个新的PostgreSQL扩展,提供类似Git的数据库数据分支、历史、合并和撤销,旨在为智能体提供隔离的数据库分支。Hacker News评论者质疑它相比现有代码管理迁移的必要性,将其与Neon和Xata比较,批评文档疑似由AI编写,并指出项目似乎仅出现数小时。其他人认为这个概念有趣但技术上不完整。总体反应谨慎且怀疑:可能是有用的智能体工作流,但关于冗余、文档质量、成熟度和实现细节的担忧仍然存在。
相关文章

原文

Git for Postgres data, built for agents. Give an agent its own branch of your real database, as nothing more than a connection string. See every field it changed, merge it into main when it's right, and undo it if it wasn't.

Gnomon runs inside the Postgres you already have. One extension, no copies, no second database.

create extension gnomon;
select gnomon.track('orders');                    -- history starts now, no backfill

select gnomon.create_branch('agent/refunds');
select gnomon.use('agent/refunds');

-- Every query from here on runs on the branch
update orders set status = 'refunded' where id = 4182;

select * from gnomon.diff('agent/refunds');       -- every row and field it changed
select gnomon.merge('agent/refunds');             -- three-way, type-aware, conflicts as rows
select gnomon.undo_merge(1);                      -- and back

A branch of production, in an instant

A branch reads everything from main and keeps only what it changes, so it opens instantly and copies nothing, however big your database is. Give one to every agent run, every pull request and every test.

Your application doesn't change. A branch is a connection string: point a session at it, and the queries it already runs read and write the branch instead of main.

Where How
A session select gnomon.use('feature')
One transaction (behind a pooler) select gnomon.begin('feature')
A connection string gnomon branch url feature
With the C module SET gnomon.branch = 'feature'
A past moment select gnomon.use('main', '2026-10-31 23:59')

Schema changes come along too. Run a migration on a branch to add a column, rename one or create a table, and main stays as it is until the merge, which brings the new shape and its data across together.

Every branch comes with a diff: each row and field it changed, before and after. Review it in your browser, comment on a single row and settle conflicts side by side. When an agent is done, Gnomon opens the review and sends you the link.

Add checks written in SQL, and Gnomon runs them on the merged result before the merge is allowed to land. An approval covers what you saw: if the branch changes afterwards, it comes back to you.

Merges that understand your data

When main and a branch change the same value, Gnomon doesn't just pick a side. Counts add up, JSON merges key by key and text merges line by line. Anything that truly clashes becomes a conflict, for a person to settle or for a query to resolve.

Branches merge into each other too, not only into main, and a single transaction can be picked from one branch onto another. Undo works the same way: take back one transaction, everything an agent did this afternoon, or a whole merge.

Any moment, exactly as it was

Gnomon keeps the history of every table you track. Read the whole database as it was at any moment, with the queries you already have, and get the same answer every time. When an agent makes a decision, you can see exactly what it saw.

-- The open invoices at the end of October
select gnomon.use('main', '2026-10-31 23:59');
select sum(amount) from invoices where status = 'open';

The same history streams out as it happens, to Kafka, NATS or a webhook, and into dbt as snapshots that keep deleted rows. Older history can move to object storage, and Gnomon brings it back when a query reaches that far.

When a check that used to pass starts failing, gnomon bisect finds the transaction that broke it: who made it, when and why.

One command gives an agent a database role, a branch of its own and a connection string. That's all it needs, whatever it's built with. It reads and writes its branch, and the database refuses the rest, main included, even when the agent asks for it by name.

# A database role, a branch of its own and a connection string
gnomon agent create support-bot
  • It merges only what you allow. Merging is for the people you choose, or for an agent within rules you write: at most twenty rows, only these tables, every check passing. Postgres enforces them, whatever the agent is told.
  • Nothing leaves before you approve. Emails, webhooks and payments an agent queues stay on its branch until the merge.
  • Every change has a name on it. Each change records the agent, the run and the reason it gave.
  • Replay what it saw. Gnomon records the moment of every read, so a replay returns exactly the rows the agent read.
  • Try a few ways at once. Start several attempts from the same moment, compare their changes and checks, and keep the best one.
  • Stop it, and take it back. One command locks an agent out, closes its branches and undoes what it merged, newest first.
  • Ready for your agents. The Gnomon MCP server, on the agent's machine or over HTTP, lets it explore the schema, try a change without keeping it, and rewind its branch to a checkpoint.

Build it into your product

The TypeScript and Python SDKs give each task your agents take on a run: a branch of its own, with limits already set. Merge it, send it for review, or let it go.

import { Gnomon } from "@bitsinfra/gnomon";
import { nodePostgres } from "@bitsinfra/gnomon/pg";

const gnomon = new Gnomon({
  app: nodePostgres(appPool),   // your application's own login
  runs: nodePostgres(runsPool), // one login shared by every agent run
});

// A branch for this task, discarded unless it's merged or submitted
await using run = await gnomon.startRun({
  task: "Refund order 4182",
  actor: "support-bot",
});
await run.sql("update orders set status = 'refunded' where id = 4182");

const { checks, changes } = await run.summary();
if (checks.passed && changes.rows <= 20) await run.merge();
else await run.submit({ title: "Refund order 4182" });

Runs can't read each other's branches, even when they share one database login. Bring the agent framework you already use: the Anthropic SDK, the Claude Agent SDK, the Vercel AI SDK, the OpenAI Agents SDK, LangChain or any MCP client gets the same few tools, bound to one run. They work through your drivers and ORMs, from node-postgres and Prisma to psycopg and Django. And in your tests, each test gets its own branch of one seeded database.

npm install @bitsinfra/gnomon      # TypeScript
pip install gnomon-postgres  # Python
Part Path What it does
SQL core src/sql The extension: history, branches, merges, review, valid time, retention, archiving. Installs on managed Postgres.
C module src/c Optional: SET gnomon.branch alone routes, real row locks, ON CONFLICT and TRUNCATE on branches, stale-read detection, faster writes and point reads.
CLI, MCP server, review app cli One Go binary: gnomon branch/diff/merge/bisect/agent/migrate/serve/mcp/daemon/archive.
SDKs sdk/ts, integrations/python @bitsinfra/gnomon and gnomon-postgres: a run per task, review, typed diffs, test helpers. One specification and conformance suite.
GitHub Action action A database branch per pull request, migrations on it, the changes as a comment.
Integrations integrations Python, Node, Ruby, Go, Java, Elixir, .NET, PHP; PostgREST and Supabase; a dbt package.
Examples, container examples, docker The tutorial's shop database and a ready-made image.

Everything is at https://gnomon.bitsinfra.com/, and the same pages live in docs:

Gnomon is open source under the PostgreSQL License. Contributions are welcome: see CONTRIBUTING.md. Report security issues privately, as described in SECURITY.md.

联系我们 contact @ memedata.com