argondb

ArgonDB: the database that is also a lakehouse — and speaks agent

This page describes a new database system, and it is also the argument for one: what ArgonDB is for, and why it is shaped the way it is. ArgonDB is designed to run the gamut from a Kubernetes cluster down to a single binary on a MacBook — one system across that whole distance, which is a claim this page spends some time on. The box and Compose shapes are what runs today; the Kubernetes shape is the reference architecture under validation.

This page is written for technical readers who are not database specialists. Every internals term gets explained the first time it appears — not just what it is, but why it matters — because ArgonDB's whole trick happens at a layer of Postgres most engineers never look at, and ten minutes spent there makes everything else on this page obvious. The tour: what ArgonDB is and where it sits, then its three components in the order that makes them make sense — stock Postgres, then Neon, then ArgonDB itself — then what the database does in practice, then the agent half, and finally how you would run it yourself, from a laptop to a cluster.

What ArgonDB is

ArgonDB is a Postgres-compatible operational database whose storage layer is the analytics layer. There is no CDC pipeline, no connector, no nightly export job. The same write that commits to Postgres becomes — within seconds, transactionally consistent, with full change history — an Apache Iceberg table any engine can read. And the whole system is built to be operated and queried by AI agents as a first-class client: budgeted responses, pinned-consistent multi-call sessions, and answers that disclose their own freshness, gaps, and limits.

Every phrase in that paragraph gets unpacked below, from the ground up.

The lineage ArgonDB sits on

Databases evolve in visible steps, and it helps to know which step this is. Four rungs, and the middle two are real products you can go and use today:

The distinction between the last two rungs is the entire project, so it is worth being concrete about it. In a lakebase the lake is downstream: something reads the database and writes the lake, and the two are therefore always a little bit out of agreement — on a cadence the vendor publishes, without a coordinate you can pin a second query to. In a postlake the lake is a shape of the database — same log, same clock, same commit boundaries — so "how far behind is the lake" has an exact answer at every instant, and a reader can pick one instant and see both shapes agree.

What ArgonDB deliberately is not

Four things it is not, in case one of them is what you came for:

The components: Postgres, then Neon, then ArgonDB

ArgonDB is three systems stacked, and the only sane way to describe it is bottom-up — because each layer exists to do something the layer under it could not.

If words like LSN, WAL, watermark or Iceberg mean nothing to you, good: each component below starts from zero and carries its own diagram.

Component 1 — Postgres: what happens when you change one row

Postgres is the world's reference open-source database, and a casual user never has cause to look inside it. But ArgonDB's entire trick happens in machinery Postgres already has, so ten minutes here makes the rest of the page obvious. Four ideas: pages, the write-ahead log, row versions, and checkpoints.

Pages and the buffer pool: the shape of data at rest

A table is rows; but disks and memory don't move rows around, they move fixed-size blocks. Postgres stores every table as a sequence of pages — 8 KB blocks, each holding as many rows as fit. When a query needs a row, Postgres loads that row's whole page into a shared area of RAM called the buffer pool (shared_buffers) and works on it there. Writes work the same way: an UPDATE changes the copy of the page in RAM, not the disk.

Why it matters: RAM is orders of magnitude faster than storage, so a database that touched the disk for every row would crawl. The buffer pool is why databases are fast. But it creates the central problem of database engineering: the freshest state of your data lives in volatile memory. Pull the plug, and every page that was changed in RAM but not yet written back is gone. Something has to make writes durable before the slow work of writing pages back happens. That something is the write-ahead log.

The write-ahead log: the diary every database keeps

The write-ahead log (WAL) is an append-only file — a diary. Before Postgres changes anything, it first appends a small record to the WAL describing the change ("in this page, this row's balance becomes 42"), in exact order. Only then does it touch the page in RAM. When your transaction commits — the moment the database promises "this happened" — Postgres forces the WAL records to disk (an fsync) and only then tells you COMMIT succeeded. The changed pages themselves can be written back to disk whenever convenient — minutes later is fine.

Why write the diary first, "ahead" of the data? Because appending to one sequential file is the fastest thing a disk can do, and it makes a crash survivable: after a power cut, Postgres reads the diary and replays any changes whose pages never made it to disk. Nothing committed is ever lost. And the same diary is the natural transport for replication — ship the WAL to a second machine, replay it there, and you have an exact copy of the database, kept current by the byte.

The property that matters for everything below: the WAL is a complete, ordered record of everything that ever happened to the database. If you have the WAL, you can reconstruct the database — at any moment in its history. Every position in the log has an address, the LSN (log sequence number — literally a byte offset into the diary), and an LSN is therefore a clock.

the write-ahead log — one file, appended to in exact order and never rewritten the database as of LSN 7/13E8A020one exact, reproducible instant each mark is one change record — "in page 90210, this row's balance becomes 42" LSN 7/13E10000 LSN 7/13E8A020 7/13F02C40 — the live head An LSN is a byte position in this one file, so it names an instant: left of the cut had happened, right of it had not. Every consistency claim below — pins, time travel, the lake's watermark — is a cut on this same line.

Keep that idea; ArgonDB uses LSNs as its universal clock for consistency, time travel, and provenance.

Row versions, and the cleanup that trails them

One more piece of Postgres that surprises people: an UPDATE does not overwrite a row. It writes a new version of it and leaves the old one in place, because transactions that started earlier are still entitled to see the world as it was when they began. That is MVCC, multi-version concurrency control, and it is why readers never block writers in Postgres.

The bill arrives later. Those dead versions have to be reclaimed, which is autovacuum's job — and vacuum may only remove a version that no open transaction could still need. So a single long-running transaction holds the cleanup horizon for the whole database: while it lives, dead rows pile up behind it. Remember this one. It is the reason ArgonDB's pins are built the way they are, and it is the single most common way a well-run Postgres gets into trouble.

Checkpoints: why the diary can normally be thrown away

If the WAL only ever grew, replaying it after a crash would take hours and the disk would fill. So periodically Postgres takes a checkpoint: it writes all modified pages from the buffer pool down to disk, then records "everything up to LSN X is now safely in the data files." Recovery only needs to replay the diary since the last checkpoint, and WAL older than that can be recycled.

Here is all of that in one picture — the processes, the shared memory they work in, and the two very different files on disk:

one Postgres instance — a family of processes sharing memory your applicationINSERT · UPDATE· COMMIT backend processes — one per connectionparse, plan, execute — they touch shared memory, not the disk WAL buffers (RAM)① the change record isappended here first,in exact order shared buffers (RAM)② the 8 KB page itself ischanged here — fast,and volatile WAL writerflushes the logto disk bg writertrickles dirtypages out checkpointerflushes all dirtypages on a schedule MVCC: an UPDATE writes a NEW row versionthe old version stays visible to older transactions autovacuumreclaims row versions no open transaction can still see WAL segments (disk)append-only, in orderCOMMIT waits for THISfsync — and nothing else data files (disk)heap + indexes, 8 KB pages③ written back later —minutes is fine replica · archive · PITRreplays the very same log— this is how copies are made ① fsync on COMMIT COMMIT is durable the moment ① reaches the disk — the pages in ③ may not be written for minutes. After a crash, Postgres replays the log since the last checkpoint: nothing committed is ever lost.

Note what the checkpoint implies: in classic Postgres the WAL is scaffolding. It is essential for the seconds between commit and checkpoint, and then it is thrown away. Hold that thought: the two components that follow are both built on refusing to throw it away.

Where a single Postgres runs out of room

Everything so far is one machine. That machine is remarkably capable, and for most applications the story ends there. But when it doesn't, the architecture — not the code quality, the architecture — sets hard limits, and every one of them traces back to the picture above.

all writesevery INSERT/UPDATE read trafficfans out — and lags PRIMARYthe only machine thatcan accept a write read replica 1a whole Postgres read replica 2a whole Postgres read replica 3a whole Postgres its storagefull copy #1 full copy #2every byte, again full copy #3every byte, again full copy #4every byte, again WAL stream to each replica — asynchronous, so replicas lag single writer: the primary is both the write ceiling and the write outage every replica pays for the whole database again — storage, RAM, warm-up a new replica starts with a full base backup first scaling stock Postgres means copies: reads fan out, writes do not — and every copy is a whole database

Read those together and a pattern emerges: nearly every one is a consequence of the database owning its own disk. That is precisely the assumption the next component removes.

Component 2 — Neon: taking storage out of the database

Neon is an open-source (Apache 2.0) reimplementation of Postgres storage. It became well known as a serverless-Postgres cloud, was acquired by Databricks in 2025, and its architecture is the foundation ArgonDB forked and now carries forward.

The core idea in one sentence: keep the diary forever, and let a storage service — not the Postgres process — own it. Postgres stops writing pages to a local disk. Instead it streams its WAL out over the network, and when it needs a page that isn't in its buffer pool it asks for it: "give me page N as of LSN L." That request has a name you'll see everywhere below — GetPage@LSN.

The pieces, one at a time

primary compute (read/write)stock Postgres + the neon extension:it ships WAL out and asks for pages read-replica & branch computesread-only, at head or at any past LSN —and not one of them holds a copy compute_ctlNeon's compute agent: configures,starts and supervises each Postgres safekeepers ×3COMMIT is acked when 2 of 3 hold the record on diskconsensus (Paxos): any one can die — no loss, no pause pageserverthe log, re-indexed by (page, LSN) + periodic page imagesanswers GetPage@LSN — any page, any instant, forever storage controllerplaces timelines on pageservers; migrates and fails them over storage brokerpub/sub: who holds which timeline, and up to what LSN object storage (S3-style)layer files — cheap, redundant, effectively unlimited connection proxy — the one piece ArgonDB removed cloud multi-tenant pooling; a self-hosted database does not need it WAL stream — the diary, live GetPage@LSN (on cache miss) ordered WAL layer files one storage service, many computes — and not one of the computes owns the data a branch is just a metadata entry ("new timeline, rooted at LSN X"), free until it diverges

What the separation buys — and what it does not

Every limit in Component 1 that traced back to "the database owns its disk" simply dissolves:

What Neon does not fix is just as important. There is still exactly one read-write compute per timeline, so the single-writer ceiling stands. And the pageserver stores pages — row-oriented, OLTP-shaped — and serves them back to Postgres. The shape problem is untouched: analytics is still someone else's problem, still solved by copying data out to a different system. That is where ArgonDB picks up.

What ArgonDB uses: all of it

A clarification, because it is usually assumed otherwise: ArgonDB does not use "Neon's storage engine." It uses all of Neon — the whole open-source system, running as designed. Every component named above is in this deployment right now: the safekeeper quorum, the pageserver, the storage broker, the storage controller with its own Postgres, compute_ctl, the neon extension inside the compute, and the local orchestration tooling the test suites drive.

Exactly one open-source component was removed: the connection proxy, which does multi-tenant connection pooling and TLS routing for Neon's cloud. A self-hosted database has no use for it. (Neon's commercial cloud control plane was never open source, so it was never on the table — the open repository is the storage system plus the storage control plane, and that is what we run.)

Since the Databricks acquisition the public Neon repository has slowed to a handful of commits a year (6 since October 2025, most recently 2026-08-31, checked 2026-09-01). ArgonDB forked it by one-time snapshot, keeps its own additions strictly separated from the inherited code, and maintains the inherited tree itself — including its own security and correctness watch. In practice it is an actively developed open continuation of this architecture.

Component 3 — ArgonDB: a second reader of the same log

Here is the situation ArgonDB starts from. Flowing through the safekeepers is a complete, ordered record of everything happening in the database. Sitting in the pageserver is every page, at every LSN, with the system catalogs — Postgres's own tables describing your tables' names, columns and types — versioned right alongside. One consumer of that stream, the pageserver's own layer store, materializes it as pages for Postgres.

