Your customers’ metrics,
live from your Postgres.

eddy is a hosted analytics database for SaaS products. Write each metric once in SQL, and every commit to your Postgres updates every customer’s numbers. Your app reads them from what looks like one more Postgres database, and agents read them over MCP.

Nothing installed in your database but one publication and one replication slot; the role needs REPLICATION and SELECT. We onboard every team by hand. How pricing works.

Walk through it ↓

eddyacme-saaslive tick 2,418

Postgres · commits

account · Northwind #42

MRR$12,400
seats38 / 40
churn riskwatch
eddy7 mscommit → fresh
nightly model6 huntil the next run

Illustration drawn from the product. The 7 ms is measured: a commit in Postgres to every model fresh, on a 7.2M-row snapshot.

01 · The stack

No warehouse, no rollup jobs, no cache to keep in sync.

The usual build for customer-facing metrics is a connector, a warehouse, a schedule, reverse ETL and a cache. eddy replaces all five: hosted like a warehouse, live like your database.

your Postgres CDC connector warehouse dbt on a schedule reverse ETL cache metrics API eddy hosted for you your appdashboardsagents
hours stale · a correction waits for the rebuild milliseconds · updates and deletes on the next tick

02 · Live

One write, every metric current in 15 ms.

On the demo’s dataset (a store: 60,000 orders, fifteen dbt models), one order placed means one row out, one row in, and every model current. Nothing else is recomputed. See the product up close →

rows out of fct_orders → dim_customers · tick 2 · 2 rows
wcustomer_idnameorderslast_amount
−11Ada Lovelace649.99
+11Ada Lovelace749.99

Nothing else in the table was touched. Every operator speaks in these weighted rows, which is why an update is never a rebuild.

eddy15 mscommit → all fourteen marts fresh
Postgres views6.3 sthe same fourteen reports, recomputed on read
Postgres agrees with eddy, row for row, after every transaction.
The demo’s own dataset: 60,000 orders, fifteen dbt models, timed on four cores. Postgres checks every mart after every transaction.

03 · Per customer

Every customer, their own state.

Shared tables with a tenant column in, a store per customer out: its own state, position and cost. One replication slot for all of them. For SaaS teams →

18.8 sto onboard 1,000 customers, 6.47M rows
6,000customer × metric checks against Postgres, all agree
440 KBstate for the median customer
1,000 simulated customers (a Shopify-shaped test dataset) on four cores. Every customer’s rows judged alone, before and after a kill mid-commit.

04 · Agents

Agents read it, act, and watch it move.

Every operation is an MCP tool. An agent reads a metric, asks where it came from, acts through your API, and sees the result on its next step. For agent builders →

Which accounts are at risk this week, and why?
eddyget_view account_health · risk = 'high'3 rows
eddyprovenance account_health · id = 5714 seats · 31 events
Contoso (#57) is high risk: usage fell after 14 seats were removed in one statement on Sep 22. It looks like a mistake; I’ll restore them.
your APIrestore_seats(account_id = 57)committed
eddyget_view account_health · id = 57high → watch
Done. The restored seats are already reflected in account_health.

05 · Connect

One replication slot, checked before anything is read.

eddy follows your write-ahead log with a role that has REPLICATION and SELECT on the published tables. It creates one publication and one replication slot, and nothing else in your database. Before anything is read, the connection check confirms the slot’s retained WAL is bounded and says how long eddy could be stopped before the slot is invalidated.

In: your Postgres, and product events from Kafka; files on S3 next. Out: a Postgres database your app, psql and Metabase read, an HTTP API, and MCP for agents. Sources and endpoints →

eddy console · connect a database
› connect app@db.internal:5432/app
checking what eddy needs before it reads anything
  pass  server_version         pgoutput, and a slot's WAL can be bounded
  pass  wal_level              logical
  pass  max_replication_slots  10, 1 in use by others, 9 free
  pass  replication_role       a member of rds_replication
  pass  tables                 all 12 tables exist in public
  pass  primary_keys           all 12 have one
  pass  publication            eddy does not exist; eddy will create it
  pass  slot                   no slot eddy yet; onboarding creates it
  pass  slot_wal_bound         max_slot_wal_keep_size = 10.0 GB: eddy can be
                               stopped for 31.4 h before its slot is invalidated
9 passed, 0 warnings, 0 failed · onboarding from one snapshot

06 · Checked

Checked against Postgres after every transaction.

In our test harness and the demo, Postgres or DuckDB recomputes every metric from scratch after every transaction and compares it row for row. These are the numbers that survive it.

0disagreements with the judge across every published run
7 msfrom a commit in Postgres to every model fresh, on a 7.2M-row inventory snapshot
45%of the test changes were updates and deletes, the kind append-only engines refuse

Also kept live with corrections included: running balances, sessions, rolling windows, funnels and cohorts, the latest row per key, top-N per group, changing dimensions and hierarchy rollups.

Measured on the demo dataset and eddy’s own harness. eddy is built by Matt Helm at Hollyburn Analytics Inc.

Get early access.

Tell us a metric you would put in front of your customers or your agents if it were live. We’ll show it staying current on your own schema.

  • A reply from a founder within two business days
  • A walkthrough on your schema, no slides
  • Early access is hosted by us and onboarded with you, by hand
  • No mailing list