ArgonDB adds a second consumer that materializes the very same stream as columnar tables for analytics. Same log, two shapes, one clock. Here is the whole system, all three layers at once:

① Postgres — stock v17, unchanged ② Neon — inherited (ArgonDB runs all of it) ③ ArgonDB — new your applicationsordinary SQL writes —INSERT · UPDATE · COMMIT apps · BI · psqlordinary Postgresclients, unchanged DuckDB · SparkTrino · Snowflake AI agentsClaude, Cursor, your own —any MCP client primary computestock Postgres v17 + theneon extension — read/write analyst computesread-only Postgres, bootedat any retained past LSN safekeepers ×3WAL durable by quorum pageserverevery page at every LSN storage controllerplacement · failover storage broker+ compute_ctl object storage (S3-style)layer files — and, below, the lake's Parquet lives here too Iceberg lakeREST catalog +Parquet files, on thesame object storage argondb-tailphysical WAL decoderatomic Iceberg committerdelta ring (seconds-fresh)full-text indexmaintenance planedisclosure layer: gaps,value notes, breakermetrics surface argondb-queryunified pinned reads argondb-textBM25 + structure argondb-ctlpublish · redeem pins argondb-mcp15 agent tools —budgeted, pinned,provenance-stamped WAL GetPage@LSN pages + WAL @ any LSN any engine reads it One write path in, two synchronized truths out, on one LSN clock. The analyst computes in ① are booted by argondb-ctl out of ②'s retained history when an agent redeems a pin — which is why the same coordinate answers in both engines.
Postgres (unchanged) inherited (Neon) new (ArgonDB) durable storage clients & feeds

What we kept, what we changed, what we added

The discipline that makes this maintainable is worth stating plainly, because it is checkable. Everything in layer ② is inherited and runs unmodified. Across the entire inherited tree, exactly eight files carry an ARGON: marker, and only two of those change behaviour:

The other six are build files: the proxy removal, and two test hooks that let the inherited storage tests run against local S3- and Azure-compatible endpoints. Everything else ArgonDB does lives in new code beside the inherited tree — which is what keeps upstream fixes cherry-pickable, and what lets us say "unchanged" and mean it.

What is new:

The next two sections explain why the columnar half is worth building at all, and then why building it inside storage changes what it can promise.

Rows vs columns, and what a lakehouse is

Postgres pages store data row by row — perfect for transactional work ("fetch order 4711, update its status"), because each row's fields sit together. Analytics asks a different shape of question: "average order value per month over 40 million orders" touches two columns of every row. Columnar formats store each column's values together instead, so a scan reads only the columns it needs, compressed tightly — routinely orders of magnitude less I/O for analytical queries. The standard columnar file format is Apache Parquet.

A data lake is the pattern of putting those files on cheap object storage rather than inside a proprietary warehouse. But a folder of files is not a database: nothing makes a multi-file update atomic, or lets two engines agree on the current version of a table. Apache Iceberg fixes that — it is an open table format, a metadata layer over Parquet files that provides real tables: atomic commits, snapshots (every commit preserves the previous versions), schema evolution, and time travel. A small service called a catalog holds the pointer to each table's current metadata, so any number of engines can read a consistent view. Lake + table semantics = lakehouse. The point of it all is openness: an Iceberg table belongs to you, on your storage, and DuckDB, Spark, Trino, Snowflake and friends all read it natively — no export, no vendor gate. Why analysts insist on this: their tooling changes every few years, and open formats are the guarantee that data outlives tools.

The pipeline problem — and how storage-level decoding deletes it

So the operational world runs on row-shaped Postgres and the analytical world runs on column-shaped lakehouses — and connecting them is one of the most duct-taped areas in data engineering. The standard answer is CDC (change data capture): a pipeline product subscribes to the database's logical replication stream — a decoded feed of row changes Postgres computes on the primary, held open by a replication slot — and writes the changes into lake files. This works, and an entire industry (Debezium + Kafka + Spark, Fivetran, and kin) exists to do it. The costs are structural: a second stateful system to operate; lag measured in minutes to hours; per-table configuration; DDL (schema changes) handled poorly or not at all; load and bookkeeping imposed on the production primary; and no consistency contract — a transaction touching five tables arrives at the lake as five uncoordinated trickles, so the lake is perpetually a little bit wrong about the relationships between tables.

ArgonDB's observation: all of that exists only because the pipeline lives outside the database. At the storage layer, everything a pipeline struggles to reconstruct is simply there:

the write-ahead log — the one Postgres already writes, read a second time W — the watermark P — where you asked the lake is complete through here your pin argondb-decoderreads the WAL physically —typed rows, catalogs and TOASTfetched at the same LSN argondb-committergroups changes by thetransaction that made them —one atomic lake commit the delta ringthe seconds above W, helddecoded in memory the lake — Iceberg through WParquet files on object storage,readable by any engine orders__changesthe changelog: every change as arow, carrying the LSN that made it a read at LSN PSQL, an agent, DuckDB Iceberg at W + the ring's changes above it, cut at Pmerged at read time — one answer, one instant, no pipeline to fall behind The lake is not a copy some pipeline keeps in step. It is the same log, decoded once, written down in a second shape — which is why a single LSN addresses both, and why a five-table transaction lands in the lake as one five-table commit.

One write path in, two synchronized truths out — and one clock, the LSN, across both. That is the whole trick. The rest of this page is what follows from it.

Pins: a coordinate you can hand around

Because both halves are addressed by the same clock, ArgonDB can give you the clock itself. That object is a pin, and it is the most useful thing on this page, so here is exactly what it is.

A pin is a coordinate. Four fields — the branch, an LSN, an optional list of tables it covers, and the moment it was minted — serialized, signed with the deployment's key, and handed back to you as a short opaque string beginning pin-. That string is the pin. You call pin() once, then pass it to any tool; every answer you get comes from the database as of that exact instant. The database has 40,000 new rows by the time you ask your fourth question, and your fourth answer still agrees with your first.

And the same coordinate is honoured by both engines. This is the distinction worth being precise about, because the two sides work differently:

AI agentcalls pin() once,then thinks for an hour the pinbranchLSN — the instantscope — which tablesminted_at+ HMAC signature the lake sideIceberg at the watermark+ the delta ring above it,merged at that LSN the Postgres sidea read-only compute bootedAT that LSN out of retainedhistory — full native SQL answered in process; costs an LSN and a query session — 64 concurrent seconds to boot; the primary is never involved a signed VALUE — no held transaction, no connection, nothing registered server-side The one exception is the lake-switched-off shape (the macOS release): a plain Postgres with no storage engine behind it. There a pin IS a held REPEATABLE READ transaction, so it holds the vacuum horizon, and the default allows 8 of them rather than 64. one coordinate, two engines — the same instant, answered two ways

The Postgres side is the move a shared-nothing database cannot make. Because storage keeps the history and computes are disposable, ArgonDB can build you a database at the moment you care about rather than freeze the one serving your customers. One consistency claim, two readers — an agent can check a columnar aggregate and a native Postgres query against each other at the same coordinate and expect them to agree.

Three consequences of a pin being a value rather than a handle:

Two further properties are easy to miss and matter the first time you hit them:

A longer treatment of why this is not implemented as a held transaction, and what it costs when it has to be, is in the MCP section below.

The promises, and which ones bend

Everything so far is mechanism. Here are the promises the mechanism exists to keep — and the useful part is not the list, it is which ones are absolute and which are allowed to sag under load.

Six are absolute. A release that broke one of these would be a bug, not a tradeoff:

Two are allowed to bend, and are written down as goals rather than guarantees:

The asymmetry is deliberate, and it is the shape of the whole contract: correctness is absolute, timeliness is negotiable. A slow tail costs you freshness, which is a state the system is built to ride out and recover from. A read that mixed instants would cost you correctness, and there is no acceptable degraded version of that — so ArgonDB refuses instead of serving it. When you find a case the rules above do not literally cover, that is the direction they resolve in.

How we know: Postgres itself is the referee

A fair question about a decoder that rebuilds your rows out of raw storage bytes: how do you know it got them right? The answer is that ArgonDB is never taken at its word. Because storage keeps history, we can boot a real read-only Postgres at the same instant, ask it for the same rows, and compare the two answers byte for byte. Postgres is the reference implementation of Postgres; making it the referee turns "is the decoder correct" from an opinion into a test that runs continuously and can fail.

That referee is the backbone of a testing system that is deliberately this project's primary engineering control, because the code is substantially AI-written, and what that calls for is not assurance, it is oracles:

ArgonDB Features

What follows is a series of features, each one something the combined Postgres, Neon and ArgonDB stack provides out of the box. They are described in the order a team meets them: getting data in, keeping the lake exact, reading it at any instant, searching it, and keeping the whole thing healthy without a maintenance job. The agent interface and the ways to run it follow in their own sections.

Ingestion without a pipeline

An application's writes land in ordinary Postgres tables; ArgonDB's decoder turns the WAL — the same write-ahead log you met above — into lake commits. The write rate and the lake watermark advance together, and the gap between them is the whole "pipeline" story — measured in seconds. Nobody configures a connector for these tables; publishing them (next section) is the entire setup.

Publish any table with one command

Nothing reaches the lake until you ask. Publishing is a deliberate, per-table choice — the default published set is empty, because no system can guess which of your tables are precious and which are scratch. argondb lake publish <table> marks the table (the marker lives in the table's own comment, so it travels with the database and its branches), backfills the full table into Iceberg at a pinned LSN, and keeps it current from then on — the same decode path as live changes, so there is no separate "initial sync" machinery to distrust.

The consequences of that choice, so none of them surprise you: a new table you create later is not automatically enrolled, because the default is "no" and it stays "no" until you say otherwise. Stopping is two-tier, mirroring DROP TABLE against DROP TABLE PURGE: unpublish stops capture and keeps every lake file and all its history, which is instant and reversible, and only an explicit purge — after showing you what it will destroy — drops the Iceberg table and deletes its files. Dropping the table in Postgres behaves like the gentle one: capture stops, the lake data stays. And temporary or unlogged tables cannot be published at all, because they generate no log records — there is literally nothing for ArgonDB to read.

Primary keys: the one schema choice the lake cares about

ArgonDB guarantees the lake copy of a published table is 100% accurate — with or without a primary key. What the primary key decides is how much work keeping it accurate costs, and it is the one schema choice worth making deliberately before you publish.

The mechanism: Postgres identifies row versions physically — "page 47, slot 3" — and that address means nothing in a columnar lake file. When a row is updated, ArgonDB must find and replace the old copy in the lake, and a primary key is the only row identity that survives the trip from physical pages to logical columns (physical addresses are recycled by vacuum; whole-row matching cannot tell two identical rows apart). With a primary key, an update becomes a small incremental lake commit: the new row plus a "delete the row with this key" marker. Without one, ArgonDB keeps its guarantee the only way that remains: it re-scans the table and rewrites its lake copy whole at the next commit — still exactly right, at full-table cost in time and system load, each cadence in which the table changed at all.

So the guidance is simple: give every table you publish a primary key. Most production schemas already do. A table without one still publishes and the lake stays exact — you have simply chosen the expensive path, and you should choose it knowingly: a small or rarely-changing table without a key costs little, while a large, busy one will spend real rewrite work on every change.

Worth knowing how the rest of the industry handles this: conventional CDC rides logical replication, and its no-key options all intrude on your primary — REPLICA IDENTITY FULL logs the entire old row into the WAL on every update (and still cannot tell duplicates apart), or nothing, in which case Postgres rejects the application's UPDATE/DELETE outright. Because ArgonDB reads storage, your schema, WAL settings and application writes are untouched either way; the cost of a missing key lands on ArgonDB's own lake-side compute, never on your write path.

Atomic multi-table commits

A Postgres transaction touching five tables becomes ONE Iceberg commit touching five tables — cross-table consistency survives into the lake. This is the consistency contract CDC pipelines structurally cannot give you (each table trickles independently), and it falls out of commit-LSN alignment: the committer never splits a transaction across lake commits. It is visible in the catalog: when several tables jump to the same watermark at once, that is an atomic cut.

Freshness without commit spam

There is a real tension here: every Iceberg commit writes files, and a lake made of millions of tiny files becomes slow for every engine that reads it. Pipelines resolve the tension by giving up freshness (commit rarely, lag by minutes). ArgonDB resolves it with the delta ring: Iceberg commit cadence is tuned for healthy file sizes, and freshness comes from merging the in-memory ring above the watermark at read time, pinned to one LSN — so a read is both current to the instant and transactionally consistent. The measurement that settles it is end to end: write a marker row, then time how long until a consistent read sees it. The answer is seconds.

The changelog: your table's history as a table

Any published table can opt into a changelog — a sibling table named orders__changes that records every change as a row: the new row image, the operation (insert/update/delete — deletes carry the key), the exact commit position (LSN), and the commit timestamp. It is built from the same decoded stream as the table itself and lands in the same atomic commits, so the history is never out of step with the data. Rows are only ever added; nothing rewrites your history.

Why you'd turn it on, per table:

The costs, stated plainly: it is opt-in per table because history has a storage price — a hot table's changelog grows with its write rate. History is kept for the retention window (48 hours by default, raisable per deployment; it can never be shorter than the pin window, for the reason in the pin section — time travel replays this table). And like everything in the lake, it is a plain Iceberg table any engine can read.

Time travel

Any table can be read at any LSN within the retention window — row-exact, reconstructed from a retained snapshot plus changelog replay. This is the LSN-as-clock idea made usable: "the same query, then and now" needs no snapshots anyone remembered to take, because history is a first-class coordinate. Same query, two coordinates:

one table, one query, two positions on the same log the pin you keptLSN 7/13E10000 the live headLSN 7/13F02C40 not written yet the green records are what happened in between the answer then the answer now these two answers differ by exactly those green records Time travel is not a snapshot somebody remembered to take. It is a second cut on the log you already have.

Search over your own text, at the same coordinate

A lot of the data people actually want to ask about is text — filings, articles, issue threads, commit messages. The usual answer is a second system: ship the text to a search service, keep it in sync, and accept that its idea of the world disagrees with the database's. ArgonDB indexes text where the text already lives. The index is built by the same maintenance plane that keeps the lake healthy, from the same decoded change stream, and every hit comes back stamped with the position it was indexed at — so a caller can see how current the index is instead of assuming. In the multi-machine shape the index travels the same road as everything else: the worker that builds it uploads finished segments to object storage and the read replicas pick them up, which is exactly why a hit states which index position answered it. Even the search index lives on the object store.

Two things make it more useful than a search box:

Enrollment is declarative: a configuration file names the columns to index, and how to chunk and section them — the tier that gives a document-shaped corpus its structure, and the one a serious text corpus wants. The direction of travel from there is toward the smallest declaration that can still carry the intent, a marker on a column with sensible defaults behind it, and toward optional embeddings computed under your own provider's key, never ours. BM25 stands entirely on its own for anyone who wants nothing to do with a model.

Branches and the lake: free until read, a full copy once used

You met branching in the Neon section: on the Postgres side a branch is metadata — instant, and nearly free until it diverges. The lake side works differently, and the difference is worth knowing before you branch a database with published tables. A branch's lake tables are materialized lazily: until something actually reads or publishes on the branch, the lake does no work and stores nothing for it, so creating branches freely costs nothing. Once a branch's lake side is used, its tables become full, independent copies — real storage, not shared files. That independence is deliberate: every branch's tables remain plain Iceberg any engine can read, and maintenance on one branch (compaction, cleanup, expiry) can never corrupt or slow another. Rule of thumb: branch as freely as you like for Postgres-side work; expect real storage the moment a branch's lake tables are read.

Gaps, lower bounds, and values the lake cannot hold

When ArgonDB must skip WAL (a re-baseline under overload), it records the gap durably and refuses to reconstruct history inside it — naming the range, the reason, and the remedy. Digests over a window touching a gap say "these counts are lower bounds." Reads served behind the live head say so. Values Postgres allows but Iceberg cannot represent (NaN in a bounded numeric, infinity dates) become NULLs with a durable, queryable record — never silently. This is what a database built for agents has to do: the worst answer is a confident wrong one.

None of this is theoretical. Put ArgonDB on a box that doubles as a build machine and let heavy compilation contend for its cores, and the machinery acts in real time — re-baselines with named ranges, digests that say "lower bound," reads that disclose how far behind the head they ran — instead of a stalled system pretending nothing happened. Pressure is the point: this is the behavior under load, not despite it.

That last one deserves its mechanism, because it is the only place ArgonDB knowingly does not carry a value across. The set is small and specific: Postgres will store a "not a number" in a fixed-precision numeric column and an "infinity" in a date, and the lake's type system has nowhere to put either. Ordinary floating-point NaN and infinity are not in this set — both sides use the same IEEE representation, so they pass through untouched. For the handful that do not fit, the two obvious policies are both bad: hard-erroring turns one odd row into a lake-wide outage, and quietly writing NULL loses data invisibly. So the mapping is the one our closest commercial equivalent publishes — chosen deliberately, so migration comparisons are like-for-like and any criticism of the mapping lands on them too — with one difference that is the whole point: they NULL it silently, we NULL it and record the table, the column, the position and the count, then disclose it on every read that touches the column. Very large or unbounded numerics do not lose anything at all; they are preserved exactly, as text.

"Every read" is carrying weight in that sentence, and it is worth unpacking, because ArgonDB has two engines that can answer the same pinned question — and on a column like this one they legitimately disagree. The lake path gives you NULL, because NULL is what the lake holds. The Postgres path gives you the original value, because an analyst compute reads pages rather than lake files and the value is still sitting there. Neither answer is wrong, and neither is allowed to be quiet about it: a lake-served answer discloses that this column's unrepresentable values read back as NULL, and a Postgres-served answer discloses that it is handing you the original where the lake copies are NULL-mapped. The two paths differ in value, but never in silence — which is the only version of this that an agent comparing the two answers can be trusted to handle.

When something goes wrong: errors you can act on

Ingestion failures are where pipeline products quietly hurt you: one unprocessable record and the standard choices are a stalled pipeline (everything stops until a human intervenes) or a silent skip (the lake is now wrong and nobody knows). ArgonDB does neither. A table that repeatedly fails to process is quarantined alone: it freezes at its last known-good position while every other table continues normally.

What you see is designed to be actionable rather than mysterious:

When a whole process dies

The failures above are the polite ones — a bad row, a value the lake cannot represent. The impolite ones end a process mid-thought: the kernel kills the biggest memory user, a DIMM starts flipping bits, a disk lies about what it wrote, or a plain bug panics. Every database faces these; the differences are in what happens next. Because ArgonDB is three layers, the full answer comes in three parts — what Postgres has always done, what Neon adds (all of which we inherit), and what ArgonDB builds on top for the lake.

What Postgres does. Crash safety is Postgres's oldest muscle, and it is the same WAL story from earlier in this page: the diary is written and made durable before the data pages it describes. After a crash, restart replays the diary since the last checkpoint, and the database comes back holding exactly the committed transactions — nothing acknowledged is lost, nothing half-done survives. Two details worth knowing because experts will ask: if any backend dies unexpectedly (an out-of-memory kill included), the postmaster deliberately restarts the whole process family and runs that same recovery, because shared memory cannot be trusted after an unexplained death — so it isn't. And power loss mid-page-write (a "torn page") is covered by writing a full image of each page to the diary the first time it changes after a checkpoint.

What Neon adds — and we inherit. Separating storage from compute turns "recover the machine" into "replace the part." The compute is stateless — its local disk is only a cache — so a crashed or OOM-killed compute is simply started again at the exact position the WAL reached; there is nothing local to salvage. The WAL itself is durable on a quorum of safekeepers the instant it is written: any one of the three can die mid-write with no data loss and no pause, which is precisely the faulty-hardware scenario a single machine cannot survive. And the pageserver's layers live on object storage, so a dead pageserver is failed over by the storage controller and rebuilt from what the safekeepers and object store already hold. Faulty hardware becomes a replacement event, not a data event.

What ArgonDB adds. The lake half follows one rule: make every step either durable or repeatable, and recovery becomes "start again" rather than "repair."

And the part we will not sweep under the rug. Robustness sections that only describe design are describing hope. A concrete case: our exact-statistics pass, computing distinct counts over a very wide text column, ran into an internal 2 GB buffer limit and panicked — and because the supervisor restarted it on cadence, it crash-looped. What the design bought: ingestion, reads, and the OLTP primary were unaffected the whole time, no data was lost or corrupted, and the blast radius was exactly "background maintenance falls behind" — compaction paused, so reads on that table slowly degraded toward the unclustered numbers above until the fix landed (the fix bounds the memory: fingerprint the values instead of holding them all). That is the shape of the promise, stated plainly: a crash costs you the freshness of background work while it is contained; it does not cost you acknowledged data, and it cannot cost you a consistent lake — the WAL quorum holds the truth, and every recovery starts from it.

Ingestion support: your extractor, and everything around it

ArgonDB Features opened with a section called "Ingestion without a pipeline," and it is worth being exact about which pipeline that is. The one that disappears is the pipeline out of the database — the connector, the queue and the transformation job a conventional stack needs to get a Postgres table into a lake. The pipeline into the database does not disappear, and it is not ours to write. The PDFs, the vendor CSVs, the partner API that changes its mind twice a year, the filings that mean nothing until somebody parses them: that code encodes what your business believes a valid record is, and nobody outside your building can write it for you. ArgonDB does not try to. What it owns is everything around that code — where the output lands, what happens to the record that will not parse, what happens when the shape upstream changes, how you re-run after you fix the extractor, and whether anyone can see any of it at three in the morning.

Every building that takes deliveries has a receiving dock, and the dock is not the truck. Whoever sends the pallets decides what is on them and how they are packed; the building owns the bay, the scales, the clipboard, and the corner where anything that arrives damaged is set down with a note on it — so the driver is not held up and nothing is quietly thrown out the back. Nobody expects the building to know how to pack a pallet. What everybody expects is that one damaged crate does not stop the delivery, and does not vanish either.

The landing zone is ordinary tables. An extractor's output target is an INSERT; there is no ingestion runtime in front of the database with its own storage, its own format and its own idea of consistency. What the extractor pulled out as structure — the source, the date, the identifier, the section it came from — lands in typed columns beside the text column that holds the prose, and from that moment everything in this part of the page is yours for free: publish the table and it is in the lake, turn on the changelog and its history is a table, pin it, read it as of an hour ago, search the text inside it, let a foreign engine read the whole thing. Landing your data and getting the platform are the same act, not two projects.

Typed columns are the gate. A landing table that accepts anything — one blob column, whatever shape the extractor emitted this week — tells you nothing, and drift into it is invisible by construction: a renamed field simply becomes a key nobody reads, and the report built on it quietly gets thinner. A declared column with a declared type refuses the record that no longer fits and says which column and what arrived instead, on the first record rather than in a quarterly review. The gate is not there to be strict. It is there so that a change upstream becomes an event with a timestamp instead of a slow contamination somebody finds later.

A bad record's home is a row, not an exception. The pattern this makes cheap is the one the industry calls a dead letter, and the shape of it here is that the failed record goes into a table beside the good ones, carrying the reason it failed, the position it came from, and enough of the original to fix it. Good rows land. The batch does not stop for one bad crate, and nothing is dropped where nobody can find it. Note that this is a different thing from the per-table quarantine described a few sections up: that one is ArgonDB containing its own decode failure, this one is your extractor keeping what it could not use. Both obey the same rule, which is that the system is never allowed to be quietly wrong. And because a transaction touching several tables becomes one lake commit, the good rows and the record of the rejected ones land as a single cut — the lake never shows you a batch without the evidence of what it left behind.

A branch is where a batch gets staged. Branching the Postgres side is metadata: instant, and near-free until it diverges. So the safe shape for a load that you are not sure about is to run it on a branch, check it there with ordinary SQL — counts against the source, distributions that should not have moved, the ten worst rows — and only then run it for real. A batch that turns out to be wrong is deleted along with the branch, at no cost and with the real database never having seen it, which is a considerably better answer than discovering the problem afterwards and writing a correction script under pressure.

The pipeline's health lives in the same system as its output. Row counts, rejects, the position each table has reached, the lake watermark, how far behind the text index is — these are tables and tool calls in the database that holds the data, not a separate monitoring stack with its own login and its own version of the truth. "Did last night's load go well" is a query. The diagnostic surface answers the same question to an agent, which is what makes an unattended load supervisable by something other than a person reading logs at breakfast.

The changelog is the re-ingest work queue. After you fix an extractor the question is scope: what has to be done again. The usual answers are both bad — reprocess everything, which is expensive and rewrites rows nobody asked you to touch, or trust a modified-at column that some writer forgot to set. Because every change is recorded with its exact commit position, "everything that moved since this coordinate" is an exact answer rather than an estimate, and re-driving a downstream step means replaying that list. It is the same mechanism a search index or a cache uses to stay current; a fixed extractor is just another consumer of it.

Where this shows up. Four ordinary nights and mornings:

The boundary is deliberate, and it cuts both ways. ArgonDB has no transformation language, no mapping UI and no library of source connectors, and does not want them: your extraction rules are business logic, and business logic is worth more living in your repository, in your language, under your tests, than expressed in somebody else's configuration format. What a database can do is make that code safe to run while everyone is asleep — give its output a typed home, refuse what ought to be refused, keep what could not be used, and stay answerable about all of it. The load itself is also the textbook case for the bulk class described below: heavy, important, and with no claim whatsoever on the front door the application is using.

Bring your own lake

Everything above works with zero lake setup: the box bundles a local object store and catalog, so the lake is just files on your disk. But the lake tier is pluggable by design — point ArgonDB at your existing object store (S3 or Azure Blob) and your own Iceberg REST catalog instead, and its tables land in the infrastructure you already run, beside the tables you already have, readable by every engine already connected to that catalog. Nothing about ArgonDB's behavior changes with the destination: same commits, same maintenance, same guarantees — your storage, your catalog, your data.

The catalog half and the object-store half are independently substitutable — bring your own catalog and keep the bundled storage, or the reverse — and the handover has a rule worth knowing, because getting it wrong quietly is the classic way this goes bad. Name none of a half's settings and you get the bundled one. Name any of them and all of them become required, and a missing one refuses at startup, naming the setting, the endpoint it was needed for, and the fix. What ArgonDB will never do is fall back to development credentials against an endpoint you supplied — that is how data lands somewhere nobody intended, with no error to notice.

Any engine reads it

The lake tables are stock Iceberg. DuckDB — a completely foreign engine, no ArgonDB code — answers real questions against the same tables ArgonDB is writing, straight through the standard REST catalog. This is the open-format promise from the lakehouse section made testable: your data's exit door is always open, even from ArgonDB itself.

The sharper version of the same test runs one count three ways at once — on Postgres (the source), on ArgonDB's own lake, and on DuckDB — all pinned to one identical coordinate, an exact LSN. Three independent engines, one frozen instant, one number apiece, all three printed, so agreement is something you check rather than something we assert.

One promise about those tables matters more the longer you keep them: the names ArgonDB writes into your lake are frozen forever. The __changes suffix on a changelog table, the _argon_ columns inside it, the Argon. properties stamped on every snapshot, the argon: markers in table comments — all of these are treated as specification constants rather than branding. Foreign engines read them, and so will queries you write years from now, so they are never renamed. Postgres never renamed its pg_ catalogs for the same reason. If the product name changes, that is our problem, not yours.

Behind the scenes: the chores you never schedule

A lakehouse silts up without maintenance: small files accumulate (every commit adds some), statistics go stale, old snapshots pile up. The usual answer is scheduled Spark maintenance jobs — one more system to run, and one more thing to forget. ArgonDB runs its own lake hygiene on a fixed cadence, coordinated with ingestion, because it is the lake's single writer and already knows, from the WAL, exactly what changed. No external ANALYZE, no Spark jobs, ever. Here is what that sweep actually does for you, in plain terms.

Keeping the files tidy (compaction). Imagine filing receipts by tossing each day's handful into a shoebox. Nothing is ever lost, but after a month, finding anything means going through every box. That is what continuous ingestion does to a lake: every commit adds a few small files, and updates add "cross-out notes" (delete files) on top. On its sweep, ArgonDB rewrites the accumulated small files into a few large, well-organized ones and folds the cross-out notes in while it is there — so readers stop paying for the mess, and the merge-on-read debt is paid down continuously instead of piling up for a weekend job.

Squeezing the files (compression). An analytical file is written once and read thousands of times, so every choice about how it is written gets made in the reader's favour. Compression is the clearest example. The lake writes Zstandard-compressed Parquet, and the setting is turned up hard rather than left at a middling default. What that costs: several times more CPU to compress, which on our bench corpus adds one to two percent to the time a compaction rewrite takes. What it buys: files about 22% smaller — and reads that get faster, not slower. A cold single-row lookup on that corpus went from 20.8 ms to 13.4 ms, because the reader's real bill is bytes fetched from storage, and there are fewer of them to fetch than there is extra decoding to do. That is the trade this product takes on purpose whenever the two sides pull apart: the write side pays, the read side gains. Nothing you already wrote is stranded by it — old files stay readable exactly as they are, and re-encode only if compaction was going to rewrite them anyway.

Keeping the shelves sorted (your primary key). A library where books are shelved in arrival order makes you walk every aisle to find one author. Sorted shelves, one aisle. If your published table has a primary key, ArgonDB keeps its lake files sorted by it whenever compaction rewrites them anyway — you do not ask for this, it is the default. Why it matters: every lake file records each column's smallest and largest value (and so does every block inside a file), so a lookup can skip whole files whose range cannot contain the key. Sorted, those ranges are tight and nearly everything is skipped; unsorted, every file spans everything and nothing can be skipped. On our half-million-row bench corpus this took a cold single-row lookup from ~16 seconds to a quarter of a second — and because the sort rides a rewrite that is already paying full write cost, it is essentially free. Your table's natural axis isn't the key? Point the sort somewhere else with argon:cluster-by=<column> in the table's comment — an explicit marker always wins — or opt out entirely with argon:cluster-by=none.

Taking out the trash (garbage collection). Postgres calls this vacuum. A lakehouse needs its own version, for a different reason: the lake never scribbles over old data — every change writes new files and keeps the old ones, like a photo album that gets a fresh page instead of a paste-over. That is precisely what makes time travel and pins work. But old pages cannot pile up forever, so the sweep does two recycling chores. Snapshot expiry retires table versions older than the retention window — the same window pins and time travel honor, so nothing anyone can still reference is ever collected. Orphan cleanup removes files on disk that no surviving snapshot names anymore (compaction's rewrites leave these behind by design). And because deleting the wrong file is the one mistake a lakehouse cannot undo, the janitor works with a checklist: a grace window so a file another writer is still finishing is never touched, an independent second derivation of what is actually safe to delete, and a bounded number of deletions per pass — slow and right beats fast and sorry.

A card catalog that stays current (statistics). Query engines plan with summaries — row counts, distinct values, ranges — and stale summaries make them pick bad plans, like navigating with last year's map. Because every change flows through ArgonDB, the sweep keeps exact statistics as it goes. There is no ANALYZE to remember and no sampling guesswork.

The search index, too. The full-text index described earlier is maintained by this same sweep, from the same change stream — it is a tenant of the maintenance plane, not a second system.

And a promise about all of it: maintenance is not optional, not scheduled, not configured — it is simply always on, in every deployment, every class of it. It runs on a fixed cadence that is a constant in the code — there is no switch to forget, no job to own, no cadence to tune, and no way to run an ArgonDB whose lake silts up because someone missed a setup step. That is an instance of a general rule, and the rule gets its own section just below.

Why there are so few knobs

You may have noticed how little of this page is configuration. That is a deliberate rule, and it is worth stating because it is the opposite of how infrastructure usually ships: ArgonDB's behaviour is on, for every customer, permanently. A behavioural setting is allowed to exist only when two real customers need opposite values — one genuine use case per position — because every option is also a new way to be misconfigured, a state that then has to be detected, documented, tested in both positions, and supported in the field by people who cannot see your machine.

The rule has a sharp corner, and the corner is the point of it. A switch that exists as insurance against our own defects — "turn the risky subsystem off if it misbehaves" — is prohibited outright. It does not bring back what was lost, it does not help you understand what happened or even establish whether that subsystem was at fault, and it advertises that the vendor does not trust its own product. The effort goes into making the operation correct, and where the operation is genuinely dangerous, into making it foolproof: recoverable grace windows before anything is destroyed, an independent second derivation of what is safe to delete, and a refusal when the blast radius looks wrong. Never into making it skippable.

None of this is about ports, paths, credentials, sizes or retention windows. Those are configuration, they are yours, and they stay. What is absent by design is the behavioural switch.

Three planes, so a bad day stays in one of them

The work ArgonDB does divides into three jobs with completely different temperaments, and the shape of the system is that each one is its own process rather than a module inside a shared one:

On a laptop, or on any single machine, all three run side by side under one supervisor; in the Kubernetes shape they come apart into separate workloads. The split itself does not change — that is the point of separating them by process rather than by deployment.

Why separate processes, in the only terms that matter to you: a role having a bad day has that day alone. You already read one of these higher up the page. Our statistics pass hit an internal buffer limit and crash-looped; the blast radius was exactly "the chores fall behind." Ingestion kept ingesting, reads kept answering, and the transactional machine never noticed. Had statistics been a thread inside the writer, the same bug would have been a lake outage.

Two consequences follow from the same shape:

Where two of the planes do meet is the lake catalog: the writer and the maintenance worker both change tables there. That collision is arbitrated the way the lake arbitrates everything — one commit wins, the loser sees that the table moved under it and redoes its work against the new version. The cost of a collision is a chore taking a little longer. It is never a damaged table, because a lake commit is all-or-nothing.

The same three-way split reappears at the front door, along a completely different axis — not who does the work, but what a caller is permitted to do. That one is the three agent surfaces, and it gets its own section below.

Telling the database which work matters more

The split above is by role — the writer, the chores, the readers — and what it protects you from is the system's own housekeeping. It does nothing about the other kind of contention, the kind where all the work is yours. The order a customer is waiting on and the overnight backfill of three years of history come through the same front door, look like the same kind of work, and by default the database has no idea that anybody cares more about one of them than the other.

Picture an apartment building with a bank of elevators. Most days nobody thinks about them at all. Then somebody moves in, and a crew with a truckload of furniture can hold every car for an hour — nothing is broken, nothing is even slow in a way you could point at, and yet everybody who lives there is standing around waiting. Well-run buildings do not ban moving in. They keep one car for the residents and give the crew the rest. The move still finishes; it just never gets to take the whole building.

ArgonDB manages its resources the same way, by class, assigned per database rather than per query or per session — because a class is a statement about what a body of work is for, and that is a property of the application, not of whoever happened to open the connection. Work in the protected class has a guaranteed share nobody can eat into: connection slots held back for it, and storage reads that go to the front of the queue. Postgres already does a miniature version of the first half, holding back a few connections so an administrator can always get in to fix things; this is that idea generalized to "this database always has slots nobody else can take." Work in the bulk class gets whatever is left and yields whenever protected traffic shows up. Yields, not stops — a bulk load that never finishes is its own kind of outage, so the guarantee cuts in both directions: protected work is never starved, and bulk work always makes progress.

Priority on reads is the half a conventional database cannot offer you, and the reason traces straight back to Component 2. Postgres hands its reads to the operating system and the filesystem, so there is no seam at which it could say that this page request matters more than that one, and no vocabulary in which to say it. ArgonDB does not own a disk; it owns a storage service, and every page it reads is a discrete GetPage@LSN request to the pageserver. A request can therefore carry the class of the database that asked for it and be scheduled on the way in. "The checkout path's reads never queue behind a hundred-million-row scan" stops being a tuning hope and becomes a property of the storage layer. Pulling storage out of the database was supposed to buy elasticity and cheap branching — this is that same separation paying out somewhere nobody was looking.

ArgonDB's own bookkeeping rides protected by default, in every deployment, with nothing to configure: the bundled catalog, and the internal tables that record what exists, where it lives, and what was skipped and why. That is deliberate, and it is the same instinct as the gaps-and-lower-bounds section above. The moment you most need to ask the system what is going on is the moment it is busiest, and a system whose answer to "what is happening right now" is the first thing to fall over under load is a system that goes dark exactly when someone is looking at it.

Where this shows up. All ordinary operational days, none of them exotic:

This is one of the few places the product asks you to declare something, and it earns that by the test set out just above: two customers genuinely need opposite answers here, because only you know which of your databases is the one that must not wait. What it deliberately is not is a priority number stapled to a query — those are famously easy to set and impossible to reason about across a whole system, which is why this is a small declared set of classes instead of a dial. And it is not a way to fit twice the work onto one machine. A class decides who waits when there is not enough to go around, which is a different question from how much there is.

Your existing queries, quietly answered by the fast side

Every application talks to Postgres, which is superb at fetching one record and slow at questions that sweep a whole table. ArgonDB already maintains the places where those sweeping questions are fast — the columnar lake, and read-only Postgres computes that never touch the primary. So the obvious move: when a session that has opted in sends one of those queries to the ordinary Postgres connection it already uses, ArgonDB hands the work to the read plane and returns the answer as if Postgres had done it. Same connection string, same driver, same results, and the transactional machine does none of the work.

The safety properties are the interesting part. Your own writes stay visible, because the far side is required to serve at or beyond your session's last commit. Anything that goes wrong — the far side slow, unreachable, or unsure — falls back to running the query locally, with the cost logged and a circuit breaker to stop a failure loop from burning resources unattended. And it is off until a session or a role opts in, so nothing about your existing traffic changes on an upgrade.

What sends a query across is isolation — the deliberate choice to move a heavy query off the transactional machine, which is a property of the query's role rather than a guess about its speed. The other reason a query might ever travel — "this one would simply be faster over there" — answers to the discipline set out in the MCP section below, and it is a strict one: no query shape is trusted with a faster path until both engines have been shown to answer it identically. Speed on its own is never a reason to risk a different answer.

The MCP server: the database's other front door

Every agent read carries a pin, a watermark, its budget and any gap — the protocol is MCP, the difference is the proof. Everything above is one half of ArgonDB. The other half is the claim that the natural client of a database is increasingly not a human with a SQL prompt but an AI agent working on a human's behalf — and that agents deserve a first-class interface, not a wrapper.

What MCP is

MCP — the Model Context Protocol — is an open standard for connecting AI models to tools and data. A server (like ArgonDB) exposes tools; each tool is a name, a natural-language description, and a typed parameter schema. An agent connects, reads the tool list, and decides — from the descriptions — which tool to call, with what arguments, to accomplish its task. Any MCP-speaking agent (Claude, and the growing ecosystem of MCP clients) can use any MCP server without custom integration. Think of it as USB for AI tooling: one connector, everything interoperates.

The subtle consequence: for an agent, the tool descriptions ARE the interface. A human reads your docs once; an agent re-reads the descriptions on every task and routes its behavior by them. That makes description text an engineering artifact — something to version, test, and measure. We do exactly that: on our research bench, an agent's tool selections are scored against a known-best oracle, and editing nothing but the descriptions measurably closed most of the agent's gap to the oracle. Tool quality decides how good the answers can be; tool descriptions decide whether the agent reaches for the right tool at all. A database vendor in the agent era is a tool provider, and both halves are product surface.

Why raw SQL is the wrong interface for an agent

You can point an agent at a plain SQL connection today — one side of every comparison below does exactly that, through a well-regarded standard Postgres MCP wrapper. Watch those transcripts and the structural problems repeat:

None of these is the model's fault. They're missing database primitives — and ArgonDB happens to have exactly the right internals to supply them: an LSN clock, a changelog, and an isolated read tier.

The contract: budgets, pins, and change digests

AI agentany MCP clientreads the tool descriptions,chooses, calls argondb-mcpanalyst surface — read-only by construction orientschema_overview · describe · profile · sample answer exactlyquery (SQL, budgeted, pinned) timepin · repin · changes · list_branches text & structuresearch · read · outline · expand lake + delta ringconsistent reads at any LSN full-text indexBM25, structural scope stored statisticsprofile answers — primary untouched call + budget_tokens answer + LSN stamp + what was trimmed the loop: pin → orient → search → query at the pin → repin + changes(since) when you return

Why a pin is not a held transaction

A pin is a signed coordinate, as Component 3 described. This section is about the road not taken, because the obvious implementation is a trap, and the trap is instructive.

The obvious way to freeze a view is to hold a transaction open. Start a repeatable-read transaction, keep the connection, and every read inside it sees the same instant: real consistency, no new machinery. But a held transaction is a held resource, and it has the cost from Component 1 — while it lives, the database cannot reclaim row versions it might still need, so cleanup stalls behind the oldest open snapshot. The bill is proportional to how long your client thinks.

An AI agent is the worst imaginable holder of such a thing. Its pauses are not a program's microseconds; they are a model's reasoning time. An agent that pins a view, wanders off to call a model, and comes back four minutes later has been holding the cleanup horizon of a production database the entire time — and it will do that on every conversation, concurrently, forever.

So the signed-value design is not cleverness for its own sake; it is the only version of this feature that survives its intended user. ArgonDB keeps the held-transaction implementation in exactly one narrow place: the lake-switched-off shape — a laptop running ArgonDB against a plain Postgres, with no storage engine behind it (the shape the macOS release ships). You can read our opinion of it off our own defaults — that fallback genuinely caps at eight concurrent pins, because each one holds a live transaction open against your database, while the real path caps nothing at all: it merely keeps sixty-four sessions warm, and a pin is valid regardless. Nothing about the feature changes between those two models. Only what a pin physically is.

One limit, stated plainly: if a deployment runs without a stable pin key configured, the server generates a random one at startup and warns. Pins then stop working across a restart. That is an availability regression, never a correctness one — a token that cannot be verified is refused, not trusted.

One SQL surface, and a faster engine behind it

A design question worth answering out loud, because the tempting alternative is everywhere: when an agent sends SQL, which engine runs it? ArgonDB's answer is that there is exactly one SQL surface and it is PostgreSQL — the dialect, the functions, the operators, the catalog, all of it. Offering a second, subtly different dialect for the columnar side would push that difference onto the caller, and an agent is the worst imaginable auditor of a subtle dialect difference: it will not notice, and it will reason confidently over the result.

The columnar engine is still there. It is demoted from a surface to an accelerator: queries whose shape is on a verified list — counts and eligible aggregates over lake tables — run columnar and transparently, and a shape only joins that list once it passes a differential test, the same query down both paths with canonically identical results. Anything unproven takes the Postgres path. You get the speed where it has been earned, and never a different answer for having got it. The accelerator is reached through the agent tools, and the envelope belongs to the accelerator rather than to any one door, so the standard is the same wherever it answers: one list of proven shapes, one standard of proof, and no faster answer that is allowed to be a different one.

That list has a shape, and it is better to say what it is than to let you discover it. Questions that summarize accelerate; questions that hand you back raw rows are answered by Postgres — a boundary drawn on purpose, not an unfinished edge. Counts, sums, averages, grouped rollups — the queries that read a lot and return a little — are what the columnar side is for and what it is proven on. Ask instead for a page of individual rows and the request goes to Postgres, which was always excellent at exactly that.

The reason is the promise, not the effort. The accelerator's entire contract is that you cannot tell it ran, and the way that is established is brutally simple: run the query down both engines and compare the answers, character for character, before the shape is ever allowed on the list. Aggregates make that easy to hold — two engines summing the same numbers agree. Raw values are where engines quietly differ without being wrong: the same date, the same number, printed in a slightly different style. A wide envelope nobody has proven is a promise that your query was fast and correct where only the first half was ever checked, and you would learn about the second half from an answer that looked fine. That is the worst possible failure for a database an agent is reasoning over, so the envelope stays narrow, stays proven, and grows one shape at a time as each one earns its place. Where we publish performance numbers, we publish the boundary with them.

The fifteen tools

An agent works through the fifteen tools of ArgonDB's analyst surface — thirteen in four families, plus two compatibility aliases:

Two compatibility aliases (execute_sql, explain_query) mirror the naming of the standard Postgres MCP servers, so an agent bred on those finds familiar handles.

Three surfaces, because trust is not one thing

Those fifteen tools are one surface, and the full product has three. This is the answer to a question every operator eventually has to ask — "what is this agent allowed to do to my database?" — and the usual answer is bad: hand it every tool and curate a list of the ones it must not call, which is a promise enforced by hope.

ArgonDB splits the tools across three separate mounts, each with its own credential and its own exposure default, so the question is answered at connect time by which mounts you plugged in:

The line between the first two and the third is the line between understanding the data and operating the database, and it shows up identically in the tool list, the credentials, and where each surface is deployed. Composing an agent's capabilities becomes a deployment decision you can reason about once, instead of an audit you have to repeat every time the tool list changes.

That last part is worth making concrete, because it is where this section meets the three planes from ArgonDB Features. In the production shape the two splits line up.

split one — by ROLE: who does the work split two — by PERMISSION: what a caller may do the transactional primarywhere your writes commit — the precious thing the writerreads the log, decodes it, lands atomic lakecommits — exactly one per database, because asingle ordered log is a single-file job the maintenance workercompaction, statistics, the text index —bursty, heavy, and the one role that can affordto be late: a bad day here costs a cadence the read planeas many identical replicas as you want — theyhold nothing that must be kept in step, and noconnection to the primary at all the diagnostic surfacetells the truth about the system, changes nothing the admin surfaceevery mutation lives here and nowhere else —loopback, dry-run by default, an explicitconfirmation on anything destructive the analyst surfaceread-only by construction: the mutating toolsare ABSENT, not forbidden — no prompt injectionturns a tool it does not have into a write no surface at all nothing calls the chores; they call themselves sits lives served beside with by One line — understanding the data versus operating the database — drawn three times over: in the tool list, in the credentials, and in the machines.

On a single box the three are three mounts on one process, and the tool-list and credential halves of the line are exactly as real.

Running ArgonDB yourself

Three shapes, one system, and no orchestration invented by us. Deployable ArgonDB is a single native binary: argondb serve starts Postgres, the storage layer, the lake plane with a bundled catalog and object store, and the agent endpoint — laptop to single server, with the lake sitting on your disk as ordinary files, and the option to point it at your own storage instead. docker compose is the reference environment: the full anatomy — three safekeepers, an object store, a catalog — in one command, and it is where every acceptance gate runs. Kubernetes is the production shape — the reference architecture under validation — and what it is for is the separation: the three planes stop being three processes under one supervisor and become three workloads — a writer, a maintenance worker, and a horizontally scaled pool of read replicas — that schedule, fail and grow independently of one another. The packaging is an ordinary Helm chart with a preset per cloud, not a control plane of our own invention. The replica pool is the part that matters most, and it is the payoff of a design decision made much earlier on this page: a pin is a value rather than a session, so replicas can be added or removed without telling anything else, and any replica holding the key can honour a pin any other one issued. The box and compose stay all-in-one.

The agent endpoint is present in every one of them — that is a deliberate rule rather than a packaging accident, because an interface that only exists in the expensive tier is not really the product's API. It even works with the lake switched off: turn the lake tier off on a laptop and the same tools answer straight out of Postgres in read-only transactions, with the lake-only ones explaining what enabling the lake would give you. That is the configuration to run beside a local model.

Using it as an analytics database only

A question that arrives as soon as people see the two halves: what if you only want the analytical one? No application to serve, no transactional traffic — just batches of data arriving, and questions asked of them afterwards. That works, and the shape it takes is worth spelling out, because one part of it surprises people: the data still goes in through Postgres. Reading is where the two halves come apart; writing never does.

Why the analytical half cannot be written to — and why that is a feature. One write path in, two synchronized truths out is a constraint as much as a slogan. The lake tables are ArgonDB's to write, and the analyst tools an agent is handed are incapable of harm by construction: read-only architecturally, with the mutating tools absent rather than merely forbidden. Anything that wrote the lake directly would be a second writer racing the committer, and the watermark — the promise that "this Iceberg snapshot equals Postgres as of LSN W" — would quietly stop being true. One write path is what makes one source of truth possible: the Postgres tables are the truth, the lake is derived from them, and every answer discloses which instant it was derived at. A single door in is not a missing feature; it is the reason the two shapes can be guaranteed to agree.

Foreign engines are worth being exact about here, because the tables are open and nothing in the Iceberg format physically stops a writer. Writing your own tables into the same catalog, beside ArgonDB's, is expected and fine — it is your catalog and your storage. Writing into a table ArgonDB manages is out of contract: our committer is that table's single writer, and a foreign commit would succeed with no error at the moment it happened while breaking the machinery that depends on ArgonDB having seen every change — the watermark, the atomic multi-table cuts, and time travel over the changelog. Read our tables with anything; write your own beside them.

What an analytics-only deployment looks like. Load through Postgres, serve from the lake, and the loading door turns out to be the good part:

What it costs, and where that points. In this shape the transactional half stays resident around the clock, including through the long quiet stretches between batches — a Postgres compute and a tail waiting for a write that arrives once an hour, or once a night. The architecture points past that, and it points there for a reason you have already met: a compute is stateless, and the tail's position in the log is durable, so neither of them is holding anything that would be lost by standing down while the log is quiet and coming back on the next write. Identity lives in the storage, never in a running process — and idle economics is simply the next thing that property buys.

Standing a write plane down would be a deliberate trade rather than a free win, though, which is why it belongs in a mode someone turns on knowingly rather than in a default. Both sides of that trade are worth understanding before anyone reaches for it:

The whole range: a laptop, a server, a cluster

"Running it yourself" above listed the three shapes you would install. This section is about the range they cover — how far down and how far up the same system goes, what stays identical across that distance, and what grows on its own when load arrives. It gets its own section because the usual answer to "we outgrew it" is a migration project, and the design described here exists so that there is not one.

The small end: one binary, analytics off

The smallest useful ArgonDB is a single native binary with the analytical half switched off. argondb serve starts Postgres, the storage layer underneath it, and the agent endpoint — no container runtime, no object store, no lake files filling up an SSD. The agent tools answer straight out of Postgres inside read-only transactions, and the few that need the lake say what turning it on would give you rather than failing. This is the configuration to run beside a model on the same machine.

That is a claim about a genuinely small machine rather than a marketing floor. A 13-inch MacBook Air with 8 GB of memory runs the whole of it: the box comes up, the agent surface answers against Postgres, consistency pins hold against concurrent writes, read-only enforcement holds, and the whole process group shuts down cleanly again.

Several databases on one machine is a supported arrangement rather than a trick: every listener the box opens has its own port override, so independent boxes run side by side, each with its own data directory, none of them aware of the others.

One server, everything on

Same binary, same command, one setting. Turn the lake tier on and the box additionally supervises the ingest writer, the maintenance worker, a bundled Iceberg catalog, and a small object store that keeps the warehouse as plain files under the data directory. Nothing extra to download, and no cloud account to open in order to have a lakehouse — which is what makes the "any engine reads it" promise above true on a machine sitting under your desk.

If you already own the storage, you keep it. The lake is configured as two independent halves — a catalog and an object store — and either may be yours while the other stays bundled; ArgonDB writes into yours exactly as it writes into its own. The rule around that is deliberately strict: naming any setting of a half declares that you are supplying that half, and a missing one is refused at startup, naming the variable and the remedy, rather than being quietly defaulted to something we invented.

One number governs how large the box gets. It takes a memory budget and divides it across the Postgres compute and the storage caches itself instead of asking you to tune several interacting figures, and below its floor it refuses to start rather than overspend the number it was given.

The evaluation shape, and the production shape

The compose stack is where the anatomy comes apart onto separate containers — three WAL servers rather than one, an object store as its own service, the catalog as its own service — while still fitting on one machine and, through docker compose, one command. Every acceptance gate in this project runs against it, which is both the reason to evaluate on it and the reason we call it a development and evaluation tier rather than something to run a business on. A single-host production user is better served by the box, which has fewer moving parts; a multi-host one by Kubernetes.

Kubernetes (the reference architecture under validation) is where the three planes described earlier stop being three processes under one supervisor and become three workloads that schedule, fail and grow independently. It is the shape for a database whose analytical and agent load is the half that grows, because that is the half that comes apart onto its own machines — and nothing else about the system changes when it does.

Any of the big clouds runs the same chart, with a preset per cloud, and the differences between them are small and boring: a storage class, an object-store endpoint, and local idioms for ingress and certificates. That smallness is the design showing through. ArgonDB asks a cloud for two things — object storage and somewhere to run containers — and speaks to the first through the two interfaces the industry actually settled on, S3 and Azure Blob, with the Iceberg REST catalog on top. A cloud is a set of endpoints, not a platform to be ported to, and the chart can bring its own object store and catalog in-cluster rather than requiring the managed ones at all.

Why this is one system and not five

The anatomy never changes; only its placement does.

one binary argondb serve — one process, one machine docker compose one machine, a container each Kubernetes separate machines that fail and grow apart writer compute writer compute writer compute WAL service WAL service ×3 WAL service ×3 page service page service page service ingest writer ingest writer ingest writer maintenance worker maintenance worker maintenance worker catalog + object store catalog + object store catalog + object store agent endpoint agent endpoint agent endpoint ×N — the one part that grows on its own The same parts, built from the same source tree, landing either as supervised child processes under one binary or as workloads on separate machines. Switching the analytical half off removes the middle three; the agent endpoint is in every shape.

A laptop is not running a cut-down edition and a cluster is not running a different product, which is why the three-planes section earlier could describe the split once and have it be true everywhere.

Moving up a tier is not a migration. A database's identity lives in the storage its WAL and page layers are written to, and in the lake and its catalog — not in any running process. Growing a deployment therefore means standing the larger topology up pointed at the same storage root and the same catalog, and letting stateless computes attach. Starting from a box, the one preparatory step is to point its storage at object storage instead of local disk, which is the same step you would take to get continuous offsite backup. No dump, no restore, no change feed to re-bootstrap, and the lake with its whole history is already portable files. The reverse direction works the same way: a cluster's database can come back down onto one machine.

"Can I run the laptop version on Kubernetes?" Yes — and the two paragraphs above are the reason, because the question is really "are these the same parts against the same storage contract," and they are. Nothing in the chart assumes a large cloud or a large cluster: a single-node Kubernetes on hardware you own is a first-class target, and the point of the whole range is that the small end is not a different product wearing the same name.

What grows on its own, and what does not

"Auto-scaling" is three different mechanisms wearing one word, so here are the three, separately.

The read plane is the part built to scale out. Replicas hold nothing that has to be kept in step: a consistency pin is a signed value rather than a session, so any replica can honour any pin issued by any other, and adding or removing one requires telling nothing else. In the Kubernetes shape that becomes a pool behind a horizontal autoscaler keyed on CPU, with a floor and a ceiling you set and cloud presets that raise the ceiling. The property underneath is the part worth carrying away: coordination-free scaling was not bolted onto the read tier, it fell out of a decision made much earlier — that the consistency token would be a value rather than a handle.

On a single machine the elasticity is vertical, and one piece of it is automatic. The read-only Postgres computes that serve full-fidelity reads are a pool that sizes itself to the hardware: given plenty of memory they are kept ready, and on a small machine they start cold, spawn on the first read that needs them, and stop themselves again after an idle period. Your explicit choice overrides that heuristic in either direction. It is the mechanism by which the same software is reasonable on a laptop and on a large server without a different build.

What does not scale by itself, mostly on purpose. There is exactly one ingest writer per database — a single ordered log is a single-file job — so the write side grows by being given a bigger machine, not more of them. Transactional read replicas are a different question from analytical ones: they follow the primary's log, so their scaling axis is replication lag rather than query load, which is precisely the coupling the lake side exists to escape. The storage layer has a horizontal axis of its own — the page service can be spread across machines, which is how one very large database stops being one very large machine — and the write side keeps its single-log constraint through all of it. And the direction all of this points is the one the analytics-only section above approaches from the other end: computes that stand down to nothing while a database is idle, and many mostly-idle databases packed onto shared hardware, which is the economics that becomes available once nothing that matters lives in a running process.

The shape of all three answers is the same as the rest of the page. The elastic load — agents and analytics — is the load that scales out, and it scales out because it was separated from the transactional machine by construction rather than by tuning. Where scaling still asks something of you, what it asks is small, and the thing it never asks is that you move your data.

What the one-system approach replaces

Everything above builds the lake as a shape of the database's own log: the same write that commits to Postgres becomes an Iceberg table, with no step in between. The ordinary way to put operational data into a lake is the opposite — a change-data-capture pipeline that reads one system and writes another, assembled from separate products and kept in step by a team. It is worth being concrete about what that assembly costs, because that cost is the argument for collapsing it.

The scenario priced below: a 100M-row operational database — high-performance OLTP, complex application functionality — that also runs substantial, continuous analytics against the same data. Every alternative is priced as a full stack against that scenario: an OLTP database sized to carry it, plus the lakehouse tier that gives it continuous analytics. All AWS-managed; on-premises deployment is out of scope (it would need priced hardware, which none of these sources publish for this comparison).

Three common ways to build a CDC-driven lake next to that database are priced below, each as of August 2026, against the same job: replicate change out of an operational Postgres into Iceberg tables on object storage and keep them fresh, at the analytics volume the scenario implies. Every dollar figure names its pricing basis and its date; every range is labelled an estimate with its arithmetic shown; and none of it is invented — where a number could not be sourced, that is said rather than filled in. People figures assume a New York City market (levels.fyi and Glassdoor filtered to New York, NY) and a loaded-cost multiplier of ~1.3x base salary. One caveat from earlier shapes the whole comparison: ArgonDB is not a CDC tool for a Postgres you already run. The choice it changes is the one you make while building a system — assemble a CDC lake around a database, or run a database that already is one.

The OLTP side is the same for every managed-Postgres alternative, so it is priced once here: Amazon RDS for PostgreSQL, Multi-AZ DB cluster (one writer plus two readable standbys, so the "replicas" line item is real, not aspirational), db.r6g.4xlarge (16 vCPU / 128 GB), with io2 Provisioned IOPS storage sized for a high-performance, complex-functionality 100M-row workload — 500 GB and 10,000 provisioned IOPS (est., no published sizing guide covers "complex functionality" at this row count, so this is a working assumption, stated so it can be challenged). Compute: 3 × $2.08/hour × 730 hours ≈ $4,555/month (three instances, no cluster discount — each readable standby bills as a full separate instance). Storage and IOPS: 500 GB × $0.125/GB-month + 10,000 × $0.10/IOPS-month ≈ $1,063/month. OLTP total: ≈ $5,620/month (est.), carried identically into the Apache OSS, Databricks and Snowflake rows below. (RDS PostgreSQL on-demand instance rate and Multi-AZ DB cluster billing model: AWS's own RDS pricing page renders its tables in JavaScript and could not be read directly, so the rate is sourced from a pricing aggregator instead — the same limitation and workaround as the Databricks DBU rate below. io2 storage/IOPS rates likewise sourced from an aggregator that republishes AWS's published io2 pricing. All read 2026-08-29, us-east-1.)

People figures below use New York City market salaries (levels.fyi and Glassdoor filtered to New York, NY) and a loaded-cost multiplier of 1.3× base salary — inside the widely cited 1.25×–1.4× range from MIT Sloan lecturer Joseph Hadzima's "true cost of an employee" convention, covering payroll taxes and standard benefits, not equipment, recruiting or office space.

Approach Platform + infra /mo OLTP database /mo People (upkeep) /mo Setup (one-time)
Apache OSS on AWS (MSK, MSK Connect, Managed Flink, Glue ETL for compaction, S3, Glue Data Catalog) ~$2,195 (est.) ~$5,620 (est.) ~$55,710 (est.) ~$100,000 (est.)
Databricks ~$16,000–$22,000 (est.) ~$5,620 (est.) ~$37,730 (est.) ~$60,000–$120,000 (est.)
Snowflake ~$9,550 (est.) ~$5,620 (est.) ~$34,110 (est.) ~$110,000 (est.)
ArgonDB ~$855 (est.) included in Platform + infra (same instance) ~$6,240 (est.) ~$5,760 (est.)

The open-source Apache stack

The all-open path wires Debezium into Kafka to capture change, lands it in Apache Iceberg through a Flink or Spark sink, and registers the tables in a managed catalog — every component here is AWS-managed, not self-hosted; on-premises is out of scope for this comparison. The software itself is free — every one of Debezium, Kafka, Flink/Spark and Iceberg is Apache 2.0 — so the bill is managed infrastructure and people, not licences.

Sized for the scenario's higher throughput rather than a small always-on pipeline: a six-broker managed Kafka on kafka.m5.large is 6 × $0.21 per broker-hour × 730 hours ≈ $920/month; three Debezium connectors on MSK Connect units add 3 × $0.11/hour × 730 ≈ $241/month for higher-throughput CDC fan-out; an eight-KPU Amazon Managed Service for Apache Flink job (sized for continuous transform load, not the one-worker minimum) is 8 × $0.11/KPU-hour × 730 ≈ $642/month; Iceberg files sit in S3 Standard at $0.023/GB-month, assumed ~5 TB for this scenario (est.) ≈ $118/month; a periodic AWS Glue ETL job handles the Spark compaction and snapshot-expiry maintenance this stack needs and today's version of this comparison omitted — 10 DPU × 2 hours/day × 30 days × $0.44/DPU-hour ≈ $264/month; and the catalog is AWS Glue Data Catalog rather than a self-hosted Nessie or Polaris host — the first 1M objects and 1M requests per month are free, and this scenario's table count and query volume land only modestly past that, ≈ $10/month (est.). Platform + infra total: ≈ $2,195/month (est.) (MSK, MSK Connect, Managed Flink and S3 rates: AWS on-demand, us-east-1, read 2026-08-23; Glue ETL and Glue Data Catalog rates: AWS on-demand, us-east-1, read 2026-08-29, fetched directly from aws.amazon.com/glue/pricing, which renders as static text). A managed Kafka from Confluent Cloud instead of MSK bills by elastic unit — a Standard cluster starts near $385/month, with capacity at $0.75 per eCKU-hour, storage at $0.08/GB-month and networking at $0.035–$0.050/GB (confluent.io/pricing, read 2026-08-23); analyst breakdowns put a typical production Standard cluster higher, around $1,000–$3,000/month.

The people are the real cost, and the roles are specific: a Kafka/streaming engineer to own the brokers, connectors and topic design (NYC senior-engineer-with-Kafka-skills base $152K–$180K, Payscale, read 2026-08-29 — midpoint $166K used below); a Spark or Flink engineer for the streaming transforms and the Iceberg sink (NYC data engineer median $170K, levels.fyi, read 2026-08-29); and a data-platform SRE to keep six moving systems alive (NYC SRE median $178,250, levels.fyi via a secondary aggregator, read 2026-08-29). Three full-time engineers, one per role — the representative point in the stack's 2–4 FTE range — loaded at 1.3×: ($166K + $170K + $178,250) × 1.3 ÷ 12 ≈ $55,710/month (est.). No vendor sells a packaged professional-services price for assembling five independent open-source projects, so setup here is internal/contracted engineering hours at the loaded rate above rather than a vendor quote: three engineers × 8 weeks × 40 hours × a ≈$107/hour blended loaded rate ≈ $100,000 (est.), standing up connectors, a Kafka cluster, an Iceberg sink, a catalog and the streaming jobs. The upkeep never ends — Kafka operations, connector schema-evolution handling, Iceberg table maintenance such as compaction and snapshot expiry, and version compatibility across all five components — which is what the People and Platform + infra lines above carry every month, on top of the one-time build.

Databricks

Databricks bills in DBUs — a normalized unit of compute, charged per second, pay-as-you-go. Continuous CDC uses Lakeflow Connect to ingest from Postgres and Lakeflow Declarative Pipelines (formerly Delta Live Tables) to transform, and the transform work needs at least the Pro tier. List rates on AWS, Premium tier, are Jobs Compute at $0.15/DBU and Declarative Pipelines at $0.20/DBU Core, $0.25/DBU Pro and $0.36/DBU Advanced; ingestion is billed in DBUs rather than per record, with the first 100 DBUs per workspace per day free. (These $/DBU figures are the published list prices as republished by Flexera's Databricks pricing guide, dated 2026-08-23; Databricks' own pricing page renders its tables in JavaScript and could not be read directly — a limitation confirmed here rather than worked around.)

At this scenario's scale — a 100M-row source feeding substantial continuous analytics, not a single small pipeline — the right comparison is the broader mid-size, multi-workload deployment a third-party breakdown put at $16,000–$22,000/month (est.) (DBU software fee plus classic-compute EC2, which routinely runs 50–70% of total Databricks spend, plus storage; dawiso.com, read 2026-08-23). CDC features additionally require the Premium tier plus Unity Catalog and serverless, and committed-use discounts apply to DBUs, not to the cloud infrastructure underneath them.

For people, a Databricks build at this scale needs a Databricks/Spark data engineer for the pipelines and schema evolution (NYC data engineer median $170K, levels.fyi, read 2026-08-29) and a platform engineer for Unity Catalog governance, cluster and cost management (no NYC-specific salary source exists for that exact title; the NYC SRE median of $178,250 above is used as a comparable platform-engineering role). Two full-time engineers, one per role, loaded at 1.3×: ($170K + $178,250) × 1.3 ÷ 12 ≈ $37,730/month (est.). Greenfield Databricks builds at this scale are routinely delivered through Databricks Professional Services or a partner SI rather than built from scratch: a mid-size migration or integration engagement runs $60,000–$120,000 (est.) as a one-time design/build cost, ahead of the FTE run-rate above (Multishoring Databricks professional-services cost guide, read 2026-08-29 — the same source notes $200–$350/hour as the underlying consulting rate for smaller fixed-scope work). The continuous work after that is pipeline tuning, serverless and cluster cost management, and handling schema drift from the source.

Snowflake

Snowflake bills in credits: a virtual warehouse burns credits per second while it runs and nothing while it is suspended. On AWS US East, on-demand credit prices are $2.00 for Standard, $3.00 for Enterprise and $4.00 for Business Critical, and storage is $23.00 per TB per month. CDC ingest is cheap in this model — Snowpipe and Snowpipe Streaming cost 0.0037 credits per GB, serverless Tasks cost 0.9 credits per compute-hour, and Openflow, the NiFi-based CDC connector, costs 0.0225 credits per vCPU-hour in the bring-your-own-cloud form (plus your own EC2). (All Snowflake rates from the Snowflake Service Consumption Table, effective 2026-08-18, AWS US East on-demand.)

The cost is the transform warehouse, and at this scenario's scale — 100M rows plus substantial, continuous analytics, not a batch job a few hours a day — the right size is Medium, run around the clock rather than suspended most of the day: Enterprise edition, Medium warehouse (4 credits/hour) × 730 hours × $3.00/credit ≈ $8,760/month. Ingest scales with it: Snowpipe Streaming at ~500 GB of change per day (est., scaled from the prior 200 GB/day estimate for this larger workload) ≈ $167/month; an Openflow connector sized to 8 vCPU continuous ≈ $394/month; storage at ~10 TB (est.) ≈ $230/month. Platform + infra total: ≈ $9,550/month (est.) (credit and storage rates sourced as above, read 2026-08-29; the monthly figures are estimates from that arithmetic). Credit runaway still lives in the warehouse line: the same Medium warehouse with disciplined auto-suspend during genuinely idle windows would cost less, so warehouse sizing and auto-suspend discipline remain the recurring cost-control job even at this scale.

The roles are a Snowflake data engineer to own ingest and the incremental models with Streams and Tasks or Dynamic Tables (NYC average $141,914, ZipRecruiter, read 2026-08-29), an analytics engineer for the transform models (NYC average $159,105, Glassdoor, read 2026-08-29), and a fractional cost and governance owner watching credit burn (using the NYC SRE median $178,250 above as the comparable rate). At roughly 2.0 FTE total (0.75, 0.75 and 0.5) — the representative point in this stack's 1.5–2 FTE range — loaded at 1.3×: ($141,914 × 0.75 + $159,105 × 0.75 + $178,250 × 0.5) × 1.3 ÷ 12 ≈ $34,110/month (est.). Snowflake does not publish a professional-services price list any more than Databricks does; no partner case study surfaced a dollar figure for a greenfield build either, so the one-time setup below uses the same consulting rate basis cited for Databricks ($200–$350/hour, midpoint ~$275/hour) applied to an estimated two engineers × 5 weeks × 40 hours (400 hours) to stand up Openflow or Snowpipe from Postgres and then Streams and Tasks or Dynamic Tables for the transforms: 400 × $275 ≈ $110,000 (est.). The continuous work after that is warehouse sizing, credit control and schema evolution.

What ArgonDB does instead

Every line item in the three sections above exists to move data between systems and to keep the copy in step. ArgonDB has none of them, because there is no copy and no second system: the lake is written by the storage layer that already wrote the database, from the same log, at the same commit boundaries. There is no CDC product to licence, no streaming cluster to run, no second compute engine to keep fed, no separate catalog to host, and no metered ingest for freshness — freshness is a property of the write path, not a job that bills. The software is self-hostable from a laptop upward; its licence and price are not yet set, so this row counts infrastructure and people only.

Priced candidly rather than at $0: the box is a cloud instance sized for this scenario, r6i.4xlarge (16 vCPU / 128 GB — a similar class to the 20-core / 91 GB unit ArgonDB is measured on), on-demand at $1.008/hour × 730 hours ≈ $736/month (instances.vantage.sh, read 2026-08-29; AWS's own EC2 on-demand pricing page renders its table in JavaScript and could not be read directly, the same limitation as the RDS and Databricks rates above — a 1-year reserved instance at $0.667/hour ≈ $487/month is available as the same lever every other row could also pull and none of them price in). Object storage for the lake, same S3 Standard rate and ~5 TB assumption as the Apache OSS row ≈ $118/month. Platform + infra total: ≈ $855/month (est.). There is no separate OLTP line — this is the same instance, not a second one — which is the structural saving this whole section exists to price.

People are not zero either: a DBA/operator fraction to run the database — no pipeline roles, because there is no pipeline — at 0.5 FTE, NYC general DBA average $115,271 (Glassdoor, New York City, NY, read 2026-08-29, noting SQL-specialist DBA titles in the same NYC dataset run $132K–$153K), loaded at 1.3×: $115,271 × 0.5 × 1.3 ÷ 12 ≈ $6,240/month (est.). A support-tier price would belong here if one existed; a grep of this repo's docs turned up none, so the accurate statement is that support pricing is not yet published, not a number. Setup is install-plus-load hours at the DBA's loaded rate, not a design/build engagement: roughly 80 hours (two weeks) × $115,271 × 1.3 ÷ 2,080 ≈ $5,760 (est.). No total is shown for any row. This row prices infrastructure and half a DBA; the rows above include engineering roles and 24×7 platform services; and ArgonDB's own engineering, on-call, support and vendor risk are not yet priced. Totals return when the rows compare equivalent service levels.

What that trades away, plainly. The three alternatives buy real things ArgonDB does not. Databricks is a full machine-learning and data-science platform with a large surrounding ecosystem; Snowflake offers enormous elastic scale-out SQL, a data marketplace and cross-account sharing; the open-source stack gives maximum flexibility, no vendor lock-in, and the ability to capture change from any source rather than one you host. And the caveat that opened this section still holds: if the requirement is to get an existing external Postgres into a lake, a CDC tool is the right answer and ArgonDB is not one. The saving described here is available only when ArgonDB is the operational database — when the lake can be a shape of its own log instead of a copy of someone else's.

Sources and pricing basis

Releases, updates, and the knowledge that travels with them

The plain version first: a release is a set of files you keep. You take it, you pin the version, and it runs on your own machines with the network unplugged. There is no account to keep current, no licence server to phone, and nothing in it that stops working because something of ours stopped working. Everything below is a consequence of that one sentence.

A release is three payloads rather than one, and separating them is the whole trick, because people usually mean only the first:

Everything is pushed by default. All three arrive as versioned releases you pin and apply on your own clock — never applied for you, never fetched behind your back, and complete on their own. An agent working against ArgonDB inside a building with no outbound network has the database, the working system, and the whole body of knowledge sitting on local disk. That is the floor, and it is deliberately a high one, because the two things it protects are the two easiest to lose by accident: nothing strands you, and nothing of yours leaves.

Pulling is an enhancement you switch on. A release-notes feed that says a new version exists and what changed in it. Knowledge updates between releases. Live questions to us, for whatever the shipped knowledge does not cover. Each is useful and each is optional, and the rule they all obey is that switching them off costs you freshness and never function. A product built on ArgonDB that breaks when our endpoint is down would make exit costs nothing untrue, so a hard dependency on us is not something we are willing to sell.

The expert runs on your hardware. The interesting half of the knowledge payload is not that it is a folder of documents but that it is served: the same agent endpoint described earlier answers questions about ArgonDB itself, seeded from the shipped knowledge and re-seeded whenever you take a newer version. So the agent building your application can ask how something works, and both the question and the code it is asking about stay on your side of the wall. We update the expert by shipping it, not by being called.

And a release carries its own proof. The checks that decide whether ArgonDB works travel inside it and are meant to be run there, on your machines, against your version — so "it works" is something you re-prove locally rather than something we assert. That is the discipline described below in "How we test what we build", pointed outward.

How an update reaches you

The plain version: we publish, you take. Nothing of ours reaches into your machines. Your side fetches a new version when it decides to, tries it against your own project, and keeps it only if that passes.

Both ends of that are run by agents, which is what makes it routine rather than a chore. Ours prepares and publishes the release. Yours receives it, tests it, merges it, and books the restart. People set the policy — how eager to be, and what needs a hand on the wheel. The agents do the moving.

our side — one commit produces every name attached to it, so they cannot drift apart build itfrom one commit check itthe gates run version itone name, every artifact write the noteswhat changed, exactly publish itno button to press your side, on your own clock — the update waits its turn notice ita check on your schedule fetch itnothing changed yet rebuild it as yoursyour own settings test ityour checks, your data merge when greenconflicts, never silent the restart window — booked, with one owner for its duration stop taking workdrain first stop itand confirm it stopped put the new one inthe version you merged start it run the checksagainst what is running we publish; you take — nothing of ours reaches into your machines merging files is not restarting a database — two events, so the risky one gets its own schedule Green ends the window; red puts the old version back — the only two ways out, and one service has one owner throughout. Each release states what it does to your data: unchanged, forward-migrating, or breaking — an unrecognized value counts as breaking.

Our side, when a change is ready. No step waits on someone to press a button, and one commit produces every name attached to it — the version tag, the file, the container image, the version your project pins — so those cannot drift apart and quietly mean different builds.

Your side, on your own clock. Your project keeps ours as a source it can fetch from, and a check on its own schedule notices when a new version appears and writes it down as work to do. Your coordinator picks that work up the way it picks up anything else: while the project is busy with its own priorities, the update waits its turn. Then four steps, all on your hardware:

Merging files is not restarting a database. Those are deliberately two events, so the risky one gets its own schedule — and the same window cannot be started twice.

Each release states what it does to your data, in a form an agent reads rather than a paragraph it has to interpret:

A value the receiving agent does not recognize counts as breaking. An unreadable contract is never read as permission.

How eager to be is a setting. Follow every change as it lands, or follow named versions only. The project we run ourselves on ArgonDB takes every change; a setup that would rather batch takes versions. It is one line of configuration either way, and it is the only knob this whole path needs.

Building on it: local, CI, fleet

"Running it yourself" and "The whole range" above were about where the database runs. This is a different three: how the people building on it work. A team gets a way of working along with the database, and it comes in three setups.

Pick the one that matches you, and each adds to the one below it.

Each setup is a strict superset of the one below it. Turning on CI does not replace the local floor, it adds to it; turning on fleet does not replace CI. There is one core to version and one body of doctrine, which is the point — two editions would be two things to keep in step, and the second one always drifts.

A setup describes one developer's environment, not the project's. That is the part worth carrying away, because it is what makes the ordinary case work. Two developers can share a big machine through a coordinator while a third works from a laptop on a plane, and all three are on the same project: same repository, same record of work in flight, same pinned ArgonDB version. "The project is on fleet" is not a sentence anyone says. What everybody shares is shared regardless of setup; what differs is where each person's work physically executes.

One laptop is the smallest fleet rather than a cut-down one. The laptop developer is a full participant that happens to run its work on itself, described by exactly the same entry as a shared build server — what it can be asked to do, how it is reached, how much it can do at once. A project's fleet is whatever its list of machines says it is: two big servers, or none, or three laptops. Nothing about the shape is assumed, which is the same property the database half of this page keeps claiming, applied to the people.

How we test what we build

The plain version: nobody's word counts for anything. Not ours, not a reviewer's, and not the word of whichever agent wrote the code. A claim is something you measure, and until it has been measured it is a guess in a confident voice. Almost everything below is an operational consequence of taking that seriously — which we have to, because this system is substantially AI-written, and the response to that is not reassurance, it is measurement.

A test's job is to refute, not to confirm. It is easy to write the test that shows the thing working, and that test tells you almost nothing. The useful one is the test that would embarrass us: the hostile input, the combination nobody would type on purpose, the ugly case sitting exactly on a boundary. When a rule says "strictly older", something checks that the equal case is not caught. When a feature has parts that can each be empty, every combination of empty gets tried rather than the two anyone thought of.

A pass counts only once the test has proven it can fail. A green light with nothing behind it is the most expensive result in software, because it buys confidence and delivers none. So a check is shown a case it must reject before its acceptance means anything, and a check nobody can make fail is treated as broken rather than as good news.

We test the way a stranger would use it, not the way we do. Our own usage is one narrow path — well-formed data, one writer, a forgiving consumer — and the bugs that surprised us have all lived outside it. So tests are written from the specification and from what a foreign caller might do, and anything we write into an open format is read back by somebody else's engine, because a round trip through your own reader hides precisely the bugs an open format exists to prevent.

Whoever did the work does not grade it. Work arrives as a set of claims, and those claims are re-run independently, on the combined result, before any of it lands. This is not suspicion; it is the only arrangement in which handing work out at scale is safe at all. It applies to a machine being described as idle exactly as much as to a test result: the way to know is to go and look.

Anything redone becomes a permanent rule. When something has to be fixed twice, the interesting question is not what broke but how the mistake got through — and the answer becomes a written rule the next piece of work has to obey. It is why the rules read oddly specific: each one is a scar. Waiting for something now always carries both a deadline and a check that whatever is being waited on is still alive, because once it wasn't, and the wait ran out the whole clock.

All of it is the same shape as the referee described earlier on this page, where a real Postgres grades ArgonDB's answers. That referee is one instance of the general rule rather than a special case: correctness against an oracle sharing none of our code, whole-system acceptance runs, the cloud shapes brought up and torn down, performance watched for regressions, security surfaces probed by something playing the attacker, and the working system itself held to the standard it holds the database to. None of that makes a system correct. What it does is make being wrong something you find out about.

Where this is going

Every direction this project takes is the same direction, which is the most useful thing to know about it. One write path in, two synchronized truths out is a constraint before it is a slogan: anything that comes later has to fit through it, and that is what keeps the two shapes of your data from ever needing to be reconciled. What grows is the amount of work the database does because it already read the log — the change feed, the history table, the search index, the statistics, the file hygiene, and the next several things a data team currently assembles by hand around a database that cannot see its own past.

The lake half deepens along the open formats rather than away from them. The Iceberg specification keeps growing — richer row lineage, better deletes — and ArgonDB follows it, because the entire point of writing your data into open tables is that the tables outlive our opinions and our company. That direction has a guarantee attached rather than a promise: nothing you write is stranded by it, because newer readers open older tables, and the names ArgonDB writes into your lake never change.

The range converges too. A laptop, a server and a cluster already run the same parts against the same storage contract; what is still a human decision at the large end — a replica floor and ceiling, a memory budget, a retention window — is exactly the class of decision the system is being pointed at making for itself, because every one of them is a measurement it is already taking. The destination is plain: one write plane, one consistency clock, no size at which you change products, and no moment at which anyone has to move the data.

And the agent half is the least settled idea in the industry rather than in this product. The claim that the natural client of a database is increasingly a program reasoning on somebody's behalf has consequences still being drawn out — answers that carry their own provenance, budgets the caller declares instead of discovering, a coordinate that makes a whole conversation consistent, an envelope of query shapes that widens only by proof. Those are database primitives, not chat features, and building them out is what the phrase speaks agent in the title is for.